3 OpenLayers.js -- OpenLayers Map Viewer Library
5 Copyright (c) 2006-2013 by OpenLayers Contributors
6 Published under the 2-clause BSD license.
7 See http://openlayers.org/dev/license.txt for the full text of the license, and http://openlayers.org/dev/authors.txt for full list of contributors.
9 Includes compressed code under the following licenses:
11 (For uncompressed versions of the code used, please see the
12 OpenLayers Github repository: <https://github.com/openlayers/openlayers>)
17 * Contains XMLHttpRequest.js <http://code.google.com/p/xmlhttprequest/>
18 * Copyright 2007 Sergey Ilinsky (http://www.ilinsky.com)
20 * Licensed under the Apache License, Version 2.0 (the "License");
21 * you may not use this file except in compliance with the License.
22 * You may obtain a copy of the License at
23 * http://www.apache.org/licenses/LICENSE-2.0
27 * OpenLayers.Util.pagePosition is based on Yahoo's getXY method, which is
28 * Copyright (c) 2006, Yahoo! Inc.
29 * All rights reserved.
31 * Redistribution and use of this software in source and binary forms, with or
32 * without modification, are permitted provided that the following conditions
35 * * Redistributions of source code must retain the above copyright notice,
36 * this list of conditions and the following disclaimer.
38 * * Redistributions in binary form must reproduce the above copyright notice,
39 * this list of conditions and the following disclaimer in the documentation
40 * and/or other materials provided with the distribution.
42 * * Neither the name of Yahoo! Inc. nor the names of its contributors may be
43 * used to endorse or promote products derived from this software without
44 * specific prior written permission of Yahoo! Inc.
46 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
47 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
50 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
51 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
52 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
53 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
54 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
55 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
56 * POSSIBILITY OF SUCH DAMAGE.
58 /* ======================================================================
59 OpenLayers/SingleFile.js
60 ====================================================================== */
62 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
63 * full list of contributors). Published under the 2-clause BSD license.
64 * See license.txt in the OpenLayers distribution or repository for the
65 * full text of the license. */
69 * Constant: VERSION_NUMBER
71 VERSION_NUMBER: "Release 2.13.1",
74 * Constant: singleFile
75 * TODO: remove this in 3.0 when we stop supporting build profiles that
76 * include OpenLayers.js
81 * Method: _getScriptLocation
82 * Return the path to this script. This is also implemented in
86 * {String} Path to this script
88 _getScriptLocation: (function() {
89 var r = new RegExp("(^|(.*?\\/))(OpenLayers[^\\/]*?\\.js)(\\?|$)"),
90 s = document.getElementsByTagName('script'),
92 for(var i=0, len=s.length; i<len; i++) {
93 src = s[i].getAttribute('src');
102 return (function() { return l; });
107 * {String} Set this to the path where control images are stored, a path
108 * given here must end with a slash. If set to '' (which is the default)
109 * OpenLayers will use its script location + "img/".
111 * You will need to set this property when you have a singlefile build of
112 * OpenLayers that either is not named "OpenLayers.js" or if you move
113 * the file in a way such that the image directory cannot be derived from
114 * the script location.
116 * If your custom OpenLayers build is named "my-custom-ol.js" and the images
117 * of OpenLayers are in a folder "/resources/external/images/ol" a correct
118 * way of including OpenLayers in your HTML would be:
121 * <script src="/path/to/my-custom-ol.js" type="text/javascript"></script>
122 * <script type="text/javascript">
123 * // tell OpenLayers where the control images are
124 * // remember the trailing slash
125 * OpenLayers.ImgPath = "/resources/external/images/ol/";
129 * Please remember that when your OpenLayers script is not named
130 * "OpenLayers.js" you will have to make sure that the default theme is
131 * loaded into the page by including an appropriate <link>-tag,
135 * <link rel="stylesheet" href="/path/to/default/style.css" type="text/css">
140 /* ======================================================================
141 OpenLayers/BaseTypes.js
142 ====================================================================== */
144 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
145 * full list of contributors). Published under the 2-clause BSD license.
146 * See license.txt in the OpenLayers distribution or repository for the
147 * full text of the license. */
150 * @requires OpenLayers/SingleFile.js
154 * Header: OpenLayers Base Types
155 * OpenLayers custom string, number and function functions are described here.
159 * Namespace: OpenLayers.String
160 * Contains convenience functions for string manipulation.
162 OpenLayers.String = {
165 * APIFunction: startsWith
166 * Test whether a string starts with another string.
169 * str - {String} The string to test.
170 * sub - {String} The substring to look for.
173 * {Boolean} The first string starts with the second.
175 startsWith: function(str, sub) {
176 return (str.indexOf(sub) == 0);
180 * APIFunction: contains
181 * Test whether a string contains another string.
184 * str - {String} The string to test.
185 * sub - {String} The substring to look for.
188 * {Boolean} The first string contains the second.
190 contains: function(str, sub) {
191 return (str.indexOf(sub) != -1);
196 * Removes leading and trailing whitespace characters from a string.
199 * str - {String} The (potentially) space padded string. This string is not
203 * {String} A trimmed version of the string with all leading and
204 * trailing spaces removed.
206 trim: function(str) {
207 return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
211 * APIFunction: camelize
212 * Camel-case a hyphenated string.
213 * Ex. "chicken-head" becomes "chickenHead", and
214 * "-chicken-head" becomes "ChickenHead".
217 * str - {String} The string to be camelized. The original is not modified.
220 * {String} The string, camelized
222 camelize: function(str) {
223 var oStringList = str.split('-');
224 var camelizedString = oStringList[0];
225 for (var i=1, len=oStringList.length; i<len; i++) {
226 var s = oStringList[i];
227 camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
229 return camelizedString;
233 * APIFunction: format
234 * Given a string with tokens in the form ${token}, return a string
235 * with tokens replaced with properties from the given context
236 * object. Represent a literal "${" by doubling it, e.g. "${${".
239 * template - {String} A string with tokens to be replaced. A template
240 * has the form "literal ${token}" where the token will be replaced
241 * by the value of context["token"].
242 * context - {Object} An optional object with properties corresponding
243 * to the tokens in the format string. If no context is sent, the
244 * window object will be used.
245 * args - {Array} Optional arguments to pass to any functions found in
246 * the context. If a context property is a function, the token
247 * will be replaced by the return from the function called with
251 * {String} A string with tokens replaced from the context object.
253 format: function(template, context, args) {
261 var replacer = function(str, match) {
264 // Loop through all subs. Example: ${a.b.c}
265 // 0 -> replacement = context[a];
266 // 1 -> replacement = context[a][b];
267 // 2 -> replacement = context[a][b][c];
268 var subs = match.split(/\.+/);
269 for (var i=0; i< subs.length; i++) {
271 replacement = context;
273 if (replacement === undefined) {
276 replacement = replacement[subs[i]];
279 if(typeof replacement == "function") {
281 replacement.apply(null, args) :
285 // If replacement is undefined, return the string 'undefined'.
286 // This is a workaround for a bugs in browsers not properly
287 // dealing with non-participating groups in regular expressions:
288 // http://blog.stevenlevithan.com/archives/npcg-javascript
289 if (typeof replacement == 'undefined') {
296 return template.replace(OpenLayers.String.tokenRegEx, replacer);
300 * Property: tokenRegEx
301 * Used to find tokens in a string.
302 * Examples: ${a}, ${a.b.c}, ${a-b}, ${5}
304 tokenRegEx: /\$\{([\w.]+?)\}/g,
307 * Property: numberRegEx
308 * Used to test strings as numbers.
310 numberRegEx: /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/,
313 * APIFunction: isNumeric
314 * Determine whether a string contains only a numeric value.
318 * OpenLayers.String.isNumeric("6.02e23") // true
319 * OpenLayers.String.isNumeric("12 dozen") // false
320 * OpenLayers.String.isNumeric("4") // true
321 * OpenLayers.String.isNumeric(" 4 ") // false
325 * {Boolean} String contains only a number.
327 isNumeric: function(value) {
328 return OpenLayers.String.numberRegEx.test(value);
332 * APIFunction: numericIf
333 * Converts a string that appears to be a numeric value into a number.
337 * trimWhitespace - {Boolean}
340 * {Number|String} a Number if the passed value is a number, a String
343 numericIf: function(value, trimWhitespace) {
344 var originalValue = value;
345 if (trimWhitespace === true && value != null && value.replace) {
346 value = value.replace(/^\s*|\s*$/g, "");
348 return OpenLayers.String.isNumeric(value) ? parseFloat(value) : originalValue;
354 * Namespace: OpenLayers.Number
355 * Contains convenience functions for manipulating numbers.
357 OpenLayers.Number = {
360 * Property: decimalSeparator
361 * Decimal separator to use when formatting numbers.
363 decimalSeparator: ".",
366 * Property: thousandsSeparator
367 * Thousands separator to use when formatting numbers.
369 thousandsSeparator: ",",
372 * APIFunction: limitSigDigs
373 * Limit the number of significant digits on a float.
380 * {Float} The number, rounded to the specified number of significant
383 limitSigDigs: function(num, sig) {
386 fig = parseFloat(num.toPrecision(sig));
392 * APIFunction: format
393 * Formats a number for output.
397 * dec - {Integer} Number of decimal places to round to.
398 * Defaults to 0. Set to null to leave decimal places unchanged.
399 * tsep - {String} Thousands separator.
401 * dsep - {String} Decimal separator.
405 * {String} A string representing the formatted number.
407 format: function(num, dec, tsep, dsep) {
408 dec = (typeof dec != "undefined") ? dec : 0;
409 tsep = (typeof tsep != "undefined") ? tsep :
410 OpenLayers.Number.thousandsSeparator;
411 dsep = (typeof dsep != "undefined") ? dsep :
412 OpenLayers.Number.decimalSeparator;
415 num = parseFloat(num.toFixed(dec));
418 var parts = num.toString().split(".");
419 if (parts.length == 1 && dec == null) {
420 // integer where we do not want to touch the decimals
424 var integer = parts[0];
426 var thousands = /(-?[0-9]+)([0-9]{3})/;
427 while(thousands.test(integer)) {
428 integer = integer.replace(thousands, "$1" + tsep + "$2");
436 var rem = parts.length > 1 ? parts[1] : "0";
438 rem = rem + new Array(dec - rem.length + 1).join("0");
440 str = integer + dsep + rem;
447 * Create a zero padded string optionally with a radix for casting numbers.
450 * num - {Number} The number to be zero padded.
451 * len - {Number} The length of the string to be returned.
452 * radix - {Number} An integer between 2 and 36 specifying the base to use
453 * for representing numeric values.
455 zeroPad: function(num, len, radix) {
456 var str = num.toString(radix || 10);
457 while (str.length < len) {
465 * Namespace: OpenLayers.Function
466 * Contains convenience functions for function manipulation.
468 OpenLayers.Function = {
471 * Bind a function to an object. Method to easily create closures with
475 * func - {Function} Input function.
476 * object - {Object} The object to bind to the input function (as this).
479 * {Function} A closure with 'this' set to the passed in object.
481 bind: function(func, object) {
482 // create a reference to all arguments past the second one
483 var args = Array.prototype.slice.apply(arguments, [2]);
485 // Push on any additional arguments from the actual function call.
486 // These will come after those sent to the bind call.
487 var newArgs = args.concat(
488 Array.prototype.slice.apply(arguments, [0])
490 return func.apply(object, newArgs);
495 * APIFunction: bindAsEventListener
496 * Bind a function to an object, and configure it to receive the event
497 * object as first parameter when called.
500 * func - {Function} Input function to serve as an event listener.
501 * object - {Object} A reference to this.
506 bindAsEventListener: function(func, object) {
507 return function(event) {
508 return func.call(object, event || window.event);
514 * A simple function to that just does "return false". We use this to
515 * avoid attaching anonymous functions to DOM event handlers, which
516 * causes "issues" on IE<8.
519 * document.onclick = OpenLayers.Function.False;
530 * A simple function to that just does "return true". We use this to
531 * avoid attaching anonymous functions to DOM event handlers, which
532 * causes "issues" on IE<8.
535 * document.onclick = OpenLayers.Function.True;
546 * A reusable function that returns ``undefined``.
556 * Namespace: OpenLayers.Array
557 * Contains convenience functions for array manipulation.
563 * Filter an array. Provides the functionality of the
564 * Array.prototype.filter extension to the ECMA-262 standard. Where
565 * available, Array.prototype.filter will be used.
567 * Based on well known example from http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/filter
570 * array - {Array} The array to be filtered. This array is not mutated.
571 * Elements added to this array by the callback will not be visited.
572 * callback - {Function} A function that is called for each element in
573 * the array. If this function returns true, the element will be
574 * included in the return. The function will be called with three
575 * arguments: the element in the array, the index of that element, and
576 * the array itself. If the optional caller parameter is specified
577 * the callback will be called with this set to caller.
578 * caller - {Object} Optional object to be set as this when the callback
582 * {Array} An array of elements from the passed in array for which the
583 * callback returns true.
585 filter: function(array, callback, caller) {
587 if (Array.prototype.filter) {
588 selected = array.filter(callback, caller);
590 var len = array.length;
591 if (typeof callback != "function") {
592 throw new TypeError();
594 for(var i=0; i<len; i++) {
597 if (callback.call(caller, val, i, array)) {
607 /* ======================================================================
608 OpenLayers/BaseTypes/Class.js
609 ====================================================================== */
611 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
612 * full list of contributors). Published under the 2-clause BSD license.
613 * See license.txt in the OpenLayers distribution or repository for the
614 * full text of the license. */
617 * @requires OpenLayers/SingleFile.js
621 * Constructor: OpenLayers.Class
622 * Base class used to construct all other classes. Includes support for
623 * multiple inheritance.
625 * This constructor is new in OpenLayers 2.5. At OpenLayers 3.0, the old
626 * syntax for creating classes and dealing with inheritance
629 * To create a new OpenLayers-style class, use the following syntax:
631 * var MyClass = OpenLayers.Class(prototype);
634 * To create a new OpenLayers-style class with multiple inheritance, use the
637 * var MyClass = OpenLayers.Class(Class1, Class2, prototype);
640 * Note that instanceof reflection will only reveal Class1 as superclass.
643 OpenLayers.Class = function() {
644 var len = arguments.length;
645 var P = arguments[0];
646 var F = arguments[len-1];
648 var C = typeof F.initialize == "function" ?
650 function(){ P.prototype.initialize.apply(this, arguments); };
653 var newArgs = [C, P].concat(
654 Array.prototype.slice.call(arguments).slice(1, len-1), F);
655 OpenLayers.inherit.apply(null, newArgs);
663 * Function: OpenLayers.inherit
666 * C - {Object} the class that inherits
667 * P - {Object} the superclass to inherit from
669 * In addition to the mandatory C and P parameters, an arbitrary number of
670 * objects can be passed, which will extend C.
672 OpenLayers.inherit = function(C, P) {
673 var F = function() {};
674 F.prototype = P.prototype;
677 for(i=2, l=arguments.length; i<l; i++) {
679 if(typeof o === "function") {
682 OpenLayers.Util.extend(C.prototype, o);
687 * APIFunction: extend
688 * Copy all properties of a source object to a destination object. Modifies
689 * the passed in destination object. Any properties on the source object
690 * that are set to undefined will not be (re)set on the destination object.
693 * destination - {Object} The object that will be modified
694 * source - {Object} The object with properties to be set on the destination
697 * {Object} The destination object.
699 OpenLayers.Util = OpenLayers.Util || {};
700 OpenLayers.Util.extend = function(destination, source) {
701 destination = destination || {};
703 for (var property in source) {
704 var value = source[property];
705 if (value !== undefined) {
706 destination[property] = value;
711 * IE doesn't include the toString property when iterating over an object's
712 * properties with the for(property in object) syntax. Explicitly check if
713 * the source has its own toString property.
717 * FF/Windows < 2.0.0.13 reports "Illegal operation on WrappedNative
718 * prototype object" when calling hawOwnProperty if the source object
719 * is an instance of window.Event.
722 var sourceIsEvt = typeof window.Event == "function"
723 && source instanceof window.Event;
726 && source.hasOwnProperty && source.hasOwnProperty("toString")) {
727 destination.toString = source.toString;
732 /* ======================================================================
733 OpenLayers/BaseTypes/Bounds.js
734 ====================================================================== */
736 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
737 * full list of contributors). Published under the 2-clause BSD license.
738 * See license.txt in the OpenLayers distribution or repository for the
739 * full text of the license. */
742 * @requires OpenLayers/BaseTypes/Class.js
746 * Class: OpenLayers.Bounds
747 * Instances of this class represent bounding boxes. Data stored as left,
748 * bottom, right, top floats. All values are initialized to null, however,
749 * you should make sure you set them before using the bounds for anything.
753 * bounds = new OpenLayers.Bounds();
754 * bounds.extend(new OpenLayers.LonLat(4,5));
755 * bounds.extend(new OpenLayers.LonLat(5,6));
756 * bounds.toBBOX(); // returns 4,5,5,6
759 OpenLayers.Bounds = OpenLayers.Class({
763 * {Number} Minimum horizontal coordinate.
769 * {Number} Minimum vertical coordinate.
775 * {Number} Maximum horizontal coordinate.
781 * {Number} Maximum vertical coordinate.
786 * Property: centerLonLat
787 * {<OpenLayers.LonLat>} A cached center location. This should not be
788 * accessed directly. Use <getCenterLonLat> instead.
793 * Constructor: OpenLayers.Bounds
794 * Construct a new bounds object. Coordinates can either be passed as four
795 * arguments, or as a single argument.
797 * Parameters (four arguments):
798 * left - {Number} The left bounds of the box. Note that for width
799 * calculations, this is assumed to be less than the right value.
800 * bottom - {Number} The bottom bounds of the box. Note that for height
801 * calculations, this is assumed to be less than the top value.
802 * right - {Number} The right bounds.
803 * top - {Number} The top bounds.
805 * Parameters (single argument):
806 * bounds - {Array(Number)} [left, bottom, right, top]
808 initialize: function(left, bottom, right, top) {
809 if (OpenLayers.Util.isArray(left)) {
816 this.left = OpenLayers.Util.toFloat(left);
818 if (bottom != null) {
819 this.bottom = OpenLayers.Util.toFloat(bottom);
822 this.right = OpenLayers.Util.toFloat(right);
825 this.top = OpenLayers.Util.toFloat(top);
831 * Create a cloned instance of this bounds.
834 * {<OpenLayers.Bounds>} A fresh copy of the bounds
837 return new OpenLayers.Bounds(this.left, this.bottom,
838 this.right, this.top);
843 * Test a two bounds for equivalence.
846 * bounds - {<OpenLayers.Bounds>}
849 * {Boolean} The passed-in bounds object has the same left,
850 * right, top, bottom components as this. Note that if bounds
851 * passed in is null, returns false.
853 equals:function(bounds) {
855 if (bounds != null) {
856 equals = ((this.left == bounds.left) &&
857 (this.right == bounds.right) &&
858 (this.top == bounds.top) &&
859 (this.bottom == bounds.bottom));
865 * APIMethod: toString
866 * Returns a string representation of the bounds object.
869 * {String} String representation of bounds object.
871 toString:function() {
872 return [this.left, this.bottom, this.right, this.top].join(",");
877 * Returns an array representation of the bounds object.
879 * Returns an array of left, bottom, right, top properties, or -- when the
880 * optional parameter is true -- an array of the bottom, left, top,
884 * reverseAxisOrder - {Boolean} Should we reverse the axis order?
887 * {Array} array of left, bottom, right, top
889 toArray: function(reverseAxisOrder) {
890 if (reverseAxisOrder === true) {
891 return [this.bottom, this.left, this.top, this.right];
893 return [this.left, this.bottom, this.right, this.top];
899 * Returns a boundingbox-string representation of the bounds object.
902 * decimal - {Integer} How many significant digits in the bbox coords?
904 * reverseAxisOrder - {Boolean} Should we reverse the axis order?
907 * {String} Simple String representation of bounds object.
908 * (e.g. "5,42,10,45")
910 toBBOX:function(decimal, reverseAxisOrder) {
911 if (decimal== null) {
914 var mult = Math.pow(10, decimal);
915 var xmin = Math.round(this.left * mult) / mult;
916 var ymin = Math.round(this.bottom * mult) / mult;
917 var xmax = Math.round(this.right * mult) / mult;
918 var ymax = Math.round(this.top * mult) / mult;
919 if (reverseAxisOrder === true) {
920 return ymin + "," + xmin + "," + ymax + "," + xmax;
922 return xmin + "," + ymin + "," + xmax + "," + ymax;
927 * APIMethod: toGeometry
928 * Create a new polygon geometry based on this bounds.
931 * {<OpenLayers.Geometry.Polygon>} A new polygon with the coordinates
934 toGeometry: function() {
935 return new OpenLayers.Geometry.Polygon([
936 new OpenLayers.Geometry.LinearRing([
937 new OpenLayers.Geometry.Point(this.left, this.bottom),
938 new OpenLayers.Geometry.Point(this.right, this.bottom),
939 new OpenLayers.Geometry.Point(this.right, this.top),
940 new OpenLayers.Geometry.Point(this.left, this.top)
946 * APIMethod: getWidth
947 * Returns the width of the bounds.
950 * {Float} The width of the bounds (right minus left).
952 getWidth:function() {
953 return (this.right - this.left);
957 * APIMethod: getHeight
958 * Returns the height of the bounds.
961 * {Float} The height of the bounds (top minus bottom).
963 getHeight:function() {
964 return (this.top - this.bottom);
969 * Returns an <OpenLayers.Size> object of the bounds.
972 * {<OpenLayers.Size>} The size of the bounds.
975 return new OpenLayers.Size(this.getWidth(), this.getHeight());
979 * APIMethod: getCenterPixel
980 * Returns the <OpenLayers.Pixel> object which represents the center of the
984 * {<OpenLayers.Pixel>} The center of the bounds in pixel space.
986 getCenterPixel:function() {
987 return new OpenLayers.Pixel( (this.left + this.right) / 2,
988 (this.bottom + this.top) / 2);
992 * APIMethod: getCenterLonLat
993 * Returns the <OpenLayers.LonLat> object which represents the center of the
997 * {<OpenLayers.LonLat>} The center of the bounds in map space.
999 getCenterLonLat:function() {
1000 if(!this.centerLonLat) {
1001 this.centerLonLat = new OpenLayers.LonLat(
1002 (this.left + this.right) / 2, (this.bottom + this.top) / 2
1005 return this.centerLonLat;
1010 * Scales the bounds around a pixel or lonlat. Note that the new
1011 * bounds may return non-integer properties, even if a pixel
1016 * origin - {<OpenLayers.Pixel> or <OpenLayers.LonLat>}
1017 * Default is center.
1020 * {<OpenLayers.Bounds>} A new bounds that is scaled by ratio
1023 scale: function(ratio, origin){
1025 origin = this.getCenterLonLat();
1030 // get origin coordinates
1031 if(origin.CLASS_NAME == "OpenLayers.LonLat"){
1039 var left = (this.left - origx) * ratio + origx;
1040 var bottom = (this.bottom - origy) * ratio + origy;
1041 var right = (this.right - origx) * ratio + origx;
1042 var top = (this.top - origy) * ratio + origy;
1044 return new OpenLayers.Bounds(left, bottom, right, top);
1049 * Shifts the coordinates of the bound by the given horizontal and vertical
1053 * var bounds = new OpenLayers.Bounds(0, 0, 10, 10);
1054 * bounds.toString();
1057 * bounds.add(-1.5, 4).toString();
1058 * // => "-1.5,4,8.5,14"
1061 * This method will throw a TypeError if it is passed null as an argument.
1064 * x - {Float} horizontal delta
1065 * y - {Float} vertical delta
1068 * {<OpenLayers.Bounds>} A new bounds whose coordinates are the same as
1069 * this, but shifted by the passed-in x and y values.
1071 add:function(x, y) {
1072 if ( (x == null) || (y == null) ) {
1073 throw new TypeError('Bounds.add cannot receive null values');
1075 return new OpenLayers.Bounds(this.left + x, this.bottom + y,
1076 this.right + x, this.top + y);
1081 * Extend the bounds to include the <OpenLayers.LonLat>,
1082 * <OpenLayers.Geometry.Point> or <OpenLayers.Bounds> specified.
1084 * Please note that this function assumes that left < right and
1088 * object - {<OpenLayers.LonLat>, <OpenLayers.Geometry.Point> or
1089 * <OpenLayers.Bounds>} The object to be included in the new bounds
1092 extend:function(object) {
1094 switch(object.CLASS_NAME) {
1095 case "OpenLayers.LonLat":
1096 this.extendXY(object.lon, object.lat);
1098 case "OpenLayers.Geometry.Point":
1099 this.extendXY(object.x, object.y);
1102 case "OpenLayers.Bounds":
1103 // clear cached center location
1104 this.centerLonLat = null;
1106 if ( (this.left == null) || (object.left < this.left)) {
1107 this.left = object.left;
1109 if ( (this.bottom == null) || (object.bottom < this.bottom) ) {
1110 this.bottom = object.bottom;
1112 if ( (this.right == null) || (object.right > this.right) ) {
1113 this.right = object.right;
1115 if ( (this.top == null) || (object.top > this.top) ) {
1116 this.top = object.top;
1124 * APIMethod: extendXY
1125 * Extend the bounds to include the XY coordinate specified.
1128 * x - {number} The X part of the the coordinate.
1129 * y - {number} The Y part of the the coordinate.
1131 extendXY:function(x, y) {
1132 // clear cached center location
1133 this.centerLonLat = null;
1135 if ((this.left == null) || (x < this.left)) {
1138 if ((this.bottom == null) || (y < this.bottom)) {
1141 if ((this.right == null) || (x > this.right)) {
1144 if ((this.top == null) || (y > this.top)) {
1150 * APIMethod: containsLonLat
1151 * Returns whether the bounds object contains the given <OpenLayers.LonLat>.
1154 * ll - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an
1155 * object with a 'lon' and 'lat' properties.
1156 * options - {Object} Optional parameters
1158 * Acceptable options:
1159 * inclusive - {Boolean} Whether or not to include the border.
1161 * worldBounds - {<OpenLayers.Bounds>} If a worldBounds is provided, the
1162 * ll will be considered as contained if it exceeds the world bounds,
1163 * but can be wrapped around the dateline so it is contained by this
1167 * {Boolean} The passed-in lonlat is within this bounds.
1169 containsLonLat: function(ll, options) {
1170 if (typeof options === "boolean") {
1171 options = {inclusive: options};
1173 options = options || {};
1174 var contains = this.contains(ll.lon, ll.lat, options.inclusive),
1175 worldBounds = options.worldBounds;
1176 if (worldBounds && !contains) {
1177 var worldWidth = worldBounds.getWidth();
1178 var worldCenterX = (worldBounds.left + worldBounds.right) / 2;
1179 var worldsAway = Math.round((ll.lon - worldCenterX) / worldWidth);
1180 contains = this.containsLonLat({
1181 lon: ll.lon - worldsAway * worldWidth,
1183 }, {inclusive: options.inclusive});
1189 * APIMethod: containsPixel
1190 * Returns whether the bounds object contains the given <OpenLayers.Pixel>.
1193 * px - {<OpenLayers.Pixel>}
1194 * inclusive - {Boolean} Whether or not to include the border. Default is
1198 * {Boolean} The passed-in pixel is within this bounds.
1200 containsPixel:function(px, inclusive) {
1201 return this.contains(px.x, px.y, inclusive);
1205 * APIMethod: contains
1206 * Returns whether the bounds object contains the given x and y.
1211 * inclusive - {Boolean} Whether or not to include the border. Default is
1215 * {Boolean} Whether or not the passed-in coordinates are within this
1218 contains:function(x, y, inclusive) {
1220 if (inclusive == null) {
1224 if (x == null || y == null) {
1228 x = OpenLayers.Util.toFloat(x);
1229 y = OpenLayers.Util.toFloat(y);
1231 var contains = false;
1233 contains = ((x >= this.left) && (x <= this.right) &&
1234 (y >= this.bottom) && (y <= this.top));
1236 contains = ((x > this.left) && (x < this.right) &&
1237 (y > this.bottom) && (y < this.top));
1243 * APIMethod: intersectsBounds
1244 * Determine whether the target bounds intersects this bounds. Bounds are
1245 * considered intersecting if any of their edges intersect or if one
1246 * bounds contains the other.
1249 * bounds - {<OpenLayers.Bounds>} The target bounds.
1250 * options - {Object} Optional parameters.
1252 * Acceptable options:
1253 * inclusive - {Boolean} Treat coincident borders as intersecting. Default
1254 * is true. If false, bounds that do not overlap but only touch at the
1255 * border will not be considered as intersecting.
1256 * worldBounds - {<OpenLayers.Bounds>} If a worldBounds is provided, two
1257 * bounds will be considered as intersecting if they intersect when
1258 * shifted to within the world bounds. This applies only to bounds that
1259 * cross or are completely outside the world bounds.
1262 * {Boolean} The passed-in bounds object intersects this bounds.
1264 intersectsBounds:function(bounds, options) {
1265 if (typeof options === "boolean") {
1266 options = {inclusive: options};
1268 options = options || {};
1269 if (options.worldBounds) {
1270 var self = this.wrapDateLine(options.worldBounds);
1271 bounds = bounds.wrapDateLine(options.worldBounds);
1275 if (options.inclusive == null) {
1276 options.inclusive = true;
1278 var intersects = false;
1280 self.left == bounds.right ||
1281 self.right == bounds.left ||
1282 self.top == bounds.bottom ||
1283 self.bottom == bounds.top
1286 // if the two bounds only touch at an edge, and inclusive is false,
1287 // then the bounds don't *really* intersect.
1288 if (options.inclusive || !mightTouch) {
1289 // otherwise, if one of the boundaries even partially contains another,
1290 // inclusive of the edges, then they do intersect.
1292 ((bounds.bottom >= self.bottom) && (bounds.bottom <= self.top)) ||
1293 ((self.bottom >= bounds.bottom) && (self.bottom <= bounds.top))
1296 ((bounds.top >= self.bottom) && (bounds.top <= self.top)) ||
1297 ((self.top > bounds.bottom) && (self.top < bounds.top))
1300 ((bounds.left >= self.left) && (bounds.left <= self.right)) ||
1301 ((self.left >= bounds.left) && (self.left <= bounds.right))
1304 ((bounds.right >= self.left) && (bounds.right <= self.right)) ||
1305 ((self.right >= bounds.left) && (self.right <= bounds.right))
1307 intersects = ((inBottom || inTop) && (inLeft || inRight));
1310 if (options.worldBounds && !intersects) {
1311 var world = options.worldBounds;
1312 var width = world.getWidth();
1313 var selfCrosses = !world.containsBounds(self);
1314 var boundsCrosses = !world.containsBounds(bounds);
1315 if (selfCrosses && !boundsCrosses) {
1316 bounds = bounds.add(-width, 0);
1317 intersects = self.intersectsBounds(bounds, {inclusive: options.inclusive});
1318 } else if (boundsCrosses && !selfCrosses) {
1319 self = self.add(-width, 0);
1320 intersects = bounds.intersectsBounds(self, {inclusive: options.inclusive});
1327 * APIMethod: containsBounds
1328 * Returns whether the bounds object contains the given <OpenLayers.Bounds>.
1330 * bounds - {<OpenLayers.Bounds>} The target bounds.
1331 * partial - {Boolean} If any of the target corners is within this bounds
1332 * consider the bounds contained. Default is false. If false, the
1333 * entire target bounds must be contained within this bounds.
1334 * inclusive - {Boolean} Treat shared edges as contained. Default is
1338 * {Boolean} The passed-in bounds object is contained within this bounds.
1340 containsBounds:function(bounds, partial, inclusive) {
1341 if (partial == null) {
1344 if (inclusive == null) {
1347 var bottomLeft = this.contains(bounds.left, bounds.bottom, inclusive);
1348 var bottomRight = this.contains(bounds.right, bounds.bottom, inclusive);
1349 var topLeft = this.contains(bounds.left, bounds.top, inclusive);
1350 var topRight = this.contains(bounds.right, bounds.top, inclusive);
1352 return (partial) ? (bottomLeft || bottomRight || topLeft || topRight)
1353 : (bottomLeft && bottomRight && topLeft && topRight);
1357 * APIMethod: determineQuadrant
1358 * Returns the the quadrant ("br", "tr", "tl", "bl") in which the given
1359 * <OpenLayers.LonLat> lies.
1362 * lonlat - {<OpenLayers.LonLat>}
1365 * {String} The quadrant ("br" "tr" "tl" "bl") of the bounds in which the
1368 determineQuadrant: function(lonlat) {
1371 var center = this.getCenterLonLat();
1373 quadrant += (lonlat.lat < center.lat) ? "b" : "t";
1374 quadrant += (lonlat.lon < center.lon) ? "l" : "r";
1380 * APIMethod: transform
1381 * Transform the Bounds object from source to dest.
1384 * source - {<OpenLayers.Projection>} Source projection.
1385 * dest - {<OpenLayers.Projection>} Destination projection.
1388 * {<OpenLayers.Bounds>} Itself, for use in chaining operations.
1390 transform: function(source, dest) {
1391 // clear cached center location
1392 this.centerLonLat = null;
1393 var ll = OpenLayers.Projection.transform(
1394 {'x': this.left, 'y': this.bottom}, source, dest);
1395 var lr = OpenLayers.Projection.transform(
1396 {'x': this.right, 'y': this.bottom}, source, dest);
1397 var ul = OpenLayers.Projection.transform(
1398 {'x': this.left, 'y': this.top}, source, dest);
1399 var ur = OpenLayers.Projection.transform(
1400 {'x': this.right, 'y': this.top}, source, dest);
1401 this.left = Math.min(ll.x, ul.x);
1402 this.bottom = Math.min(ll.y, lr.y);
1403 this.right = Math.max(lr.x, ur.x);
1404 this.top = Math.max(ul.y, ur.y);
1409 * APIMethod: wrapDateLine
1410 * Wraps the bounds object around the dateline.
1413 * maxExtent - {<OpenLayers.Bounds>}
1414 * options - {Object} Some possible options are:
1417 * leftTolerance - {float} Allow for a margin of error
1418 * with the 'left' value of this
1421 * rightTolerance - {float} Allow for a margin of error
1422 * with the 'right' value of
1427 * {<OpenLayers.Bounds>} A copy of this bounds, but wrapped around the
1428 * "dateline" (as specified by the borders of
1429 * maxExtent). Note that this function only returns
1430 * a different bounds value if this bounds is
1431 * *entirely* outside of the maxExtent. If this
1432 * bounds straddles the dateline (is part in/part
1433 * out of maxExtent), the returned bounds will always
1434 * cross the left edge of the given maxExtent.
1437 wrapDateLine: function(maxExtent, options) {
1438 options = options || {};
1440 var leftTolerance = options.leftTolerance || 0;
1441 var rightTolerance = options.rightTolerance || 0;
1443 var newBounds = this.clone();
1446 var width = maxExtent.getWidth();
1449 while (newBounds.left < maxExtent.left &&
1450 newBounds.right - rightTolerance <= maxExtent.left ) {
1451 newBounds = newBounds.add(width, 0);
1455 while (newBounds.left + leftTolerance >= maxExtent.right &&
1456 newBounds.right > maxExtent.right ) {
1457 newBounds = newBounds.add(-width, 0);
1460 // crosses right only? force left
1461 var newLeft = newBounds.left + leftTolerance;
1462 if (newLeft < maxExtent.right && newLeft > maxExtent.left &&
1463 newBounds.right - rightTolerance > maxExtent.right) {
1464 newBounds = newBounds.add(-width, 0);
1471 CLASS_NAME: "OpenLayers.Bounds"
1475 * APIFunction: fromString
1476 * Alternative constructor that builds a new OpenLayers.Bounds from a
1480 * OpenLayers.Bounds.fromString("5,42,10,45");
1481 * // => equivalent to ...
1482 * new OpenLayers.Bounds(5, 42, 10, 45);
1486 * str - {String} Comma-separated bounds string. (e.g. "5,42,10,45")
1487 * reverseAxisOrder - {Boolean} Does the string use reverse axis order?
1490 * {<OpenLayers.Bounds>} New bounds object built from the
1493 OpenLayers.Bounds.fromString = function(str, reverseAxisOrder) {
1494 var bounds = str.split(",");
1495 return OpenLayers.Bounds.fromArray(bounds, reverseAxisOrder);
1499 * APIFunction: fromArray
1500 * Alternative constructor that builds a new OpenLayers.Bounds from an array.
1503 * OpenLayers.Bounds.fromArray( [5, 42, 10, 45] );
1504 * // => equivalent to ...
1505 * new OpenLayers.Bounds(5, 42, 10, 45);
1509 * bbox - {Array(Float)} Array of bounds values (e.g. [5,42,10,45])
1510 * reverseAxisOrder - {Boolean} Does the array use reverse axis order?
1513 * {<OpenLayers.Bounds>} New bounds object built from the passed-in Array.
1515 OpenLayers.Bounds.fromArray = function(bbox, reverseAxisOrder) {
1516 return reverseAxisOrder === true ?
1517 new OpenLayers.Bounds(bbox[1], bbox[0], bbox[3], bbox[2]) :
1518 new OpenLayers.Bounds(bbox[0], bbox[1], bbox[2], bbox[3]);
1522 * APIFunction: fromSize
1523 * Alternative constructor that builds a new OpenLayers.Bounds from a size.
1526 * OpenLayers.Bounds.fromSize( new OpenLayers.Size(10, 20) );
1527 * // => equivalent to ...
1528 * new OpenLayers.Bounds(0, 20, 10, 0);
1532 * size - {<OpenLayers.Size> or Object} <OpenLayers.Size> or an object with
1533 * both 'w' and 'h' properties.
1536 * {<OpenLayers.Bounds>} New bounds object built from the passed-in size.
1538 OpenLayers.Bounds.fromSize = function(size) {
1539 return new OpenLayers.Bounds(0,
1546 * Function: oppositeQuadrant
1547 * Get the opposite quadrant for a given quadrant string.
1550 * OpenLayers.Bounds.oppositeQuadrant( "tl" );
1553 * OpenLayers.Bounds.oppositeQuadrant( "tr" );
1558 * quadrant - {String} two character quadrant shortstring
1561 * {String} The opposing quadrant ("br" "tr" "tl" "bl"). For Example, if
1562 * you pass in "bl" it returns "tr", if you pass in "br" it
1563 * returns "tl", etc.
1565 OpenLayers.Bounds.oppositeQuadrant = function(quadrant) {
1568 opp += (quadrant.charAt(0) == 't') ? 'b' : 't';
1569 opp += (quadrant.charAt(1) == 'l') ? 'r' : 'l';
1573 /* ======================================================================
1574 OpenLayers/BaseTypes/Element.js
1575 ====================================================================== */
1577 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
1578 * full list of contributors). Published under the 2-clause BSD license.
1579 * See license.txt in the OpenLayers distribution or repository for the
1580 * full text of the license. */
1583 * @requires OpenLayers/Util.js
1584 * @requires OpenLayers/BaseTypes.js
1588 * Namespace: OpenLayers.Element
1590 OpenLayers.Element = {
1593 * APIFunction: visible
1596 * element - {DOMElement}
1599 * {Boolean} Is the element visible?
1601 visible: function(element) {
1602 return OpenLayers.Util.getElement(element).style.display != 'none';
1606 * APIFunction: toggle
1607 * Toggle the visibility of element(s) passed in
1610 * element - {DOMElement} Actually user can pass any number of elements
1612 toggle: function() {
1613 for (var i=0, len=arguments.length; i<len; i++) {
1614 var element = OpenLayers.Util.getElement(arguments[i]);
1615 var display = OpenLayers.Element.visible(element) ? 'none'
1617 element.style.display = display;
1622 * APIFunction: remove
1623 * Remove the specified element from the DOM.
1626 * element - {DOMElement}
1628 remove: function(element) {
1629 element = OpenLayers.Util.getElement(element);
1630 element.parentNode.removeChild(element);
1634 * APIFunction: getHeight
1637 * element - {DOMElement}
1640 * {Integer} The offset height of the element passed in
1642 getHeight: function(element) {
1643 element = OpenLayers.Util.getElement(element);
1644 return element.offsetHeight;
1648 * Function: hasClass
1649 * Tests if an element has the given CSS class name.
1652 * element - {DOMElement} A DOM element node.
1653 * name - {String} The CSS class name to search for.
1656 * {Boolean} The element has the given class name.
1658 hasClass: function(element, name) {
1659 var names = element.className;
1660 return (!!names && new RegExp("(^|\\s)" + name + "(\\s|$)").test(names));
1664 * Function: addClass
1665 * Add a CSS class name to an element. Safe where element already has
1669 * element - {DOMElement} A DOM element node.
1670 * name - {String} The CSS class name to add.
1673 * {DOMElement} The element.
1675 addClass: function(element, name) {
1676 if(!OpenLayers.Element.hasClass(element, name)) {
1677 element.className += (element.className ? " " : "") + name;
1683 * Function: removeClass
1684 * Remove a CSS class name from an element. Safe where element does not
1685 * have the class name.
1688 * element - {DOMElement} A DOM element node.
1689 * name - {String} The CSS class name to remove.
1692 * {DOMElement} The element.
1694 removeClass: function(element, name) {
1695 var names = element.className;
1697 element.className = OpenLayers.String.trim(
1699 new RegExp("(^|\\s+)" + name + "(\\s+|$)"), " "
1707 * Function: toggleClass
1708 * Remove a CSS class name from an element if it exists. Add the class name
1709 * if it doesn't exist.
1712 * element - {DOMElement} A DOM element node.
1713 * name - {String} The CSS class name to toggle.
1716 * {DOMElement} The element.
1718 toggleClass: function(element, name) {
1719 if(OpenLayers.Element.hasClass(element, name)) {
1720 OpenLayers.Element.removeClass(element, name);
1722 OpenLayers.Element.addClass(element, name);
1728 * APIFunction: getStyle
1731 * element - {DOMElement}
1737 getStyle: function(element, style) {
1738 element = OpenLayers.Util.getElement(element);
1741 if (element && element.style) {
1742 value = element.style[OpenLayers.String.camelize(style)];
1744 if (document.defaultView &&
1745 document.defaultView.getComputedStyle) {
1747 var css = document.defaultView.getComputedStyle(element, null);
1748 value = css ? css.getPropertyValue(style) : null;
1749 } else if (element.currentStyle) {
1750 value = element.currentStyle[OpenLayers.String.camelize(style)];
1754 var positions = ['left', 'top', 'right', 'bottom'];
1756 (OpenLayers.Util.indexOf(positions,style) != -1) &&
1757 (OpenLayers.Element.getStyle(element, 'position') == 'static')) {
1762 return value == 'auto' ? null : value;
1766 /* ======================================================================
1767 OpenLayers/BaseTypes/LonLat.js
1768 ====================================================================== */
1770 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
1771 * full list of contributors). Published under the 2-clause BSD license.
1772 * See license.txt in the OpenLayers distribution or repository for the
1773 * full text of the license. */
1776 * @requires OpenLayers/BaseTypes/Class.js
1780 * Class: OpenLayers.LonLat
1781 * This class represents a longitude and latitude pair
1783 OpenLayers.LonLat = OpenLayers.Class({
1787 * {Float} The x-axis coodinate in map units
1793 * {Float} The y-axis coordinate in map units
1798 * Constructor: OpenLayers.LonLat
1799 * Create a new map location. Coordinates can be passed either as two
1800 * arguments, or as a single argument.
1802 * Parameters (two arguments):
1803 * lon - {Number} The x-axis coordinate in map units. If your map is in
1804 * a geographic projection, this will be the Longitude. Otherwise,
1805 * it will be the x coordinate of the map location in your map units.
1806 * lat - {Number} The y-axis coordinate in map units. If your map is in
1807 * a geographic projection, this will be the Latitude. Otherwise,
1808 * it will be the y coordinate of the map location in your map units.
1810 * Parameters (single argument):
1811 * location - {Array(Float)} [lon, lat]
1813 initialize: function(lon, lat) {
1814 if (OpenLayers.Util.isArray(lon)) {
1818 this.lon = OpenLayers.Util.toFloat(lon);
1819 this.lat = OpenLayers.Util.toFloat(lat);
1824 * Return a readable string version of the lonlat
1827 * {String} String representation of OpenLayers.LonLat object.
1828 * (e.g. <i>"lon=5,lat=42"</i>)
1830 toString:function() {
1831 return ("lon=" + this.lon + ",lat=" + this.lat);
1835 * APIMethod: toShortString
1838 * {String} Shortened String representation of OpenLayers.LonLat object.
1839 * (e.g. <i>"5, 42"</i>)
1841 toShortString:function() {
1842 return (this.lon + ", " + this.lat);
1849 * {<OpenLayers.LonLat>} New OpenLayers.LonLat object with the same lon
1853 return new OpenLayers.LonLat(this.lon, this.lat);
1864 * {<OpenLayers.LonLat>} A new OpenLayers.LonLat object with the lon and
1865 * lat passed-in added to this's.
1867 add:function(lon, lat) {
1868 if ( (lon == null) || (lat == null) ) {
1869 throw new TypeError('LonLat.add cannot receive null values');
1871 return new OpenLayers.LonLat(this.lon + OpenLayers.Util.toFloat(lon),
1872 this.lat + OpenLayers.Util.toFloat(lat));
1879 * ll - {<OpenLayers.LonLat>}
1882 * {Boolean} Boolean value indicating whether the passed-in
1883 * <OpenLayers.LonLat> object has the same lon and lat
1884 * components as this.
1885 * Note: if ll passed in is null, returns false
1887 equals:function(ll) {
1890 equals = ((this.lon == ll.lon && this.lat == ll.lat) ||
1891 (isNaN(this.lon) && isNaN(this.lat) && isNaN(ll.lon) && isNaN(ll.lat)));
1897 * APIMethod: transform
1898 * Transform the LonLat object from source to dest. This transformation is
1899 * *in place*: if you want a *new* lonlat, use .clone() first.
1902 * source - {<OpenLayers.Projection>} Source projection.
1903 * dest - {<OpenLayers.Projection>} Destination projection.
1906 * {<OpenLayers.LonLat>} Itself, for use in chaining operations.
1908 transform: function(source, dest) {
1909 var point = OpenLayers.Projection.transform(
1910 {'x': this.lon, 'y': this.lat}, source, dest);
1917 * APIMethod: wrapDateLine
1920 * maxExtent - {<OpenLayers.Bounds>}
1923 * {<OpenLayers.LonLat>} A copy of this lonlat, but wrapped around the
1924 * "dateline" (as specified by the borders of
1927 wrapDateLine: function(maxExtent) {
1929 var newLonLat = this.clone();
1933 while (newLonLat.lon < maxExtent.left) {
1934 newLonLat.lon += maxExtent.getWidth();
1938 while (newLonLat.lon > maxExtent.right) {
1939 newLonLat.lon -= maxExtent.getWidth();
1946 CLASS_NAME: "OpenLayers.LonLat"
1950 * Function: fromString
1951 * Alternative constructor that builds a new <OpenLayers.LonLat> from a
1955 * str - {String} Comma-separated Lon,Lat coordinate string.
1956 * (e.g. <i>"5,40"</i>)
1959 * {<OpenLayers.LonLat>} New <OpenLayers.LonLat> object built from the
1962 OpenLayers.LonLat.fromString = function(str) {
1963 var pair = str.split(",");
1964 return new OpenLayers.LonLat(pair[0], pair[1]);
1968 * Function: fromArray
1969 * Alternative constructor that builds a new <OpenLayers.LonLat> from an
1970 * array of two numbers that represent lon- and lat-values.
1973 * arr - {Array(Float)} Array of lon/lat values (e.g. [5,-42])
1976 * {<OpenLayers.LonLat>} New <OpenLayers.LonLat> object built from the
1979 OpenLayers.LonLat.fromArray = function(arr) {
1980 var gotArr = OpenLayers.Util.isArray(arr),
1981 lon = gotArr && arr[0],
1982 lat = gotArr && arr[1];
1983 return new OpenLayers.LonLat(lon, lat);
1985 /* ======================================================================
1986 OpenLayers/BaseTypes/Pixel.js
1987 ====================================================================== */
1989 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
1990 * full list of contributors). Published under the 2-clause BSD license.
1991 * See license.txt in the OpenLayers distribution or repository for the
1992 * full text of the license. */
1995 * @requires OpenLayers/BaseTypes/Class.js
1999 * Class: OpenLayers.Pixel
2000 * This class represents a screen coordinate, in x and y coordinates
2002 OpenLayers.Pixel = OpenLayers.Class({
2006 * {Number} The x coordinate
2012 * {Number} The y coordinate
2017 * Constructor: OpenLayers.Pixel
2018 * Create a new OpenLayers.Pixel instance
2021 * x - {Number} The x coordinate
2022 * y - {Number} The y coordinate
2025 * An instance of OpenLayers.Pixel
2027 initialize: function(x, y) {
2028 this.x = parseFloat(x);
2029 this.y = parseFloat(y);
2034 * Cast this object into a string
2037 * {String} The string representation of Pixel. ex: "x=200.4,y=242.2"
2039 toString:function() {
2040 return ("x=" + this.x + ",y=" + this.y);
2045 * Return a clone of this pixel object
2048 * {<OpenLayers.Pixel>} A clone pixel
2051 return new OpenLayers.Pixel(this.x, this.y);
2056 * Determine whether one pixel is equivalent to another
2059 * px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object with
2060 * a 'x' and 'y' properties.
2063 * {Boolean} The point passed in as parameter is equal to this. Note that
2064 * if px passed in is null, returns false.
2066 equals:function(px) {
2069 equals = ((this.x == px.x && this.y == px.y) ||
2070 (isNaN(this.x) && isNaN(this.y) && isNaN(px.x) && isNaN(px.y)));
2076 * APIMethod: distanceTo
2077 * Returns the distance to the pixel point passed in as a parameter.
2080 * px - {<OpenLayers.Pixel>}
2083 * {Float} The pixel point passed in as parameter to calculate the
2086 distanceTo:function(px) {
2088 Math.pow(this.x - px.x, 2) +
2089 Math.pow(this.y - px.y, 2)
2101 * {<OpenLayers.Pixel>} A new Pixel with this pixel's x&y augmented by the
2104 add:function(x, y) {
2105 if ( (x == null) || (y == null) ) {
2106 throw new TypeError('Pixel.add cannot receive null values');
2108 return new OpenLayers.Pixel(this.x + x, this.y + y);
2115 * px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object with
2116 * a 'x' and 'y' properties.
2119 * {<OpenLayers.Pixel>} A new Pixel with this pixel's x&y augmented by the
2120 * x&y values of the pixel passed in.
2122 offset:function(px) {
2123 var newPx = this.clone();
2125 newPx = this.add(px.x, px.y);
2130 CLASS_NAME: "OpenLayers.Pixel"
2132 /* ======================================================================
2133 OpenLayers/BaseTypes/Size.js
2134 ====================================================================== */
2136 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
2137 * full list of contributors). Published under the 2-clause BSD license.
2138 * See license.txt in the OpenLayers distribution or repository for the
2139 * full text of the license. */
2142 * @requires OpenLayers/BaseTypes/Class.js
2146 * Class: OpenLayers.Size
2147 * Instances of this class represent a width/height pair
2149 OpenLayers.Size = OpenLayers.Class({
2165 * Constructor: OpenLayers.Size
2166 * Create an instance of OpenLayers.Size
2169 * w - {Number} width
2170 * h - {Number} height
2172 initialize: function(w, h) {
2173 this.w = parseFloat(w);
2174 this.h = parseFloat(h);
2179 * Return the string representation of a size object
2182 * {String} The string representation of OpenLayers.Size object.
2183 * (e.g. <i>"w=55,h=66"</i>)
2185 toString:function() {
2186 return ("w=" + this.w + ",h=" + this.h);
2191 * Create a clone of this size object
2194 * {<OpenLayers.Size>} A new OpenLayers.Size object with the same w and h
2198 return new OpenLayers.Size(this.w, this.h);
2204 * Determine where this size is equal to another
2207 * sz - {<OpenLayers.Size>|Object} An OpenLayers.Size or an object with
2208 * a 'w' and 'h' properties.
2211 * {Boolean} The passed in size has the same h and w properties as this one.
2212 * Note that if sz passed in is null, returns false.
2214 equals:function(sz) {
2217 equals = ((this.w == sz.w && this.h == sz.h) ||
2218 (isNaN(this.w) && isNaN(this.h) && isNaN(sz.w) && isNaN(sz.h)));
2223 CLASS_NAME: "OpenLayers.Size"
2225 /* ======================================================================
2226 OpenLayers/Console.js
2227 ====================================================================== */
2229 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
2230 * full list of contributors). Published under the 2-clause BSD license.
2231 * See license.txt in the OpenLayers distribution or repository for the
2232 * full text of the license. */
2235 * @requires OpenLayers/BaseTypes/Class.js
2239 * Namespace: OpenLayers.Console
2240 * The OpenLayers.Console namespace is used for debugging and error logging.
2241 * If the Firebug Lite (../Firebug/firebug.js) is included before this script,
2242 * calls to OpenLayers.Console methods will get redirected to window.console.
2243 * This makes use of the Firebug extension where available and allows for
2244 * cross-browser debugging Firebug style.
2247 * Note that behavior will differ with the Firebug extention and Firebug Lite.
2248 * Most notably, the Firebug Lite console does not currently allow for
2249 * hyperlinks to code or for clicking on object to explore their properties.
2252 OpenLayers.Console = {
2254 * Create empty functions for all console methods. The real value of these
2255 * properties will be set if Firebug Lite (../Firebug/firebug.js script) is
2256 * included. We explicitly require the Firebug Lite script to trigger
2257 * functionality of the OpenLayers.Console methods.
2262 * Log an object in the console. The Firebug Lite console logs string
2263 * representation of objects. Given multiple arguments, they will
2264 * be cast to strings and logged with a space delimiter. If the first
2265 * argument is a string with printf-like formatting, subsequent arguments
2266 * will be used in string substitution. Any additional arguments (beyond
2267 * the number substituted in a format string) will be appended in a space-
2276 * APIFunction: debug
2277 * Writes a message to the console, including a hyperlink to the line
2278 * where it was called.
2280 * May be called with multiple arguments as with OpenLayers.Console.log().
2285 debug: function() {},
2289 * Writes a message to the console with the visual "info" icon and color
2290 * coding and a hyperlink to the line where it was called.
2292 * May be called with multiple arguments as with OpenLayers.Console.log().
2297 info: function() {},
2301 * Writes a message to the console with the visual "warning" icon and
2302 * color coding and a hyperlink to the line where it was called.
2304 * May be called with multiple arguments as with OpenLayers.Console.log().
2309 warn: function() {},
2312 * APIFunction: error
2313 * Writes a message to the console with the visual "error" icon and color
2314 * coding and a hyperlink to the line where it was called.
2316 * May be called with multiple arguments as with OpenLayers.Console.log().
2321 error: function() {},
2324 * APIFunction: userError
2325 * A single interface for showing error messages to the user. The default
2326 * behavior is a Javascript alert, though this can be overridden by
2327 * reassigning OpenLayers.Console.userError to a different function.
2329 * Expects a single error message
2334 userError: function(error) {
2339 * APIFunction: assert
2340 * Tests that an expression is true. If not, it will write a message to
2341 * the console and throw an exception.
2343 * May be called with multiple arguments as with OpenLayers.Console.log().
2348 assert: function() {},
2352 * Prints an interactive listing of all properties of the object. This
2353 * looks identical to the view that you would see in the DOM tab.
2361 * APIFunction: dirxml
2362 * Prints the XML source tree of an HTML or XML element. This looks
2363 * identical to the view that you would see in the HTML tab. You can click
2364 * on any node to inspect it in the HTML tab.
2369 dirxml: function() {},
2372 * APIFunction: trace
2373 * Prints an interactive stack trace of JavaScript execution at the point
2374 * where it is called. The stack trace details the functions on the stack,
2375 * as well as the values that were passed as arguments to each function.
2376 * You can click each function to take you to its source in the Script tab,
2377 * and click each argument value to inspect it in the DOM or HTML tabs.
2380 trace: function() {},
2383 * APIFunction: group
2384 * Writes a message to the console and opens a nested block to indent all
2385 * future messages sent to the console. Call OpenLayers.Console.groupEnd()
2386 * to close the block.
2388 * May be called with multiple arguments as with OpenLayers.Console.log().
2393 group: function() {},
2396 * APIFunction: groupEnd
2397 * Closes the most recently opened block created by a call to
2398 * OpenLayers.Console.group
2400 groupEnd: function() {},
2404 * Creates a new timer under the given name. Call
2405 * OpenLayers.Console.timeEnd(name)
2406 * with the same name to stop the timer and print the time elapsed.
2411 time: function() {},
2414 * APIFunction: timeEnd
2415 * Stops a timer created by a call to OpenLayers.Console.time(name) and
2416 * writes the time elapsed.
2421 timeEnd: function() {},
2424 * APIFunction: profile
2425 * Turns on the JavaScript profiler. The optional argument title would
2426 * contain the text to be printed in the header of the profile report.
2428 * This function is not currently implemented in Firebug Lite.
2431 * title - {String} Optional title for the profiler
2433 profile: function() {},
2436 * APIFunction: profileEnd
2437 * Turns off the JavaScript profiler and prints its report.
2439 * This function is not currently implemented in Firebug Lite.
2441 profileEnd: function() {},
2444 * APIFunction: count
2445 * Writes the number of times that the line of code where count was called
2446 * was executed. The optional argument title will print a message in
2447 * addition to the number of the count.
2449 * This function is not currently implemented in Firebug Lite.
2452 * title - {String} Optional title to be printed with count
2454 count: function() {},
2456 CLASS_NAME: "OpenLayers.Console"
2460 * Execute an anonymous function to extend the OpenLayers.Console namespace
2461 * if the firebug.js script is included. This closure is used so that the
2462 * "scripts" and "i" variables don't pollute the global namespace.
2466 * If Firebug Lite is included (before this script), re-route all
2467 * OpenLayers.Console calls to the console object.
2469 var scripts = document.getElementsByTagName("script");
2470 for(var i=0, len=scripts.length; i<len; ++i) {
2471 if(scripts[i].src.indexOf("firebug.js") != -1) {
2473 OpenLayers.Util.extend(OpenLayers.Console, console);
2479 /* ======================================================================
2481 ====================================================================== */
2483 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
2484 * full list of contributors). Published under the 2-clause BSD license.
2485 * See license.txt in the OpenLayers distribution or repository for the
2486 * full text of the license. */
2489 * @requires OpenLayers/BaseTypes.js
2490 * @requires OpenLayers/Console.js
2494 * Namespace: OpenLayers.Lang
2495 * Internationalization namespace. Contains dictionaries in various languages
2496 * and methods to set and get the current language.
2502 * {String} Current language code to use in OpenLayers. Use the
2503 * <setCode> method to set this value and the <getCode> method to
2509 * APIProperty: defaultCode
2510 * {String} Default language to use when a specific language can't be
2511 * found. Default is "en".
2516 * APIFunction: getCode
2517 * Get the current language code.
2520 * {String} The current language code.
2522 getCode: function() {
2523 if(!OpenLayers.Lang.code) {
2524 OpenLayers.Lang.setCode();
2526 return OpenLayers.Lang.code;
2530 * APIFunction: setCode
2531 * Set the language code for string translation. This code is used by
2532 * the <OpenLayers.Lang.translate> method.
2535 * code - {String} These codes follow the IETF recommendations at
2536 * http://www.ietf.org/rfc/rfc3066.txt. If no value is set, the
2537 * browser's language setting will be tested. If no <OpenLayers.Lang>
2538 * dictionary exists for the code, the <OpenLayers.String.defaultLang>
2541 setCode: function(code) {
2544 code = (OpenLayers.BROWSER_NAME == "msie") ?
2545 navigator.userLanguage : navigator.language;
2547 var parts = code.split('-');
2548 parts[0] = parts[0].toLowerCase();
2549 if(typeof OpenLayers.Lang[parts[0]] == "object") {
2553 // check for regional extensions
2555 var testLang = parts[0] + '-' + parts[1].toUpperCase();
2556 if(typeof OpenLayers.Lang[testLang] == "object") {
2561 OpenLayers.Console.warn(
2562 'Failed to find OpenLayers.Lang.' + parts.join("-") +
2563 ' dictionary, falling back to default language'
2565 lang = OpenLayers.Lang.defaultCode;
2568 OpenLayers.Lang.code = lang;
2572 * APIMethod: translate
2573 * Looks up a key from a dictionary based on the current language string.
2574 * The value of <getCode> will be used to determine the appropriate
2575 * dictionary. Dictionaries are stored in <OpenLayers.Lang>.
2578 * key - {String} The key for an i18n string value in the dictionary.
2579 * context - {Object} Optional context to be used with
2580 * <OpenLayers.String.format>.
2583 * {String} A internationalized string.
2585 translate: function(key, context) {
2586 var dictionary = OpenLayers.Lang[OpenLayers.Lang.getCode()];
2587 var message = dictionary && dictionary[key];
2589 // Message not found, fall back to message key
2593 message = OpenLayers.String.format(message, context);
2602 * APIMethod: OpenLayers.i18n
2603 * Alias for <OpenLayers.Lang.translate>. Looks up a key from a dictionary
2604 * based on the current language string. The value of
2605 * <OpenLayers.Lang.getCode> will be used to determine the appropriate
2606 * dictionary. Dictionaries are stored in <OpenLayers.Lang>.
2609 * key - {String} The key for an i18n string value in the dictionary.
2610 * context - {Object} Optional context to be used with
2611 * <OpenLayers.String.format>.
2614 * {String} A internationalized string.
2616 OpenLayers.i18n = OpenLayers.Lang.translate;
2617 /* ======================================================================
2619 ====================================================================== */
2621 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
2622 * full list of contributors). Published under the 2-clause BSD license.
2623 * See license.txt in the OpenLayers distribution or repository for the
2624 * full text of the license. */
2627 * @requires OpenLayers/BaseTypes.js
2628 * @requires OpenLayers/BaseTypes/Bounds.js
2629 * @requires OpenLayers/BaseTypes/Element.js
2630 * @requires OpenLayers/BaseTypes/LonLat.js
2631 * @requires OpenLayers/BaseTypes/Pixel.js
2632 * @requires OpenLayers/BaseTypes/Size.js
2633 * @requires OpenLayers/Lang.js
2639 OpenLayers.Util = OpenLayers.Util || {};
2642 * Function: getElement
2643 * This is the old $() from prototype
2646 * e - {String or DOMElement or Window}
2649 * {Array(DOMElement) or DOMElement}
2651 OpenLayers.Util.getElement = function() {
2654 for (var i=0, len=arguments.length; i<len; i++) {
2655 var element = arguments[i];
2656 if (typeof element == 'string') {
2657 element = document.getElementById(element);
2659 if (arguments.length == 1) {
2662 elements.push(element);
2668 * Function: isElement
2669 * A cross-browser implementation of "e instanceof Element".
2672 * o - {Object} The object to test.
2677 OpenLayers.Util.isElement = function(o) {
2678 return !!(o && o.nodeType === 1);
2683 * Tests that the provided object is an array.
2684 * This test handles the cross-IFRAME case not caught
2685 * by "a instanceof Array" and should be used instead.
2688 * a - {Object} the object test.
2691 * {Boolean} true if the object is an array.
2693 OpenLayers.Util.isArray = function(a) {
2694 return (Object.prototype.toString.call(a) === '[object Array]');
2698 * Function: removeItem
2699 * Remove an object from an array. Iterates through the array
2700 * to find the item, then removes it.
2707 * {Array} A reference to the array
2709 OpenLayers.Util.removeItem = function(array, item) {
2710 for(var i = array.length - 1; i >= 0; i--) {
2711 if(array[i] == item) {
2713 //break;more than once??
2721 * Seems to exist already in FF, but not in MOZ.
2728 * {Integer} The index at which the first object was found in the array.
2729 * If not found, returns -1.
2731 OpenLayers.Util.indexOf = function(array, obj) {
2732 // use the build-in function if available.
2733 if (typeof array.indexOf == "function") {
2734 return array.indexOf(obj);
2736 for (var i = 0, len = array.length; i < len; i++) {
2737 if (array[i] == obj) {
2749 * Compiled regular expression to match dots ("."). This is used for replacing
2750 * dots in identifiers. Because object identifiers are frequently used for
2751 * DOM element identifiers by the library, we avoid using dots to make for
2752 * more sensible CSS selectors.
2754 * TODO: Use a module pattern to avoid bloating the API with stuff like this.
2756 OpenLayers.Util.dotless = /\./g;
2759 * Function: modifyDOMElement
2761 * Modifies many properties of a DOM element all at once. Passing in
2762 * null to an individual parameter will avoid setting the attribute.
2765 * element - {DOMElement} DOM element to modify.
2766 * id - {String} The element id attribute to set. Note that dots (".") will be
2767 * replaced with underscore ("_") in setting the element id.
2768 * px - {<OpenLayers.Pixel>|Object} The element left and top position,
2769 * OpenLayers.Pixel or an object with
2770 * a 'x' and 'y' properties.
2771 * sz - {<OpenLayers.Size>|Object} The element width and height,
2772 * OpenLayers.Size or an object with a
2773 * 'w' and 'h' properties.
2774 * position - {String} The position attribute. eg: absolute,
2776 * border - {String} The style.border attribute. eg:
2778 * overflow - {String} The style.overview attribute.
2779 * opacity - {Float} Fractional value (0.0 - 1.0)
2781 OpenLayers.Util.modifyDOMElement = function(element, id, px, sz, position,
2782 border, overflow, opacity) {
2785 element.id = id.replace(OpenLayers.Util.dotless, "_");
2788 element.style.left = px.x + "px";
2789 element.style.top = px.y + "px";
2792 element.style.width = sz.w + "px";
2793 element.style.height = sz.h + "px";
2796 element.style.position = position;
2799 element.style.border = border;
2802 element.style.overflow = overflow;
2804 if (parseFloat(opacity) >= 0.0 && parseFloat(opacity) < 1.0) {
2805 element.style.filter = 'alpha(opacity=' + (opacity * 100) + ')';
2806 element.style.opacity = opacity;
2807 } else if (parseFloat(opacity) == 1.0) {
2808 element.style.filter = '';
2809 element.style.opacity = '';
2814 * Function: createDiv
2815 * Creates a new div and optionally set some standard attributes.
2816 * Null may be passed to each parameter if you do not wish to
2817 * set a particular attribute.
2818 * Note - zIndex is NOT set on the resulting div.
2821 * id - {String} An identifier for this element. If no id is
2822 * passed an identifier will be created
2823 * automatically. Note that dots (".") will be replaced with
2824 * underscore ("_") when generating ids.
2825 * px - {<OpenLayers.Pixel>|Object} The element left and top position,
2826 * OpenLayers.Pixel or an object with
2827 * a 'x' and 'y' properties.
2828 * sz - {<OpenLayers.Size>|Object} The element width and height,
2829 * OpenLayers.Size or an object with a
2830 * 'w' and 'h' properties.
2831 * imgURL - {String} A url pointing to an image to use as a
2833 * position - {String} The style.position value. eg: absolute,
2835 * border - {String} The the style.border value.
2836 * eg: 2px solid black
2837 * overflow - {String} The style.overflow value. Eg. hidden
2838 * opacity - {Float} Fractional value (0.0 - 1.0)
2841 * {DOMElement} A DOM Div created with the specified attributes.
2843 OpenLayers.Util.createDiv = function(id, px, sz, imgURL, position,
2844 border, overflow, opacity) {
2846 var dom = document.createElement('div');
2849 dom.style.backgroundImage = 'url(' + imgURL + ')';
2852 //set generic properties
2854 id = OpenLayers.Util.createUniqueID("OpenLayersDiv");
2857 position = "absolute";
2859 OpenLayers.Util.modifyDOMElement(dom, id, px, sz, position,
2860 border, overflow, opacity);
2866 * Function: createImage
2867 * Creates an img element with specific attribute values.
2870 * id - {String} The id field for the img. If none assigned one will be
2871 * automatically generated.
2872 * px - {<OpenLayers.Pixel>|Object} The element left and top position,
2873 * OpenLayers.Pixel or an object with
2874 * a 'x' and 'y' properties.
2875 * sz - {<OpenLayers.Size>|Object} The element width and height,
2876 * OpenLayers.Size or an object with a
2877 * 'w' and 'h' properties.
2878 * imgURL - {String} The url to use as the image source.
2879 * position - {String} The style.position value.
2880 * border - {String} The border to place around the image.
2881 * opacity - {Float} Fractional value (0.0 - 1.0)
2882 * delayDisplay - {Boolean} If true waits until the image has been
2886 * {DOMElement} A DOM Image created with the specified attributes.
2888 OpenLayers.Util.createImage = function(id, px, sz, imgURL, position, border,
2889 opacity, delayDisplay) {
2891 var image = document.createElement("img");
2893 //set generic properties
2895 id = OpenLayers.Util.createUniqueID("OpenLayersDiv");
2898 position = "relative";
2900 OpenLayers.Util.modifyDOMElement(image, id, px, sz, position,
2901 border, null, opacity);
2904 image.style.display = "none";
2905 function display() {
2906 image.style.display = "";
2907 OpenLayers.Event.stopObservingElement(image);
2909 OpenLayers.Event.observe(image, "load", display);
2910 OpenLayers.Event.observe(image, "error", display);
2913 //set special properties
2914 image.style.alt = id;
2915 image.galleryImg = "no";
2924 * Property: IMAGE_RELOAD_ATTEMPTS
2925 * {Integer} How many times should we try to reload an image before giving up?
2928 OpenLayers.IMAGE_RELOAD_ATTEMPTS = 0;
2931 * Property: alphaHackNeeded
2932 * {Boolean} true if the png alpha hack is necessary and possible, false otherwise.
2934 OpenLayers.Util.alphaHackNeeded = null;
2937 * Function: alphaHack
2938 * Checks whether it's necessary (and possible) to use the png alpha
2939 * hack which allows alpha transparency for png images under Internet
2943 * {Boolean} true if the png alpha hack is necessary and possible, false otherwise.
2945 OpenLayers.Util.alphaHack = function() {
2946 if (OpenLayers.Util.alphaHackNeeded == null) {
2947 var arVersion = navigator.appVersion.split("MSIE");
2948 var version = parseFloat(arVersion[1]);
2951 // IEs4Lin dies when trying to access document.body.filters, because
2952 // the property is there, but requires a DLL that can't be provided. This
2953 // means that we need to wrap this in a try/catch so that this can
2957 filter = !!(document.body.filters);
2960 OpenLayers.Util.alphaHackNeeded = (filter &&
2961 (version >= 5.5) && (version < 7));
2963 return OpenLayers.Util.alphaHackNeeded;
2967 * Function: modifyAlphaImageDiv
2970 * div - {DOMElement} Div containing Alpha-adjusted Image
2972 * px - {<OpenLayers.Pixel>|Object} OpenLayers.Pixel or an object with
2973 * a 'x' and 'y' properties.
2974 * sz - {<OpenLayers.Size>|Object} OpenLayers.Size or an object with
2975 * a 'w' and 'h' properties.
2977 * position - {String}
2979 * sizing - {String} 'crop', 'scale', or 'image'. Default is "scale"
2980 * opacity - {Float} Fractional value (0.0 - 1.0)
2982 OpenLayers.Util.modifyAlphaImageDiv = function(div, id, px, sz, imgURL,
2983 position, border, sizing,
2986 OpenLayers.Util.modifyDOMElement(div, id, px, sz, position,
2987 null, null, opacity);
2989 var img = div.childNodes[0];
2994 OpenLayers.Util.modifyDOMElement(img, div.id + "_innerImage", null, sz,
2995 "relative", border);
2997 if (OpenLayers.Util.alphaHack()) {
2998 if(div.style.display != "none") {
2999 div.style.display = "inline-block";
3001 if (sizing == null) {
3005 div.style.filter = "progid:DXImageTransform.Microsoft" +
3006 ".AlphaImageLoader(src='" + img.src + "', " +
3007 "sizingMethod='" + sizing + "')";
3008 if (parseFloat(div.style.opacity) >= 0.0 &&
3009 parseFloat(div.style.opacity) < 1.0) {
3010 div.style.filter += " alpha(opacity=" + div.style.opacity * 100 + ")";
3013 img.style.filter = "alpha(opacity=0)";
3018 * Function: createAlphaImageDiv
3022 * px - {<OpenLayers.Pixel>|Object} OpenLayers.Pixel or an object with
3023 * a 'x' and 'y' properties.
3024 * sz - {<OpenLayers.Size>|Object} OpenLayers.Size or an object with
3025 * a 'w' and 'h' properties.
3027 * position - {String}
3029 * sizing - {String} 'crop', 'scale', or 'image'. Default is "scale"
3030 * opacity - {Float} Fractional value (0.0 - 1.0)
3031 * delayDisplay - {Boolean} If true waits until the image has been
3035 * {DOMElement} A DOM Div created with a DOM Image inside it. If the hack is
3036 * needed for transparency in IE, it is added.
3038 OpenLayers.Util.createAlphaImageDiv = function(id, px, sz, imgURL,
3039 position, border, sizing,
3040 opacity, delayDisplay) {
3042 var div = OpenLayers.Util.createDiv();
3043 var img = OpenLayers.Util.createImage(null, null, null, null, null, null,
3044 null, delayDisplay);
3045 img.className = "olAlphaImg";
3046 div.appendChild(img);
3048 OpenLayers.Util.modifyAlphaImageDiv(div, id, px, sz, imgURL, position,
3049 border, sizing, opacity);
3056 * Function: upperCaseObject
3057 * Creates a new hashtable and copies over all the keys from the
3058 * passed-in object, but storing them under an uppercased
3059 * version of the key at which they were stored.
3065 * {Object} A new Object with all the same keys but uppercased
3067 OpenLayers.Util.upperCaseObject = function (object) {
3069 for (var key in object) {
3070 uObject[key.toUpperCase()] = object[key];
3076 * Function: applyDefaults
3077 * Takes an object and copies any properties that don't exist from
3078 * another properties, by analogy with OpenLayers.Util.extend() from
3082 * to - {Object} The destination object.
3083 * from - {Object} The source object. Any properties of this object that
3084 * are undefined in the to object will be set on the to object.
3087 * {Object} A reference to the to object. Note that the to argument is modified
3088 * in place and returned by this function.
3090 OpenLayers.Util.applyDefaults = function (to, from) {
3093 * FF/Windows < 2.0.0.13 reports "Illegal operation on WrappedNative
3094 * prototype object" when calling hawOwnProperty if the source object is an
3095 * instance of window.Event.
3097 var fromIsEvt = typeof window.Event == "function"
3098 && from instanceof window.Event;
3100 for (var key in from) {
3101 if (to[key] === undefined ||
3102 (!fromIsEvt && from.hasOwnProperty
3103 && from.hasOwnProperty(key) && !to.hasOwnProperty(key))) {
3104 to[key] = from[key];
3108 * IE doesn't include the toString property when iterating over an object's
3109 * properties with the for(property in object) syntax. Explicitly check if
3110 * the source has its own toString property.
3112 if(!fromIsEvt && from && from.hasOwnProperty
3113 && from.hasOwnProperty('toString') && !to.hasOwnProperty('toString')) {
3114 to.toString = from.toString;
3121 * Function: getParameterString
3127 * {String} A concatenation of the properties of an object in
3128 * http parameter notation.
3129 * (ex. <i>"key1=value1&key2=value2&key3=value3"</i>)
3130 * If a parameter is actually a list, that parameter will then
3131 * be set to a comma-seperated list of values (foo,bar) instead
3132 * of being URL escaped (foo%3Abar).
3134 OpenLayers.Util.getParameterString = function(params) {
3135 var paramsArray = [];
3137 for (var key in params) {
3138 var value = params[key];
3139 if ((value != null) && (typeof value != 'function')) {
3141 if (typeof value == 'object' && value.constructor == Array) {
3142 /* value is an array; encode items and separate with "," */
3143 var encodedItemArray = [];
3145 for (var itemIndex=0, len=value.length; itemIndex<len; itemIndex++) {
3146 item = value[itemIndex];
3147 encodedItemArray.push(encodeURIComponent(
3148 (item === null || item === undefined) ? "" : item)
3151 encodedValue = encodedItemArray.join(",");
3154 /* value is a string; simply encode */
3155 encodedValue = encodeURIComponent(value);
3157 paramsArray.push(encodeURIComponent(key) + "=" + encodedValue);
3161 return paramsArray.join("&");
3165 * Function: urlAppend
3166 * Appends a parameter string to a url. This function includes the logic for
3167 * using the appropriate character (none, & or ?) to append to the url before
3168 * appending the param string.
3171 * url - {String} The url to append to
3172 * paramStr - {String} The param string to append
3175 * {String} The new url
3177 OpenLayers.Util.urlAppend = function(url, paramStr) {
3180 var parts = (url + " ").split(/[?&]/);
3181 newUrl += (parts.pop() === " " ?
3183 parts.length ? "&" + paramStr : "?" + paramStr);
3189 * Function: getImagesLocation
3192 * {String} The fully formatted image location string
3194 OpenLayers.Util.getImagesLocation = function() {
3195 return OpenLayers.ImgPath || (OpenLayers._getScriptLocation() + "img/");
3199 * Function: getImageLocation
3202 * {String} The fully formatted location string for a specified image
3204 OpenLayers.Util.getImageLocation = function(image) {
3205 return OpenLayers.Util.getImagesLocation() + image;
3211 * Execute functions until one of them doesn't throw an error.
3212 * Capitalized because "try" is a reserved word in JavaScript.
3213 * Taken directly from OpenLayers.Util.Try()
3216 * [*] - {Function} Any number of parameters may be passed to Try()
3217 * It will attempt to execute each of them until one of them
3218 * successfully executes.
3219 * If none executes successfully, returns null.
3222 * {*} The value returned by the first successfully executed function.
3224 OpenLayers.Util.Try = function() {
3225 var returnValue = null;
3227 for (var i=0, len=arguments.length; i<len; i++) {
3228 var lambda = arguments[i];
3230 returnValue = lambda();
3239 * Function: getXmlNodeValue
3245 * {String} The text value of the given node, without breaking in firefox or IE
3247 OpenLayers.Util.getXmlNodeValue = function(node) {
3249 OpenLayers.Util.Try(
3253 val = node.textContent;
3256 val = node.firstChild.nodeValue;
3260 val = node.textContent;
3266 * Function: mouseLeft
3270 * div - {HTMLDivElement}
3275 OpenLayers.Util.mouseLeft = function (evt, div) {
3276 // start with the element to which the mouse has moved
3277 var target = (evt.relatedTarget) ? evt.relatedTarget : evt.toElement;
3278 // walk up the DOM tree.
3279 while (target != div && target != null) {
3280 target = target.parentNode;
3282 // if the target we stop at isn't the div, then we've left the div.
3283 return (target != div);
3287 * Property: precision
3288 * {Number} The number of significant digits to retain to avoid
3289 * floating point precision errors.
3291 * We use 14 as a "safe" default because, although IEEE 754 double floats
3292 * (standard on most modern operating systems) support up to about 16
3293 * significant digits, 14 significant digits are sufficient to represent
3294 * sub-millimeter accuracy in any coordinate system that anyone is likely to
3295 * use with OpenLayers.
3297 * If DEFAULT_PRECISION is set to 0, the original non-truncating behavior
3298 * of OpenLayers <2.8 is preserved. Be aware that this will cause problems
3299 * with certain projections, e.g. spherical Mercator.
3302 OpenLayers.Util.DEFAULT_PRECISION = 14;
3306 * Convenience method to cast an object to a Number, rounded to the
3307 * desired floating point precision.
3310 * number - {Number} The number to cast and round.
3311 * precision - {Number} An integer suitable for use with
3312 * Number.toPrecision(). Defaults to OpenLayers.Util.DEFAULT_PRECISION.
3313 * If set to 0, no rounding is performed.
3316 * {Number} The cast, rounded number.
3318 OpenLayers.Util.toFloat = function (number, precision) {
3319 if (precision == null) {
3320 precision = OpenLayers.Util.DEFAULT_PRECISION;
3322 if (typeof number !== "number") {
3323 number = parseFloat(number);
3325 return precision === 0 ? number :
3326 parseFloat(number.toPrecision(precision));
3338 OpenLayers.Util.rad = function(x) {return x*Math.PI/180;};
3349 OpenLayers.Util.deg = function(x) {return x*180/Math.PI;};
3352 * Property: VincentyConstants
3353 * {Object} Constants for Vincenty functions.
3355 OpenLayers.Util.VincentyConstants = {
3362 * APIFunction: distVincenty
3363 * Given two objects representing points with geographic coordinates, this
3364 * calculates the distance between those points on the surface of an
3368 * p1 - {<OpenLayers.LonLat>} (or any object with both .lat, .lon properties)
3369 * p2 - {<OpenLayers.LonLat>} (or any object with both .lat, .lon properties)
3372 * {Float} The distance (in km) between the two input points as measured on an
3373 * ellipsoid. Note that the input point objects must be in geographic
3374 * coordinates (decimal degrees) and the return distance is in kilometers.
3376 OpenLayers.Util.distVincenty = function(p1, p2) {
3377 var ct = OpenLayers.Util.VincentyConstants;
3378 var a = ct.a, b = ct.b, f = ct.f;
3380 var L = OpenLayers.Util.rad(p2.lon - p1.lon);
3381 var U1 = Math.atan((1-f) * Math.tan(OpenLayers.Util.rad(p1.lat)));
3382 var U2 = Math.atan((1-f) * Math.tan(OpenLayers.Util.rad(p2.lat)));
3383 var sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
3384 var sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
3385 var lambda = L, lambdaP = 2*Math.PI;
3387 while (Math.abs(lambda-lambdaP) > 1e-12 && --iterLimit>0) {
3388 var sinLambda = Math.sin(lambda), cosLambda = Math.cos(lambda);
3389 var sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) +
3390 (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda));
3392 return 0; // co-incident points
3394 var cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda;
3395 var sigma = Math.atan2(sinSigma, cosSigma);
3396 var alpha = Math.asin(cosU1 * cosU2 * sinLambda / sinSigma);
3397 var cosSqAlpha = Math.cos(alpha) * Math.cos(alpha);
3398 var cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha;
3399 var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
3401 lambda = L + (1-C) * f * Math.sin(alpha) *
3402 (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
3405 return NaN; // formula failed to converge
3407 var uSq = cosSqAlpha * (a*a - b*b) / (b*b);
3408 var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
3409 var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
3410 var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
3411 B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
3412 var s = b*A*(sigma-deltaSigma);
3413 var d = s.toFixed(3)/1000; // round to 1mm precision
3418 * APIFunction: destinationVincenty
3419 * Calculate destination point given start point lat/long (numeric degrees),
3420 * bearing (numeric degrees) & distance (in m).
3421 * Adapted from Chris Veness work, see
3422 * http://www.movable-type.co.uk/scripts/latlong-vincenty-direct.html
3425 * lonlat - {<OpenLayers.LonLat>} (or any object with both .lat, .lon
3426 * properties) The start point.
3427 * brng - {Float} The bearing (degrees).
3428 * dist - {Float} The ground distance (meters).
3431 * {<OpenLayers.LonLat>} The destination point.
3433 OpenLayers.Util.destinationVincenty = function(lonlat, brng, dist) {
3434 var u = OpenLayers.Util;
3435 var ct = u.VincentyConstants;
3436 var a = ct.a, b = ct.b, f = ct.f;
3438 var lon1 = lonlat.lon;
3439 var lat1 = lonlat.lat;
3442 var alpha1 = u.rad(brng);
3443 var sinAlpha1 = Math.sin(alpha1);
3444 var cosAlpha1 = Math.cos(alpha1);
3446 var tanU1 = (1-f) * Math.tan(u.rad(lat1));
3447 var cosU1 = 1 / Math.sqrt((1 + tanU1*tanU1)), sinU1 = tanU1*cosU1;
3448 var sigma1 = Math.atan2(tanU1, cosAlpha1);
3449 var sinAlpha = cosU1 * sinAlpha1;
3450 var cosSqAlpha = 1 - sinAlpha*sinAlpha;
3451 var uSq = cosSqAlpha * (a*a - b*b) / (b*b);
3452 var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
3453 var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
3455 var sigma = s / (b*A), sigmaP = 2*Math.PI;
3456 while (Math.abs(sigma-sigmaP) > 1e-12) {
3457 var cos2SigmaM = Math.cos(2*sigma1 + sigma);
3458 var sinSigma = Math.sin(sigma);
3459 var cosSigma = Math.cos(sigma);
3460 var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
3461 B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
3463 sigma = s / (b*A) + deltaSigma;
3466 var tmp = sinU1*sinSigma - cosU1*cosSigma*cosAlpha1;
3467 var lat2 = Math.atan2(sinU1*cosSigma + cosU1*sinSigma*cosAlpha1,
3468 (1-f)*Math.sqrt(sinAlpha*sinAlpha + tmp*tmp));
3469 var lambda = Math.atan2(sinSigma*sinAlpha1, cosU1*cosSigma - sinU1*sinSigma*cosAlpha1);
3470 var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
3471 var L = lambda - (1-C) * f * sinAlpha *
3472 (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
3474 var revAz = Math.atan2(sinAlpha, -tmp); // final bearing
3476 return new OpenLayers.LonLat(lon1+u.deg(L), u.deg(lat2));
3480 * Function: getParameters
3481 * Parse the parameters from a URL or from the current page itself into a
3482 * JavaScript Object. Note that parameter values with commas are separated
3483 * out into an Array.
3486 * url - {String} Optional url used to extract the query string.
3487 * If url is null or is not supplied, query string is taken
3488 * from the page location.
3489 * options - {Object} Additional options. Optional.
3492 * splitArgs - {Boolean} Split comma delimited params into arrays? Default is
3496 * {Object} An object of key/value pairs from the query string.
3498 OpenLayers.Util.getParameters = function(url, options) {
3499 options = options || {};
3500 // if no url specified, take it from the location bar
3501 url = (url === null || url === undefined) ? window.location.href : url;
3503 //parse out parameters portion of url string
3504 var paramsString = "";
3505 if (OpenLayers.String.contains(url, '?')) {
3506 var start = url.indexOf('?') + 1;
3507 var end = OpenLayers.String.contains(url, "#") ?
3508 url.indexOf('#') : url.length;
3509 paramsString = url.substring(start, end);
3512 var parameters = {};
3513 var pairs = paramsString.split(/[&;]/);
3514 for(var i=0, len=pairs.length; i<len; ++i) {
3515 var keyValue = pairs[i].split('=');
3518 var key = keyValue[0];
3520 key = decodeURIComponent(key);
3522 key = unescape(key);
3525 // being liberal by replacing "+" with " "
3526 var value = (keyValue[1] || '').replace(/\+/g, " ");
3529 value = decodeURIComponent(value);
3531 value = unescape(value);
3534 // follow OGC convention of comma delimited values
3535 if (options.splitArgs !== false) {
3536 value = value.split(",");
3539 //if there's only one value, do not return as array
3540 if (value.length == 1) {
3544 parameters[key] = value;
3551 * Property: lastSeqID
3552 * {Integer} The ever-incrementing count variable.
3553 * Used for generating unique ids.
3555 OpenLayers.Util.lastSeqID = 0;
3558 * Function: createUniqueID
3559 * Create a unique identifier for this session. Each time this function
3560 * is called, a counter is incremented. The return will be the optional
3561 * prefix (defaults to "id_") appended with the counter value.
3564 * prefix - {String} Optional string to prefix unique id. Default is "id_".
3565 * Note that dots (".") in the prefix will be replaced with underscore ("_").
3568 * {String} A unique id string, built on the passed in prefix.
3570 OpenLayers.Util.createUniqueID = function(prefix) {
3571 if (prefix == null) {
3574 prefix = prefix.replace(OpenLayers.Util.dotless, "_");
3576 OpenLayers.Util.lastSeqID += 1;
3577 return prefix + OpenLayers.Util.lastSeqID;
3581 * Constant: INCHES_PER_UNIT
3582 * {Object} Constant inches per unit -- borrowed from MapServer mapscale.c
3583 * derivation of nautical miles from http://en.wikipedia.org/wiki/Nautical_mile
3584 * Includes the full set of units supported by CS-MAP (http://trac.osgeo.org/csmap/)
3585 * and PROJ.4 (http://trac.osgeo.org/proj/)
3586 * The hardcoded table is maintain in a CS-MAP source code module named CSdataU.c
3587 * The hardcoded table of PROJ.4 units are in pj_units.c.
3589 OpenLayers.INCHES_PER_UNIT = {
3598 OpenLayers.INCHES_PER_UNIT["in"]= OpenLayers.INCHES_PER_UNIT.inches;
3599 OpenLayers.INCHES_PER_UNIT["degrees"] = OpenLayers.INCHES_PER_UNIT.dd;
3600 OpenLayers.INCHES_PER_UNIT["nmi"] = 1852 * OpenLayers.INCHES_PER_UNIT.m;
3602 // Units from CS-Map
3603 OpenLayers.METERS_PER_INCH = 0.02540005080010160020;
3604 OpenLayers.Util.extend(OpenLayers.INCHES_PER_UNIT, {
3605 "Inch": OpenLayers.INCHES_PER_UNIT.inches,
3606 "Meter": 1.0 / OpenLayers.METERS_PER_INCH, //EPSG:9001
3607 "Foot": 0.30480060960121920243 / OpenLayers.METERS_PER_INCH, //EPSG:9003
3608 "IFoot": 0.30480000000000000000 / OpenLayers.METERS_PER_INCH, //EPSG:9002
3609 "ClarkeFoot": 0.3047972651151 / OpenLayers.METERS_PER_INCH, //EPSG:9005
3610 "SearsFoot": 0.30479947153867624624 / OpenLayers.METERS_PER_INCH, //EPSG:9041
3611 "GoldCoastFoot": 0.30479971018150881758 / OpenLayers.METERS_PER_INCH, //EPSG:9094
3612 "IInch": 0.02540000000000000000 / OpenLayers.METERS_PER_INCH,
3613 "MicroInch": 0.00002540000000000000 / OpenLayers.METERS_PER_INCH,
3614 "Mil": 0.00000002540000000000 / OpenLayers.METERS_PER_INCH,
3615 "Centimeter": 0.01000000000000000000 / OpenLayers.METERS_PER_INCH,
3616 "Kilometer": 1000.00000000000000000000 / OpenLayers.METERS_PER_INCH, //EPSG:9036
3617 "Yard": 0.91440182880365760731 / OpenLayers.METERS_PER_INCH,
3618 "SearsYard": 0.914398414616029 / OpenLayers.METERS_PER_INCH, //EPSG:9040
3619 "IndianYard": 0.91439853074444079983 / OpenLayers.METERS_PER_INCH, //EPSG:9084
3620 "IndianYd37": 0.91439523 / OpenLayers.METERS_PER_INCH, //EPSG:9085
3621 "IndianYd62": 0.9143988 / OpenLayers.METERS_PER_INCH, //EPSG:9086
3622 "IndianYd75": 0.9143985 / OpenLayers.METERS_PER_INCH, //EPSG:9087
3623 "IndianFoot": 0.30479951 / OpenLayers.METERS_PER_INCH, //EPSG:9080
3624 "IndianFt37": 0.30479841 / OpenLayers.METERS_PER_INCH, //EPSG:9081
3625 "IndianFt62": 0.3047996 / OpenLayers.METERS_PER_INCH, //EPSG:9082
3626 "IndianFt75": 0.3047995 / OpenLayers.METERS_PER_INCH, //EPSG:9083
3627 "Mile": 1609.34721869443738887477 / OpenLayers.METERS_PER_INCH,
3628 "IYard": 0.91440000000000000000 / OpenLayers.METERS_PER_INCH, //EPSG:9096
3629 "IMile": 1609.34400000000000000000 / OpenLayers.METERS_PER_INCH, //EPSG:9093
3630 "NautM": 1852.00000000000000000000 / OpenLayers.METERS_PER_INCH, //EPSG:9030
3631 "Lat-66": 110943.316488932731 / OpenLayers.METERS_PER_INCH,
3632 "Lat-83": 110946.25736872234125 / OpenLayers.METERS_PER_INCH,
3633 "Decimeter": 0.10000000000000000000 / OpenLayers.METERS_PER_INCH,
3634 "Millimeter": 0.00100000000000000000 / OpenLayers.METERS_PER_INCH,
3635 "Dekameter": 10.00000000000000000000 / OpenLayers.METERS_PER_INCH,
3636 "Decameter": 10.00000000000000000000 / OpenLayers.METERS_PER_INCH,
3637 "Hectometer": 100.00000000000000000000 / OpenLayers.METERS_PER_INCH,
3638 "GermanMeter": 1.0000135965 / OpenLayers.METERS_PER_INCH, //EPSG:9031
3639 "CaGrid": 0.999738 / OpenLayers.METERS_PER_INCH,
3640 "ClarkeChain": 20.1166194976 / OpenLayers.METERS_PER_INCH, //EPSG:9038
3641 "GunterChain": 20.11684023368047 / OpenLayers.METERS_PER_INCH, //EPSG:9033
3642 "BenoitChain": 20.116782494375872 / OpenLayers.METERS_PER_INCH, //EPSG:9062
3643 "SearsChain": 20.11676512155 / OpenLayers.METERS_PER_INCH, //EPSG:9042
3644 "ClarkeLink": 0.201166194976 / OpenLayers.METERS_PER_INCH, //EPSG:9039
3645 "GunterLink": 0.2011684023368047 / OpenLayers.METERS_PER_INCH, //EPSG:9034
3646 "BenoitLink": 0.20116782494375872 / OpenLayers.METERS_PER_INCH, //EPSG:9063
3647 "SearsLink": 0.2011676512155 / OpenLayers.METERS_PER_INCH, //EPSG:9043
3648 "Rod": 5.02921005842012 / OpenLayers.METERS_PER_INCH,
3649 "IntnlChain": 20.1168 / OpenLayers.METERS_PER_INCH, //EPSG:9097
3650 "IntnlLink": 0.201168 / OpenLayers.METERS_PER_INCH, //EPSG:9098
3651 "Perch": 5.02921005842012 / OpenLayers.METERS_PER_INCH,
3652 "Pole": 5.02921005842012 / OpenLayers.METERS_PER_INCH,
3653 "Furlong": 201.1684023368046 / OpenLayers.METERS_PER_INCH,
3654 "Rood": 3.778266898 / OpenLayers.METERS_PER_INCH,
3655 "CapeFoot": 0.3047972615 / OpenLayers.METERS_PER_INCH,
3656 "Brealey": 375.00000000000000000000 / OpenLayers.METERS_PER_INCH,
3657 "ModAmFt": 0.304812252984505969011938 / OpenLayers.METERS_PER_INCH,
3658 "Fathom": 1.8288 / OpenLayers.METERS_PER_INCH,
3659 "NautM-UK": 1853.184 / OpenLayers.METERS_PER_INCH,
3660 "50kilometers": 50000.0 / OpenLayers.METERS_PER_INCH,
3661 "150kilometers": 150000.0 / OpenLayers.METERS_PER_INCH
3664 //unit abbreviations supported by PROJ.4
3665 OpenLayers.Util.extend(OpenLayers.INCHES_PER_UNIT, {
3666 "mm": OpenLayers.INCHES_PER_UNIT["Meter"] / 1000.0,
3667 "cm": OpenLayers.INCHES_PER_UNIT["Meter"] / 100.0,
3668 "dm": OpenLayers.INCHES_PER_UNIT["Meter"] * 100.0,
3669 "km": OpenLayers.INCHES_PER_UNIT["Meter"] * 1000.0,
3670 "kmi": OpenLayers.INCHES_PER_UNIT["nmi"], //International Nautical Mile
3671 "fath": OpenLayers.INCHES_PER_UNIT["Fathom"], //International Fathom
3672 "ch": OpenLayers.INCHES_PER_UNIT["IntnlChain"], //International Chain
3673 "link": OpenLayers.INCHES_PER_UNIT["IntnlLink"], //International Link
3674 "us-in": OpenLayers.INCHES_PER_UNIT["inches"], //U.S. Surveyor's Inch
3675 "us-ft": OpenLayers.INCHES_PER_UNIT["Foot"], //U.S. Surveyor's Foot
3676 "us-yd": OpenLayers.INCHES_PER_UNIT["Yard"], //U.S. Surveyor's Yard
3677 "us-ch": OpenLayers.INCHES_PER_UNIT["GunterChain"], //U.S. Surveyor's Chain
3678 "us-mi": OpenLayers.INCHES_PER_UNIT["Mile"], //U.S. Surveyor's Statute Mile
3679 "ind-yd": OpenLayers.INCHES_PER_UNIT["IndianYd37"], //Indian Yard
3680 "ind-ft": OpenLayers.INCHES_PER_UNIT["IndianFt37"], //Indian Foot
3681 "ind-ch": 20.11669506 / OpenLayers.METERS_PER_INCH //Indian Chain
3685 * Constant: DOTS_PER_INCH
3686 * {Integer} 72 (A sensible default)
3688 OpenLayers.DOTS_PER_INCH = 72;
3691 * Function: normalizeScale
3697 * {Float} A normalized scale value, in 1 / X format.
3698 * This means that if a value less than one ( already 1/x) is passed
3699 * in, it just returns scale directly. Otherwise, it returns
3702 OpenLayers.Util.normalizeScale = function (scale) {
3703 var normScale = (scale > 1.0) ? (1.0 / scale)
3709 * Function: getResolutionFromScale
3713 * units - {String} Index into OpenLayers.INCHES_PER_UNIT hashtable.
3714 * Default is degrees
3717 * {Float} The corresponding resolution given passed-in scale and unit
3718 * parameters. If the given scale is falsey, the returned resolution will
3721 OpenLayers.Util.getResolutionFromScale = function (scale, units) {
3724 if (units == null) {
3727 var normScale = OpenLayers.Util.normalizeScale(scale);
3728 resolution = 1 / (normScale * OpenLayers.INCHES_PER_UNIT[units]
3729 * OpenLayers.DOTS_PER_INCH);
3735 * Function: getScaleFromResolution
3738 * resolution - {Float}
3739 * units - {String} Index into OpenLayers.INCHES_PER_UNIT hashtable.
3740 * Default is degrees
3743 * {Float} The corresponding scale given passed-in resolution and unit
3746 OpenLayers.Util.getScaleFromResolution = function (resolution, units) {
3748 if (units == null) {
3752 var scale = resolution * OpenLayers.INCHES_PER_UNIT[units] *
3753 OpenLayers.DOTS_PER_INCH;
3758 * Function: pagePosition
3759 * Calculates the position of an element on the page (see
3760 * http://code.google.com/p/doctype/wiki/ArticlePageOffset)
3762 * OpenLayers.Util.pagePosition is based on Yahoo's getXY method, which is
3763 * Copyright (c) 2006, Yahoo! Inc.
3764 * All rights reserved.
3766 * Redistribution and use of this software in source and binary forms, with or
3767 * without modification, are permitted provided that the following conditions
3770 * * Redistributions of source code must retain the above copyright notice,
3771 * this list of conditions and the following disclaimer.
3773 * * Redistributions in binary form must reproduce the above copyright notice,
3774 * this list of conditions and the following disclaimer in the documentation
3775 * and/or other materials provided with the distribution.
3777 * * Neither the name of Yahoo! Inc. nor the names of its contributors may be
3778 * used to endorse or promote products derived from this software without
3779 * specific prior written permission of Yahoo! Inc.
3781 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
3782 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
3783 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3784 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
3785 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
3786 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
3787 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3788 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3789 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3790 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3791 * POSSIBILITY OF SUCH DAMAGE.
3794 * forElement - {DOMElement}
3797 * {Array} two item array, Left value then Top value.
3799 OpenLayers.Util.pagePosition = function(forElement) {
3800 // NOTE: If element is hidden (display none or disconnected or any the
3801 // ancestors are hidden) we get (0,0) by default but we still do the
3802 // accumulation of scroll position.
3805 var viewportElement = OpenLayers.Util.getViewportElement();
3806 if (!forElement || forElement == window || forElement == viewportElement) {
3807 // viewport is always at 0,0 as that defined the coordinate system for
3808 // this function - this avoids special case checks in the code below
3812 // Gecko browsers normally use getBoxObjectFor to calculate the position.
3813 // When invoked for an element with an implicit absolute position though it
3814 // can be off by one. Therefore the recursive implementation is used in
3815 // those (relatively rare) cases.
3816 var BUGGY_GECKO_BOX_OBJECT =
3817 OpenLayers.IS_GECKO && document.getBoxObjectFor &&
3818 OpenLayers.Element.getStyle(forElement, 'position') == 'absolute' &&
3819 (forElement.style.top == '' || forElement.style.left == '');
3824 if (forElement.getBoundingClientRect) { // IE
3825 box = forElement.getBoundingClientRect();
3826 var scrollTop = window.pageYOffset || viewportElement.scrollTop;
3827 var scrollLeft = window.pageXOffset || viewportElement.scrollLeft;
3829 pos[0] = box.left + scrollLeft;
3830 pos[1] = box.top + scrollTop;
3832 } else if (document.getBoxObjectFor && !BUGGY_GECKO_BOX_OBJECT) { // gecko
3833 // Gecko ignores the scroll values for ancestors, up to 1.9. See:
3834 // https://bugzilla.mozilla.org/show_bug.cgi?id=328881 and
3835 // https://bugzilla.mozilla.org/show_bug.cgi?id=330619
3837 box = document.getBoxObjectFor(forElement);
3838 var vpBox = document.getBoxObjectFor(viewportElement);
3839 pos[0] = box.screenX - vpBox.screenX;
3840 pos[1] = box.screenY - vpBox.screenY;
3842 } else { // safari/opera
3843 pos[0] = forElement.offsetLeft;
3844 pos[1] = forElement.offsetTop;
3845 parent = forElement.offsetParent;
3846 if (parent != forElement) {
3848 pos[0] += parent.offsetLeft;
3849 pos[1] += parent.offsetTop;
3850 parent = parent.offsetParent;
3854 var browser = OpenLayers.BROWSER_NAME;
3856 // opera & (safari absolute) incorrectly account for body offsetTop
3857 if (browser == "opera" || (browser == "safari" &&
3858 OpenLayers.Element.getStyle(forElement, 'position') == 'absolute')) {
3859 pos[1] -= document.body.offsetTop;
3862 // accumulate the scroll positions for everything but the body element
3863 parent = forElement.offsetParent;
3864 while (parent && parent != document.body) {
3865 pos[0] -= parent.scrollLeft;
3866 // see https://bugs.opera.com/show_bug.cgi?id=249965
3867 if (browser != "opera" || parent.tagName != 'TR') {
3868 pos[1] -= parent.scrollTop;
3870 parent = parent.offsetParent;
3878 * Function: getViewportElement
3879 * Returns die viewport element of the document. The viewport element is
3880 * usually document.documentElement, except in IE,where it is either
3881 * document.body or document.documentElement, depending on the document's
3882 * compatibility mode (see
3883 * http://code.google.com/p/doctype/wiki/ArticleClientViewportElement)
3888 OpenLayers.Util.getViewportElement = function() {
3889 var viewportElement = arguments.callee.viewportElement;
3890 if (viewportElement == undefined) {
3891 viewportElement = (OpenLayers.BROWSER_NAME == "msie" &&
3892 document.compatMode != 'CSS1Compat') ? document.body :
3893 document.documentElement;
3894 arguments.callee.viewportElement = viewportElement;
3896 return viewportElement;
3900 * Function: isEquivalentUrl
3901 * Test two URLs for equivalence.
3903 * Setting 'ignoreCase' allows for case-independent comparison.
3905 * Comparison is based on:
3907 * - Host (evaluated without the port)
3908 * - Port (set 'ignorePort80' to ignore "80" values)
3909 * - Hash ( set 'ignoreHash' to disable)
3910 * - Pathname (for relative <-> absolute comparison)
3911 * - Arguments (so they can be out of order)
3916 * options - {Object} Allows for customization of comparison:
3917 * 'ignoreCase' - Default is True
3918 * 'ignorePort80' - Default is True
3919 * 'ignoreHash' - Default is True
3922 * {Boolean} Whether or not the two URLs are equivalent
3924 OpenLayers.Util.isEquivalentUrl = function(url1, url2, options) {
3925 options = options || {};
3927 OpenLayers.Util.applyDefaults(options, {
3934 var urlObj1 = OpenLayers.Util.createUrlObject(url1, options);
3935 var urlObj2 = OpenLayers.Util.createUrlObject(url2, options);
3937 //compare all keys except for "args" (treated below)
3938 for(var key in urlObj1) {
3939 if(key !== "args") {
3940 if(urlObj1[key] != urlObj2[key]) {
3946 // compare search args - irrespective of order
3947 for(var key in urlObj1.args) {
3948 if(urlObj1.args[key] != urlObj2.args[key]) {
3951 delete urlObj2.args[key];
3953 // urlObj2 shouldn't have any args left
3954 for(var key in urlObj2.args) {
3962 * Function: createUrlObject
3966 * options - {Object} A hash of options.
3969 * ignoreCase - {Boolean} lowercase url,
3970 * ignorePort80 - {Boolean} don't include explicit port if port is 80,
3971 * ignoreHash - {Boolean} Don't include part of url after the hash (#).
3972 * splitArgs - {Boolean} Split comma delimited params into arrays? Default is
3976 * {Object} An object with separate url, a, port, host, and args parsed out
3977 * and ready for comparison
3979 OpenLayers.Util.createUrlObject = function(url, options) {
3980 options = options || {};
3982 // deal with relative urls first
3983 if(!(/^\w+:\/\//).test(url)) {
3984 var loc = window.location;
3985 var port = loc.port ? ":" + loc.port : "";
3986 var fullUrl = loc.protocol + "//" + loc.host.split(":").shift() + port;
3987 if(url.indexOf("/") === 0) {
3989 url = fullUrl + url;
3991 // relative to current path
3992 var parts = loc.pathname.split("/");
3994 url = fullUrl + parts.join("/") + "/" + url;
3998 if (options.ignoreCase) {
3999 url = url.toLowerCase();
4002 var a = document.createElement('a');
4007 //host (without port)
4008 urlObject.host = a.host.split(":").shift();
4011 urlObject.protocol = a.protocol;
4013 //port (get uniform browser behavior with port 80 here)
4014 if(options.ignorePort80) {
4015 urlObject.port = (a.port == "80" || a.port == "0") ? "" : a.port;
4017 urlObject.port = (a.port == "" || a.port == "0") ? "80" : a.port;
4021 urlObject.hash = (options.ignoreHash || a.hash === "#") ? "" : a.hash;
4024 var queryString = a.search;
4026 var qMark = url.indexOf("?");
4027 queryString = (qMark != -1) ? url.substr(qMark) : "";
4029 urlObject.args = OpenLayers.Util.getParameters(queryString,
4030 {splitArgs: options.splitArgs});
4034 // This is a workaround for Internet Explorer where
4035 // window.location.pathname has a leading "/", but
4036 // a.pathname has no leading "/".
4037 urlObject.pathname = (a.pathname.charAt(0) == "/") ? a.pathname : "/" + a.pathname;
4043 * Function: removeTail
4044 * Takes a url and removes everything after the ? and #
4047 * url - {String} The url to process
4050 * {String} The string with all queryString and Hash removed
4052 OpenLayers.Util.removeTail = function(url) {
4055 var qMark = url.indexOf("?");
4056 var hashMark = url.indexOf("#");
4059 head = (hashMark != -1) ? url.substr(0,hashMark) : url;
4061 head = (hashMark != -1) ? url.substr(0,Math.min(qMark, hashMark))
4062 : url.substr(0, qMark);
4068 * Constant: IS_GECKO
4069 * {Boolean} True if the userAgent reports the browser to use the Gecko engine
4071 OpenLayers.IS_GECKO = (function() {
4072 var ua = navigator.userAgent.toLowerCase();
4073 return ua.indexOf("webkit") == -1 && ua.indexOf("gecko") != -1;
4077 * Constant: CANVAS_SUPPORTED
4078 * {Boolean} True if canvas 2d is supported.
4080 OpenLayers.CANVAS_SUPPORTED = (function() {
4081 var elem = document.createElement('canvas');
4082 return !!(elem.getContext && elem.getContext('2d'));
4086 * Constant: BROWSER_NAME
4088 * A substring of the navigator.userAgent property. Depending on the userAgent
4089 * property, this will be the empty string or one of the following:
4090 * * "opera" -- Opera
4091 * * "msie" -- Internet Explorer
4092 * * "safari" -- Safari
4093 * * "firefox" -- Firefox
4094 * * "mozilla" -- Mozilla
4096 OpenLayers.BROWSER_NAME = (function() {
4098 var ua = navigator.userAgent.toLowerCase();
4099 if (ua.indexOf("opera") != -1) {
4101 } else if (ua.indexOf("msie") != -1) {
4103 } else if (ua.indexOf("safari") != -1) {
4105 } else if (ua.indexOf("mozilla") != -1) {
4106 if (ua.indexOf("firefox") != -1) {
4116 * Function: getBrowserName
4119 * {String} A string which specifies which is the current
4120 * browser in which we are running.
4122 * Currently-supported browser detection and codes:
4123 * * 'opera' -- Opera
4124 * * 'msie' -- Internet Explorer
4125 * * 'safari' -- Safari
4126 * * 'firefox' -- Firefox
4127 * * 'mozilla' -- Mozilla
4129 * If we are unable to property identify the browser, we
4130 * return an empty string.
4132 OpenLayers.Util.getBrowserName = function() {
4133 return OpenLayers.BROWSER_NAME;
4137 * Method: getRenderedDimensions
4138 * Renders the contentHTML offscreen to determine actual dimensions for
4139 * popup sizing. As we need layout to determine dimensions the content
4140 * is rendered -9999px to the left and absolute to ensure the
4141 * scrollbars do not flicker
4145 * size - {<OpenLayers.Size>} If either the 'w' or 'h' properties is
4146 * specified, we fix that dimension of the div to be measured. This is
4147 * useful in the case where we have a limit in one dimension and must
4148 * therefore meaure the flow in the other dimension.
4149 * options - {Object}
4152 * displayClass - {String} Optional parameter. A CSS class name(s) string
4153 * to provide the CSS context of the rendered content.
4154 * containerElement - {DOMElement} Optional parameter. Insert the HTML to
4155 * this node instead of the body root when calculating dimensions.
4158 * {<OpenLayers.Size>}
4160 OpenLayers.Util.getRenderedDimensions = function(contentHTML, size, options) {
4164 // create temp container div with restricted size
4165 var container = document.createElement("div");
4166 container.style.visibility = "hidden";
4168 var containerElement = (options && options.containerElement)
4169 ? options.containerElement : document.body;
4171 // Opera and IE7 can't handle a node with position:aboslute if it inherits
4172 // position:absolute from a parent.
4173 var parentHasPositionAbsolute = false;
4174 var superContainer = null;
4175 var parent = containerElement;
4176 while (parent && parent.tagName.toLowerCase()!="body") {
4177 var parentPosition = OpenLayers.Element.getStyle(parent, "position");
4178 if(parentPosition == "absolute") {
4179 parentHasPositionAbsolute = true;
4181 } else if (parentPosition && parentPosition != "static") {
4184 parent = parent.parentNode;
4186 if(parentHasPositionAbsolute && (containerElement.clientHeight === 0 ||
4187 containerElement.clientWidth === 0) ){
4188 superContainer = document.createElement("div");
4189 superContainer.style.visibility = "hidden";
4190 superContainer.style.position = "absolute";
4191 superContainer.style.overflow = "visible";
4192 superContainer.style.width = document.body.clientWidth + "px";
4193 superContainer.style.height = document.body.clientHeight + "px";
4194 superContainer.appendChild(container);
4196 container.style.position = "absolute";
4198 //fix a dimension, if specified.
4202 container.style.width = w + "px";
4203 } else if (size.h) {
4205 container.style.height = h + "px";
4209 //add css classes, if specified
4210 if (options && options.displayClass) {
4211 container.className = options.displayClass;
4214 // create temp content div and assign content
4215 var content = document.createElement("div");
4216 content.innerHTML = contentHTML;
4218 // we need overflow visible when calculating the size
4219 content.style.overflow = "visible";
4220 if (content.childNodes) {
4221 for (var i=0, l=content.childNodes.length; i<l; i++) {
4222 if (!content.childNodes[i].style) continue;
4223 content.childNodes[i].style.overflow = "visible";
4227 // add content to restricted container
4228 container.appendChild(content);
4230 // append container to body for rendering
4231 if (superContainer) {
4232 containerElement.appendChild(superContainer);
4234 containerElement.appendChild(container);
4237 // calculate scroll width of content and add corners and shadow width
4239 w = parseInt(content.scrollWidth);
4241 // update container width to allow height to adjust
4242 container.style.width = w + "px";
4244 // capture height and add shadow and corner image widths
4246 h = parseInt(content.scrollHeight);
4250 container.removeChild(content);
4251 if (superContainer) {
4252 superContainer.removeChild(container);
4253 containerElement.removeChild(superContainer);
4255 containerElement.removeChild(container);
4258 return new OpenLayers.Size(w, h);
4262 * APIFunction: getScrollbarWidth
4263 * This function has been modified by the OpenLayers from the original version,
4264 * written by Matthew Eernisse and released under the Apache 2
4267 * http://www.fleegix.org/articles/2006/05/30/getting-the-scrollbar-width-in-pixels
4269 * It has been modified simply to cache its value, since it is physically
4270 * impossible that this code could ever run in more than one browser at
4276 OpenLayers.Util.getScrollbarWidth = function() {
4278 var scrollbarWidth = OpenLayers.Util._scrollbarWidth;
4280 if (scrollbarWidth == null) {
4286 // Outer scrolling div
4287 scr = document.createElement('div');
4288 scr.style.position = 'absolute';
4289 scr.style.top = '-1000px';
4290 scr.style.left = '-1000px';
4291 scr.style.width = '100px';
4292 scr.style.height = '50px';
4293 // Start with no scrollbar
4294 scr.style.overflow = 'hidden';
4296 // Inner content div
4297 inn = document.createElement('div');
4298 inn.style.width = '100%';
4299 inn.style.height = '200px';
4301 // Put the inner div in the scrolling div
4302 scr.appendChild(inn);
4303 // Append the scrolling div to the doc
4304 document.body.appendChild(scr);
4306 // Width of the inner div sans scrollbar
4307 wNoScroll = inn.offsetWidth;
4309 // Add the scrollbar
4310 scr.style.overflow = 'scroll';
4311 // Width of the inner div width scrollbar
4312 wScroll = inn.offsetWidth;
4314 // Remove the scrolling div from the doc
4315 document.body.removeChild(document.body.lastChild);
4317 // Pixel width of the scroller
4318 OpenLayers.Util._scrollbarWidth = (wNoScroll - wScroll);
4319 scrollbarWidth = OpenLayers.Util._scrollbarWidth;
4322 return scrollbarWidth;
4326 * APIFunction: getFormattedLonLat
4327 * This function will return latitude or longitude value formatted as
4330 * coordinate - {Float} the coordinate value to be formatted
4331 * axis - {String} value of either 'lat' or 'lon' to indicate which axis is to
4332 * to be formatted (default = lat)
4333 * dmsOption - {String} specify the precision of the output can be one of:
4334 * 'dms' show degrees minutes and seconds
4335 * 'dm' show only degrees and minutes
4336 * 'd' show only degrees
4339 * {String} the coordinate value formatted as a string
4341 OpenLayers.Util.getFormattedLonLat = function(coordinate, axis, dmsOption) {
4343 dmsOption = 'dms'; //default to show degree, minutes, seconds
4346 coordinate = (coordinate+540)%360 - 180; // normalize for sphere being round
4348 var abscoordinate = Math.abs(coordinate);
4349 var coordinatedegrees = Math.floor(abscoordinate);
4351 var coordinateminutes = (abscoordinate - coordinatedegrees)/(1/60);
4352 var tempcoordinateminutes = coordinateminutes;
4353 coordinateminutes = Math.floor(coordinateminutes);
4354 var coordinateseconds = (tempcoordinateminutes - coordinateminutes)/(1/60);
4355 coordinateseconds = Math.round(coordinateseconds*10);
4356 coordinateseconds /= 10;
4358 if( coordinateseconds >= 60) {
4359 coordinateseconds -= 60;
4360 coordinateminutes += 1;
4361 if( coordinateminutes >= 60) {
4362 coordinateminutes -= 60;
4363 coordinatedegrees += 1;
4367 if( coordinatedegrees < 10 ) {
4368 coordinatedegrees = "0" + coordinatedegrees;
4370 var str = coordinatedegrees + "\u00B0";
4372 if (dmsOption.indexOf('dm') >= 0) {
4373 if( coordinateminutes < 10 ) {
4374 coordinateminutes = "0" + coordinateminutes;
4376 str += coordinateminutes + "'";
4378 if (dmsOption.indexOf('dms') >= 0) {
4379 if( coordinateseconds < 10 ) {
4380 coordinateseconds = "0" + coordinateseconds;
4382 str += coordinateseconds + '"';
4386 if (axis == "lon") {
4387 str += coordinate < 0 ? OpenLayers.i18n("W") : OpenLayers.i18n("E");
4389 str += coordinate < 0 ? OpenLayers.i18n("S") : OpenLayers.i18n("N");
4394 /* ======================================================================
4395 OpenLayers/Events.js
4396 ====================================================================== */
4398 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
4399 * full list of contributors). Published under the 2-clause BSD license.
4400 * See license.txt in the OpenLayers distribution or repository for the
4401 * full text of the license. */
4405 * @requires OpenLayers/Util.js
4409 * Namespace: OpenLayers.Event
4410 * Utility functions for event handling.
4412 OpenLayers.Event = {
4415 * Property: observers
4416 * {Object} A hashtable cache of the event observers. Keyed by
4417 * element._eventCacheID
4422 * Constant: KEY_SPACE
4428 * Constant: KEY_BACKSPACE
4440 * Constant: KEY_RETURN
4452 * Constant: KEY_LEFT
4464 * Constant: KEY_RIGHT
4470 * Constant: KEY_DOWN
4476 * Constant: KEY_DELETE
4484 * Cross browser event element detection.
4490 * {DOMElement} The element that caused the event
4492 element: function(event) {
4493 return event.target || event.srcElement;
4497 * Method: isSingleTouch
4498 * Determine whether event was caused by a single touch
4506 isSingleTouch: function(event) {
4507 return event.touches && event.touches.length == 1;
4511 * Method: isMultiTouch
4512 * Determine whether event was caused by a multi touch
4520 isMultiTouch: function(event) {
4521 return event.touches && event.touches.length > 1;
4525 * Method: isLeftClick
4526 * Determine whether event was caused by a left click.
4534 isLeftClick: function(event) {
4535 return (((event.which) && (event.which == 1)) ||
4536 ((event.button) && (event.button == 1)));
4540 * Method: isRightClick
4541 * Determine whether event was caused by a right mouse click.
4549 isRightClick: function(event) {
4550 return (((event.which) && (event.which == 3)) ||
4551 ((event.button) && (event.button == 2)));
4556 * Stops an event from propagating.
4560 * allowDefault - {Boolean} If true, we stop the event chain but
4561 * still allow the default browser behaviour (text selection,
4562 * radio-button clicking, etc). Default is false.
4564 stop: function(event, allowDefault) {
4566 if (!allowDefault) {
4567 OpenLayers.Event.preventDefault(event);
4570 if (event.stopPropagation) {
4571 event.stopPropagation();
4573 event.cancelBubble = true;
4578 * Method: preventDefault
4579 * Cancels the event if it is cancelable, without stopping further
4580 * propagation of the event.
4585 preventDefault: function(event) {
4586 if (event.preventDefault) {
4587 event.preventDefault();
4589 event.returnValue = false;
4594 * Method: findElement
4598 * tagName - {String}
4601 * {DOMElement} The first node with the given tagName, starting from the
4602 * node the event was triggered on and traversing the DOM upwards
4604 findElement: function(event, tagName) {
4605 var element = OpenLayers.Event.element(event);
4606 while (element.parentNode && (!element.tagName ||
4607 (element.tagName.toUpperCase() != tagName.toUpperCase()))){
4608 element = element.parentNode;
4617 * elementParam - {DOMElement || String}
4619 * observer - {function}
4620 * useCapture - {Boolean}
4622 observe: function(elementParam, name, observer, useCapture) {
4623 var element = OpenLayers.Util.getElement(elementParam);
4624 useCapture = useCapture || false;
4626 if (name == 'keypress' &&
4627 (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
4628 || element.attachEvent)) {
4632 //if observers cache has not yet been created, create it
4633 if (!this.observers) {
4634 this.observers = {};
4637 //if not already assigned, make a new unique cache ID
4638 if (!element._eventCacheID) {
4639 var idPrefix = "eventCacheID_";
4641 idPrefix = element.id + "_" + idPrefix;
4643 element._eventCacheID = OpenLayers.Util.createUniqueID(idPrefix);
4646 var cacheID = element._eventCacheID;
4648 //if there is not yet a hash entry for this element, add one
4649 if (!this.observers[cacheID]) {
4650 this.observers[cacheID] = [];
4653 //add a new observer to this element's list
4654 this.observers[cacheID].push({
4657 'observer': observer,
4658 'useCapture': useCapture
4661 //add the actual browser event listener
4662 if (element.addEventListener) {
4663 element.addEventListener(name, observer, useCapture);
4664 } else if (element.attachEvent) {
4665 element.attachEvent('on' + name, observer);
4670 * Method: stopObservingElement
4671 * Given the id of an element to stop observing, cycle through the
4672 * element's cached observers, calling stopObserving on each one,
4673 * skipping those entries which can no longer be removed.
4676 * elementParam - {DOMElement || String}
4678 stopObservingElement: function(elementParam) {
4679 var element = OpenLayers.Util.getElement(elementParam);
4680 var cacheID = element._eventCacheID;
4682 this._removeElementObservers(OpenLayers.Event.observers[cacheID]);
4686 * Method: _removeElementObservers
4689 * elementObservers - {Array(Object)} Array of (element, name,
4690 * observer, usecapture) objects,
4691 * taken directly from hashtable
4693 _removeElementObservers: function(elementObservers) {
4694 if (elementObservers) {
4695 for(var i = elementObservers.length-1; i >= 0; i--) {
4696 var entry = elementObservers[i];
4697 OpenLayers.Event.stopObserving.apply(this, [
4698 entry.element, entry.name, entry.observer, entry.useCapture
4705 * Method: stopObserving
4708 * elementParam - {DOMElement || String}
4710 * observer - {function}
4711 * useCapture - {Boolean}
4714 * {Boolean} Whether or not the event observer was removed
4716 stopObserving: function(elementParam, name, observer, useCapture) {
4717 useCapture = useCapture || false;
4719 var element = OpenLayers.Util.getElement(elementParam);
4720 var cacheID = element._eventCacheID;
4722 if (name == 'keypress') {
4723 if ( navigator.appVersion.match(/Konqueror|Safari|KHTML/) ||
4724 element.detachEvent) {
4729 // find element's entry in this.observers cache and remove it
4730 var foundEntry = false;
4731 var elementObservers = OpenLayers.Event.observers[cacheID];
4732 if (elementObservers) {
4734 // find the specific event type in the element's list
4736 while(!foundEntry && i < elementObservers.length) {
4737 var cacheEntry = elementObservers[i];
4739 if ((cacheEntry.name == name) &&
4740 (cacheEntry.observer == observer) &&
4741 (cacheEntry.useCapture == useCapture)) {
4743 elementObservers.splice(i, 1);
4744 if (elementObservers.length == 0) {
4745 delete OpenLayers.Event.observers[cacheID];
4754 //actually remove the event listener from browser
4756 if (element.removeEventListener) {
4757 element.removeEventListener(name, observer, useCapture);
4758 } else if (element && element.detachEvent) {
4759 element.detachEvent('on' + name, observer);
4766 * Method: unloadCache
4767 * Cycle through all the element entries in the events cache and call
4768 * stopObservingElement on each.
4770 unloadCache: function() {
4771 // check for OpenLayers.Event before checking for observers, because
4772 // OpenLayers.Event may be undefined in IE if no map instance was
4774 if (OpenLayers.Event && OpenLayers.Event.observers) {
4775 for (var cacheID in OpenLayers.Event.observers) {
4776 var elementObservers = OpenLayers.Event.observers[cacheID];
4777 OpenLayers.Event._removeElementObservers.apply(this,
4778 [elementObservers]);
4780 OpenLayers.Event.observers = false;
4784 CLASS_NAME: "OpenLayers.Event"
4787 /* prevent memory leaks in IE */
4788 OpenLayers.Event.observe(window, 'unload', OpenLayers.Event.unloadCache, false);
4791 * Class: OpenLayers.Events
4793 OpenLayers.Events = OpenLayers.Class({
4796 * Constant: BROWSER_EVENTS
4797 * {Array(String)} supported events
4800 "mouseover", "mouseout",
4801 "mousedown", "mouseup", "mousemove",
4802 "click", "dblclick", "rightclick", "dblrightclick",
4803 "resize", "focus", "blur",
4804 "touchstart", "touchmove", "touchend",
4809 * Property: listeners
4810 * {Object} Hashtable of Array(Function): events listener functions
4816 * {Object} the code object issuing application events
4822 * {DOMElement} the DOM element receiving browser events
4827 * Property: eventHandler
4828 * {Function} bound event handler attached to elements
4833 * APIProperty: fallThrough
4839 * APIProperty: includeXY
4840 * {Boolean} Should the .xy property automatically be created for browser
4841 * mouse events? In general, this should be false. If it is true, then
4842 * mouse events will automatically generate a '.xy' property on the
4843 * event object that is passed. (Prior to OpenLayers 2.7, this was true
4844 * by default.) Otherwise, you can call the getMousePosition on the
4845 * relevant events handler on the object available via the 'evt.object'
4846 * property of the evt object. So, for most events, you can call:
4847 * function named(evt) {
4848 * this.xy = this.object.events.getMousePosition(evt)
4851 * This option typically defaults to false for performance reasons:
4852 * when creating an events object whose primary purpose is to manage
4853 * relatively positioned mouse events within a div, it may make
4854 * sense to set it to true.
4856 * This option is also used to control whether the events object caches
4857 * offsets. If this is false, it will not: the reason for this is that
4858 * it is only expected to be called many times if the includeXY property
4859 * is set to true. If you set this to true, you are expected to clear
4860 * the offset cache manually (using this.clearMouseCache()) if:
4861 * the border of the element changes
4862 * the location of the element in the page changes
4867 * APIProperty: extensions
4868 * {Object} Event extensions registered with this instance. Keys are
4869 * event types, values are {OpenLayers.Events.*} extension instances or
4870 * {Boolean} for events that an instantiated extension provides in
4871 * addition to the one it was created for.
4873 * Extensions create an event in addition to browser events, which usually
4874 * fires when a sequence of browser events is completed. Extensions are
4875 * automatically instantiated when a listener is registered for an event
4876 * provided by an extension.
4878 * Extensions are created in the <OpenLayers.Events> namespace using
4879 * <OpenLayers.Class>, and named after the event they provide.
4880 * The constructor receives the target <OpenLayers.Events> instance as
4881 * argument. Extensions that need to capture browser events before they
4882 * propagate can register their listeners events using <register>, with
4883 * {extension: true} as 4th argument.
4885 * If an extension creates more than one event, an alias for each event
4886 * type should be created and reference the same class. The constructor
4887 * should set a reference in the target's extensions registry to itself.
4889 * Below is a minimal extension that provides the "foostart" and "fooend"
4890 * event types, which replace the native "click" event type if clicked on
4891 * an element with the css class "foo":
4894 * OpenLayers.Events.foostart = OpenLayers.Class({
4895 * initialize: function(target) {
4896 * this.target = target;
4897 * this.target.register("click", this, this.doStuff, {extension: true});
4898 * // only required if extension provides more than one event type
4899 * this.target.extensions["foostart"] = true;
4900 * this.target.extensions["fooend"] = true;
4902 * destroy: function() {
4903 * var target = this.target;
4904 * target.unregister("click", this, this.doStuff);
4905 * delete this.target;
4906 * // only required if extension provides more than one event type
4907 * delete target.extensions["foostart"];
4908 * delete target.extensions["fooend"];
4910 * doStuff: function(evt) {
4911 * var propagate = true;
4912 * if (OpenLayers.Event.element(evt).className === "foo") {
4913 * propagate = false;
4914 * var target = this.target;
4915 * target.triggerEvent("foostart");
4916 * window.setTimeout(function() {
4917 * target.triggerEvent("fooend");
4923 * // only required if extension provides more than one event type
4924 * OpenLayers.Events.fooend = OpenLayers.Events.foostart;
4931 * Property: extensionCount
4932 * {Object} Keys are event types (like in <listeners>), values are the
4933 * number of extension listeners for each event type.
4935 extensionCount: null,
4938 * Method: clearMouseListener
4939 * A version of <clearMouseCache> that is bound to this instance so that
4940 * it can be used with <OpenLayers.Event.observe> and
4941 * <OpenLayers.Event.stopObserving>.
4943 clearMouseListener: null,
4946 * Constructor: OpenLayers.Events
4947 * Construct an OpenLayers.Events object.
4950 * object - {Object} The js object to which this Events object is being added
4951 * element - {DOMElement} A dom element to respond to browser events
4952 * eventTypes - {Array(String)} Deprecated. Array of custom application
4953 * events. A listener may be registered for any named event, regardless
4954 * of the values provided here.
4955 * fallThrough - {Boolean} Allow events to fall through after these have
4957 * options - {Object} Options for the events object.
4959 initialize: function (object, element, eventTypes, fallThrough, options) {
4960 OpenLayers.Util.extend(this, options);
4961 this.object = object;
4962 this.fallThrough = fallThrough;
4963 this.listeners = {};
4964 this.extensions = {};
4965 this.extensionCount = {};
4966 this._msTouches = [];
4968 // if a dom element is specified, add a listeners list
4969 // for browser events on the element and register them
4970 if (element != null) {
4971 this.attachToElement(element);
4976 * APIMethod: destroy
4978 destroy: function () {
4979 for (var e in this.extensions) {
4980 if (typeof this.extensions[e] !== "boolean") {
4981 this.extensions[e].destroy();
4984 this.extensions = null;
4986 OpenLayers.Event.stopObservingElement(this.element);
4987 if(this.element.hasScrollEvent) {
4988 OpenLayers.Event.stopObserving(
4989 window, "scroll", this.clearMouseListener
4993 this.element = null;
4995 this.listeners = null;
4997 this.fallThrough = null;
4998 this.eventHandler = null;
5002 * APIMethod: addEventType
5003 * Deprecated. Any event can be triggered without adding it first.
5006 * eventName - {String}
5008 addEventType: function(eventName) {
5012 * Method: attachToElement
5015 * element - {HTMLDOMElement} a DOM element to attach browser events to
5017 attachToElement: function (element) {
5019 OpenLayers.Event.stopObservingElement(this.element);
5021 // keep a bound copy of handleBrowserEvent() so that we can
5022 // pass the same function to both Event.observe() and .stopObserving()
5023 this.eventHandler = OpenLayers.Function.bindAsEventListener(
5024 this.handleBrowserEvent, this
5027 // to be used with observe and stopObserving
5028 this.clearMouseListener = OpenLayers.Function.bind(
5029 this.clearMouseCache, this
5032 this.element = element;
5033 var msTouch = !!window.navigator.msMaxTouchPoints;
5035 for (var i = 0, len = this.BROWSER_EVENTS.length; i < len; i++) {
5036 type = this.BROWSER_EVENTS[i];
5037 // register the event cross-browser
5038 OpenLayers.Event.observe(element, type, this.eventHandler
5040 if (msTouch && type.indexOf('touch') === 0) {
5041 this.addMsTouchListener(element, type, this.eventHandler);
5044 // disable dragstart in IE so that mousedown/move/up works normally
5045 OpenLayers.Event.observe(element, "dragstart", OpenLayers.Event.stop);
5050 * Convenience method for registering listeners with a common scope.
5051 * Internally, this method calls <register> as shown in the examples
5056 * // register a single listener for the "loadstart" event
5057 * events.on({"loadstart": loadStartListener});
5059 * // this is equivalent to the following
5060 * events.register("loadstart", undefined, loadStartListener);
5062 * // register multiple listeners to be called with the same `this` object
5064 * "loadstart": loadStartListener,
5065 * "loadend": loadEndListener,
5069 * // this is equivalent to the following
5070 * events.register("loadstart", object, loadStartListener);
5071 * events.register("loadend", object, loadEndListener);
5077 on: function(object) {
5078 for(var type in object) {
5079 if(type != "scope" && object.hasOwnProperty(type)) {
5080 this.register(type, object.scope, object[type]);
5086 * APIMethod: register
5087 * Register an event on the events object.
5089 * When the event is triggered, the 'func' function will be called, in the
5090 * context of 'obj'. Imagine we were to register an event, specifying an
5091 * OpenLayers.Bounds Object as 'obj'. When the event is triggered, the
5092 * context in the callback function will be our Bounds object. This means
5093 * that within our callback function, we can access the properties and
5094 * methods of the Bounds object through the "this" variable. So our
5095 * callback could execute something like:
5096 * : leftStr = "Left: " + this.left;
5100 * : centerStr = "Center: " + this.getCenterLonLat();
5103 * type - {String} Name of the event to register
5104 * obj - {Object} The object to bind the context to for the callback#.
5105 * If no object is specified, default is the Events's 'object' property.
5106 * func - {Function} The callback function. If no callback is
5107 * specified, this function does nothing.
5108 * priority - {Boolean|Object} If true, adds the new listener to the
5109 * *front* of the events queue instead of to the end.
5111 * Valid options for priority:
5112 * extension - {Boolean} If true, then the event will be registered as
5113 * extension event. Extension events are handled before all other
5116 register: function (type, obj, func, priority) {
5117 if (type in OpenLayers.Events && !this.extensions[type]) {
5118 this.extensions[type] = new OpenLayers.Events[type](this);
5124 var listeners = this.listeners[type];
5127 this.listeners[type] = listeners;
5128 this.extensionCount[type] = 0;
5130 var listener = {obj: obj, func: func};
5132 listeners.splice(this.extensionCount[type], 0, listener);
5133 if (typeof priority === "object" && priority.extension) {
5134 this.extensionCount[type]++;
5137 listeners.push(listener);
5143 * APIMethod: registerPriority
5144 * Same as register() but adds the new listener to the *front* of the
5145 * events queue instead of to the end.
5147 * TODO: get rid of this in 3.0 - Decide whether listeners should be
5148 * called in the order they were registered or in reverse order.
5152 * type - {String} Name of the event to register
5153 * obj - {Object} The object to bind the context to for the callback#.
5154 * If no object is specified, default is the Events's
5155 * 'object' property.
5156 * func - {Function} The callback function. If no callback is
5157 * specified, this function does nothing.
5159 registerPriority: function (type, obj, func) {
5160 this.register(type, obj, func, true);
5165 * Convenience method for unregistering listeners with a common scope.
5166 * Internally, this method calls <unregister> as shown in the examples
5171 * // unregister a single listener for the "loadstart" event
5172 * events.un({"loadstart": loadStartListener});
5174 * // this is equivalent to the following
5175 * events.unregister("loadstart", undefined, loadStartListener);
5177 * // unregister multiple listeners with the same `this` object
5179 * "loadstart": loadStartListener,
5180 * "loadend": loadEndListener,
5184 * // this is equivalent to the following
5185 * events.unregister("loadstart", object, loadStartListener);
5186 * events.unregister("loadend", object, loadEndListener);
5189 un: function(object) {
5190 for(var type in object) {
5191 if(type != "scope" && object.hasOwnProperty(type)) {
5192 this.unregister(type, object.scope, object[type]);
5198 * APIMethod: unregister
5202 * obj - {Object} If none specified, defaults to this.object
5205 unregister: function (type, obj, func) {
5209 var listeners = this.listeners[type];
5210 if (listeners != null) {
5211 for (var i=0, len=listeners.length; i<len; i++) {
5212 if (listeners[i].obj == obj && listeners[i].func == func) {
5213 listeners.splice(i, 1);
5222 * Remove all listeners for a given event type. If type is not registered,
5228 remove: function(type) {
5229 if (this.listeners[type] != null) {
5230 this.listeners[type] = [];
5235 * APIMethod: triggerEvent
5236 * Trigger a specified registered event.
5240 * evt - {Event || Object} will be passed to the listeners.
5243 * {Boolean} The last listener return. If a listener returns false, the
5244 * chain of listeners will stop getting called.
5246 triggerEvent: function (type, evt) {
5247 var listeners = this.listeners[type];
5250 if(!listeners || listeners.length == 0) {
5254 // prep evt object with object & div references
5258 evt.object = this.object;
5259 evt.element = this.element;
5264 // execute all callbacks registered for specified type
5265 // get a clone of the listeners array to
5266 // allow for splicing during callbacks
5267 listeners = listeners.slice();
5269 for (var i=0, len=listeners.length; i<len; i++) {
5270 var callback = listeners[i];
5271 // bind the context to callback.obj
5272 continueChain = callback.func.apply(callback.obj, [evt]);
5274 if ((continueChain != undefined) && (continueChain == false)) {
5275 // if callback returns false, execute no more callbacks.
5279 // don't fall through to other DOM elements
5280 if (!this.fallThrough) {
5281 OpenLayers.Event.stop(evt, true);
5283 return continueChain;
5287 * Method: handleBrowserEvent
5288 * Basically just a wrapper to the triggerEvent() function, but takes
5289 * care to set a property 'xy' on the event with the current mouse
5295 handleBrowserEvent: function (evt) {
5296 var type = evt.type, listeners = this.listeners[type];
5297 if(!listeners || listeners.length == 0) {
5298 // noone's listening, bail out
5301 // add clientX & clientY to all events - corresponds to average x, y
5302 var touches = evt.touches;
5303 if (touches && touches[0]) {
5306 var num = touches.length;
5308 for (var i=0; i<num; ++i) {
5309 touch = this.getTouchClientXY(touches[i]);
5313 evt.clientX = x / num;
5314 evt.clientY = y / num;
5316 if (this.includeXY) {
5317 evt.xy = this.getMousePosition(evt);
5319 this.triggerEvent(type, evt);
5323 * Method: getTouchClientXY
5324 * WebKit has a few bugs for clientX/clientY. This method detects them
5325 * and calculate the correct values.
5328 * evt - {Touch} a Touch object from a TouchEvent
5331 * {Object} An object with only clientX and clientY properties with the
5332 * calculated values.
5334 getTouchClientXY: function (evt) {
5335 // olMochWin is to override window, used for testing
5336 var win = window.olMockWin || window,
5337 winPageX = win.pageXOffset,
5338 winPageY = win.pageYOffset,
5342 if (evt.pageY === 0 && Math.floor(y) > Math.floor(evt.pageY) ||
5343 evt.pageX === 0 && Math.floor(x) > Math.floor(evt.pageX)) {
5344 // iOS4 include scroll offset in clientX/Y
5347 } else if (y < (evt.pageY - winPageY) || x < (evt.pageX - winPageX) ) {
5348 // Some Android browsers have totally bogus values for clientX/Y
5349 // when scrolling/zooming a page
5350 x = evt.pageX - winPageX;
5351 y = evt.pageY - winPageY;
5364 * APIMethod: clearMouseCache
5365 * Clear cached data about the mouse position. This should be called any
5366 * time the element that events are registered on changes position
5369 clearMouseCache: function() {
5370 this.element.scrolls = null;
5371 this.element.lefttop = null;
5372 this.element.offsets = null;
5376 * Method: getMousePosition
5382 * {<OpenLayers.Pixel>} The current xy coordinate of the mouse, adjusted
5385 getMousePosition: function (evt) {
5386 if (!this.includeXY) {
5387 this.clearMouseCache();
5388 } else if (!this.element.hasScrollEvent) {
5389 OpenLayers.Event.observe(window, "scroll", this.clearMouseListener);
5390 this.element.hasScrollEvent = true;
5393 if (!this.element.scrolls) {
5394 var viewportElement = OpenLayers.Util.getViewportElement();
5395 this.element.scrolls = [
5396 window.pageXOffset || viewportElement.scrollLeft,
5397 window.pageYOffset || viewportElement.scrollTop
5401 if (!this.element.lefttop) {
5402 this.element.lefttop = [
5403 (document.documentElement.clientLeft || 0),
5404 (document.documentElement.clientTop || 0)
5408 if (!this.element.offsets) {
5409 this.element.offsets = OpenLayers.Util.pagePosition(this.element);
5412 return new OpenLayers.Pixel(
5413 (evt.clientX + this.element.scrolls[0]) - this.element.offsets[0]
5414 - this.element.lefttop[0],
5415 (evt.clientY + this.element.scrolls[1]) - this.element.offsets[1]
5416 - this.element.lefttop[1]
5421 * Method: addMsTouchListener
5424 * element - {DOMElement} The DOM element to register the listener on
5425 * type - {String} The event type
5426 * handler - {Function} the handler
5428 addMsTouchListener: function (element, type, handler) {
5429 var eventHandler = this.eventHandler;
5430 var touches = this._msTouches;
5432 function msHandler(evt) {
5433 handler(OpenLayers.Util.applyDefaults({
5434 stopPropagation: function() {
5435 for (var i=touches.length-1; i>=0; --i) {
5436 touches[i].stopPropagation();
5439 preventDefault: function() {
5440 for (var i=touches.length-1; i>=0; --i) {
5441 touches[i].preventDefault();
5450 return this.addMsTouchListenerStart(element, type, msHandler);
5452 return this.addMsTouchListenerEnd(element, type, msHandler);
5454 return this.addMsTouchListenerMove(element, type, msHandler);
5456 throw 'Unknown touch event type';
5461 * Method: addMsTouchListenerStart
5464 * element - {DOMElement} The DOM element to register the listener on
5465 * type - {String} The event type
5466 * handler - {Function} the handler
5468 addMsTouchListenerStart: function(element, type, handler) {
5469 var touches = this._msTouches;
5471 var cb = function(e) {
5473 var alreadyInArray = false;
5474 for (var i=0, ii=touches.length; i<ii; ++i) {
5475 if (touches[i].pointerId == e.pointerId) {
5476 alreadyInArray = true;
5480 if (!alreadyInArray) {
5484 e.touches = touches.slice();
5488 OpenLayers.Event.observe(element, 'MSPointerDown', cb);
5490 // Need to also listen for end events to keep the _msTouches list
5492 var internalCb = function(e) {
5493 for (var i=0, ii=touches.length; i<ii; ++i) {
5494 if (touches[i].pointerId == e.pointerId) {
5495 touches.splice(i, 1);
5500 OpenLayers.Event.observe(element, 'MSPointerUp', internalCb);
5504 * Method: addMsTouchListenerMove
5507 * element - {DOMElement} The DOM element to register the listener on
5508 * type - {String} The event type
5509 * handler - {Function} the handler
5511 addMsTouchListenerMove: function (element, type, handler) {
5512 var touches = this._msTouches;
5513 var cb = function(e) {
5515 //Don't fire touch moves when mouse isn't down
5516 if (e.pointerType == e.MSPOINTER_TYPE_MOUSE && e.buttons == 0) {
5520 if (touches.length == 1 && touches[0].pageX == e.pageX &&
5521 touches[0].pageY == e.pageY) {
5522 // don't trigger event when pointer has not moved
5525 for (var i=0, ii=touches.length; i<ii; ++i) {
5526 if (touches[i].pointerId == e.pointerId) {
5532 e.touches = touches.slice();
5536 OpenLayers.Event.observe(element, 'MSPointerMove', cb);
5540 * Method: addMsTouchListenerEnd
5543 * element - {DOMElement} The DOM element to register the listener on
5544 * type - {String} The event type
5545 * handler - {Function} the handler
5547 addMsTouchListenerEnd: function (element, type, handler) {
5548 var touches = this._msTouches;
5550 var cb = function(e) {
5552 for (var i=0, ii=touches.length; i<ii; ++i) {
5553 if (touches[i].pointerId == e.pointerId) {
5554 touches.splice(i, 1);
5559 e.touches = touches.slice();
5563 OpenLayers.Event.observe(element, 'MSPointerUp', cb);
5566 CLASS_NAME: "OpenLayers.Events"
5568 /* ======================================================================
5569 OpenLayers/Events/buttonclick.js
5570 ====================================================================== */
5572 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
5573 * full list of contributors). Published under the 2-clause BSD license.
5574 * See license.txt in the OpenLayers distribution or repository for the
5575 * full text of the license. */
5578 * @requires OpenLayers/Events.js
5582 * Class: OpenLayers.Events.buttonclick
5583 * Extension event type for handling buttons on top of a dom element. This
5584 * event type fires "buttonclick" on its <target> when a button was
5585 * clicked. Buttons are detected by the "olButton" class.
5587 * This event type makes sure that button clicks do not interfere with other
5588 * events that are registered on the same <element>.
5590 * Event types provided by this extension:
5591 * - *buttonclick* Triggered when a button is clicked. Listeners receive an
5592 * object with a *buttonElement* property referencing the dom element of
5593 * the clicked button, and an *buttonXY* property with the click position
5594 * relative to the button.
5596 OpenLayers.Events.buttonclick = OpenLayers.Class({
5600 * {<OpenLayers.Events>} The events instance that the buttonclick event will
5607 * {Array} Events to observe and conditionally stop from propagating when
5608 * an element with the olButton class (or its olAlphaImg child) is
5612 'mousedown', 'mouseup', 'click', 'dblclick',
5613 'touchstart', 'touchmove', 'touchend', 'keydown'
5617 * Property: startRegEx
5618 * {RegExp} Regular expression to test Event.type for events that start
5619 * a buttonclick sequence.
5621 startRegEx: /^mousedown|touchstart$/,
5624 * Property: cancelRegEx
5625 * {RegExp} Regular expression to test Event.type for events that cancel
5626 * a buttonclick sequence.
5628 cancelRegEx: /^touchmove$/,
5631 * Property: completeRegEx
5632 * {RegExp} Regular expression to test Event.type for events that complete
5633 * a buttonclick sequence.
5635 completeRegEx: /^mouseup|touchend$/,
5638 * Property: startEvt
5639 * {Event} The event that started the click sequence
5643 * Constructor: OpenLayers.Events.buttonclick
5644 * Construct a buttonclick event type. Applications are not supposed to
5645 * create instances of this class - they are created on demand by
5646 * <OpenLayers.Events> instances.
5649 * target - {<OpenLayers.Events>} The events instance that the buttonclick
5650 * event will be triggered on.
5652 initialize: function(target) {
5653 this.target = target;
5654 for (var i=this.events.length-1; i>=0; --i) {
5655 this.target.register(this.events[i], this, this.buttonClick, {
5664 destroy: function() {
5665 for (var i=this.events.length-1; i>=0; --i) {
5666 this.target.unregister(this.events[i], this, this.buttonClick);
5672 * Method: getPressedButton
5673 * Get the pressed button, if any. Returns undefined if no button
5677 * element - {DOMElement} The event target.
5680 * {DOMElement} The button element, or undefined.
5682 getPressedButton: function(element) {
5683 var depth = 3, // limit the search depth
5686 if(OpenLayers.Element.hasClass(element, "olButton")) {
5691 element = element.parentNode;
5692 } while(--depth > 0 && element);
5698 * Check for event target elements that should be ignored by OpenLayers.
5701 * element - {DOMElement} The event target.
5703 ignore: function(element) {
5707 if (element.nodeName.toLowerCase() === 'a') {
5711 element = element.parentNode;
5712 } while (--depth > 0 && element);
5717 * Method: buttonClick
5718 * Check if a button was clicked, and fire the buttonclick event
5723 buttonClick: function(evt) {
5724 var propagate = true,
5725 element = OpenLayers.Event.element(evt);
5726 if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf("mouse"))) {
5727 // was a button pressed?
5728 var button = this.getPressedButton(element);
5730 if (evt.type === "keydown") {
5731 switch (evt.keyCode) {
5732 case OpenLayers.Event.KEY_RETURN:
5733 case OpenLayers.Event.KEY_SPACE:
5734 this.target.triggerEvent("buttonclick", {
5735 buttonElement: button
5737 OpenLayers.Event.stop(evt);
5741 } else if (this.startEvt) {
5742 if (this.completeRegEx.test(evt.type)) {
5743 var pos = OpenLayers.Util.pagePosition(button);
5744 var viewportElement = OpenLayers.Util.getViewportElement();
5745 var scrollTop = window.pageYOffset || viewportElement.scrollTop;
5746 var scrollLeft = window.pageXOffset || viewportElement.scrollLeft;
5747 pos[0] = pos[0] - scrollLeft;
5748 pos[1] = pos[1] - scrollTop;
5750 this.target.triggerEvent("buttonclick", {
5751 buttonElement: button,
5753 x: this.startEvt.clientX - pos[0],
5754 y: this.startEvt.clientY - pos[1]
5758 if (this.cancelRegEx.test(evt.type)) {
5759 delete this.startEvt;
5761 OpenLayers.Event.stop(evt);
5764 if (this.startRegEx.test(evt.type)) {
5765 this.startEvt = evt;
5766 OpenLayers.Event.stop(evt);
5770 propagate = !this.ignore(OpenLayers.Event.element(evt));
5771 delete this.startEvt;
5778 /* ======================================================================
5779 OpenLayers/Util/vendorPrefix.js
5780 ====================================================================== */
5782 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
5783 * full list of contributors). Published under the 2-clause BSD license.
5784 * See license.txt in the OpenLayers distribution or repository for the
5785 * full text of the license. */
5788 * @requires OpenLayers/SingleFile.js
5791 OpenLayers.Util = OpenLayers.Util || {};
5793 * Namespace: OpenLayers.Util.vendorPrefix
5794 * A collection of utility functions to detect vendor prefixed features
5796 OpenLayers.Util.vendorPrefix = (function() {
5799 var VENDOR_PREFIXES = ["", "O", "ms", "Moz", "Webkit"],
5800 divStyle = document.createElement("div").style,
5806 * Function: domToCss
5807 * Converts a upper camel case DOM style property name to a CSS property
5808 * i.e. transformOrigin -> transform-origin
5809 * or WebkitTransformOrigin -> -webkit-transform-origin
5812 * prefixedDom - {String} The property to convert
5815 * {String} The CSS property
5817 function domToCss(prefixedDom) {
5818 if (!prefixedDom) { return null; }
5820 replace(/([A-Z])/g, function(c) { return "-" + c.toLowerCase(); }).
5821 replace(/^ms-/, "-ms-");
5826 * Detect which property is used for a CSS property
5829 * property - {String} The standard (unprefixed) CSS property name
5832 * {String} The standard CSS property, prefixed property or null if not
5835 function css(property) {
5836 if (cssCache[property] === undefined) {
5837 var domProperty = property.
5838 replace(/(-[\s\S])/g, function(c) { return c.charAt(1).toUpperCase(); });
5839 var prefixedDom = style(domProperty);
5840 cssCache[property] = domToCss(prefixedDom);
5842 return cssCache[property];
5847 * Detect which property is used for a JS property/method
5850 * obj - {Object} The object to test on
5851 * property - {String} The standard (unprefixed) JS property name
5854 * {String} The standard JS property, prefixed property or null if not
5857 function js(obj, property) {
5858 if (jsCache[property] === undefined) {
5861 l = VENDOR_PREFIXES.length,
5863 isStyleObj = (typeof obj.cssText !== "undefined");
5865 jsCache[property] = null;
5867 prefix = VENDOR_PREFIXES[i];
5870 // js prefix should be lower-case, while style
5871 // properties have upper case on first character
5872 prefix = prefix.toLowerCase();
5874 tmpProp = prefix + property.charAt(0).toUpperCase() + property.slice(1);
5879 if(obj[tmpProp] !== undefined) {
5880 jsCache[property] = tmpProp;
5885 return jsCache[property];
5890 * Detect which property is used for a DOM style property
5893 * property - {String} The standard (unprefixed) style property name
5896 * {String} The standard style property, prefixed property or null if not
5899 function style(property) {
5900 return js(divStyle, property);
5913 /* ======================================================================
5914 OpenLayers/Animation.js
5915 ====================================================================== */
5917 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
5918 * full list of contributors). Published under the 2-clause BSD license.
5919 * See license.txt in the OpenLayers distribution or repository for the
5920 * full text of the license. */
5923 * @requires OpenLayers/SingleFile.js
5924 * @requires OpenLayers/Util/vendorPrefix.js
5928 * Namespace: OpenLayers.Animation
5929 * A collection of utility functions for executing methods that repaint a
5930 * portion of the browser window. These methods take advantage of the
5931 * browser's scheduled repaints where requestAnimationFrame is available.
5933 OpenLayers.Animation = (function(window) {
5936 * Property: isNative
5937 * {Boolean} true if a native requestAnimationFrame function is available
5939 var requestAnimationFrame = OpenLayers.Util.vendorPrefix.js(window, "requestAnimationFrame");
5940 var isNative = !!(requestAnimationFrame);
5943 * Function: requestFrame
5944 * Schedule a function to be called at the next available animation frame.
5945 * Uses the native method where available. Where requestAnimationFrame is
5946 * not available, setTimeout will be called with a 16ms delay.
5949 * callback - {Function} The function to be called at the next animation frame.
5950 * element - {DOMElement} Optional element that visually bounds the animation.
5952 var requestFrame = (function() {
5953 var request = window[requestAnimationFrame] ||
5954 function(callback, element) {
5955 window.setTimeout(callback, 16);
5957 // bind to window to avoid illegal invocation of native function
5958 return function(callback, element) {
5959 request.apply(window, [callback, element]);
5963 // private variables for animation loops
5969 * Executes a method with <requestFrame> in series for some
5973 * callback - {Function} The function to be called at the next animation frame.
5974 * duration - {Number} Optional duration for the loop. If not provided, the
5975 * animation loop will execute indefinitely.
5976 * element - {DOMElement} Optional element that visually bounds the animation.
5979 * {Number} Identifier for the animation loop. Used to stop animations with
5982 function start(callback, duration, element) {
5983 duration = duration > 0 ? duration : Number.POSITIVE_INFINITY;
5985 var start = +new Date;
5986 loops[id] = function() {
5987 if (loops[id] && +new Date - start <= duration) {
5990 requestFrame(loops[id], element);
5996 requestFrame(loops[id], element);
6002 * Terminates an animation loop started with <start>.
6005 * id - {Number} Identifier returned from <start>.
6013 requestFrame: requestFrame,
6019 /* ======================================================================
6021 ====================================================================== */
6023 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
6024 * full list of contributors). Published under the 2-clause BSD license.
6025 * See license.txt in the OpenLayers distribution or repository for the
6026 * full text of the license. */
6029 * @requires OpenLayers/BaseTypes/Class.js
6030 * @requires OpenLayers/Animation.js
6034 * Namespace: OpenLayers.Tween
6036 OpenLayers.Tween = OpenLayers.Class({
6039 * APIProperty: easing
6040 * {<OpenLayers.Easing>(Function)} Easing equation used for the animation
6041 * Defaultly set to OpenLayers.Easing.Expo.easeOut
6046 * APIProperty: begin
6047 * {Object} Values to start the animation with
6052 * APIProperty: finish
6053 * {Object} Values to finish the animation with
6058 * APIProperty: duration
6059 * {int} duration of the tween (number of steps)
6064 * APIProperty: callbacks
6065 * {Object} An object with start, eachStep and done properties whose values
6066 * are functions to be call during the animation. They are passed the
6067 * current computed value as argument.
6073 * {int} Step counter
6078 * APIProperty: minFrameRate
6079 * {Number} The minimum framerate for animations in frames per second. After
6080 * each step, the time spent in the animation is compared to the calculated
6081 * time at this frame rate. If the animation runs longer than the calculated
6082 * time, the next step is skipped. Default is 30.
6087 * Property: startTime
6088 * {Number} The timestamp of the first execution step. Used for skipping
6094 * Property: animationId
6095 * {int} Loop id returned by OpenLayers.Animation.start
6101 * {Boolean} Tells if the easing is currently playing
6106 * Constructor: OpenLayers.Tween
6110 * easing - {<OpenLayers.Easing>(Function)} easing function method to use
6112 initialize: function(easing) {
6113 this.easing = (easing) ? easing : OpenLayers.Easing.Expo.easeOut;
6118 * Plays the Tween, and calls the callback method on each step
6121 * begin - {Object} values to start the animation with
6122 * finish - {Object} values to finish the animation with
6123 * duration - {int} duration of the tween (number of steps)
6124 * options - {Object} hash of options (callbacks (start, eachStep, done),
6127 start: function(begin, finish, duration, options) {
6128 this.playing = true;
6130 this.finish = finish;
6131 this.duration = duration;
6132 this.callbacks = options.callbacks;
6133 this.minFrameRate = options.minFrameRate || 30;
6135 this.startTime = new Date().getTime();
6136 OpenLayers.Animation.stop(this.animationId);
6137 this.animationId = null;
6138 if (this.callbacks && this.callbacks.start) {
6139 this.callbacks.start.call(this, this.begin);
6141 this.animationId = OpenLayers.Animation.start(
6142 OpenLayers.Function.bind(this.play, this)
6148 * Stops the Tween, and calls the done callback
6149 * Doesn't do anything if animation is already finished
6152 if (!this.playing) {
6156 if (this.callbacks && this.callbacks.done) {
6157 this.callbacks.done.call(this, this.finish);
6159 OpenLayers.Animation.stop(this.animationId);
6160 this.animationId = null;
6161 this.playing = false;
6166 * Calls the appropriate easing method
6170 for (var i in this.begin) {
6171 var b = this.begin[i];
6172 var f = this.finish[i];
6173 if (b == null || f == null || isNaN(b) || isNaN(f)) {
6174 throw new TypeError('invalid value for Tween');
6178 value[i] = this.easing.apply(this, [this.time, b, c, this.duration]);
6182 if (this.callbacks && this.callbacks.eachStep) {
6183 // skip frames if frame rate drops below threshold
6184 if ((new Date().getTime() - this.startTime) / this.time <= 1000 / this.minFrameRate) {
6185 this.callbacks.eachStep.call(this, value);
6189 if (this.time > this.duration) {
6195 * Create empty functions for all easing methods.
6197 CLASS_NAME: "OpenLayers.Tween"
6201 * Namespace: OpenLayers.Easing
6204 * Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>
6206 OpenLayers.Easing = {
6208 * Create empty functions for all easing methods.
6210 CLASS_NAME: "OpenLayers.Easing"
6214 * Namespace: OpenLayers.Easing.Linear
6216 OpenLayers.Easing.Linear = {
6223 * b - {Float} beginning position
6224 * c - {Float} total change
6225 * d - {Float} duration of the transition
6230 easeIn: function(t, b, c, d) {
6239 * b - {Float} beginning position
6240 * c - {Float} total change
6241 * d - {Float} duration of the transition
6246 easeOut: function(t, b, c, d) {
6251 * Function: easeInOut
6255 * b - {Float} beginning position
6256 * c - {Float} total change
6257 * d - {Float} duration of the transition
6262 easeInOut: function(t, b, c, d) {
6266 CLASS_NAME: "OpenLayers.Easing.Linear"
6270 * Namespace: OpenLayers.Easing.Expo
6272 OpenLayers.Easing.Expo = {
6279 * b - {Float} beginning position
6280 * c - {Float} total change
6281 * d - {Float} duration of the transition
6286 easeIn: function(t, b, c, d) {
6287 return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
6295 * b - {Float} beginning position
6296 * c - {Float} total change
6297 * d - {Float} duration of the transition
6302 easeOut: function(t, b, c, d) {
6303 return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
6307 * Function: easeInOut
6311 * b - {Float} beginning position
6312 * c - {Float} total change
6313 * d - {Float} duration of the transition
6318 easeInOut: function(t, b, c, d) {
6320 if (t==d) return b+c;
6321 if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
6322 return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
6325 CLASS_NAME: "OpenLayers.Easing.Expo"
6329 * Namespace: OpenLayers.Easing.Quad
6331 OpenLayers.Easing.Quad = {
6338 * b - {Float} beginning position
6339 * c - {Float} total change
6340 * d - {Float} duration of the transition
6345 easeIn: function(t, b, c, d) {
6346 return c*(t/=d)*t + b;
6354 * b - {Float} beginning position
6355 * c - {Float} total change
6356 * d - {Float} duration of the transition
6361 easeOut: function(t, b, c, d) {
6362 return -c *(t/=d)*(t-2) + b;
6366 * Function: easeInOut
6370 * b - {Float} beginning position
6371 * c - {Float} total change
6372 * d - {Float} duration of the transition
6377 easeInOut: function(t, b, c, d) {
6378 if ((t/=d/2) < 1) return c/2*t*t + b;
6379 return -c/2 * ((--t)*(t-2) - 1) + b;
6382 CLASS_NAME: "OpenLayers.Easing.Quad"
6384 /* ======================================================================
6385 OpenLayers/Projection.js
6386 ====================================================================== */
6388 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
6389 * full list of contributors). Published under the 2-clause BSD license.
6390 * See license.txt in the OpenLayers distribution or repository for the
6391 * full text of the license. */
6394 * @requires OpenLayers/BaseTypes/Class.js
6395 * @requires OpenLayers/Util.js
6399 * Namespace: OpenLayers.Projection
6400 * Methods for coordinate transforms between coordinate systems. By default,
6401 * OpenLayers ships with the ability to transform coordinates between
6402 * geographic (EPSG:4326) and web or spherical mercator (EPSG:900913 et al.)
6403 * coordinate reference systems. See the <transform> method for details
6406 * Additional transforms may be added by using the <proj4js at http://proj4js.org/>
6407 * library. If the proj4js library is included, the <transform> method
6408 * will work between any two coordinate reference systems with proj4js
6411 * If the proj4js library is not included, or if you wish to allow transforms
6412 * between arbitrary coordinate reference systems, use the <addTransform>
6413 * method to register a custom transform method.
6415 OpenLayers.Projection = OpenLayers.Class({
6419 * {Object} Proj4js.Proj instance.
6424 * Property: projCode
6430 * Property: titleRegEx
6431 * {RegExp} regular expression to strip the title from a proj4js definition
6433 titleRegEx: /\+title=[^\+]*/,
6436 * Constructor: OpenLayers.Projection
6437 * This class offers several methods for interacting with a wrapped
6438 * pro4js projection object.
6441 * projCode - {String} A string identifying the Well Known Identifier for
6443 * options - {Object} An optional object to set additional properties
6444 * on the projection.
6447 * {<OpenLayers.Projection>} A projection object.
6449 initialize: function(projCode, options) {
6450 OpenLayers.Util.extend(this, options);
6451 this.projCode = projCode;
6452 if (typeof Proj4js == "object") {
6453 this.proj = new Proj4js.Proj(projCode);
6458 * APIMethod: getCode
6459 * Get the string SRS code.
6462 * {String} The SRS code.
6464 getCode: function() {
6465 return this.proj ? this.proj.srsCode : this.projCode;
6469 * APIMethod: getUnits
6470 * Get the units string for the projection -- returns null if
6471 * proj4js is not available.
6474 * {String} The units abbreviation.
6476 getUnits: function() {
6477 return this.proj ? this.proj.units : null;
6482 * Convert projection to string (getCode wrapper).
6485 * {String} The projection code.
6487 toString: function() {
6488 return this.getCode();
6493 * Test equality of two projection instances. Determines equality based
6494 * soley on the projection code.
6497 * {Boolean} The two projections are equivalent.
6499 equals: function(projection) {
6500 var p = projection, equals = false;
6502 if (!(p instanceof OpenLayers.Projection)) {
6503 p = new OpenLayers.Projection(p);
6505 if ((typeof Proj4js == "object") && this.proj.defData && p.proj.defData) {
6506 equals = this.proj.defData.replace(this.titleRegEx, "") ==
6507 p.proj.defData.replace(this.titleRegEx, "");
6508 } else if (p.getCode) {
6509 var source = this.getCode(), target = p.getCode();
6510 equals = source == target ||
6511 !!OpenLayers.Projection.transforms[source] &&
6512 OpenLayers.Projection.transforms[source][target] ===
6513 OpenLayers.Projection.nullTransform;
6520 * Destroy projection object.
6522 destroy: function() {
6524 delete this.projCode;
6527 CLASS_NAME: "OpenLayers.Projection"
6531 * Property: transforms
6532 * {Object} Transforms is an object, with from properties, each of which may
6533 * have a to property. This allows you to define projections without
6534 * requiring support for proj4js to be included.
6536 * This object has keys which correspond to a 'source' projection object. The
6537 * keys should be strings, corresponding to the projection.getCode() value.
6538 * Each source projection object should have a set of destination projection
6539 * keys included in the object.
6541 * Each value in the destination object should be a transformation function,
6542 * where the function is expected to be passed an object with a .x and a .y
6543 * property. The function should return the object, with the .x and .y
6544 * transformed according to the transformation function.
6546 * Note - Properties on this object should not be set directly. To add a
6547 * transform method to this object, use the <addTransform> method. For an
6548 * example of usage, see the OpenLayers.Layer.SphericalMercator file.
6550 OpenLayers.Projection.transforms = {};
6553 * APIProperty: defaults
6554 * {Object} Defaults for the SRS codes known to OpenLayers (currently
6555 * EPSG:4326, CRS:84, urn:ogc:def:crs:EPSG:6.6:4326, EPSG:900913, EPSG:3857,
6556 * EPSG:102113 and EPSG:102100). Keys are the SRS code, values are units,
6557 * maxExtent (the validity extent for the SRS) and yx (true if this SRS is
6558 * known to have a reverse axis order).
6560 OpenLayers.Projection.defaults = {
6563 maxExtent: [-180, -90, 180, 90],
6568 maxExtent: [-180, -90, 180, 90]
6572 maxExtent: [-20037508.34, -20037508.34, 20037508.34, 20037508.34]
6577 * APIMethod: addTransform
6578 * Set a custom transform method between two projections. Use this method in
6579 * cases where the proj4js lib is not available or where custom projections
6580 * need to be handled.
6583 * from - {String} The code for the source projection
6584 * to - {String} the code for the destination projection
6585 * method - {Function} A function that takes a point as an argument and
6586 * transforms that point from the source to the destination projection
6587 * in place. The original point should be modified.
6589 OpenLayers.Projection.addTransform = function(from, to, method) {
6590 if (method === OpenLayers.Projection.nullTransform) {
6591 var defaults = OpenLayers.Projection.defaults[from];
6592 if (defaults && !OpenLayers.Projection.defaults[to]) {
6593 OpenLayers.Projection.defaults[to] = defaults;
6596 if(!OpenLayers.Projection.transforms[from]) {
6597 OpenLayers.Projection.transforms[from] = {};
6599 OpenLayers.Projection.transforms[from][to] = method;
6603 * APIMethod: transform
6604 * Transform a point coordinate from one projection to another. Note that
6605 * the input point is transformed in place.
6608 * point - {<OpenLayers.Geometry.Point> | Object} An object with x and y
6609 * properties representing coordinates in those dimensions.
6610 * source - {OpenLayers.Projection} Source map coordinate system
6611 * dest - {OpenLayers.Projection} Destination map coordinate system
6614 * point - {object} A transformed coordinate. The original point is modified.
6616 OpenLayers.Projection.transform = function(point, source, dest) {
6617 if (source && dest) {
6618 if (!(source instanceof OpenLayers.Projection)) {
6619 source = new OpenLayers.Projection(source);
6621 if (!(dest instanceof OpenLayers.Projection)) {
6622 dest = new OpenLayers.Projection(dest);
6624 if (source.proj && dest.proj) {
6625 point = Proj4js.transform(source.proj, dest.proj, point);
6627 var sourceCode = source.getCode();
6628 var destCode = dest.getCode();
6629 var transforms = OpenLayers.Projection.transforms;
6630 if (transforms[sourceCode] && transforms[sourceCode][destCode]) {
6631 transforms[sourceCode][destCode](point);
6639 * APIFunction: nullTransform
6640 * A null transformation - useful for defining projection aliases when
6641 * proj4js is not available:
6644 * OpenLayers.Projection.addTransform("EPSG:3857", "EPSG:900913",
6645 * OpenLayers.Projection.nullTransform);
6646 * OpenLayers.Projection.addTransform("EPSG:900913", "EPSG:3857",
6647 * OpenLayers.Projection.nullTransform);
6650 OpenLayers.Projection.nullTransform = function(point) {
6655 * Note: Transforms for web mercator <-> geographic
6656 * OpenLayers recognizes EPSG:3857, EPSG:900913, EPSG:102113 and EPSG:102100.
6657 * OpenLayers originally started referring to EPSG:900913 as web mercator.
6658 * The EPSG has declared EPSG:3857 to be web mercator.
6659 * ArcGIS 10 recognizes the EPSG:3857, EPSG:102113, and EPSG:102100 as
6660 * 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.
6661 * For geographic, OpenLayers recognizes EPSG:4326, CRS:84 and
6662 * urn:ogc:def:crs:EPSG:6.6:4326. OpenLayers also knows about the reverse axis
6663 * order for EPSG:4326.
6667 var pole = 20037508.34;
6669 function inverseMercator(xy) {
6670 xy.x = 180 * xy.x / pole;
6671 xy.y = 180 / Math.PI * (2 * Math.atan(Math.exp((xy.y / pole) * Math.PI)) - Math.PI / 2);
6675 function forwardMercator(xy) {
6676 xy.x = xy.x * pole / 180;
6677 var y = Math.log(Math.tan((90 + xy.y) * Math.PI / 360)) / Math.PI * pole;
6678 xy.y = Math.max(-20037508.34, Math.min(y, 20037508.34));
6682 function map(base, codes) {
6683 var add = OpenLayers.Projection.addTransform;
6684 var same = OpenLayers.Projection.nullTransform;
6685 var i, len, code, other, j;
6686 for (i=0, len=codes.length; i<len; ++i) {
6688 add(base, code, forwardMercator);
6689 add(code, base, inverseMercator);
6690 for (j=i+1; j<len; ++j) {
6692 add(code, other, same);
6693 add(other, code, same);
6698 // list of equivalent codes for web mercator
6699 var mercator = ["EPSG:900913", "EPSG:3857", "EPSG:102113", "EPSG:102100"],
6700 geographic = ["CRS:84", "urn:ogc:def:crs:EPSG:6.6:4326", "EPSG:4326"],
6702 for (i=mercator.length-1; i>=0; --i) {
6703 map(mercator[i], geographic);
6705 for (i=geographic.length-1; i>=0; --i) {
6706 map(geographic[i], mercator);
6710 /* ======================================================================
6712 ====================================================================== */
6714 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
6715 * full list of contributors). Published under the 2-clause BSD license.
6716 * See license.txt in the OpenLayers distribution or repository for the
6717 * full text of the license. */
6720 * @requires OpenLayers/BaseTypes/Class.js
6721 * @requires OpenLayers/Util.js
6722 * @requires OpenLayers/Util/vendorPrefix.js
6723 * @requires OpenLayers/Events.js
6724 * @requires OpenLayers/Tween.js
6725 * @requires OpenLayers/Projection.js
6729 * Class: OpenLayers.Map
6730 * Instances of OpenLayers.Map are interactive maps embedded in a web page.
6731 * Create a new map with the <OpenLayers.Map> constructor.
6733 * On their own maps do not provide much functionality. To extend a map
6734 * it's necessary to add controls (<OpenLayers.Control>) and
6735 * layers (<OpenLayers.Layer>) to the map.
6737 OpenLayers.Map = OpenLayers.Class({
6740 * Constant: Z_INDEX_BASE
6741 * {Object} Base z-indexes for different classes of thing
6752 * APIProperty: events
6753 * {<OpenLayers.Events>}
6755 * Register a listener for a particular event with the following syntax:
6757 * map.events.register(type, obj, listener);
6760 * Listeners will be called with a reference to an event object. The
6761 * properties of this event depends on exactly what happened.
6763 * All event objects have at least the following properties:
6764 * object - {Object} A reference to map.events.object.
6765 * element - {DOMElement} A reference to map.events.element.
6767 * Browser events have the following additional properties:
6768 * xy - {<OpenLayers.Pixel>} The pixel location of the event (relative
6769 * to the the map viewport).
6771 * Supported map event types:
6772 * preaddlayer - triggered before a layer has been added. The event
6773 * object will include a *layer* property that references the layer
6774 * to be added. When a listener returns "false" the adding will be
6776 * addlayer - triggered after a layer has been added. The event object
6777 * will include a *layer* property that references the added layer.
6778 * preremovelayer - triggered before a layer has been removed. The event
6779 * object will include a *layer* property that references the layer
6780 * to be removed. When a listener returns "false" the removal will be
6782 * removelayer - triggered after a layer has been removed. The event
6783 * object will include a *layer* property that references the removed
6785 * changelayer - triggered after a layer name change, order change,
6786 * opacity change, params change, visibility change (actual visibility,
6787 * not the layer's visibility property) or attribution change (due to
6788 * extent change). Listeners will receive an event object with *layer*
6789 * and *property* properties. The *layer* property will be a reference
6790 * to the changed layer. The *property* property will be a key to the
6791 * changed property (name, order, opacity, params, visibility or
6793 * movestart - triggered after the start of a drag, pan, or zoom. The event
6794 * object may include a *zoomChanged* property that tells whether the
6796 * move - triggered after each drag, pan, or zoom
6797 * moveend - triggered after a drag, pan, or zoom completes
6798 * zoomend - triggered after a zoom completes
6799 * mouseover - triggered after mouseover the map
6800 * mouseout - triggered after mouseout the map
6801 * mousemove - triggered after mousemove the map
6802 * changebaselayer - triggered after the base layer changes
6803 * updatesize - triggered after the <updateSize> method was executed
6808 * {String} Unique identifier for the map
6813 * Property: fractionalZoom
6814 * {Boolean} For a base layer that supports it, allow the map resolution
6815 * to be set to a value between one of the values in the resolutions
6816 * array. Default is false.
6818 * When fractionalZoom is set to true, it is possible to zoom to
6819 * an arbitrary extent. This requires a base layer from a source
6820 * that supports requests for arbitrary extents (i.e. not cached
6821 * tiles on a regular lattice). This means that fractionalZoom
6822 * will not work with commercial layers (Google, Yahoo, VE), layers
6823 * using TileCache, or any other pre-cached data sources.
6825 * If you are using fractionalZoom, then you should also use
6826 * <getResolutionForZoom> instead of layer.resolutions[zoom] as the
6827 * former works for non-integer zoom levels.
6829 fractionalZoom: false,
6832 * APIProperty: events
6833 * {<OpenLayers.Events>} An events object that handles all
6839 * APIProperty: allOverlays
6840 * {Boolean} Allow the map to function with "overlays" only. Defaults to
6841 * false. If true, the lowest layer in the draw order will act as
6842 * the base layer. In addition, if set to true, all layers will
6843 * have isBaseLayer set to false when they are added to the map.
6846 * If you set map.allOverlays to true, then you *cannot* use
6847 * map.setBaseLayer or layer.setIsBaseLayer. With allOverlays true,
6848 * the lowest layer in the draw layer is the base layer. So, to change
6849 * the base layer, use <setLayerIndex> or <raiseLayer> to set the layer
6856 * {DOMElement|String} The element that contains the map (or an id for
6857 * that element). If the <OpenLayers.Map> constructor is called
6858 * with two arguments, this should be provided as the first argument.
6859 * Alternatively, the map constructor can be called with the options
6860 * object as the only argument. In this case (one argument), a
6861 * div property may or may not be provided. If the div property
6862 * is not provided, the map can be rendered to a container later
6863 * using the <render> method.
6866 * If you are calling <render> after map construction, do not use
6867 * <maxResolution> auto. Instead, divide your <maxExtent> by your
6868 * maximum expected dimension.
6873 * Property: dragging
6874 * {Boolean} The map is currently being dragged.
6880 * {<OpenLayers.Size>} Size of the main div (this.div)
6885 * Property: viewPortDiv
6886 * {HTMLDivElement} The element that represents the map viewport
6891 * Property: layerContainerOrigin
6892 * {<OpenLayers.LonLat>} The lonlat at which the later container was
6893 * re-initialized (on-zoom)
6895 layerContainerOrigin: null,
6898 * Property: layerContainerDiv
6899 * {HTMLDivElement} The element that contains the layers.
6901 layerContainerDiv: null,
6904 * APIProperty: layers
6905 * {Array(<OpenLayers.Layer>)} Ordered list of layers in the map
6910 * APIProperty: controls
6911 * {Array(<OpenLayers.Control>)} List of controls associated with the map.
6913 * If not provided in the map options at construction, the map will
6914 * by default be given the following controls if present in the build:
6915 * - <OpenLayers.Control.Navigation> or <OpenLayers.Control.TouchNavigation>
6916 * - <OpenLayers.Control.Zoom> or <OpenLayers.Control.PanZoom>
6917 * - <OpenLayers.Control.ArgParser>
6918 * - <OpenLayers.Control.Attribution>
6924 * {Array(<OpenLayers.Popup>)} List of popups associated with the map
6929 * APIProperty: baseLayer
6930 * {<OpenLayers.Layer>} The currently selected base layer. This determines
6931 * min/max zoom level, projection, etc.
6937 * {<OpenLayers.LonLat>} The current center of the map
6942 * Property: resolution
6943 * {Float} The resolution of the map.
6949 * {Integer} The current zoom level of the map
6954 * Property: panRatio
6955 * {Float} The ratio of the current extent within
6956 * which panning will tween.
6961 * APIProperty: options
6962 * {Object} The options object passed to the class constructor. Read-only.
6969 * APIProperty: tileSize
6970 * {<OpenLayers.Size>} Set in the map options to override the default tile
6971 * size for this map.
6976 * APIProperty: projection
6977 * {String} Set in the map options to specify the default projection
6978 * for layers added to this map. When using a projection other than EPSG:4326
6979 * (CRS:84, Geographic) or EPSG:3857 (EPSG:900913, Web Mercator),
6980 * also set maxExtent, maxResolution or resolutions. Default is "EPSG:4326".
6981 * Note that the projection of the map is usually determined
6982 * by that of the current baseLayer (see <baseLayer> and <getProjectionObject>).
6984 projection: "EPSG:4326",
6987 * APIProperty: units
6988 * {String} The map units. Possible values are 'degrees' (or 'dd'), 'm',
6989 * 'ft', 'km', 'mi', 'inches'. Normally taken from the projection.
6990 * Only required if both map and layers do not define a projection,
6991 * or if they define a projection which does not define units
6996 * APIProperty: resolutions
6997 * {Array(Float)} A list of map resolutions (map units per pixel) in
6998 * descending order. If this is not set in the layer constructor, it
6999 * will be set based on other resolution related properties
7000 * (maxExtent, maxResolution, maxScale, etc.).
7005 * APIProperty: maxResolution
7006 * {Float} Required if you are not displaying the whole world on a tile
7007 * with the size specified in <tileSize>.
7009 maxResolution: null,
7012 * APIProperty: minResolution
7015 minResolution: null,
7018 * APIProperty: maxScale
7024 * APIProperty: minScale
7030 * APIProperty: maxExtent
7031 * {<OpenLayers.Bounds>|Array} If provided as an array, the array
7032 * should consist of four values (left, bottom, right, top).
7033 * The maximum extent for the map.
7034 * Default depends on projection; if this is one of those defined in OpenLayers.Projection.defaults
7035 * (EPSG:4326 or web mercator), maxExtent will be set to the value defined there;
7036 * else, defaults to null.
7037 * To restrict user panning and zooming of the map, use <restrictedExtent> instead.
7038 * The value for <maxExtent> will change calculations for tile URLs.
7043 * APIProperty: minExtent
7044 * {<OpenLayers.Bounds>|Array} If provided as an array, the array
7045 * should consist of four values (left, bottom, right, top).
7046 * The minimum extent for the map. Defaults to null.
7051 * APIProperty: restrictedExtent
7052 * {<OpenLayers.Bounds>|Array} If provided as an array, the array
7053 * should consist of four values (left, bottom, right, top).
7054 * Limit map navigation to this extent where possible.
7055 * If a non-null restrictedExtent is set, panning will be restricted
7056 * to the given bounds. In addition, zooming to a resolution that
7057 * displays more than the restricted extent will center the map
7058 * on the restricted extent. If you wish to limit the zoom level
7059 * or resolution, use maxResolution.
7061 restrictedExtent: null,
7064 * APIProperty: numZoomLevels
7065 * {Integer} Number of zoom levels for the map. Defaults to 16. Set a
7066 * different value in the map options if needed.
7071 * APIProperty: theme
7072 * {String} Relative path to a CSS file from which to load theme styles.
7073 * Specify null in the map options (e.g. {theme: null}) if you
7074 * want to get cascading style declarations - by putting links to
7075 * stylesheets or style declarations directly in your page.
7080 * APIProperty: displayProjection
7081 * {<OpenLayers.Projection>} Requires proj4js support for projections other
7082 * than EPSG:4326 or EPSG:900913/EPSG:3857. Projection used by
7083 * several controls to display data to user. If this property is set,
7084 * it will be set on any control which has a null displayProjection
7085 * property at the time the control is added to the map.
7087 displayProjection: null,
7090 * APIProperty: tileManager
7091 * {<OpenLayers.TileManager>|Object} By default, and if the build contains
7092 * TileManager.js, the map will use the TileManager to queue image requests
7093 * and to cache tile image elements. To create a map without a TileManager
7094 * configure the map with tileManager: null. To create a TileManager with
7095 * non-default options, supply the options instead or alternatively supply
7096 * an instance of {<OpenLayers.TileManager>}.
7100 * APIProperty: fallThrough
7101 * {Boolean} Should OpenLayers allow events on the map to fall through to
7102 * other elements on the page, or should it swallow them? (#457)
7103 * Default is to swallow.
7108 * APIProperty: autoUpdateSize
7109 * {Boolean} Should OpenLayers automatically update the size of the map
7110 * when the resize event is fired. Default is true.
7112 autoUpdateSize: true,
7115 * APIProperty: eventListeners
7116 * {Object} If set as an option at construction, the eventListeners
7117 * object will be registered with <OpenLayers.Events.on>. Object
7118 * structure must be a listeners object as shown in the example for
7119 * the events.on method.
7121 eventListeners: null,
7124 * Property: panTween
7125 * {<OpenLayers.Tween>} Animated panning tween object, see panTo()
7130 * APIProperty: panMethod
7131 * {Function} The Easing function to be used for tweening. Default is
7132 * OpenLayers.Easing.Expo.easeOut. Setting this to 'null' turns off
7135 panMethod: OpenLayers.Easing.Expo.easeOut,
7138 * Property: panDuration
7139 * {Integer} The number of steps to be passed to the
7140 * OpenLayers.Tween.start() method when the map is
7147 * Property: zoomTween
7148 * {<OpenLayers.Tween>} Animated zooming tween object, see zoomTo()
7153 * APIProperty: zoomMethod
7154 * {Function} The Easing function to be used for tweening. Default is
7155 * OpenLayers.Easing.Quad.easeOut. Setting this to 'null' turns off
7158 zoomMethod: OpenLayers.Easing.Quad.easeOut,
7161 * Property: zoomDuration
7162 * {Integer} The number of steps to be passed to the
7163 * OpenLayers.Tween.start() method when the map is zoomed.
7169 * Property: paddingForPopups
7170 * {<OpenLayers.Bounds>} Outside margin of the popup. Used to prevent
7171 * the popup from getting too close to the map border.
7173 paddingForPopups : null,
7176 * Property: layerContainerOriginPx
7177 * {Object} Cached object representing the layer container origin (in pixels).
7179 layerContainerOriginPx: null,
7183 * {Object} An object with a 'x' and 'y' values that is the lower
7184 * left of maxExtent in viewport pixel space.
7185 * Used to verify in moveByPx that the new location we're moving to
7186 * is valid. It is also used in the getLonLatFromViewPortPx function
7193 * {Object} An object with a 'x' and 'y' values that is the top
7194 * right of maxExtent in viewport pixel space.
7195 * Used to verify in moveByPx that the new location we're moving to
7201 * Constructor: OpenLayers.Map
7202 * Constructor for a new OpenLayers.Map instance. There are two possible
7203 * ways to call the map constructor. See the examples below.
7206 * div - {DOMElement|String} The element or id of an element in your page
7207 * that will contain the map. May be omitted if the <div> option is
7208 * provided or if you intend to call the <render> method later.
7209 * options - {Object} Optional object with properties to tag onto the map.
7211 * Valid options (in addition to the listed API properties):
7212 * center - {<OpenLayers.LonLat>|Array} The default initial center of the map.
7213 * If provided as array, the first value is the x coordinate,
7214 * and the 2nd value is the y coordinate.
7215 * Only specify if <layers> is provided.
7216 * Note that if an ArgParser/Permalink control is present,
7217 * and the querystring contains coordinates, center will be set
7218 * by that, and this option will be ignored.
7219 * zoom - {Number} The initial zoom level for the map. Only specify if
7220 * <layers> is provided.
7221 * Note that if an ArgParser/Permalink control is present,
7222 * and the querystring contains a zoom level, zoom will be set
7223 * by that, and this option will be ignored.
7224 * extent - {<OpenLayers.Bounds>|Array} The initial extent of the map.
7225 * If provided as an array, the array should consist of
7226 * four values (left, bottom, right, top).
7227 * Only specify if <center> and <zoom> are not provided.
7231 * // create a map with default options in an element with the id "map1"
7232 * var map = new OpenLayers.Map("map1");
7234 * // create a map with non-default options in an element with id "map2"
7236 * projection: "EPSG:3857",
7237 * maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000),
7238 * center: new OpenLayers.LonLat(-12356463.476333, 5621521.4854095)
7240 * var map = new OpenLayers.Map("map2", options);
7242 * // map with non-default options - same as above but with a single argument,
7243 * // a restricted extent, and using arrays for bounds and center
7244 * var map = new OpenLayers.Map({
7246 * projection: "EPSG:3857",
7247 * maxExtent: [-18924313.432222, -15538711.094146, 18924313.432222, 15538711.094146],
7248 * restrictedExtent: [-13358338.893333, -9608371.5085962, 13358338.893333, 9608371.5085962],
7249 * center: [-12356463.476333, 5621521.4854095]
7252 * // create a map without a reference to a container - call render later
7253 * var map = new OpenLayers.Map({
7254 * projection: "EPSG:3857",
7255 * maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000)
7259 initialize: function (div, options) {
7261 // If only one argument is provided, check if it is an object.
7262 if(arguments.length === 1 && typeof div === "object") {
7264 div = options && options.div;
7267 // Simple-type defaults are set in class definition.
7268 // Now set complex-type defaults
7269 this.tileSize = new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH,
7270 OpenLayers.Map.TILE_HEIGHT);
7272 this.paddingForPopups = new OpenLayers.Bounds(15, 15, 15, 15);
7274 this.theme = OpenLayers._getScriptLocation() +
7275 'theme/default/style.css';
7277 // backup original options
7278 this.options = OpenLayers.Util.extend({}, options);
7280 // now override default options
7281 OpenLayers.Util.extend(this, options);
7283 var projCode = this.projection instanceof OpenLayers.Projection ?
7284 this.projection.projCode : this.projection;
7285 OpenLayers.Util.applyDefaults(this, OpenLayers.Projection.defaults[projCode]);
7287 // allow extents and center to be arrays
7288 if (this.maxExtent && !(this.maxExtent instanceof OpenLayers.Bounds)) {
7289 this.maxExtent = new OpenLayers.Bounds(this.maxExtent);
7291 if (this.minExtent && !(this.minExtent instanceof OpenLayers.Bounds)) {
7292 this.minExtent = new OpenLayers.Bounds(this.minExtent);
7294 if (this.restrictedExtent && !(this.restrictedExtent instanceof OpenLayers.Bounds)) {
7295 this.restrictedExtent = new OpenLayers.Bounds(this.restrictedExtent);
7297 if (this.center && !(this.center instanceof OpenLayers.LonLat)) {
7298 this.center = new OpenLayers.LonLat(this.center);
7301 // initialize layers array
7304 this.id = OpenLayers.Util.createUniqueID("OpenLayers.Map_");
7306 this.div = OpenLayers.Util.getElement(div);
7308 this.div = document.createElement("div");
7309 this.div.style.height = "1px";
7310 this.div.style.width = "1px";
7313 OpenLayers.Element.addClass(this.div, 'olMap');
7315 // the viewPortDiv is the outermost div we modify
7316 var id = this.id + "_OpenLayers_ViewPort";
7317 this.viewPortDiv = OpenLayers.Util.createDiv(id, null, null, null,
7320 this.viewPortDiv.style.width = "100%";
7321 this.viewPortDiv.style.height = "100%";
7322 this.viewPortDiv.className = "olMapViewport";
7323 this.div.appendChild(this.viewPortDiv);
7325 this.events = new OpenLayers.Events(
7326 this, this.viewPortDiv, null, this.fallThrough,
7330 if (OpenLayers.TileManager && this.tileManager !== null) {
7331 if (!(this.tileManager instanceof OpenLayers.TileManager)) {
7332 this.tileManager = new OpenLayers.TileManager(this.tileManager);
7334 this.tileManager.addMap(this);
7337 // the layerContainerDiv is the one that holds all the layers
7338 id = this.id + "_OpenLayers_Container";
7339 this.layerContainerDiv = OpenLayers.Util.createDiv(id);
7340 this.layerContainerDiv.style.zIndex=this.Z_INDEX_BASE['Popup']-1;
7341 this.layerContainerOriginPx = {x: 0, y: 0};
7342 this.applyTransform();
7344 this.viewPortDiv.appendChild(this.layerContainerDiv);
7347 if(this.eventListeners instanceof Object) {
7348 this.events.on(this.eventListeners);
7351 if (this.autoUpdateSize === true) {
7352 // updateSize on catching the window's resize
7353 // Note that this is ok, as updateSize() does nothing if the
7354 // map's size has not actually changed.
7355 this.updateSizeDestroy = OpenLayers.Function.bind(this.updateSize,
7357 OpenLayers.Event.observe(window, 'resize',
7358 this.updateSizeDestroy);
7361 // only append link stylesheet if the theme property is set
7363 // check existing links for equivalent url
7365 var nodes = document.getElementsByTagName('link');
7366 for(var i=0, len=nodes.length; i<len; ++i) {
7367 if(OpenLayers.Util.isEquivalentUrl(nodes.item(i).href,
7373 // only add a new node if one with an equivalent url hasn't already
7376 var cssNode = document.createElement('link');
7377 cssNode.setAttribute('rel', 'stylesheet');
7378 cssNode.setAttribute('type', 'text/css');
7379 cssNode.setAttribute('href', this.theme);
7380 document.getElementsByTagName('head')[0].appendChild(cssNode);
7384 if (this.controls == null) { // default controls
7386 if (OpenLayers.Control != null) { // running full or lite?
7387 // Navigation or TouchNavigation depending on what is in build
7388 if (OpenLayers.Control.Navigation) {
7389 this.controls.push(new OpenLayers.Control.Navigation());
7390 } else if (OpenLayers.Control.TouchNavigation) {
7391 this.controls.push(new OpenLayers.Control.TouchNavigation());
7393 if (OpenLayers.Control.Zoom) {
7394 this.controls.push(new OpenLayers.Control.Zoom());
7395 } else if (OpenLayers.Control.PanZoom) {
7396 this.controls.push(new OpenLayers.Control.PanZoom());
7399 if (OpenLayers.Control.ArgParser) {
7400 this.controls.push(new OpenLayers.Control.ArgParser());
7402 if (OpenLayers.Control.Attribution) {
7403 this.controls.push(new OpenLayers.Control.Attribution());
7408 for(var i=0, len=this.controls.length; i<len; i++) {
7409 this.addControlToMap(this.controls[i]);
7414 this.unloadDestroy = OpenLayers.Function.bind(this.destroy, this);
7417 // always call map.destroy()
7418 OpenLayers.Event.observe(window, 'unload', this.unloadDestroy);
7420 // add any initial layers
7421 if (options && options.layers) {
7423 * If you have set options.center, the map center property will be
7424 * set at this point. However, since setCenter has not been called,
7425 * addLayers gets confused. So we delete the map center in this
7426 * case. Because the check below uses options.center, it will
7427 * be properly set below.
7431 this.addLayers(options.layers);
7432 // set center (and optionally zoom)
7433 if (options.center && !this.getCenter()) {
7434 // zoom can be undefined here
7435 this.setCenter(options.center, options.zoom);
7439 if (this.panMethod) {
7440 this.panTween = new OpenLayers.Tween(this.panMethod);
7442 if (this.zoomMethod && this.applyTransform.transform) {
7443 this.zoomTween = new OpenLayers.Tween(this.zoomMethod);
7448 * APIMethod: getViewport
7449 * Get the DOMElement representing the view port.
7454 getViewport: function() {
7455 return this.viewPortDiv;
7460 * Render the map to a specified container.
7463 * div - {String|DOMElement} The container that the map should be rendered
7464 * to. If different than the current container, the map viewport
7465 * will be moved from the current to the new container.
7467 render: function(div) {
7468 this.div = OpenLayers.Util.getElement(div);
7469 OpenLayers.Element.addClass(this.div, 'olMap');
7470 this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);
7471 this.div.appendChild(this.viewPortDiv);
7476 * Method: unloadDestroy
7477 * Function that is called to destroy the map on page unload. stored here
7478 * so that if map is manually destroyed, we can unregister this.
7480 unloadDestroy: null,
7483 * Method: updateSizeDestroy
7484 * When the map is destroyed, we need to stop listening to updateSize
7485 * events: this method stores the function we need to unregister in
7488 updateSizeDestroy: null,
7491 * APIMethod: destroy
7493 * Note that if you are using an application which removes a container
7494 * of the map from the DOM, you need to ensure that you destroy the
7495 * map *before* this happens; otherwise, the page unload handler
7496 * will fail because the DOM elements that map.destroy() wants
7497 * to clean up will be gone. (See
7498 * http://trac.osgeo.org/openlayers/ticket/2277 for more information).
7499 * This will apply to GeoExt and also to other applications which
7500 * modify the DOM of the container of the OpenLayers Map.
7502 destroy:function() {
7503 // if unloadDestroy is null, we've already been destroyed
7504 if (!this.unloadDestroy) {
7508 // make sure panning doesn't continue after destruction
7510 this.panTween.stop();
7511 this.panTween = null;
7513 // make sure zooming doesn't continue after destruction
7514 if(this.zoomTween) {
7515 this.zoomTween.stop();
7516 this.zoomTween = null;
7519 // map has been destroyed. dont do it again!
7520 OpenLayers.Event.stopObserving(window, 'unload', this.unloadDestroy);
7521 this.unloadDestroy = null;
7523 if (this.updateSizeDestroy) {
7524 OpenLayers.Event.stopObserving(window, 'resize',
7525 this.updateSizeDestroy);
7528 this.paddingForPopups = null;
7530 if (this.controls != null) {
7531 for (var i = this.controls.length - 1; i>=0; --i) {
7532 this.controls[i].destroy();
7534 this.controls = null;
7536 if (this.layers != null) {
7537 for (var i = this.layers.length - 1; i>=0; --i) {
7538 //pass 'false' to destroy so that map wont try to set a new
7539 // baselayer after each baselayer is removed
7540 this.layers[i].destroy(false);
7544 if (this.viewPortDiv && this.viewPortDiv.parentNode) {
7545 this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);
7547 this.viewPortDiv = null;
7549 if (this.tileManager) {
7550 this.tileManager.removeMap(this);
7551 this.tileManager = null;
7554 if(this.eventListeners) {
7555 this.events.un(this.eventListeners);
7556 this.eventListeners = null;
7558 this.events.destroy();
7561 this.options = null;
7565 * APIMethod: setOptions
7566 * Change the map options
7569 * options - {Object} Hashtable of options to tag to the map
7571 setOptions: function(options) {
7572 var updatePxExtent = this.minPx &&
7573 options.restrictedExtent != this.restrictedExtent;
7574 OpenLayers.Util.extend(this, options);
7575 // force recalculation of minPx and maxPx
7576 updatePxExtent && this.moveTo(this.getCachedCenter(), this.zoom, {
7577 forceZoomChange: true
7582 * APIMethod: getTileSize
7583 * Get the tile size for the map
7586 * {<OpenLayers.Size>}
7588 getTileSize: function() {
7589 return this.tileSize;
7595 * Get a list of objects given a property and a match item.
7598 * array - {String} A property on the map whose value is an array.
7599 * property - {String} A property on each item of the given array.
7600 * match - {String | Object} A string to match. Can also be a regular
7601 * expression literal or object. In addition, it can be any object
7602 * with a method named test. For reqular expressions or other, if
7603 * match.test(map[array][i][property]) evaluates to true, the item will
7604 * be included in the array returned. If no items are found, an empty
7605 * array is returned.
7608 * {Array} An array of items where the given property matches the given
7611 getBy: function(array, property, match) {
7612 var test = (typeof match.test == "function");
7613 var found = OpenLayers.Array.filter(this[array], function(item) {
7614 return item[property] == match || (test && match.test(item[property]));
7620 * APIMethod: getLayersBy
7621 * Get a list of layers with properties matching the given criteria.
7624 * property - {String} A layer property to be matched.
7625 * match - {String | Object} A string to match. Can also be a regular
7626 * expression literal or object. In addition, it can be any object
7627 * with a method named test. For reqular expressions or other, if
7628 * match.test(layer[property]) evaluates to true, the layer will be
7629 * included in the array returned. If no layers are found, an empty
7630 * array is returned.
7633 * {Array(<OpenLayers.Layer>)} A list of layers matching the given criteria.
7634 * An empty array is returned if no matches are found.
7636 getLayersBy: function(property, match) {
7637 return this.getBy("layers", property, match);
7641 * APIMethod: getLayersByName
7642 * Get a list of layers with names matching the given name.
7645 * match - {String | Object} A layer name. The name can also be a regular
7646 * expression literal or object. In addition, it can be any object
7647 * with a method named test. For reqular expressions or other, if
7648 * name.test(layer.name) evaluates to true, the layer will be included
7649 * in the list of layers returned. If no layers are found, an empty
7650 * array is returned.
7653 * {Array(<OpenLayers.Layer>)} A list of layers matching the given name.
7654 * An empty array is returned if no matches are found.
7656 getLayersByName: function(match) {
7657 return this.getLayersBy("name", match);
7661 * APIMethod: getLayersByClass
7662 * Get a list of layers of a given class (CLASS_NAME).
7665 * match - {String | Object} A layer class name. The match can also be a
7666 * regular expression literal or object. In addition, it can be any
7667 * object with a method named test. For reqular expressions or other,
7668 * if type.test(layer.CLASS_NAME) evaluates to true, the layer will
7669 * be included in the list of layers returned. If no layers are
7670 * found, an empty array is returned.
7673 * {Array(<OpenLayers.Layer>)} A list of layers matching the given class.
7674 * An empty array is returned if no matches are found.
7676 getLayersByClass: function(match) {
7677 return this.getLayersBy("CLASS_NAME", match);
7681 * APIMethod: getControlsBy
7682 * Get a list of controls with properties matching the given criteria.
7685 * property - {String} A control property to be matched.
7686 * match - {String | Object} A string to match. Can also be a regular
7687 * expression literal or object. In addition, it can be any object
7688 * with a method named test. For reqular expressions or other, if
7689 * match.test(layer[property]) evaluates to true, the layer will be
7690 * included in the array returned. If no layers are found, an empty
7691 * array is returned.
7694 * {Array(<OpenLayers.Control>)} A list of controls matching the given
7695 * criteria. An empty array is returned if no matches are found.
7697 getControlsBy: function(property, match) {
7698 return this.getBy("controls", property, match);
7702 * APIMethod: getControlsByClass
7703 * Get a list of controls of a given class (CLASS_NAME).
7706 * match - {String | Object} A control class name. The match can also be a
7707 * regular expression literal or object. In addition, it can be any
7708 * object with a method named test. For reqular expressions or other,
7709 * if type.test(control.CLASS_NAME) evaluates to true, the control will
7710 * be included in the list of controls returned. If no controls are
7711 * found, an empty array is returned.
7714 * {Array(<OpenLayers.Control>)} A list of controls matching the given class.
7715 * An empty array is returned if no matches are found.
7717 getControlsByClass: function(match) {
7718 return this.getControlsBy("CLASS_NAME", match);
7721 /********************************************************/
7723 /* Layer Functions */
7725 /* The following functions deal with adding and */
7726 /* removing Layers to and from the Map */
7728 /********************************************************/
7731 * APIMethod: getLayer
7732 * Get a layer based on its id
7735 * id - {String} A layer id
7738 * {<OpenLayers.Layer>} The Layer with the corresponding id from the map's
7739 * layer collection, or null if not found.
7741 getLayer: function(id) {
7742 var foundLayer = null;
7743 for (var i=0, len=this.layers.length; i<len; i++) {
7744 var layer = this.layers[i];
7745 if (layer.id == id) {
7754 * Method: setLayerZIndex
7757 * layer - {<OpenLayers.Layer>}
7760 setLayerZIndex: function (layer, zIdx) {
7762 this.Z_INDEX_BASE[layer.isBaseLayer ? 'BaseLayer' : 'Overlay']
7767 * Method: resetLayersZIndex
7768 * Reset each layer's z-index based on layer's array index
7770 resetLayersZIndex: function() {
7771 for (var i=0, len=this.layers.length; i<len; i++) {
7772 var layer = this.layers[i];
7773 this.setLayerZIndex(layer, i);
7778 * APIMethod: addLayer
7781 * layer - {<OpenLayers.Layer>}
7784 * {Boolean} True if the layer has been added to the map.
7786 addLayer: function (layer) {
7787 for(var i = 0, len = this.layers.length; i < len; i++) {
7788 if (this.layers[i] == layer) {
7792 if (this.events.triggerEvent("preaddlayer", {layer: layer}) === false) {
7795 if(this.allOverlays) {
7796 layer.isBaseLayer = false;
7799 layer.div.className = "olLayerDiv";
7800 layer.div.style.overflow = "";
7801 this.setLayerZIndex(layer, this.layers.length);
7803 if (layer.isFixed) {
7804 this.viewPortDiv.appendChild(layer.div);
7806 this.layerContainerDiv.appendChild(layer.div);
7808 this.layers.push(layer);
7811 if (layer.isBaseLayer || (this.allOverlays && !this.baseLayer)) {
7812 if (this.baseLayer == null) {
7813 // set the first baselaye we add as the baselayer
7814 this.setBaseLayer(layer);
7816 layer.setVisibility(false);
7822 this.events.triggerEvent("addlayer", {layer: layer});
7823 layer.events.triggerEvent("added", {map: this, layer: layer});
7830 * APIMethod: addLayers
7833 * layers - {Array(<OpenLayers.Layer>)}
7835 addLayers: function (layers) {
7836 for (var i=0, len=layers.length; i<len; i++) {
7837 this.addLayer(layers[i]);
7842 * APIMethod: removeLayer
7843 * Removes a layer from the map by removing its visual element (the
7844 * layer.div property), then removing it from the map's internal list
7845 * of layers, setting the layer's map property to null.
7847 * a "removelayer" event is triggered.
7849 * very worthy of mention is that simply removing a layer from a map
7850 * will not cause the removal of any popups which may have been created
7851 * by the layer. this is due to the fact that it was decided at some
7852 * point that popups would not belong to layers. thus there is no way
7853 * for us to know here to which layer the popup belongs.
7855 * A simple solution to this is simply to call destroy() on the layer.
7856 * the default OpenLayers.Layer class's destroy() function
7857 * automatically takes care to remove itself from whatever map it has
7860 * The correct solution is for the layer itself to register an
7861 * event-handler on "removelayer" and when it is called, if it
7862 * recognizes itself as the layer being removed, then it cycles through
7863 * its own personal list of popups, removing them from the map.
7866 * layer - {<OpenLayers.Layer>}
7867 * setNewBaseLayer - {Boolean} Default is true
7869 removeLayer: function(layer, setNewBaseLayer) {
7870 if (this.events.triggerEvent("preremovelayer", {layer: layer}) === false) {
7873 if (setNewBaseLayer == null) {
7874 setNewBaseLayer = true;
7877 if (layer.isFixed) {
7878 this.viewPortDiv.removeChild(layer.div);
7880 this.layerContainerDiv.removeChild(layer.div);
7882 OpenLayers.Util.removeItem(this.layers, layer);
7883 layer.removeMap(this);
7886 // if we removed the base layer, need to set a new one
7887 if(this.baseLayer == layer) {
7888 this.baseLayer = null;
7889 if(setNewBaseLayer) {
7890 for(var i=0, len=this.layers.length; i<len; i++) {
7891 var iLayer = this.layers[i];
7892 if (iLayer.isBaseLayer || this.allOverlays) {
7893 this.setBaseLayer(iLayer);
7900 this.resetLayersZIndex();
7902 this.events.triggerEvent("removelayer", {layer: layer});
7903 layer.events.triggerEvent("removed", {map: this, layer: layer});
7907 * APIMethod: getNumLayers
7910 * {Int} The number of layers attached to the map.
7912 getNumLayers: function () {
7913 return this.layers.length;
7917 * APIMethod: getLayerIndex
7920 * layer - {<OpenLayers.Layer>}
7923 * {Integer} The current (zero-based) index of the given layer in the map's
7924 * layer stack. Returns -1 if the layer isn't on the map.
7926 getLayerIndex: function (layer) {
7927 return OpenLayers.Util.indexOf(this.layers, layer);
7931 * APIMethod: setLayerIndex
7932 * Move the given layer to the specified (zero-based) index in the layer
7933 * list, changing its z-index in the map display. Use
7934 * map.getLayerIndex() to find out the current index of a layer. Note
7935 * that this cannot (or at least should not) be effectively used to
7936 * raise base layers above overlays.
7939 * layer - {<OpenLayers.Layer>}
7942 setLayerIndex: function (layer, idx) {
7943 var base = this.getLayerIndex(layer);
7946 } else if (idx > this.layers.length) {
7947 idx = this.layers.length;
7950 this.layers.splice(base, 1);
7951 this.layers.splice(idx, 0, layer);
7952 for (var i=0, len=this.layers.length; i<len; i++) {
7953 this.setLayerZIndex(this.layers[i], i);
7955 this.events.triggerEvent("changelayer", {
7956 layer: layer, property: "order"
7958 if(this.allOverlays) {
7960 this.setBaseLayer(layer);
7961 } else if(this.baseLayer !== this.layers[0]) {
7962 this.setBaseLayer(this.layers[0]);
7969 * APIMethod: raiseLayer
7970 * Change the index of the given layer by delta. If delta is positive,
7971 * the layer is moved up the map's layer stack; if delta is negative,
7972 * the layer is moved down. Again, note that this cannot (or at least
7973 * should not) be effectively used to raise base layers above overlays.
7976 * layer - {<OpenLayers.Layer>}
7979 raiseLayer: function (layer, delta) {
7980 var idx = this.getLayerIndex(layer) + delta;
7981 this.setLayerIndex(layer, idx);
7985 * APIMethod: setBaseLayer
7986 * Allows user to specify one of the currently-loaded layers as the Map's
7990 * newBaseLayer - {<OpenLayers.Layer>}
7992 setBaseLayer: function(newBaseLayer) {
7994 if (newBaseLayer != this.baseLayer) {
7996 // ensure newBaseLayer is already loaded
7997 if (OpenLayers.Util.indexOf(this.layers, newBaseLayer) != -1) {
7999 // preserve center and scale when changing base layers
8000 var center = this.getCachedCenter();
8001 var newResolution = OpenLayers.Util.getResolutionFromScale(
8002 this.getScale(), newBaseLayer.units
8005 // make the old base layer invisible
8006 if (this.baseLayer != null && !this.allOverlays) {
8007 this.baseLayer.setVisibility(false);
8010 // set new baselayer
8011 this.baseLayer = newBaseLayer;
8013 if(!this.allOverlays || this.baseLayer.visibility) {
8014 this.baseLayer.setVisibility(true);
8015 // Layer may previously have been visible but not in range.
8016 // In this case we need to redraw it to make it visible.
8017 if (this.baseLayer.inRange === false) {
8018 this.baseLayer.redraw();
8023 if (center != null) {
8024 // new zoom level derived from old scale
8025 var newZoom = this.getZoomForResolution(
8026 newResolution || this.resolution, true
8028 // zoom and force zoom change
8029 this.setCenter(center, newZoom, false, true);
8032 this.events.triggerEvent("changebaselayer", {
8033 layer: this.baseLayer
8040 /********************************************************/
8042 /* Control Functions */
8044 /* The following functions deal with adding and */
8045 /* removing Controls to and from the Map */
8047 /********************************************************/
8050 * APIMethod: addControl
8051 * Add the passed over control to the map. Optionally
8052 * position the control at the given pixel.
8055 * control - {<OpenLayers.Control>}
8056 * px - {<OpenLayers.Pixel>}
8058 addControl: function (control, px) {
8059 this.controls.push(control);
8060 this.addControlToMap(control, px);
8064 * APIMethod: addControls
8065 * Add all of the passed over controls to the map.
8066 * You can pass over an optional second array
8067 * with pixel-objects to position the controls.
8068 * The indices of the two arrays should match and
8069 * you can add null as pixel for those controls
8070 * you want to be autopositioned.
8073 * controls - {Array(<OpenLayers.Control>)}
8074 * pixels - {Array(<OpenLayers.Pixel>)}
8076 addControls: function (controls, pixels) {
8077 var pxs = (arguments.length === 1) ? [] : pixels;
8078 for (var i=0, len=controls.length; i<len; i++) {
8079 var ctrl = controls[i];
8080 var px = (pxs[i]) ? pxs[i] : null;
8081 this.addControl( ctrl, px );
8086 * Method: addControlToMap
8090 * control - {<OpenLayers.Control>}
8091 * px - {<OpenLayers.Pixel>}
8093 addControlToMap: function (control, px) {
8094 // If a control doesn't have a div at this point, it belongs in the
8096 control.outsideViewport = (control.div != null);
8098 // If the map has a displayProjection, and the control doesn't, set
8099 // the display projection.
8100 if (this.displayProjection && !control.displayProjection) {
8101 control.displayProjection = this.displayProjection;
8104 control.setMap(this);
8105 var div = control.draw(px);
8107 if(!control.outsideViewport) {
8108 div.style.zIndex = this.Z_INDEX_BASE['Control'] +
8109 this.controls.length;
8110 this.viewPortDiv.appendChild( div );
8113 if(control.autoActivate) {
8119 * APIMethod: getControl
8122 * id - {String} ID of the control to return.
8125 * {<OpenLayers.Control>} The control from the map's list of controls
8126 * which has a matching 'id'. If none found,
8129 getControl: function (id) {
8130 var returnControl = null;
8131 for(var i=0, len=this.controls.length; i<len; i++) {
8132 var control = this.controls[i];
8133 if (control.id == id) {
8134 returnControl = control;
8138 return returnControl;
8142 * APIMethod: removeControl
8143 * Remove a control from the map. Removes the control both from the map
8144 * object's internal array of controls, as well as from the map's
8145 * viewPort (assuming the control was not added outsideViewport)
8148 * control - {<OpenLayers.Control>} The control to remove.
8150 removeControl: function (control) {
8151 //make sure control is non-null and actually part of our map
8152 if ( (control) && (control == this.getControl(control.id)) ) {
8153 if (control.div && (control.div.parentNode == this.viewPortDiv)) {
8154 this.viewPortDiv.removeChild(control.div);
8156 OpenLayers.Util.removeItem(this.controls, control);
8160 /********************************************************/
8162 /* Popup Functions */
8164 /* The following functions deal with adding and */
8165 /* removing Popups to and from the Map */
8167 /********************************************************/
8170 * APIMethod: addPopup
8173 * popup - {<OpenLayers.Popup>}
8174 * exclusive - {Boolean} If true, closes all other popups first
8176 addPopup: function(popup, exclusive) {
8179 //remove all other popups from screen
8180 for (var i = this.popups.length - 1; i >= 0; --i) {
8181 this.removePopup(this.popups[i]);
8186 this.popups.push(popup);
8187 var popupDiv = popup.draw();
8189 popupDiv.style.zIndex = this.Z_INDEX_BASE['Popup'] +
8191 this.layerContainerDiv.appendChild(popupDiv);
8196 * APIMethod: removePopup
8199 * popup - {<OpenLayers.Popup>}
8201 removePopup: function(popup) {
8202 OpenLayers.Util.removeItem(this.popups, popup);
8204 try { this.layerContainerDiv.removeChild(popup.div); }
8205 catch (e) { } // Popups sometimes apparently get disconnected
8206 // from the layerContainerDiv, and cause complaints.
8211 /********************************************************/
8213 /* Container Div Functions */
8215 /* The following functions deal with the access to */
8216 /* and maintenance of the size of the container div */
8218 /********************************************************/
8221 * APIMethod: getSize
8224 * {<OpenLayers.Size>} An <OpenLayers.Size> object that represents the
8225 * size, in pixels, of the div into which OpenLayers
8227 * Note - A clone() of this locally cached variable is
8228 * returned, so as not to allow users to modify it.
8230 getSize: function () {
8232 if (this.size != null) {
8233 size = this.size.clone();
8239 * APIMethod: updateSize
8240 * This function should be called by any external code which dynamically
8241 * changes the size of the map div (because mozilla wont let us catch
8242 * the "onresize" for an element)
8244 updateSize: function() {
8245 // the div might have moved on the page, also
8246 var newSize = this.getCurrentSize();
8247 if (newSize && !isNaN(newSize.h) && !isNaN(newSize.w)) {
8248 this.events.clearMouseCache();
8249 var oldSize = this.getSize();
8250 if (oldSize == null) {
8251 this.size = oldSize = newSize;
8253 if (!newSize.equals(oldSize)) {
8255 // store the new size
8256 this.size = newSize;
8258 //notify layers of mapresize
8259 for(var i=0, len=this.layers.length; i<len; i++) {
8260 this.layers[i].onMapResize();
8263 var center = this.getCachedCenter();
8265 if (this.baseLayer != null && center != null) {
8266 var zoom = this.getZoom();
8268 this.setCenter(center, zoom);
8273 this.events.triggerEvent("updatesize");
8277 * Method: getCurrentSize
8280 * {<OpenLayers.Size>} A new <OpenLayers.Size> object with the dimensions
8283 getCurrentSize: function() {
8285 var size = new OpenLayers.Size(this.div.clientWidth,
8286 this.div.clientHeight);
8288 if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {
8289 size.w = this.div.offsetWidth;
8290 size.h = this.div.offsetHeight;
8292 if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {
8293 size.w = parseInt(this.div.style.width);
8294 size.h = parseInt(this.div.style.height);
8300 * Method: calculateBounds
8303 * center - {<OpenLayers.LonLat>} Default is this.getCenter()
8304 * resolution - {float} Default is this.getResolution()
8307 * {<OpenLayers.Bounds>} A bounds based on resolution, center, and
8310 calculateBounds: function(center, resolution) {
8314 if (center == null) {
8315 center = this.getCachedCenter();
8317 if (resolution == null) {
8318 resolution = this.getResolution();
8321 if ((center != null) && (resolution != null)) {
8322 var halfWDeg = (this.size.w * resolution) / 2;
8323 var halfHDeg = (this.size.h * resolution) / 2;
8325 extent = new OpenLayers.Bounds(center.lon - halfWDeg,
8326 center.lat - halfHDeg,
8327 center.lon + halfWDeg,
8328 center.lat + halfHDeg);
8335 /********************************************************/
8337 /* Zoom, Center, Pan Functions */
8339 /* The following functions handle the validation, */
8340 /* getting and setting of the Zoom Level and Center */
8341 /* as well as the panning of the Map */
8343 /********************************************************/
8345 * APIMethod: getCenter
8348 * {<OpenLayers.LonLat>}
8350 getCenter: function () {
8352 var cachedCenter = this.getCachedCenter();
8354 center = cachedCenter.clone();
8360 * Method: getCachedCenter
8363 * {<OpenLayers.LonLat>}
8365 getCachedCenter: function() {
8366 if (!this.center && this.size) {
8367 this.center = this.getLonLatFromViewPortPx({
8376 * APIMethod: getZoom
8381 getZoom: function () {
8387 * Allows user to pan by a value of screen pixels
8392 * options - {Object} Options to configure panning:
8393 * - *animate* {Boolean} Use panTo instead of setCenter. Default is true.
8394 * - *dragging* {Boolean} Call setCenter with dragging true. Default is
8397 pan: function(dx, dy, options) {
8398 options = OpenLayers.Util.applyDefaults(options, {
8402 if (options.dragging) {
8403 if (dx != 0 || dy != 0) {
8404 this.moveByPx(dx, dy);
8408 var centerPx = this.getViewPortPxFromLonLat(this.getCachedCenter());
8411 var newCenterPx = centerPx.add(dx, dy);
8413 if (this.dragging || !newCenterPx.equals(centerPx)) {
8414 var newCenterLonLat = this.getLonLatFromViewPortPx(newCenterPx);
8415 if (options.animate) {
8416 this.panTo(newCenterLonLat);
8418 this.moveTo(newCenterLonLat);
8420 this.dragging = false;
8421 this.events.triggerEvent("moveend");
8431 * Allows user to pan to a new lonlat
8432 * If the new lonlat is in the current extent the map will slide smoothly
8435 * lonlat - {<OpenLayers.LonLat>}
8437 panTo: function(lonlat) {
8438 if (this.panTween && this.getExtent().scale(this.panRatio).containsLonLat(lonlat)) {
8439 var center = this.getCachedCenter();
8441 // center will not change, don't do nothing
8442 if (lonlat.equals(center)) {
8446 var from = this.getPixelFromLonLat(center);
8447 var to = this.getPixelFromLonLat(lonlat);
8448 var vector = { x: to.x - from.x, y: to.y - from.y };
8449 var last = { x: 0, y: 0 };
8451 this.panTween.start( { x: 0, y: 0 }, vector, this.panDuration, {
8453 eachStep: OpenLayers.Function.bind(function(px) {
8454 var x = px.x - last.x,
8456 this.moveByPx(x, y);
8457 last.x = Math.round(px.x);
8458 last.y = Math.round(px.y);
8460 done: OpenLayers.Function.bind(function(px) {
8461 this.moveTo(lonlat);
8462 this.dragging = false;
8463 this.events.triggerEvent("moveend");
8468 this.setCenter(lonlat);
8473 * APIMethod: setCenter
8474 * Set the map center (and optionally, the zoom level).
8477 * lonlat - {<OpenLayers.LonLat>|Array} The new center location.
8478 * If provided as array, the first value is the x coordinate,
8479 * and the 2nd value is the y coordinate.
8480 * zoom - {Integer} Optional zoom level.
8481 * dragging - {Boolean} Specifies whether or not to trigger
8482 * movestart/end events
8483 * forceZoomChange - {Boolean} Specifies whether or not to trigger zoom
8484 * change events (needed on baseLayer change)
8486 * TBD: reconsider forceZoomChange in 3.0
8488 setCenter: function(lonlat, zoom, dragging, forceZoomChange) {
8489 if (this.panTween) {
8490 this.panTween.stop();
8492 if (this.zoomTween) {
8493 this.zoomTween.stop();
8495 this.moveTo(lonlat, zoom, {
8496 'dragging': dragging,
8497 'forceZoomChange': forceZoomChange
8503 * Drag the map by pixels.
8509 moveByPx: function(dx, dy) {
8510 var hw = this.size.w / 2;
8511 var hh = this.size.h / 2;
8514 var wrapDateLine = this.baseLayer.wrapDateLine;
8515 var xRestriction = 0;
8516 var yRestriction = 0;
8517 if (this.restrictedExtent) {
8520 // wrapping the date line makes no sense for restricted extents
8521 wrapDateLine = false;
8523 dx = wrapDateLine ||
8524 x <= this.maxPx.x - xRestriction &&
8525 x >= this.minPx.x + xRestriction ? Math.round(dx) : 0;
8526 dy = y <= this.maxPx.y - yRestriction &&
8527 y >= this.minPx.y + yRestriction ? Math.round(dy) : 0;
8529 if (!this.dragging) {
8530 this.dragging = true;
8531 this.events.triggerEvent("movestart");
8535 this.layerContainerOriginPx.x -= dx;
8540 this.layerContainerOriginPx.y -= dy;
8544 this.applyTransform();
8546 for (i=0, len=this.layers.length; i<len; ++i) {
8547 layer = this.layers[i];
8548 if (layer.visibility &&
8549 (layer === this.baseLayer || layer.inRange)) {
8550 layer.moveByPx(dx, dy);
8551 layer.events.triggerEvent("move");
8554 this.events.triggerEvent("move");
8559 * Method: adjustZoom
8562 * zoom - {Number} The zoom level to adjust
8565 * {Integer} Adjusted zoom level that shows a map not wider than its
8566 * <baseLayer>'s maxExtent.
8568 adjustZoom: function(zoom) {
8569 if (this.baseLayer && this.baseLayer.wrapDateLine) {
8570 var resolution, resolutions = this.baseLayer.resolutions,
8571 maxResolution = this.getMaxExtent().getWidth() / this.size.w;
8572 if (this.getResolutionForZoom(zoom) > maxResolution) {
8573 if (this.fractionalZoom) {
8574 zoom = this.getZoomForResolution(maxResolution);
8576 for (var i=zoom|0, ii=resolutions.length; i<ii; ++i) {
8577 if (resolutions[i] <= maxResolution) {
8589 * APIMethod: getMinZoom
8590 * Returns the minimum zoom level for the current map view. If the base
8591 * layer is configured with <wrapDateLine> set to true, this will be the
8592 * first zoom level that shows no more than one world width in the current
8593 * map viewport. Components that rely on this value (e.g. zoom sliders)
8594 * should also listen to the map's "updatesize" event and call this method
8595 * in the "updatesize" listener.
8598 * {Number} Minimum zoom level that shows a map not wider than its
8599 * <baseLayer>'s maxExtent. This is an Integer value, unless the map is
8600 * configured with <fractionalZoom> set to true.
8602 getMinZoom: function() {
8603 return this.adjustZoom(0);
8610 * lonlat - {<OpenLayers.LonLat>}
8612 * options - {Object}
8614 moveTo: function(lonlat, zoom, options) {
8615 if (lonlat != null && !(lonlat instanceof OpenLayers.LonLat)) {
8616 lonlat = new OpenLayers.LonLat(lonlat);
8622 zoom = parseFloat(zoom);
8623 if (!this.fractionalZoom) {
8624 zoom = Math.round(zoom);
8627 var requestedZoom = zoom;
8628 zoom = this.adjustZoom(zoom);
8629 if (zoom !== requestedZoom) {
8630 // zoom was adjusted, so keep old lonlat to avoid panning
8631 lonlat = this.getCenter();
8633 // dragging is false by default
8634 var dragging = options.dragging || this.dragging;
8635 // forceZoomChange is false by default
8636 var forceZoomChange = options.forceZoomChange;
8638 if (!this.getCachedCenter() && !this.isValidLonLat(lonlat)) {
8639 lonlat = this.maxExtent.getCenterLonLat();
8640 this.center = lonlat.clone();
8643 if(this.restrictedExtent != null) {
8644 // In 3.0, decide if we want to change interpretation of maxExtent.
8645 if(lonlat == null) {
8646 lonlat = this.center;
8649 zoom = this.getZoom();
8651 var resolution = this.getResolutionForZoom(zoom);
8652 var extent = this.calculateBounds(lonlat, resolution);
8653 if(!this.restrictedExtent.containsBounds(extent)) {
8654 var maxCenter = this.restrictedExtent.getCenterLonLat();
8655 if(extent.getWidth() > this.restrictedExtent.getWidth()) {
8656 lonlat = new OpenLayers.LonLat(maxCenter.lon, lonlat.lat);
8657 } else if(extent.left < this.restrictedExtent.left) {
8658 lonlat = lonlat.add(this.restrictedExtent.left -
8660 } else if(extent.right > this.restrictedExtent.right) {
8661 lonlat = lonlat.add(this.restrictedExtent.right -
8664 if(extent.getHeight() > this.restrictedExtent.getHeight()) {
8665 lonlat = new OpenLayers.LonLat(lonlat.lon, maxCenter.lat);
8666 } else if(extent.bottom < this.restrictedExtent.bottom) {
8667 lonlat = lonlat.add(0, this.restrictedExtent.bottom -
8670 else if(extent.top > this.restrictedExtent.top) {
8671 lonlat = lonlat.add(0, this.restrictedExtent.top -
8677 var zoomChanged = forceZoomChange || (
8678 (this.isValidZoomLevel(zoom)) &&
8679 (zoom != this.getZoom()) );
8681 var centerChanged = (this.isValidLonLat(lonlat)) &&
8682 (!lonlat.equals(this.center));
8684 // if neither center nor zoom will change, no need to do anything
8685 if (zoomChanged || centerChanged || dragging) {
8686 dragging || this.events.triggerEvent("movestart", {
8687 zoomChanged: zoomChanged
8690 if (centerChanged) {
8691 if (!zoomChanged && this.center) {
8692 // if zoom hasnt changed, just slide layerContainer
8693 // (must be done before setting this.center to new value)
8694 this.centerLayerContainer(lonlat);
8696 this.center = lonlat.clone();
8699 var res = zoomChanged ?
8700 this.getResolutionForZoom(zoom) : this.getResolution();
8701 // (re)set the layerContainerDiv's location
8702 if (zoomChanged || this.layerContainerOrigin == null) {
8703 this.layerContainerOrigin = this.getCachedCenter();
8704 this.layerContainerOriginPx.x = 0;
8705 this.layerContainerOriginPx.y = 0;
8706 this.applyTransform();
8707 var maxExtent = this.getMaxExtent({restricted: true});
8708 var maxExtentCenter = maxExtent.getCenterLonLat();
8709 var lonDelta = this.center.lon - maxExtentCenter.lon;
8710 var latDelta = maxExtentCenter.lat - this.center.lat;
8711 var extentWidth = Math.round(maxExtent.getWidth() / res);
8712 var extentHeight = Math.round(maxExtent.getHeight() / res);
8714 x: (this.size.w - extentWidth) / 2 - lonDelta / res,
8715 y: (this.size.h - extentHeight) / 2 - latDelta / res
8718 x: this.minPx.x + Math.round(maxExtent.getWidth() / res),
8719 y: this.minPx.y + Math.round(maxExtent.getHeight() / res)
8725 this.resolution = res;
8728 var bounds = this.getExtent();
8730 //send the move call to the baselayer and all the overlays
8732 if(this.baseLayer.visibility) {
8733 this.baseLayer.moveTo(bounds, zoomChanged, options.dragging);
8734 options.dragging || this.baseLayer.events.triggerEvent(
8735 "moveend", {zoomChanged: zoomChanged}
8739 bounds = this.baseLayer.getExtent();
8741 for (var i=this.layers.length-1; i>=0; --i) {
8742 var layer = this.layers[i];
8743 if (layer !== this.baseLayer && !layer.isBaseLayer) {
8744 var inRange = layer.calculateInRange();
8745 if (layer.inRange != inRange) {
8746 // the inRange property has changed. If the layer is
8747 // no longer in range, we turn it off right away. If
8748 // the layer is no longer out of range, the moveTo
8749 // call below will turn on the layer.
8750 layer.inRange = inRange;
8752 layer.display(false);
8754 this.events.triggerEvent("changelayer", {
8755 layer: layer, property: "visibility"
8758 if (inRange && layer.visibility) {
8759 layer.moveTo(bounds, zoomChanged, options.dragging);
8760 options.dragging || layer.events.triggerEvent(
8761 "moveend", {zoomChanged: zoomChanged}
8767 this.events.triggerEvent("move");
8768 dragging || this.events.triggerEvent("moveend");
8772 for (var i=0, len=this.popups.length; i<len; i++) {
8773 this.popups[i].updatePosition();
8775 this.events.triggerEvent("zoomend");
8781 * Method: centerLayerContainer
8782 * This function takes care to recenter the layerContainerDiv.
8785 * lonlat - {<OpenLayers.LonLat>}
8787 centerLayerContainer: function (lonlat) {
8788 var originPx = this.getViewPortPxFromLonLat(this.layerContainerOrigin);
8789 var newPx = this.getViewPortPxFromLonLat(lonlat);
8791 if ((originPx != null) && (newPx != null)) {
8792 var oldLeft = this.layerContainerOriginPx.x;
8793 var oldTop = this.layerContainerOriginPx.y;
8794 var newLeft = Math.round(originPx.x - newPx.x);
8795 var newTop = Math.round(originPx.y - newPx.y);
8796 this.applyTransform(
8797 (this.layerContainerOriginPx.x = newLeft),
8798 (this.layerContainerOriginPx.y = newTop));
8799 var dx = oldLeft - newLeft;
8800 var dy = oldTop - newTop;
8809 * Method: isValidZoomLevel
8812 * zoomLevel - {Integer}
8815 * {Boolean} Whether or not the zoom level passed in is non-null and
8816 * within the min/max range of zoom levels.
8818 isValidZoomLevel: function(zoomLevel) {
8819 return ( (zoomLevel != null) &&
8821 (zoomLevel < this.getNumZoomLevels()) );
8825 * Method: isValidLonLat
8828 * lonlat - {<OpenLayers.LonLat>}
8831 * {Boolean} Whether or not the lonlat passed in is non-null and within
8832 * the maxExtent bounds
8834 isValidLonLat: function(lonlat) {
8836 if (lonlat != null) {
8837 var maxExtent = this.getMaxExtent();
8838 var worldBounds = this.baseLayer.wrapDateLine && maxExtent;
8839 valid = maxExtent.containsLonLat(lonlat, {worldBounds: worldBounds});
8844 /********************************************************/
8848 /* Accessor functions to Layer Options parameters */
8850 /********************************************************/
8853 * APIMethod: getProjection
8854 * This method returns a string representing the projection. In
8855 * the case of projection support, this will be the srsCode which
8856 * is loaded -- otherwise it will simply be the string value that
8857 * was passed to the projection at startup.
8859 * FIXME: In 3.0, we will remove getProjectionObject, and instead
8860 * return a Projection object from this function.
8863 * {String} The Projection string from the base layer or null.
8865 getProjection: function() {
8866 var projection = this.getProjectionObject();
8867 return projection ? projection.getCode() : null;
8871 * APIMethod: getProjectionObject
8872 * Returns the projection obect from the baselayer.
8875 * {<OpenLayers.Projection>} The Projection of the base layer.
8877 getProjectionObject: function() {
8878 var projection = null;
8879 if (this.baseLayer != null) {
8880 projection = this.baseLayer.projection;
8886 * APIMethod: getMaxResolution
8889 * {String} The Map's Maximum Resolution
8891 getMaxResolution: function() {
8892 var maxResolution = null;
8893 if (this.baseLayer != null) {
8894 maxResolution = this.baseLayer.maxResolution;
8896 return maxResolution;
8900 * APIMethod: getMaxExtent
8903 * options - {Object}
8906 * restricted - {Boolean} If true, returns restricted extent (if it is
8910 * {<OpenLayers.Bounds>} The maxExtent property as set on the current
8911 * baselayer, unless the 'restricted' option is set, in which case
8912 * the 'restrictedExtent' option from the map is returned (if it
8915 getMaxExtent: function (options) {
8916 var maxExtent = null;
8917 if(options && options.restricted && this.restrictedExtent){
8918 maxExtent = this.restrictedExtent;
8919 } else if (this.baseLayer != null) {
8920 maxExtent = this.baseLayer.maxExtent;
8926 * APIMethod: getNumZoomLevels
8929 * {Integer} The total number of zoom levels that can be displayed by the
8930 * current baseLayer.
8932 getNumZoomLevels: function() {
8933 var numZoomLevels = null;
8934 if (this.baseLayer != null) {
8935 numZoomLevels = this.baseLayer.numZoomLevels;
8937 return numZoomLevels;
8940 /********************************************************/
8942 /* Baselayer Functions */
8944 /* The following functions, all publicly exposed */
8945 /* in the API?, are all merely wrappers to the */
8946 /* the same calls on whatever layer is set as */
8947 /* the current base layer */
8949 /********************************************************/
8952 * APIMethod: getExtent
8955 * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat
8956 * bounds of the current viewPort.
8957 * If no baselayer is set, returns null.
8959 getExtent: function () {
8961 if (this.baseLayer != null) {
8962 extent = this.baseLayer.getExtent();
8968 * APIMethod: getResolution
8971 * {Float} The current resolution of the map.
8972 * If no baselayer is set, returns null.
8974 getResolution: function () {
8975 var resolution = null;
8976 if (this.baseLayer != null) {
8977 resolution = this.baseLayer.getResolution();
8978 } else if(this.allOverlays === true && this.layers.length > 0) {
8979 // while adding the 1st layer to the map in allOverlays mode,
8980 // this.baseLayer is not set yet when we need the resolution
8981 // for calculateInRange.
8982 resolution = this.layers[0].getResolution();
8988 * APIMethod: getUnits
8991 * {Float} The current units of the map.
8992 * If no baselayer is set, returns null.
8994 getUnits: function () {
8996 if (this.baseLayer != null) {
8997 units = this.baseLayer.units;
9003 * APIMethod: getScale
9006 * {Float} The current scale denominator of the map.
9007 * If no baselayer is set, returns null.
9009 getScale: function () {
9011 if (this.baseLayer != null) {
9012 var res = this.getResolution();
9013 var units = this.baseLayer.units;
9014 scale = OpenLayers.Util.getScaleFromResolution(res, units);
9021 * APIMethod: getZoomForExtent
9024 * bounds - {<OpenLayers.Bounds>}
9025 * closest - {Boolean} Find the zoom level that most closely fits the
9026 * specified bounds. Note that this may result in a zoom that does
9027 * not exactly contain the entire extent.
9031 * {Integer} A suitable zoom level for the specified bounds.
9032 * If no baselayer is set, returns null.
9034 getZoomForExtent: function (bounds, closest) {
9036 if (this.baseLayer != null) {
9037 zoom = this.baseLayer.getZoomForExtent(bounds, closest);
9043 * APIMethod: getResolutionForZoom
9049 * {Float} A suitable resolution for the specified zoom. If no baselayer
9050 * is set, returns null.
9052 getResolutionForZoom: function(zoom) {
9053 var resolution = null;
9054 if(this.baseLayer) {
9055 resolution = this.baseLayer.getResolutionForZoom(zoom);
9061 * APIMethod: getZoomForResolution
9064 * resolution - {Float}
9065 * closest - {Boolean} Find the zoom level that corresponds to the absolute
9066 * closest resolution, which may result in a zoom whose corresponding
9067 * resolution is actually smaller than we would have desired (if this
9068 * is being called from a getZoomForExtent() call, then this means that
9069 * the returned zoom index might not actually contain the entire
9070 * extent specified... but it'll be close).
9074 * {Integer} A suitable zoom level for the specified resolution.
9075 * If no baselayer is set, returns null.
9077 getZoomForResolution: function(resolution, closest) {
9079 if (this.baseLayer != null) {
9080 zoom = this.baseLayer.getZoomForResolution(resolution, closest);
9085 /********************************************************/
9087 /* Zooming Functions */
9089 /* The following functions, all publicly exposed */
9090 /* in the API, are all merely wrappers to the */
9091 /* the setCenter() function */
9093 /********************************************************/
9097 * Zoom to a specific zoom level. Zooming will be animated unless the map
9098 * is configured with {zoomMethod: null}. To zoom without animation, use
9099 * <setCenter> without a lonlat argument.
9104 zoomTo: function(zoom, xy) {
9105 // non-API arguments:
9106 // xy - {<OpenLayers.Pixel>} optional zoom origin
9109 if (map.isValidZoomLevel(zoom)) {
9110 if (map.baseLayer.wrapDateLine) {
9111 zoom = map.adjustZoom(zoom);
9113 if (map.zoomTween) {
9114 var currentRes = map.getResolution(),
9115 targetRes = map.getResolutionForZoom(zoom),
9117 end = {scale: currentRes / targetRes};
9118 if (map.zoomTween.playing && map.zoomTween.duration < 3 * map.zoomDuration) {
9119 // update the end scale, and reuse the running zoomTween
9120 map.zoomTween.finish = {
9121 scale: map.zoomTween.finish.scale * end.scale
9125 var size = map.getSize();
9126 xy = {x: size.w / 2, y: size.h / 2};
9128 map.zoomTween.start(start, end, map.zoomDuration, {
9129 minFrameRate: 50, // don't spend much time zooming
9131 eachStep: function(data) {
9132 var containerOrigin = map.layerContainerOriginPx,
9134 dx = ((scale - 1) * (containerOrigin.x - xy.x)) | 0,
9135 dy = ((scale - 1) * (containerOrigin.y - xy.y)) | 0;
9136 map.applyTransform(containerOrigin.x + dx, containerOrigin.y + dy, scale);
9138 done: function(data) {
9139 map.applyTransform();
9140 var resolution = map.getResolution() / data.scale,
9141 zoom = map.getZoomForResolution(resolution, true)
9142 map.moveTo(map.getZoomTargetCenter(xy, resolution), zoom, true);
9149 map.getZoomTargetCenter(xy, map.getResolutionForZoom(zoom)) :
9151 map.setCenter(center, zoom);
9160 zoomIn: function() {
9161 this.zoomTo(this.getZoom() + 1);
9165 * APIMethod: zoomOut
9168 zoomOut: function() {
9169 this.zoomTo(this.getZoom() - 1);
9173 * APIMethod: zoomToExtent
9174 * Zoom to the passed in bounds, recenter
9177 * bounds - {<OpenLayers.Bounds>|Array} If provided as an array, the array
9178 * should consist of four values (left, bottom, right, top).
9179 * closest - {Boolean} Find the zoom level that most closely fits the
9180 * specified bounds. Note that this may result in a zoom that does
9181 * not exactly contain the entire extent.
9185 zoomToExtent: function(bounds, closest) {
9186 if (!(bounds instanceof OpenLayers.Bounds)) {
9187 bounds = new OpenLayers.Bounds(bounds);
9189 var center = bounds.getCenterLonLat();
9190 if (this.baseLayer.wrapDateLine) {
9191 var maxExtent = this.getMaxExtent();
9193 //fix straddling bounds (in the case of a bbox that straddles the
9194 // dateline, it's left and right boundaries will appear backwards.
9195 // we fix this by allowing a right value that is greater than the
9196 // max value at the dateline -- this allows us to pass a valid
9197 // bounds to calculate zoom)
9199 bounds = bounds.clone();
9200 while (bounds.right < bounds.left) {
9201 bounds.right += maxExtent.getWidth();
9203 //if the bounds was straddling (see above), then the center point
9204 // we got from it was wrong. So we take our new bounds and ask it
9207 center = bounds.getCenterLonLat().wrapDateLine(maxExtent);
9209 this.setCenter(center, this.getZoomForExtent(bounds, closest));
9213 * APIMethod: zoomToMaxExtent
9214 * Zoom to the full extent and recenter.
9217 * options - {Object}
9220 * restricted - {Boolean} True to zoom to restricted extent if it is
9221 * set. Defaults to true.
9223 zoomToMaxExtent: function(options) {
9224 //restricted is true by default
9225 var restricted = (options) ? options.restricted : true;
9227 var maxExtent = this.getMaxExtent({
9228 'restricted': restricted
9230 this.zoomToExtent(maxExtent);
9234 * APIMethod: zoomToScale
9235 * Zoom to a specified scale
9239 * closest - {Boolean} Find the zoom level that most closely fits the
9240 * specified scale. Note that this may result in a zoom that does
9241 * not exactly contain the entire extent.
9245 zoomToScale: function(scale, closest) {
9246 var res = OpenLayers.Util.getResolutionFromScale(scale,
9247 this.baseLayer.units);
9249 var halfWDeg = (this.size.w * res) / 2;
9250 var halfHDeg = (this.size.h * res) / 2;
9251 var center = this.getCachedCenter();
9253 var extent = new OpenLayers.Bounds(center.lon - halfWDeg,
9254 center.lat - halfHDeg,
9255 center.lon + halfWDeg,
9256 center.lat + halfHDeg);
9257 this.zoomToExtent(extent, closest);
9260 /********************************************************/
9262 /* Translation Functions */
9264 /* The following functions translate between */
9265 /* LonLat, LayerPx, and ViewPortPx */
9267 /********************************************************/
9270 // TRANSLATION: LonLat <-> ViewPortPx
9274 * Method: getLonLatFromViewPortPx
9277 * viewPortPx - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or
9278 * an object with a 'x'
9279 * and 'y' properties.
9282 * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view
9283 * port <OpenLayers.Pixel>, translated into lon/lat
9284 * by the current base layer.
9286 getLonLatFromViewPortPx: function (viewPortPx) {
9288 if (this.baseLayer != null) {
9289 lonlat = this.baseLayer.getLonLatFromViewPortPx(viewPortPx);
9295 * APIMethod: getViewPortPxFromLonLat
9298 * lonlat - {<OpenLayers.LonLat>}
9301 * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in
9302 * <OpenLayers.LonLat>, translated into view port
9303 * pixels by the current base layer.
9305 getViewPortPxFromLonLat: function (lonlat) {
9307 if (this.baseLayer != null) {
9308 px = this.baseLayer.getViewPortPxFromLonLat(lonlat);
9314 * Method: getZoomTargetCenter
9317 * xy - {<OpenLayers.Pixel>} The zoom origin pixel location on the screen
9318 * resolution - {Float} The resolution we want to get the center for
9321 * {<OpenLayers.LonLat>} The location of the map center after the
9322 * transformation described by the origin xy and the target resolution.
9324 getZoomTargetCenter: function (xy, resolution) {
9326 size = this.getSize(),
9327 deltaX = size.w/2 - xy.x,
9328 deltaY = xy.y - size.h/2,
9329 zoomPoint = this.getLonLatFromPixel(xy);
9331 lonlat = new OpenLayers.LonLat(
9332 zoomPoint.lon + deltaX * resolution,
9333 zoomPoint.lat + deltaY * resolution
9340 // CONVENIENCE TRANSLATION FUNCTIONS FOR API
9344 * APIMethod: getLonLatFromPixel
9347 * px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object with
9348 * a 'x' and 'y' properties.
9351 * {<OpenLayers.LonLat>} An OpenLayers.LonLat corresponding to the given
9352 * OpenLayers.Pixel, translated into lon/lat by the
9353 * current base layer
9355 getLonLatFromPixel: function (px) {
9356 return this.getLonLatFromViewPortPx(px);
9360 * APIMethod: getPixelFromLonLat
9361 * Returns a pixel location given a map location. The map location is
9362 * translated to an integer pixel location (in viewport pixel
9363 * coordinates) by the current base layer.
9366 * lonlat - {<OpenLayers.LonLat>} A map location.
9369 * {<OpenLayers.Pixel>} An OpenLayers.Pixel corresponding to the
9370 * <OpenLayers.LonLat> translated into view port pixels by the current
9373 getPixelFromLonLat: function (lonlat) {
9374 var px = this.getViewPortPxFromLonLat(lonlat);
9375 px.x = Math.round(px.x);
9376 px.y = Math.round(px.y);
9381 * Method: getGeodesicPixelSize
9384 * px - {<OpenLayers.Pixel>} The pixel to get the geodesic length for. If
9385 * not provided, the center pixel of the map viewport will be used.
9388 * {<OpenLayers.Size>} The geodesic size of the pixel in kilometers.
9390 getGeodesicPixelSize: function(px) {
9391 var lonlat = px ? this.getLonLatFromPixel(px) : (
9392 this.getCachedCenter() || new OpenLayers.LonLat(0, 0));
9393 var res = this.getResolution();
9394 var left = lonlat.add(-res / 2, 0);
9395 var right = lonlat.add(res / 2, 0);
9396 var bottom = lonlat.add(0, -res / 2);
9397 var top = lonlat.add(0, res / 2);
9398 var dest = new OpenLayers.Projection("EPSG:4326");
9399 var source = this.getProjectionObject() || dest;
9400 if(!source.equals(dest)) {
9401 left.transform(source, dest);
9402 right.transform(source, dest);
9403 bottom.transform(source, dest);
9404 top.transform(source, dest);
9407 return new OpenLayers.Size(
9408 OpenLayers.Util.distVincenty(left, right),
9409 OpenLayers.Util.distVincenty(bottom, top)
9416 // TRANSLATION: ViewPortPx <-> LayerPx
9420 * APIMethod: getViewPortPxFromLayerPx
9423 * layerPx - {<OpenLayers.Pixel>}
9426 * {<OpenLayers.Pixel>} Layer Pixel translated into ViewPort Pixel
9429 getViewPortPxFromLayerPx:function(layerPx) {
9430 var viewPortPx = null;
9431 if (layerPx != null) {
9432 var dX = this.layerContainerOriginPx.x;
9433 var dY = this.layerContainerOriginPx.y;
9434 viewPortPx = layerPx.add(dX, dY);
9440 * APIMethod: getLayerPxFromViewPortPx
9443 * viewPortPx - {<OpenLayers.Pixel>}
9446 * {<OpenLayers.Pixel>} ViewPort Pixel translated into Layer Pixel
9449 getLayerPxFromViewPortPx:function(viewPortPx) {
9451 if (viewPortPx != null) {
9452 var dX = -this.layerContainerOriginPx.x;
9453 var dY = -this.layerContainerOriginPx.y;
9454 layerPx = viewPortPx.add(dX, dY);
9455 if (isNaN(layerPx.x) || isNaN(layerPx.y)) {
9463 // TRANSLATION: LonLat <-> LayerPx
9467 * Method: getLonLatFromLayerPx
9470 * px - {<OpenLayers.Pixel>}
9473 * {<OpenLayers.LonLat>}
9475 getLonLatFromLayerPx: function (px) {
9476 //adjust for displacement of layerContainerDiv
9477 px = this.getViewPortPxFromLayerPx(px);
9478 return this.getLonLatFromViewPortPx(px);
9482 * APIMethod: getLayerPxFromLonLat
9485 * lonlat - {<OpenLayers.LonLat>} lonlat
9488 * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in
9489 * <OpenLayers.LonLat>, translated into layer pixels
9490 * by the current base layer
9492 getLayerPxFromLonLat: function (lonlat) {
9493 //adjust for displacement of layerContainerDiv
9494 var px = this.getPixelFromLonLat(lonlat);
9495 return this.getLayerPxFromViewPortPx(px);
9499 * Method: applyTransform
9500 * Applies the given transform to the <layerContainerDiv>. This method has
9501 * a 2-stage fallback from translate3d/scale3d via translate/scale to plain
9502 * style.left/style.top, in which case no scaling is supported.
9505 * x - {Number} x parameter for the translation. Defaults to the x value of
9506 * the map's <layerContainerOriginPx>
9507 * y - {Number} y parameter for the translation. Defaults to the y value of
9508 * the map's <layerContainerOriginPx>
9509 * scale - {Number} scale. Defaults to 1 if not provided.
9511 applyTransform: function(x, y, scale) {
9513 var origin = this.layerContainerOriginPx,
9514 needTransform = scale !== 1;
9518 var style = this.layerContainerDiv.style,
9519 transform = this.applyTransform.transform,
9520 template = this.applyTransform.template;
9522 if (transform === undefined) {
9523 transform = OpenLayers.Util.vendorPrefix.style('transform');
9524 this.applyTransform.transform = transform;
9526 // Try translate3d, but only if the viewPortDiv has a transform
9527 // defined in a stylesheet
9528 var computedStyle = OpenLayers.Element.getStyle(this.viewPortDiv,
9529 OpenLayers.Util.vendorPrefix.css('transform'));
9530 if (!computedStyle || computedStyle !== 'none') {
9531 template = ['translate3d(', ',0) ', 'scale3d(', ',1)'];
9532 style[transform] = [template[0], '0,0', template[1]].join('');
9534 // If no transform is defined in the stylesheet or translate3d
9535 // does not stick, use translate and scale
9536 if (!template || !~style[transform].indexOf(template[0])) {
9537 template = ['translate(', ') ', 'scale(', ')'];
9539 this.applyTransform.template = template;
9543 // If we do 3d transforms, we always want to use them. If we do 2d
9544 // transforms, we only use them when we need to.
9545 if (transform !== null && (template[0] === 'translate3d(' || needTransform === true)) {
9546 // Our 2d transforms are combined with style.left and style.top, so
9547 // adjust x and y values and set the origin as left and top
9548 if (needTransform === true && template[0] === 'translate(') {
9551 style.left = origin.x + 'px';
9552 style.top = origin.y + 'px';
9554 style[transform] = [
9555 template[0], x, 'px,', y, 'px', template[1],
9556 template[2], scale, ',', scale, template[3]
9559 style.left = x + 'px';
9560 style.top = y + 'px';
9561 // We previously might have had needTransform, so remove transform
9562 if (transform !== null) {
9563 style[transform] = '';
9568 CLASS_NAME: "OpenLayers.Map"
9572 * Constant: TILE_WIDTH
9573 * {Integer} 256 Default tile width (unless otherwise specified)
9575 OpenLayers.Map.TILE_WIDTH = 256;
9577 * Constant: TILE_HEIGHT
9578 * {Integer} 256 Default tile height (unless otherwise specified)
9580 OpenLayers.Map.TILE_HEIGHT = 256;
9581 /* ======================================================================
9583 ====================================================================== */
9585 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
9586 * full list of contributors). Published under the 2-clause BSD license.
9587 * See license.txt in the OpenLayers distribution or repository for the
9588 * full text of the license. */
9592 * @requires OpenLayers/BaseTypes/Class.js
9593 * @requires OpenLayers/Map.js
9594 * @requires OpenLayers/Projection.js
9598 * Class: OpenLayers.Layer
9600 OpenLayers.Layer = OpenLayers.Class({
9621 * APIProperty: opacity
9622 * {Float} The layer's opacity. Float number between 0.0 and 1.0. Default
9628 * APIProperty: alwaysInRange
9629 * {Boolean} If a layer's display should not be scale-based, this should
9630 * be set to true. This will cause the layer, as an overlay, to always
9631 * be 'active', by always returning true from the calculateInRange()
9634 * If not explicitly specified for a layer, its value will be
9635 * determined on startup in initResolutions() based on whether or not
9636 * any scale-specific properties have been set as options on the
9637 * layer. If no scale-specific options have been set on the layer, we
9638 * assume that it should always be in range.
9640 * See #987 for more info.
9642 alwaysInRange: null,
9645 * Constant: RESOLUTION_PROPERTIES
9646 * {Array} The properties that are used for calculating resolutions
9649 RESOLUTION_PROPERTIES: [
9650 'scales', 'resolutions',
9651 'maxScale', 'minScale',
9652 'maxResolution', 'minResolution',
9653 'numZoomLevels', 'maxZoomLevel'
9657 * APIProperty: events
9658 * {<OpenLayers.Events>}
9660 * Register a listener for a particular event with the following syntax:
9662 * layer.events.register(type, obj, listener);
9665 * Listeners will be called with a reference to an event object. The
9666 * properties of this event depends on exactly what happened.
9668 * All event objects have at least the following properties:
9669 * object - {Object} A reference to layer.events.object.
9670 * element - {DOMElement} A reference to layer.events.element.
9672 * Supported map event types:
9673 * loadstart - Triggered when layer loading starts. When using a Vector
9674 * layer with a Fixed or BBOX strategy, the event object includes
9675 * a *filter* property holding the OpenLayers.Filter used when
9676 * calling read on the protocol.
9677 * loadend - Triggered when layer loading ends. When using a Vector layer
9678 * with a Fixed or BBOX strategy, the event object includes a
9679 * *response* property holding an OpenLayers.Protocol.Response object.
9680 * visibilitychanged - Triggered when the layer's visibility property is
9681 * changed, e.g. by turning the layer on or off in the layer switcher.
9682 * Note that the actual visibility of the layer can also change if it
9683 * gets out of range (see <calculateInRange>). If you also want to catch
9684 * these cases, register for the map's 'changelayer' event instead.
9685 * move - Triggered when layer moves (triggered with every mousemove
9687 * moveend - Triggered when layer is done moving, object passed as
9688 * argument has a zoomChanged boolean property which tells that the
9690 * added - Triggered after the layer is added to a map. Listeners will
9691 * receive an object with a *map* property referencing the map and a
9692 * *layer* property referencing the layer.
9693 * removed - Triggered after the layer is removed from the map. Listeners
9694 * will receive an object with a *map* property referencing the map and
9695 * a *layer* property referencing the layer.
9701 * {<OpenLayers.Map>} This variable is set when the layer is added to
9702 * the map, via the accessor function setMap().
9707 * APIProperty: isBaseLayer
9708 * {Boolean} Whether or not the layer is a base layer. This should be set
9709 * individually by all subclasses. Default is false
9715 * {Boolean} The layer's images have an alpha channel. Default is false.
9720 * APIProperty: displayInLayerSwitcher
9721 * {Boolean} Display the layer's name in the layer switcher. Default is
9724 displayInLayerSwitcher: true,
9727 * APIProperty: visibility
9728 * {Boolean} The layer should be displayed in the map. Default is true.
9733 * APIProperty: attribution
9734 * {String} Attribution string, displayed when an
9735 * <OpenLayers.Control.Attribution> has been added to the map.
9741 * {Boolean} The current map resolution is within the layer's min/max
9742 * range. This is set in <OpenLayers.Map.setCenter> whenever the zoom
9748 * Propery: imageSize
9749 * {<OpenLayers.Size>} For layers with a gutter, the image is larger than
9750 * the tile by twice the gutter in each dimension.
9758 * {Object} An optional object whose properties will be set on the layer.
9759 * Any of the layer properties can be set as a property of the options
9760 * object and sent to the constructor when the layer is created.
9765 * APIProperty: eventListeners
9766 * {Object} If set as an option at construction, the eventListeners
9767 * object will be registered with <OpenLayers.Events.on>. Object
9768 * structure must be a listeners object as shown in the example for
9769 * the events.on method.
9771 eventListeners: null,
9774 * APIProperty: gutter
9775 * {Integer} Determines the width (in pixels) of the gutter around image
9776 * tiles to ignore. By setting this property to a non-zero value,
9777 * images will be requested that are wider and taller than the tile
9778 * size by a value of 2 x gutter. This allows artifacts of rendering
9779 * at tile edges to be ignored. Set a gutter value that is equal to
9780 * half the size of the widest symbol that needs to be displayed.
9781 * Defaults to zero. Non-tiled layers always have zero gutter.
9786 * APIProperty: projection
9787 * {<OpenLayers.Projection>} or {<String>} Specifies the projection of the layer.
9788 * Can be set in the layer options. If not specified in the layer options,
9789 * it is set to the default projection specified in the map,
9790 * when the layer is added to the map.
9791 * Projection along with default maxExtent and resolutions
9792 * are set automatically with commercial baselayers in EPSG:3857,
9793 * such as Google, Bing and OpenStreetMap, and do not need to be specified.
9794 * Otherwise, if specifying projection, also set maxExtent,
9795 * maxResolution or resolutions as appropriate.
9796 * When using vector layers with strategies, layer projection should be set
9797 * to the projection of the source data if that is different from the map default.
9799 * Can be either a string or an <OpenLayers.Projection> object;
9800 * if a string is passed, will be converted to an object when
9801 * the layer is added to the map.
9807 * APIProperty: units
9808 * {String} The layer map units. Defaults to null. Possible values
9809 * are 'degrees' (or 'dd'), 'm', 'ft', 'km', 'mi', 'inches'.
9810 * Normally taken from the projection.
9811 * Only required if both map and layers do not define a projection,
9812 * or if they define a projection which does not define units.
9817 * APIProperty: scales
9818 * {Array} An array of map scales in descending order. The values in the
9819 * array correspond to the map scale denominator. Note that these
9820 * values only make sense if the display (monitor) resolution of the
9821 * client is correctly guessed by whomever is configuring the
9822 * application. In addition, the units property must also be set.
9823 * Use <resolutions> instead wherever possible.
9828 * APIProperty: resolutions
9829 * {Array} A list of map resolutions (map units per pixel) in descending
9830 * order. If this is not set in the layer constructor, it will be set
9831 * based on other resolution related properties (maxExtent,
9832 * maxResolution, maxScale, etc.).
9837 * APIProperty: maxExtent
9838 * {<OpenLayers.Bounds>|Array} If provided as an array, the array
9839 * should consist of four values (left, bottom, right, top).
9840 * The maximum extent for the layer. Defaults to null.
9842 * The center of these bounds will not stray outside
9843 * of the viewport extent during panning. In addition, if
9844 * <displayOutsideMaxExtent> is set to false, data will not be
9845 * requested that falls completely outside of these bounds.
9850 * APIProperty: minExtent
9851 * {<OpenLayers.Bounds>|Array} If provided as an array, the array
9852 * should consist of four values (left, bottom, right, top).
9853 * The minimum extent for the layer. Defaults to null.
9858 * APIProperty: maxResolution
9859 * {Float} Default max is 360 deg / 256 px, which corresponds to
9860 * zoom level 0 on gmaps. Specify a different value in the layer
9861 * options if you are not using the default <OpenLayers.Map.tileSize>
9862 * and displaying the whole world.
9864 maxResolution: null,
9867 * APIProperty: minResolution
9870 minResolution: null,
9873 * APIProperty: numZoomLevels
9876 numZoomLevels: null,
9879 * APIProperty: minScale
9885 * APIProperty: maxScale
9891 * APIProperty: displayOutsideMaxExtent
9892 * {Boolean} Request map tiles that are completely outside of the max
9893 * extent for this layer. Defaults to false.
9895 displayOutsideMaxExtent: false,
9898 * APIProperty: wrapDateLine
9899 * {Boolean} Wraps the world at the international dateline, so the map can
9900 * be panned infinitely in longitudinal direction. Only use this on the
9901 * base layer, and only if the layer's maxExtent equals the world bounds.
9902 * #487 for more info.
9904 wrapDateLine: false,
9907 * Property: metadata
9908 * {Object} This object can be used to store additional information on a
9914 * Constructor: OpenLayers.Layer
9917 * name - {String} The layer name
9918 * options - {Object} Hashtable of extra options to tag onto the layer
9920 initialize: function(name, options) {
9924 options = OpenLayers.Util.extend({}, options);
9925 // make sure we respect alwaysInRange if set on the prototype
9926 if (this.alwaysInRange != null) {
9927 options.alwaysInRange = this.alwaysInRange;
9929 this.addOptions(options);
9933 if (this.id == null) {
9935 this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
9937 this.div = OpenLayers.Util.createDiv(this.id);
9938 this.div.style.width = "100%";
9939 this.div.style.height = "100%";
9940 this.div.dir = "ltr";
9942 this.events = new OpenLayers.Events(this, this.div);
9943 if(this.eventListeners instanceof Object) {
9944 this.events.on(this.eventListeners);
9952 * Destroy is a destructor: this is to alleviate cyclic references which
9953 * the Javascript garbage cleaner can not take care of on its own.
9956 * setNewBaseLayer - {Boolean} Set a new base layer when this layer has
9957 * been destroyed. Default is true.
9959 destroy: function(setNewBaseLayer) {
9960 if (setNewBaseLayer == null) {
9961 setNewBaseLayer = true;
9963 if (this.map != null) {
9964 this.map.removeLayer(this, setNewBaseLayer);
9966 this.projection = null;
9970 this.options = null;
9973 if(this.eventListeners) {
9974 this.events.un(this.eventListeners);
9976 this.events.destroy();
9978 this.eventListeners = null;
9986 * obj - {<OpenLayers.Layer>} The layer to be cloned
9989 * {<OpenLayers.Layer>} An exact clone of this <OpenLayers.Layer>
9991 clone: function (obj) {
9994 obj = new OpenLayers.Layer(this.name, this.getOptions());
9997 // catch any randomly tagged-on properties
9998 OpenLayers.Util.applyDefaults(obj, this);
10000 // a cloned layer should never have its map property set
10001 // because it has not been added to a map yet.
10008 * Method: getOptions
10009 * Extracts an object from the layer with the properties that were set as
10010 * options, but updates them with the values currently set on the
10014 * {Object} the <options> of the layer, representing the current state.
10016 getOptions: function() {
10018 for(var o in this.options) {
10019 options[o] = this[o];
10025 * APIMethod: setName
10026 * Sets the new layer name for this layer. Can trigger a changelayer event
10030 * newName - {String} The new name.
10032 setName: function(newName) {
10033 if (newName != this.name) {
10034 this.name = newName;
10035 if (this.map != null) {
10036 this.map.events.triggerEvent("changelayer", {
10045 * APIMethod: addOptions
10048 * newOptions - {Object}
10049 * reinitialize - {Boolean} If set to true, and if resolution options of the
10050 * current baseLayer were changed, the map will be recentered to make
10051 * sure that it is displayed with a valid resolution, and a
10052 * changebaselayer event will be triggered.
10054 addOptions: function (newOptions, reinitialize) {
10056 if (this.options == null) {
10061 // make sure this.projection references a projection object
10062 if(typeof newOptions.projection == "string") {
10063 newOptions.projection = new OpenLayers.Projection(newOptions.projection);
10065 if (newOptions.projection) {
10066 // get maxResolution, units and maxExtent from projection defaults if
10067 // they are not defined already
10068 OpenLayers.Util.applyDefaults(newOptions,
10069 OpenLayers.Projection.defaults[newOptions.projection.getCode()]);
10071 // allow array for extents
10072 if (newOptions.maxExtent && !(newOptions.maxExtent instanceof OpenLayers.Bounds)) {
10073 newOptions.maxExtent = new OpenLayers.Bounds(newOptions.maxExtent);
10075 if (newOptions.minExtent && !(newOptions.minExtent instanceof OpenLayers.Bounds)) {
10076 newOptions.minExtent = new OpenLayers.Bounds(newOptions.minExtent);
10080 // update our copy for clone
10081 OpenLayers.Util.extend(this.options, newOptions);
10083 // add new options to this
10084 OpenLayers.Util.extend(this, newOptions);
10086 // get the units from the projection, if we have a projection
10087 // and it it has units
10088 if(this.projection && this.projection.getUnits()) {
10089 this.units = this.projection.getUnits();
10092 // re-initialize resolutions if necessary, i.e. if any of the
10093 // properties of the "properties" array defined below is set
10094 // in the new options
10096 // store current resolution so we can try to restore it later
10097 var resolution = this.map.getResolution();
10098 var properties = this.RESOLUTION_PROPERTIES.concat(
10099 ["projection", "units", "minExtent", "maxExtent"]
10101 for(var o in newOptions) {
10102 if(newOptions.hasOwnProperty(o) &&
10103 OpenLayers.Util.indexOf(properties, o) >= 0) {
10105 this.initResolutions();
10106 if (reinitialize && this.map.baseLayer === this) {
10107 // update map position, and restore previous resolution
10108 this.map.setCenter(this.map.getCenter(),
10109 this.map.getZoomForResolution(resolution),
10112 // trigger a changebaselayer event to make sure that
10113 // all controls (especially
10114 // OpenLayers.Control.PanZoomBar) get notified of the
10116 this.map.events.triggerEvent("changebaselayer", {
10127 * APIMethod: onMapResize
10128 * This function can be implemented by subclasses
10130 onMapResize: function() {
10131 //this function can be implemented by subclasses
10135 * APIMethod: redraw
10136 * Redraws the layer. Returns true if the layer was redrawn, false if not.
10139 * {Boolean} The layer was redrawn.
10141 redraw: function() {
10142 var redrawn = false;
10145 // min/max Range may have changed
10146 this.inRange = this.calculateInRange();
10148 // map's center might not yet be set
10149 var extent = this.getExtent();
10151 if (extent && this.inRange && this.visibility) {
10152 var zoomChanged = true;
10153 this.moveTo(extent, zoomChanged, false);
10154 this.events.triggerEvent("moveend",
10155 {"zoomChanged": zoomChanged});
10166 * bounds - {<OpenLayers.Bounds>}
10167 * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to
10168 * do some init work in that case.
10169 * dragging - {Boolean}
10171 moveTo:function(bounds, zoomChanged, dragging) {
10172 var display = this.visibility;
10173 if (!this.isBaseLayer) {
10174 display = display && this.inRange;
10176 this.display(display);
10181 * Move the layer based on pixel vector. To be implemented by subclasses.
10184 * dx - {Number} The x coord of the displacement vector.
10185 * dy - {Number} The y coord of the displacement vector.
10187 moveByPx: function(dx, dy) {
10192 * Set the map property for the layer. This is done through an accessor
10193 * so that subclasses can override this and take special action once
10194 * they have their map variable set.
10196 * Here we take care to bring over any of the necessary default
10197 * properties from the map.
10200 * map - {<OpenLayers.Map>}
10202 setMap: function(map) {
10203 if (this.map == null) {
10207 // grab some essential layer data from the map if it hasn't already
10209 this.maxExtent = this.maxExtent || this.map.maxExtent;
10210 this.minExtent = this.minExtent || this.map.minExtent;
10212 this.projection = this.projection || this.map.projection;
10213 if (typeof this.projection == "string") {
10214 this.projection = new OpenLayers.Projection(this.projection);
10217 // Check the projection to see if we can get units -- if not, refer
10219 this.units = this.projection.getUnits() ||
10220 this.units || this.map.units;
10222 this.initResolutions();
10224 if (!this.isBaseLayer) {
10225 this.inRange = this.calculateInRange();
10226 var show = ((this.visibility) && (this.inRange));
10227 this.div.style.display = show ? "" : "none";
10230 // deal with gutters
10231 this.setTileSize();
10237 * Called at the end of the map.addLayer sequence. At this point, the map
10238 * will have a base layer. To be overridden by subclasses.
10240 afterAdd: function() {
10244 * APIMethod: removeMap
10245 * Just as setMap() allows each layer the possibility to take a
10246 * personalized action on being added to the map, removeMap() allows
10247 * each layer to take a personalized action on being removed from it.
10248 * For now, this will be mostly unused, except for the EventPane layer,
10249 * which needs this hook so that it can remove the special invisible
10253 * map - {<OpenLayers.Map>}
10255 removeMap: function(map) {
10256 //to be overridden by subclasses
10260 * APIMethod: getImageSize
10263 * bounds - {<OpenLayers.Bounds>} optional tile bounds, can be used
10264 * by subclasses that have to deal with different tile sizes at the
10265 * layer extent edges (e.g. Zoomify)
10268 * {<OpenLayers.Size>} The size that the image should be, taking into
10271 getImageSize: function(bounds) {
10272 return (this.imageSize || this.tileSize);
10276 * APIMethod: setTileSize
10277 * Set the tile size based on the map size. This also sets layer.imageSize
10278 * or use by Tile.Image.
10281 * size - {<OpenLayers.Size>}
10283 setTileSize: function(size) {
10284 var tileSize = (size) ? size :
10285 ((this.tileSize) ? this.tileSize :
10286 this.map.getTileSize());
10287 this.tileSize = tileSize;
10289 // layers with gutters need non-null tile sizes
10290 //if(tileSize == null) {
10291 // OpenLayers.console.error("Error in layer.setMap() for " +
10292 // this.name + ": layers with " +
10293 // "gutters need non-null tile sizes");
10295 this.imageSize = new OpenLayers.Size(tileSize.w + (2*this.gutter),
10296 tileSize.h + (2*this.gutter));
10301 * APIMethod: getVisibility
10304 * {Boolean} The layer should be displayed (if in range).
10306 getVisibility: function() {
10307 return this.visibility;
10311 * APIMethod: setVisibility
10312 * Set the visibility flag for the layer and hide/show & redraw
10313 * accordingly. Fire event unless otherwise specified
10315 * Note that visibility is no longer simply whether or not the layer's
10316 * style.display is set to "block". Now we store a 'visibility' state
10317 * property on the layer class, this allows us to remember whether or
10318 * not we *desire* for a layer to be visible. In the case where the
10319 * map's resolution is out of the layer's range, this desire may be
10323 * visibility - {Boolean} Whether or not to display the layer (if in range)
10325 setVisibility: function(visibility) {
10326 if (visibility != this.visibility) {
10327 this.visibility = visibility;
10328 this.display(visibility);
10330 if (this.map != null) {
10331 this.map.events.triggerEvent("changelayer", {
10333 property: "visibility"
10336 this.events.triggerEvent("visibilitychanged");
10341 * APIMethod: display
10342 * Hide or show the Layer. This is designed to be used internally, and
10343 * is not generally the way to enable or disable the layer. For that,
10344 * use the setVisibility function instead..
10347 * display - {Boolean}
10349 display: function(display) {
10350 if (display != (this.div.style.display != "none")) {
10351 this.div.style.display = (display && this.calculateInRange()) ? "block" : "none";
10356 * APIMethod: calculateInRange
10359 * {Boolean} The layer is displayable at the current map's current
10360 * resolution. Note that if 'alwaysInRange' is true for the layer,
10361 * this function will always return true.
10363 calculateInRange: function() {
10364 var inRange = false;
10366 if (this.alwaysInRange) {
10370 var resolution = this.map.getResolution();
10371 inRange = ( (resolution >= this.minResolution) &&
10372 (resolution <= this.maxResolution) );
10379 * APIMethod: setIsBaseLayer
10382 * isBaseLayer - {Boolean}
10384 setIsBaseLayer: function(isBaseLayer) {
10385 if (isBaseLayer != this.isBaseLayer) {
10386 this.isBaseLayer = isBaseLayer;
10387 if (this.map != null) {
10388 this.map.events.triggerEvent("changebaselayer", {
10395 /********************************************************/
10397 /* Baselayer Functions */
10399 /********************************************************/
10402 * Method: initResolutions
10403 * This method's responsibility is to set up the 'resolutions' array
10404 * for the layer -- this array is what the layer will use to interface
10405 * between the zoom levels of the map and the resolution display
10408 * The user has several options that determine how the array is set up.
10410 * For a detailed explanation, see the following wiki from the
10411 * openlayers.org homepage:
10412 * http://trac.openlayers.org/wiki/SettingZoomLevels
10414 initResolutions: function() {
10416 // ok we want resolutions, here's our strategy:
10418 // 1. if resolutions are defined in the layer config, use them
10419 // 2. else, if scales are defined in the layer config then derive
10420 // resolutions from these scales
10421 // 3. else, attempt to calculate resolutions from maxResolution,
10422 // minResolution, numZoomLevels, maxZoomLevel set in the
10424 // 4. if we still don't have resolutions, and if resolutions
10425 // are defined in the same, use them
10426 // 5. else, if scales are defined in the map then derive
10427 // resolutions from these scales
10428 // 6. else, attempt to calculate resolutions from maxResolution,
10429 // minResolution, numZoomLevels, maxZoomLevel set in the
10431 // 7. hope for the best!
10434 var props = {}, alwaysInRange = true;
10436 // get resolution data from layer config
10437 // (we also set alwaysInRange in the layer as appropriate)
10438 for(i=0, len=this.RESOLUTION_PROPERTIES.length; i<len; i++) {
10439 p = this.RESOLUTION_PROPERTIES[i];
10440 props[p] = this.options[p];
10441 if(alwaysInRange && this.options[p]) {
10442 alwaysInRange = false;
10445 if(this.options.alwaysInRange == null) {
10446 this.alwaysInRange = alwaysInRange;
10449 // if we don't have resolutions then attempt to derive them from scales
10450 if(props.resolutions == null) {
10451 props.resolutions = this.resolutionsFromScales(props.scales);
10454 // if we still don't have resolutions then attempt to calculate them
10455 if(props.resolutions == null) {
10456 props.resolutions = this.calculateResolutions(props);
10459 // if we couldn't calculate resolutions then we look at we have
10461 if(props.resolutions == null) {
10462 for(i=0, len=this.RESOLUTION_PROPERTIES.length; i<len; i++) {
10463 p = this.RESOLUTION_PROPERTIES[i];
10464 props[p] = this.options[p] != null ?
10465 this.options[p] : this.map[p];
10467 if(props.resolutions == null) {
10468 props.resolutions = this.resolutionsFromScales(props.scales);
10470 if(props.resolutions == null) {
10471 props.resolutions = this.calculateResolutions(props);
10475 // ok, we new need to set properties in the instance
10477 // get maxResolution from the config if it's defined there
10479 if(this.options.maxResolution &&
10480 this.options.maxResolution !== "auto") {
10481 maxResolution = this.options.maxResolution;
10483 if(this.options.minScale) {
10484 maxResolution = OpenLayers.Util.getResolutionFromScale(
10485 this.options.minScale, this.units);
10488 // get minResolution from the config if it's defined there
10490 if(this.options.minResolution &&
10491 this.options.minResolution !== "auto") {
10492 minResolution = this.options.minResolution;
10494 if(this.options.maxScale) {
10495 minResolution = OpenLayers.Util.getResolutionFromScale(
10496 this.options.maxScale, this.units);
10499 if(props.resolutions) {
10501 //sort resolutions array descendingly
10502 props.resolutions.sort(function(a, b) {
10506 // if we still don't have a maxResolution get it from the
10507 // resolutions array
10508 if(!maxResolution) {
10509 maxResolution = props.resolutions[0];
10512 // if we still don't have a minResolution get it from the
10513 // resolutions array
10514 if(!minResolution) {
10515 var lastIdx = props.resolutions.length - 1;
10516 minResolution = props.resolutions[lastIdx];
10520 this.resolutions = props.resolutions;
10521 if(this.resolutions) {
10522 len = this.resolutions.length;
10523 this.scales = new Array(len);
10524 for(i=0; i<len; i++) {
10525 this.scales[i] = OpenLayers.Util.getScaleFromResolution(
10526 this.resolutions[i], this.units);
10528 this.numZoomLevels = len;
10530 this.minResolution = minResolution;
10531 if(minResolution) {
10532 this.maxScale = OpenLayers.Util.getScaleFromResolution(
10533 minResolution, this.units);
10535 this.maxResolution = maxResolution;
10536 if(maxResolution) {
10537 this.minScale = OpenLayers.Util.getScaleFromResolution(
10538 maxResolution, this.units);
10543 * Method: resolutionsFromScales
10544 * Derive resolutions from scales.
10547 * scales - {Array(Number)} Scales
10550 * {Array(Number)} Resolutions
10552 resolutionsFromScales: function(scales) {
10553 if(scales == null) {
10556 var resolutions, i, len;
10557 len = scales.length;
10558 resolutions = new Array(len);
10559 for(i=0; i<len; i++) {
10560 resolutions[i] = OpenLayers.Util.getResolutionFromScale(
10561 scales[i], this.units);
10563 return resolutions;
10567 * Method: calculateResolutions
10568 * Calculate resolutions based on the provided properties.
10571 * props - {Object} Properties
10574 * {Array({Number})} Array of resolutions.
10576 calculateResolutions: function(props) {
10578 var viewSize, wRes, hRes;
10580 // determine maxResolution
10581 var maxResolution = props.maxResolution;
10582 if(props.minScale != null) {
10584 OpenLayers.Util.getResolutionFromScale(props.minScale,
10586 } else if(maxResolution == "auto" && this.maxExtent != null) {
10587 viewSize = this.map.getSize();
10588 wRes = this.maxExtent.getWidth() / viewSize.w;
10589 hRes = this.maxExtent.getHeight() / viewSize.h;
10590 maxResolution = Math.max(wRes, hRes);
10593 // determine minResolution
10594 var minResolution = props.minResolution;
10595 if(props.maxScale != null) {
10597 OpenLayers.Util.getResolutionFromScale(props.maxScale,
10599 } else if(props.minResolution == "auto" && this.minExtent != null) {
10600 viewSize = this.map.getSize();
10601 wRes = this.minExtent.getWidth() / viewSize.w;
10602 hRes = this.minExtent.getHeight()/ viewSize.h;
10603 minResolution = Math.max(wRes, hRes);
10606 if(typeof maxResolution !== "number" &&
10607 typeof minResolution !== "number" &&
10608 this.maxExtent != null) {
10609 // maxResolution for default grid sets assumes that at zoom
10610 // level zero, the whole world fits on one tile.
10611 var tileSize = this.map.getTileSize();
10612 maxResolution = Math.max(
10613 this.maxExtent.getWidth() / tileSize.w,
10614 this.maxExtent.getHeight() / tileSize.h
10618 // determine numZoomLevels
10619 var maxZoomLevel = props.maxZoomLevel;
10620 var numZoomLevels = props.numZoomLevels;
10621 if(typeof minResolution === "number" &&
10622 typeof maxResolution === "number" && numZoomLevels === undefined) {
10623 var ratio = maxResolution / minResolution;
10624 numZoomLevels = Math.floor(Math.log(ratio) / Math.log(2)) + 1;
10625 } else if(numZoomLevels === undefined && maxZoomLevel != null) {
10626 numZoomLevels = maxZoomLevel + 1;
10629 // are we able to calculate resolutions?
10630 if(typeof numZoomLevels !== "number" || numZoomLevels <= 0 ||
10631 (typeof maxResolution !== "number" &&
10632 typeof minResolution !== "number")) {
10636 // now we have numZoomLevels and at least one of maxResolution
10637 // or minResolution, we can populate the resolutions array
10639 var resolutions = new Array(numZoomLevels);
10641 if(typeof minResolution == "number" &&
10642 typeof maxResolution == "number") {
10643 // if maxResolution and minResolution are set, we calculate
10644 // the base for exponential scaling that starts at
10645 // maxResolution and ends at minResolution in numZoomLevels
10648 (maxResolution / minResolution),
10649 (1 / (numZoomLevels - 1))
10654 if(typeof maxResolution === "number") {
10655 for(i=0; i<numZoomLevels; i++) {
10656 resolutions[i] = maxResolution / Math.pow(base, i);
10659 for(i=0; i<numZoomLevels; i++) {
10660 resolutions[numZoomLevels - 1 - i] =
10661 minResolution * Math.pow(base, i);
10665 return resolutions;
10669 * APIMethod: getResolution
10672 * {Float} The currently selected resolution of the map, taken from the
10673 * resolutions array, indexed by current zoom level.
10675 getResolution: function() {
10676 var zoom = this.map.getZoom();
10677 return this.getResolutionForZoom(zoom);
10681 * APIMethod: getExtent
10684 * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat
10685 * bounds of the current viewPort.
10687 getExtent: function() {
10688 // just use stock map calculateBounds function -- passing no arguments
10689 // means it will user map's current center & resolution
10691 return this.map.calculateBounds();
10695 * APIMethod: getZoomForExtent
10698 * extent - {<OpenLayers.Bounds>}
10699 * closest - {Boolean} Find the zoom level that most closely fits the
10700 * specified bounds. Note that this may result in a zoom that does
10701 * not exactly contain the entire extent.
10702 * Default is false.
10705 * {Integer} The index of the zoomLevel (entry in the resolutions array)
10706 * for the passed-in extent. We do this by calculating the ideal
10707 * resolution for the given extent (based on the map size) and then
10708 * calling getZoomForResolution(), passing along the 'closest'
10711 getZoomForExtent: function(extent, closest) {
10712 var viewSize = this.map.getSize();
10713 var idealResolution = Math.max( extent.getWidth() / viewSize.w,
10714 extent.getHeight() / viewSize.h );
10716 return this.getZoomForResolution(idealResolution, closest);
10720 * Method: getDataExtent
10721 * Calculates the max extent which includes all of the data for the layer.
10722 * This function is to be implemented by subclasses.
10725 * {<OpenLayers.Bounds>}
10727 getDataExtent: function () {
10728 //to be implemented by subclasses
10732 * APIMethod: getResolutionForZoom
10738 * {Float} A suitable resolution for the specified zoom.
10740 getResolutionForZoom: function(zoom) {
10741 zoom = Math.max(0, Math.min(zoom, this.resolutions.length - 1));
10743 if(this.map.fractionalZoom) {
10744 var low = Math.floor(zoom);
10745 var high = Math.ceil(zoom);
10746 resolution = this.resolutions[low] -
10747 ((zoom-low) * (this.resolutions[low]-this.resolutions[high]));
10749 resolution = this.resolutions[Math.round(zoom)];
10755 * APIMethod: getZoomForResolution
10758 * resolution - {Float}
10759 * closest - {Boolean} Find the zoom level that corresponds to the absolute
10760 * closest resolution, which may result in a zoom whose corresponding
10761 * resolution is actually smaller than we would have desired (if this
10762 * is being called from a getZoomForExtent() call, then this means that
10763 * the returned zoom index might not actually contain the entire
10764 * extent specified... but it'll be close).
10765 * Default is false.
10768 * {Integer} The index of the zoomLevel (entry in the resolutions array)
10769 * that corresponds to the best fit resolution given the passed in
10770 * value and the 'closest' specification.
10772 getZoomForResolution: function(resolution, closest) {
10774 if(this.map.fractionalZoom) {
10776 var highZoom = this.resolutions.length - 1;
10777 var highRes = this.resolutions[lowZoom];
10778 var lowRes = this.resolutions[highZoom];
10780 for(i=0, len=this.resolutions.length; i<len; ++i) {
10781 res = this.resolutions[i];
10782 if(res >= resolution) {
10786 if(res <= resolution) {
10792 var dRes = highRes - lowRes;
10794 zoom = lowZoom + ((highRes - resolution) / dRes);
10800 var minDiff = Number.POSITIVE_INFINITY;
10801 for(i=0, len=this.resolutions.length; i<len; i++) {
10803 diff = Math.abs(this.resolutions[i] - resolution);
10804 if (diff > minDiff) {
10809 if (this.resolutions[i] < resolution) {
10814 zoom = Math.max(0, i-1);
10820 * APIMethod: getLonLatFromViewPortPx
10823 * viewPortPx - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or
10824 * an object with a 'x'
10825 * and 'y' properties.
10828 * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in
10829 * view port <OpenLayers.Pixel>, translated into lon/lat by the layer.
10831 getLonLatFromViewPortPx: function (viewPortPx) {
10833 var map = this.map;
10834 if (viewPortPx != null && map.minPx) {
10835 var res = map.getResolution();
10836 var maxExtent = map.getMaxExtent({restricted: true});
10837 var lon = (viewPortPx.x - map.minPx.x) * res + maxExtent.left;
10838 var lat = (map.minPx.y - viewPortPx.y) * res + maxExtent.top;
10839 lonlat = new OpenLayers.LonLat(lon, lat);
10841 if (this.wrapDateLine) {
10842 lonlat = lonlat.wrapDateLine(this.maxExtent);
10849 * APIMethod: getViewPortPxFromLonLat
10850 * Returns a pixel location given a map location. This method will return
10851 * fractional pixel values.
10854 * lonlat - {<OpenLayers.LonLat>|Object} An OpenLayers.LonLat or
10855 * an object with a 'lon'
10856 * and 'lat' properties.
10859 * {<OpenLayers.Pixel>} An <OpenLayers.Pixel> which is the passed-in
10860 * lonlat translated into view port pixels.
10862 getViewPortPxFromLonLat: function (lonlat, resolution) {
10864 if (lonlat != null) {
10865 resolution = resolution || this.map.getResolution();
10866 var extent = this.map.calculateBounds(null, resolution);
10867 px = new OpenLayers.Pixel(
10868 (1/resolution * (lonlat.lon - extent.left)),
10869 (1/resolution * (extent.top - lonlat.lat))
10876 * APIMethod: setOpacity
10877 * Sets the opacity for the entire layer (all images)
10880 * opacity - {Float}
10882 setOpacity: function(opacity) {
10883 if (opacity != this.opacity) {
10884 this.opacity = opacity;
10885 var childNodes = this.div.childNodes;
10886 for(var i = 0, len = childNodes.length; i < len; ++i) {
10887 var element = childNodes[i].firstChild || childNodes[i];
10888 var lastChild = childNodes[i].lastChild;
10889 //TODO de-uglify this
10890 if (lastChild && lastChild.nodeName.toLowerCase() === "iframe") {
10891 element = lastChild.parentNode;
10893 OpenLayers.Util.modifyDOMElement(element, null, null, null,
10894 null, null, null, opacity);
10896 if (this.map != null) {
10897 this.map.events.triggerEvent("changelayer", {
10899 property: "opacity"
10906 * Method: getZIndex
10909 * {Integer} the z-index of this layer
10911 getZIndex: function () {
10912 return this.div.style.zIndex;
10916 * Method: setZIndex
10919 * zIndex - {Integer}
10921 setZIndex: function (zIndex) {
10922 this.div.style.zIndex = zIndex;
10926 * Method: adjustBounds
10927 * This function will take a bounds, and if wrapDateLine option is set
10928 * on the layer, it will return a bounds which is wrapped around the
10929 * world. We do not wrap for bounds which *cross* the
10930 * maxExtent.left/right, only bounds which are entirely to the left
10931 * or entirely to the right.
10934 * bounds - {<OpenLayers.Bounds>}
10936 adjustBounds: function (bounds) {
10939 // Adjust the extent of a bounds in map units by the
10940 // layer's gutter in pixels.
10941 var mapGutter = this.gutter * this.map.getResolution();
10942 bounds = new OpenLayers.Bounds(bounds.left - mapGutter,
10943 bounds.bottom - mapGutter,
10944 bounds.right + mapGutter,
10945 bounds.top + mapGutter);
10948 if (this.wrapDateLine) {
10949 // wrap around the date line, within the limits of rounding error
10950 var wrappingOptions = {
10951 'rightTolerance':this.getResolution(),
10952 'leftTolerance':this.getResolution()
10954 bounds = bounds.wrapDateLine(this.maxExtent, wrappingOptions);
10960 CLASS_NAME: "OpenLayers.Layer"
10962 /* ======================================================================
10963 OpenLayers/Layer/SphericalMercator.js
10964 ====================================================================== */
10966 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
10967 * full list of contributors). Published under the 2-clause BSD license.
10968 * See license.txt in the OpenLayers distribution or repository for the
10969 * full text of the license. */
10972 * @requires OpenLayers/Layer.js
10973 * @requires OpenLayers/Projection.js
10977 * Class: OpenLayers.Layer.SphericalMercator
10978 * A mixin for layers that wraps up the pieces neccesary to have a coordinate
10979 * conversion for working with commercial APIs which use a spherical
10980 * mercator projection. Using this layer as a base layer, additional
10981 * layers can be used as overlays if they are in the same projection.
10983 * A layer is given properties of this object by setting the sphericalMercator
10984 * property to true.
10986 * More projection information:
10987 * - http://spatialreference.org/ref/user/google-projection/
10990 * +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0
10991 * +k=1.0 +units=m +nadgrids=@null +no_defs
10994 * 900913=PROJCS["WGS84 / Simple Mercator", GEOGCS["WGS 84",
10995 * DATUM["WGS_1984", SPHEROID["WGS_1984", 6378137.0, 298.257223563]],
10996 * PRIMEM["Greenwich", 0.0], UNIT["degree", 0.017453292519943295],
10997 * AXIS["Longitude", EAST], AXIS["Latitude", NORTH]],
10998 * PROJECTION["Mercator_1SP_Google"],
10999 * PARAMETER["latitude_of_origin", 0.0], PARAMETER["central_meridian", 0.0],
11000 * PARAMETER["scale_factor", 1.0], PARAMETER["false_easting", 0.0],
11001 * PARAMETER["false_northing", 0.0], UNIT["m", 1.0], AXIS["x", EAST],
11002 * AXIS["y", NORTH], AUTHORITY["EPSG","900913"]]
11004 OpenLayers.Layer.SphericalMercator = {
11007 * Method: getExtent
11008 * Get the map's extent.
11011 * {<OpenLayers.Bounds>} The map extent.
11013 getExtent: function() {
11015 if (this.sphericalMercator) {
11016 extent = this.map.calculateBounds();
11018 extent = OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this);
11024 * Method: getLonLatFromViewPortPx
11025 * Get a map location from a pixel location
11028 * viewPortPx - {<OpenLayers.Pixel>}
11031 * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view
11032 * port OpenLayers.Pixel, translated into lon/lat by map lib
11033 * If the map lib is not loaded or not centered, returns null
11035 getLonLatFromViewPortPx: function (viewPortPx) {
11036 return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this, arguments);
11040 * Method: getViewPortPxFromLonLat
11041 * Get a pixel location from a map location
11044 * lonlat - {<OpenLayers.LonLat>}
11047 * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in
11048 * OpenLayers.LonLat, translated into view port pixels by map lib
11049 * If map lib is not loaded or not centered, returns null
11051 getViewPortPxFromLonLat: function (lonlat) {
11052 return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this, arguments);
11056 * Method: initMercatorParameters
11057 * Set up the mercator parameters on the layer: resolutions,
11058 * projection, units.
11060 initMercatorParameters: function() {
11061 // set up properties for Mercator - assume EPSG:900913
11062 this.RESOLUTIONS = [];
11063 var maxResolution = 156543.03390625;
11064 for(var zoom=0; zoom<=this.MAX_ZOOM_LEVEL; ++zoom) {
11065 this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom);
11068 this.projection = this.projection || "EPSG:900913";
11072 * APIMethod: forwardMercator
11073 * Given a lon,lat in EPSG:4326, return a point in Spherical Mercator.
11080 * {<OpenLayers.LonLat>} The coordinates transformed to Mercator.
11082 forwardMercator: (function() {
11083 var gg = new OpenLayers.Projection("EPSG:4326");
11084 var sm = new OpenLayers.Projection("EPSG:900913");
11085 return function(lon, lat) {
11086 var point = OpenLayers.Projection.transform({x: lon, y: lat}, gg, sm);
11087 return new OpenLayers.LonLat(point.x, point.y);
11092 * APIMethod: inverseMercator
11093 * Given a x,y in Spherical Mercator, return a point in EPSG:4326.
11096 * x - {float} A map x in Spherical Mercator.
11097 * y - {float} A map y in Spherical Mercator.
11100 * {<OpenLayers.LonLat>} The coordinates transformed to EPSG:4326.
11102 inverseMercator: (function() {
11103 var gg = new OpenLayers.Projection("EPSG:4326");
11104 var sm = new OpenLayers.Projection("EPSG:900913");
11105 return function(x, y) {
11106 var point = OpenLayers.Projection.transform({x: x, y: y}, sm, gg);
11107 return new OpenLayers.LonLat(point.x, point.y);
11112 /* ======================================================================
11113 OpenLayers/Layer/EventPane.js
11114 ====================================================================== */
11116 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
11117 * full list of contributors). Published under the 2-clause BSD license.
11118 * See license.txt in the OpenLayers distribution or repository for the
11119 * full text of the license. */
11123 * @requires OpenLayers/Layer.js
11124 * @requires OpenLayers/Util.js
11128 * Class: OpenLayers.Layer.EventPane
11129 * Base class for 3rd party layers, providing a DOM element which isolates
11130 * the 3rd-party layer from mouse events.
11131 * Only used by Google layers.
11133 * Automatically instantiated by the Google constructor, and not usually instantiated directly.
11135 * Create a new event pane layer with the
11136 * <OpenLayers.Layer.EventPane> constructor.
11139 * - <OpenLayers.Layer>
11141 OpenLayers.Layer.EventPane = OpenLayers.Class(OpenLayers.Layer, {
11144 * APIProperty: smoothDragPan
11145 * {Boolean} smoothDragPan determines whether non-public/internal API
11146 * methods are used for better performance while dragging EventPane
11147 * layers. When not in sphericalMercator mode, the smoother dragging
11148 * doesn't actually move north/south directly with the number of
11149 * pixels moved, resulting in a slight offset when you drag your mouse
11150 * north south with this option on. If this visual disparity bothers
11151 * you, you should turn this option off, or use spherical mercator.
11154 smoothDragPan: true,
11157 * Property: isBaseLayer
11158 * {Boolean} EventPaned layers are always base layers, by necessity.
11163 * APIProperty: isFixed
11164 * {Boolean} EventPaned layers are fixed by default.
11170 * {DOMElement} A reference to the element that controls the events.
11176 * Property: mapObject
11177 * {Object} This is the object which will be used to load the 3rd party library
11178 * in the case of the google layer, this will be of type GMap,
11179 * in the case of the ve layer, this will be of type VEMap
11185 * Constructor: OpenLayers.Layer.EventPane
11186 * Create a new event pane layer
11190 * options - {Object} Hashtable of extra options to tag onto the layer
11192 initialize: function(name, options) {
11193 OpenLayers.Layer.prototype.initialize.apply(this, arguments);
11194 if (this.pane == null) {
11195 this.pane = OpenLayers.Util.createDiv(this.div.id + "_EventPane");
11200 * APIMethod: destroy
11201 * Deconstruct this layer.
11203 destroy: function() {
11204 this.mapObject = null;
11206 OpenLayers.Layer.prototype.destroy.apply(this, arguments);
11212 * Set the map property for the layer. This is done through an accessor
11213 * so that subclasses can override this and take special action once
11214 * they have their map variable set.
11217 * map - {<OpenLayers.Map>}
11219 setMap: function(map) {
11220 OpenLayers.Layer.prototype.setMap.apply(this, arguments);
11222 this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;
11223 this.pane.style.display = this.div.style.display;
11224 this.pane.style.width="100%";
11225 this.pane.style.height="100%";
11226 if (OpenLayers.BROWSER_NAME == "msie") {
11227 this.pane.style.background =
11228 "url(" + OpenLayers.Util.getImageLocation("blank.gif") + ")";
11231 if (this.isFixed) {
11232 this.map.viewPortDiv.appendChild(this.pane);
11234 this.map.layerContainerDiv.appendChild(this.pane);
11237 // once our layer has been added to the map, we can load it
11238 this.loadMapObject();
11240 // if map didn't load, display warning
11241 if (this.mapObject == null) {
11242 this.loadWarningMessage();
11247 * APIMethod: removeMap
11248 * On being removed from the map, we'll like to remove the invisible 'pane'
11249 * div that we added to it on creation.
11252 * map - {<OpenLayers.Map>}
11254 removeMap: function(map) {
11255 if (this.pane && this.pane.parentNode) {
11256 this.pane.parentNode.removeChild(this.pane);
11258 OpenLayers.Layer.prototype.removeMap.apply(this, arguments);
11262 * Method: loadWarningMessage
11263 * If we can't load the map lib, then display an error message to the
11264 * user and tell them where to go for help.
11266 * This function sets up the layout for the warning message. Each 3rd
11267 * party layer must implement its own getWarningHTML() function to
11268 * provide the actual warning message.
11270 loadWarningMessage:function() {
11272 this.div.style.backgroundColor = "darkblue";
11274 var viewSize = this.map.getSize();
11276 var msgW = Math.min(viewSize.w, 300);
11277 var msgH = Math.min(viewSize.h, 200);
11278 var size = new OpenLayers.Size(msgW, msgH);
11280 var centerPx = new OpenLayers.Pixel(viewSize.w/2, viewSize.h/2);
11282 var topLeft = centerPx.add(-size.w/2, -size.h/2);
11284 var div = OpenLayers.Util.createDiv(this.name + "_warning",
11292 div.style.padding = "7px";
11293 div.style.backgroundColor = "yellow";
11295 div.innerHTML = this.getWarningHTML();
11296 this.div.appendChild(div);
11300 * Method: getWarningHTML
11301 * To be implemented by subclasses.
11304 * {String} String with information on why layer is broken, how to get
11307 getWarningHTML:function() {
11308 //should be implemented by subclasses
11314 * Set the display on the pane
11317 * display - {Boolean}
11319 display: function(display) {
11320 OpenLayers.Layer.prototype.display.apply(this, arguments);
11321 this.pane.style.display = this.div.style.display;
11325 * Method: setZIndex
11326 * Set the z-index order for the pane.
11331 setZIndex: function (zIndex) {
11332 OpenLayers.Layer.prototype.setZIndex.apply(this, arguments);
11333 this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;
11338 * Move the layer based on pixel vector. To be implemented by subclasses.
11341 * dx - {Number} The x coord of the displacement vector.
11342 * dy - {Number} The y coord of the displacement vector.
11344 moveByPx: function(dx, dy) {
11345 OpenLayers.Layer.prototype.moveByPx.apply(this, arguments);
11347 if (this.dragPanMapObject) {
11348 this.dragPanMapObject(dx, -dy);
11350 this.moveTo(this.map.getCachedCenter());
11356 * Handle calls to move the layer.
11359 * bounds - {<OpenLayers.Bounds>}
11360 * zoomChanged - {Boolean}
11361 * dragging - {Boolean}
11363 moveTo:function(bounds, zoomChanged, dragging) {
11364 OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
11366 if (this.mapObject != null) {
11368 var newCenter = this.map.getCenter();
11369 var newZoom = this.map.getZoom();
11371 if (newCenter != null) {
11373 var moOldCenter = this.getMapObjectCenter();
11374 var oldCenter = this.getOLLonLatFromMapObjectLonLat(moOldCenter);
11376 var moOldZoom = this.getMapObjectZoom();
11377 var oldZoom= this.getOLZoomFromMapObjectZoom(moOldZoom);
11379 if (!(newCenter.equals(oldCenter)) || newZoom != oldZoom) {
11381 if (!zoomChanged && oldCenter && this.dragPanMapObject &&
11382 this.smoothDragPan) {
11383 var oldPx = this.map.getViewPortPxFromLonLat(oldCenter);
11384 var newPx = this.map.getViewPortPxFromLonLat(newCenter);
11385 this.dragPanMapObject(newPx.x-oldPx.x, oldPx.y-newPx.y);
11387 var center = this.getMapObjectLonLatFromOLLonLat(newCenter);
11388 var zoom = this.getMapObjectZoomFromOLZoom(newZoom);
11389 this.setMapObjectCenter(center, zoom, dragging);
11397 /********************************************************/
11399 /* Baselayer Functions */
11401 /********************************************************/
11404 * Method: getLonLatFromViewPortPx
11405 * Get a map location from a pixel location
11408 * viewPortPx - {<OpenLayers.Pixel>}
11411 * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view
11412 * port OpenLayers.Pixel, translated into lon/lat by map lib
11413 * If the map lib is not loaded or not centered, returns null
11415 getLonLatFromViewPortPx: function (viewPortPx) {
11417 if ( (this.mapObject != null) &&
11418 (this.getMapObjectCenter() != null) ) {
11419 var moPixel = this.getMapObjectPixelFromOLPixel(viewPortPx);
11420 var moLonLat = this.getMapObjectLonLatFromMapObjectPixel(moPixel);
11421 lonlat = this.getOLLonLatFromMapObjectLonLat(moLonLat);
11428 * Method: getViewPortPxFromLonLat
11429 * Get a pixel location from a map location
11432 * lonlat - {<OpenLayers.LonLat>}
11435 * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in
11436 * OpenLayers.LonLat, translated into view port pixels by map lib
11437 * If map lib is not loaded or not centered, returns null
11439 getViewPortPxFromLonLat: function (lonlat) {
11440 var viewPortPx = null;
11441 if ( (this.mapObject != null) &&
11442 (this.getMapObjectCenter() != null) ) {
11444 var moLonLat = this.getMapObjectLonLatFromOLLonLat(lonlat);
11445 var moPixel = this.getMapObjectPixelFromMapObjectLonLat(moLonLat);
11447 viewPortPx = this.getOLPixelFromMapObjectPixel(moPixel);
11452 /********************************************************/
11454 /* Translation Functions */
11456 /* The following functions translate Map Object and */
11457 /* OL formats for Pixel, LonLat */
11459 /********************************************************/
11462 // TRANSLATION: MapObject LatLng <-> OpenLayers.LonLat
11466 * Method: getOLLonLatFromMapObjectLonLat
11467 * Get an OL style map location from a 3rd party style map location
11470 * moLonLat - {Object}
11473 * {<OpenLayers.LonLat>} An OpenLayers.LonLat, translated from the passed in
11475 * Returns null if null value is passed in
11477 getOLLonLatFromMapObjectLonLat: function(moLonLat) {
11478 var olLonLat = null;
11479 if (moLonLat != null) {
11480 var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);
11481 var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);
11482 olLonLat = new OpenLayers.LonLat(lon, lat);
11488 * Method: getMapObjectLonLatFromOLLonLat
11489 * Get a 3rd party map location from an OL map location.
11492 * olLonLat - {<OpenLayers.LonLat>}
11495 * {Object} A MapObject LonLat, translated from the passed in
11496 * OpenLayers.LonLat
11497 * Returns null if null value is passed in
11499 getMapObjectLonLatFromOLLonLat: function(olLonLat) {
11500 var moLatLng = null;
11501 if (olLonLat != null) {
11502 moLatLng = this.getMapObjectLonLatFromLonLat(olLonLat.lon,
11510 // TRANSLATION: MapObject Pixel <-> OpenLayers.Pixel
11514 * Method: getOLPixelFromMapObjectPixel
11515 * Get an OL pixel location from a 3rd party pixel location.
11518 * moPixel - {Object}
11521 * {<OpenLayers.Pixel>} An OpenLayers.Pixel, translated from the passed in
11523 * Returns null if null value is passed in
11525 getOLPixelFromMapObjectPixel: function(moPixel) {
11526 var olPixel = null;
11527 if (moPixel != null) {
11528 var x = this.getXFromMapObjectPixel(moPixel);
11529 var y = this.getYFromMapObjectPixel(moPixel);
11530 olPixel = new OpenLayers.Pixel(x, y);
11536 * Method: getMapObjectPixelFromOLPixel
11537 * Get a 3rd party pixel location from an OL pixel location
11540 * olPixel - {<OpenLayers.Pixel>}
11543 * {Object} A MapObject Pixel, translated from the passed in
11545 * Returns null if null value is passed in
11547 getMapObjectPixelFromOLPixel: function(olPixel) {
11548 var moPixel = null;
11549 if (olPixel != null) {
11550 moPixel = this.getMapObjectPixelFromXY(olPixel.x, olPixel.y);
11555 CLASS_NAME: "OpenLayers.Layer.EventPane"
11557 /* ======================================================================
11558 OpenLayers/Layer/FixedZoomLevels.js
11559 ====================================================================== */
11561 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
11562 * full list of contributors). Published under the 2-clause BSD license.
11563 * See license.txt in the OpenLayers distribution or repository for the
11564 * full text of the license. */
11567 * @requires OpenLayers/Layer.js
11571 * Class: OpenLayers.Layer.FixedZoomLevels
11572 * Some Layers will already have established zoom levels (like google
11573 * or ve). Instead of trying to determine them and populate a resolutions[]
11574 * Array with those values, we will hijack the resolution functionality
11577 * When you subclass FixedZoomLevels:
11579 * The initResolutions() call gets nullified, meaning no resolutions[] array
11580 * is set up. Which would be a big problem getResolution() in Layer, since
11581 * it merely takes map.zoom and indexes into resolutions[]... but....
11583 * The getResolution() call is also overridden. Instead of using the
11584 * resolutions[] array, we simply calculate the current resolution based
11585 * on the current extent and the current map size. But how will we be able
11586 * to calculate the current extent without knowing the resolution...?
11588 * The getExtent() function is also overridden. Instead of calculating extent
11589 * based on the center point and the current resolution, we instead
11590 * calculate the extent by getting the lonlats at the top-left and
11591 * bottom-right by using the getLonLatFromViewPortPx() translation function,
11592 * taken from the pixel locations (0,0) and the size of the map. But how
11593 * will we be able to do lonlat-px translation without resolution....?
11595 * The getZoomForResolution() method is overridden. Instead of indexing into
11596 * the resolutions[] array, we call OpenLayers.Layer.getExent(), passing in
11597 * the desired resolution. With this extent, we then call getZoomForExtent()
11600 * Whenever you implement a layer using OpenLayers.Layer.FixedZoomLevels,
11601 * it is your responsibility to provide the following three functions:
11603 * - getLonLatFromViewPortPx
11604 * - getViewPortPxFromLonLat
11605 * - getZoomForExtent
11607 * ...those three functions should generally be provided by any reasonable
11608 * API that you might be working from.
11611 OpenLayers.Layer.FixedZoomLevels = OpenLayers.Class({
11613 /********************************************************/
11615 /* Baselayer Functions */
11617 /* The following functions must all be implemented */
11618 /* by all base layers */
11620 /********************************************************/
11623 * Constructor: OpenLayers.Layer.FixedZoomLevels
11624 * Create a new fixed zoom levels layer.
11626 initialize: function() {
11627 //this class is only just to add the following functions...
11628 // nothing to actually do here... but it is probably a good
11629 // idea to have layers that use these functions call this
11630 // inititalize() anyways, in case at some point we decide we
11631 // do want to put some functionality or state in here.
11635 * Method: initResolutions
11636 * Populate the resolutions array
11638 initResolutions: function() {
11640 var props = ['minZoomLevel', 'maxZoomLevel', 'numZoomLevels'];
11642 for(var i=0, len=props.length; i<len; i++) {
11643 var property = props[i];
11644 this[property] = (this.options[property] != null)
11645 ? this.options[property]
11646 : this.map[property];
11649 if ( (this.minZoomLevel == null) ||
11650 (this.minZoomLevel < this.MIN_ZOOM_LEVEL) ){
11651 this.minZoomLevel = this.MIN_ZOOM_LEVEL;
11655 // At this point, we know what the minimum desired zoom level is, and
11656 // we must calculate the total number of zoom levels.
11658 // Because we allow for the setting of either the 'numZoomLevels'
11659 // or the 'maxZoomLevel' properties... on either the layer or the
11660 // map, we have to define some rules to see which we take into
11661 // account first in this calculation.
11663 // The following is the precedence list for these properties:
11665 // (1) numZoomLevels set on layer
11666 // (2) maxZoomLevel set on layer
11667 // (3) numZoomLevels set on map
11668 // (4) maxZoomLevel set on map*
11669 // (5) none of the above*
11671 // *Note that options (4) and (5) are only possible if the user
11672 // _explicitly_ sets the 'numZoomLevels' property on the map to
11673 // null, since it is set by default to 16.
11677 // Note to future: In 3.0, I think we should remove the default
11678 // value of 16 for map.numZoomLevels. Rather, I think that value
11679 // should be set as a default on the Layer.WMS class. If someone
11680 // creates a 3rd party layer and does not specify any 'minZoomLevel',
11681 // 'maxZoomLevel', or 'numZoomLevels', and has not explicitly
11682 // specified any of those on the map object either.. then I think
11683 // it is fair to say that s/he wants all the zoom levels available.
11685 // By making map.numZoomLevels *null* by default, that will be the
11686 // case. As it is, I don't feel comfortable changing that right now
11687 // as it would be a glaring API change and actually would probably
11688 // break many peoples' codes.
11691 //the number of zoom levels we'd like to have.
11692 var desiredZoomLevels;
11694 //this is the maximum number of zoom levels the layer will allow,
11695 // given the specified starting minimum zoom level.
11696 var limitZoomLevels = this.MAX_ZOOM_LEVEL - this.minZoomLevel + 1;
11698 if ( ((this.options.numZoomLevels == null) &&
11699 (this.options.maxZoomLevel != null)) // (2)
11701 ((this.numZoomLevels == null) &&
11702 (this.maxZoomLevel != null)) // (4)
11704 //calculate based on specified maxZoomLevel (on layer or map)
11705 desiredZoomLevels = this.maxZoomLevel - this.minZoomLevel + 1;
11707 //calculate based on specified numZoomLevels (on layer or map)
11708 // this covers cases (1) and (3)
11709 desiredZoomLevels = this.numZoomLevels;
11712 if (desiredZoomLevels != null) {
11713 //Now that we know what we would *like* the number of zoom levels
11714 // to be, based on layer or map options, we have to make sure that
11715 // it does not conflict with the actual limit, as specified by
11716 // the constants on the layer itself (and calculated into the
11717 // 'limitZoomLevels' variable).
11718 this.numZoomLevels = Math.min(desiredZoomLevels, limitZoomLevels);
11720 // case (5) -- neither 'numZoomLevels' not 'maxZoomLevel' was
11721 // set on either the layer or the map. So we just use the
11722 // maximum limit as calculated by the layer's constants.
11723 this.numZoomLevels = limitZoomLevels;
11726 //now that the 'numZoomLevels' is appropriately, safely set,
11727 // we go back and re-calculate the 'maxZoomLevel'.
11728 this.maxZoomLevel = this.minZoomLevel + this.numZoomLevels - 1;
11730 if (this.RESOLUTIONS != null) {
11731 var resolutionsIndex = 0;
11732 this.resolutions = [];
11733 for(var i= this.minZoomLevel; i <= this.maxZoomLevel; i++) {
11734 this.resolutions[resolutionsIndex++] = this.RESOLUTIONS[i];
11736 this.maxResolution = this.resolutions[0];
11737 this.minResolution = this.resolutions[this.resolutions.length - 1];
11742 * APIMethod: getResolution
11743 * Get the current map resolution
11746 * {Float} Map units per Pixel
11748 getResolution: function() {
11750 if (this.resolutions != null) {
11751 return OpenLayers.Layer.prototype.getResolution.apply(this, arguments);
11753 var resolution = null;
11755 var viewSize = this.map.getSize();
11756 var extent = this.getExtent();
11758 if ((viewSize != null) && (extent != null)) {
11759 resolution = Math.max( extent.getWidth() / viewSize.w,
11760 extent.getHeight() / viewSize.h );
11767 * APIMethod: getExtent
11768 * Calculates using px-> lonlat translation functions on tl and br
11769 * corners of viewport
11772 * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat
11773 * bounds of the current viewPort.
11775 getExtent: function () {
11776 var size = this.map.getSize();
11777 var tl = this.getLonLatFromViewPortPx({
11780 var br = this.getLonLatFromViewPortPx({
11781 x: size.w, y: size.h
11784 if ((tl != null) && (br != null)) {
11785 return new OpenLayers.Bounds(tl.lon, br.lat, br.lon, tl.lat);
11792 * Method: getZoomForResolution
11793 * Get the zoom level for a given resolution
11796 * resolution - {Float}
11799 * {Integer} A suitable zoom level for the specified resolution.
11800 * If no baselayer is set, returns null.
11802 getZoomForResolution: function(resolution) {
11804 if (this.resolutions != null) {
11805 return OpenLayers.Layer.prototype.getZoomForResolution.apply(this, arguments);
11807 var extent = OpenLayers.Layer.prototype.getExtent.apply(this, []);
11808 return this.getZoomForExtent(extent);
11815 /********************************************************/
11817 /* Translation Functions */
11819 /* The following functions translate GMaps and OL */
11820 /* formats for Pixel, LonLat, Bounds, and Zoom */
11822 /********************************************************/
11826 // TRANSLATION: MapObject Zoom <-> OpenLayers Zoom
11830 * Method: getOLZoomFromMapObjectZoom
11831 * Get the OL zoom index from the map object zoom level
11834 * moZoom - {Integer}
11837 * {Integer} An OpenLayers Zoom level, translated from the passed in zoom
11838 * Returns null if null value is passed in
11840 getOLZoomFromMapObjectZoom: function(moZoom) {
11842 if (moZoom != null) {
11843 zoom = moZoom - this.minZoomLevel;
11844 if (this.map.baseLayer !== this) {
11845 zoom = this.map.baseLayer.getZoomForResolution(
11846 this.getResolutionForZoom(zoom)
11854 * Method: getMapObjectZoomFromOLZoom
11855 * Get the map object zoom level from the OL zoom level
11858 * olZoom - {Integer}
11861 * {Integer} A MapObject level, translated from the passed in olZoom
11862 * Returns null if null value is passed in
11864 getMapObjectZoomFromOLZoom: function(olZoom) {
11866 if (olZoom != null) {
11867 zoom = olZoom + this.minZoomLevel;
11868 if (this.map.baseLayer !== this) {
11869 zoom = this.getZoomForResolution(
11870 this.map.baseLayer.getResolutionForZoom(zoom)
11877 CLASS_NAME: "OpenLayers.Layer.FixedZoomLevels"
11880 /* ======================================================================
11881 OpenLayers/Layer/Google.js
11882 ====================================================================== */
11884 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
11885 * full list of contributors). Published under the 2-clause BSD license.
11886 * See license.txt in the OpenLayers distribution or repository for the
11887 * full text of the license. */
11891 * @requires OpenLayers/Layer/SphericalMercator.js
11892 * @requires OpenLayers/Layer/EventPane.js
11893 * @requires OpenLayers/Layer/FixedZoomLevels.js
11894 * @requires OpenLayers/Lang.js
11898 * Class: OpenLayers.Layer.Google
11900 * Provides a wrapper for Google's Maps API
11901 * Normally the Terms of Use for this API do not allow wrapping, but Google
11902 * have provided written consent to OpenLayers for this - see email in
11903 * http://osgeo-org.1560.n6.nabble.com/Google-Maps-API-Terms-of-Use-changes-tp4910013p4911981.html
11906 * - <OpenLayers.Layer.SphericalMercator>
11907 * - <OpenLayers.Layer.EventPane>
11908 * - <OpenLayers.Layer.FixedZoomLevels>
11910 OpenLayers.Layer.Google = OpenLayers.Class(
11911 OpenLayers.Layer.EventPane,
11912 OpenLayers.Layer.FixedZoomLevels, {
11915 * Constant: MIN_ZOOM_LEVEL
11921 * Constant: MAX_ZOOM_LEVEL
11924 MAX_ZOOM_LEVEL: 21,
11927 * Constant: RESOLUTIONS
11928 * {Array(Float)} Hardcode these resolutions so that they are more closely
11929 * tied with the standard wms projection
11943 0.0006866455078125,
11944 0.00034332275390625,
11945 0.000171661376953125,
11946 0.0000858306884765625,
11947 0.00004291534423828125,
11948 0.00002145767211914062,
11949 0.00001072883605957031,
11950 0.00000536441802978515,
11951 0.00000268220901489257,
11952 0.0000013411045074462891,
11953 0.00000067055225372314453
11957 * APIProperty: type
11963 * APIProperty: wrapDateLine
11964 * {Boolean} Allow user to pan forever east/west. Default is true.
11965 * Setting this to false only restricts panning if
11966 * <sphericalMercator> is true.
11968 wrapDateLine: true,
11971 * APIProperty: sphericalMercator
11972 * {Boolean} Should the map act as a mercator-projected map? This will
11973 * cause all interactions with the map to be in the actual map
11974 * projection, which allows support for vector drawing, overlaying
11977 sphericalMercator: false,
11980 * Property: version
11981 * {Number} The version of the Google Maps API
11986 * Constructor: OpenLayers.Layer.Google
11989 * name - {String} A name for the layer.
11990 * options - {Object} An optional object whose properties will be set
11993 initialize: function(name, options) {
11994 options = options || {};
11995 if(!options.version) {
11996 options.version = typeof GMap2 === "function" ? "2" : "3";
11998 var mixin = OpenLayers.Layer.Google["v" +
11999 options.version.replace(/\./g, "_")];
12001 OpenLayers.Util.applyDefaults(options, mixin);
12003 throw "Unsupported Google Maps API version: " + options.version;
12006 OpenLayers.Util.applyDefaults(options, mixin.DEFAULTS);
12007 if (options.maxExtent) {
12008 options.maxExtent = options.maxExtent.clone();
12011 OpenLayers.Layer.EventPane.prototype.initialize.apply(this,
12013 OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,
12016 if (this.sphericalMercator) {
12017 OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
12018 this.initMercatorParameters();
12024 * Create a clone of this layer
12027 * {<OpenLayers.Layer.Google>} An exact clone of this layer
12029 clone: function() {
12031 * This method isn't intended to be called by a subclass and it
12032 * doesn't call the same method on the superclass. We don't call
12033 * the super's clone because we don't want properties that are set
12034 * on this layer after initialize (i.e. this.mapObject etc.).
12036 return new OpenLayers.Layer.Google(
12037 this.name, this.getOptions()
12042 * APIMethod: setVisibility
12043 * Set the visibility flag for the layer and hide/show & redraw
12044 * accordingly. Fire event unless otherwise specified
12046 * Note that visibility is no longer simply whether or not the layer's
12047 * style.display is set to "block". Now we store a 'visibility' state
12048 * property on the layer class, this allows us to remember whether or
12049 * not we *desire* for a layer to be visible. In the case where the
12050 * map's resolution is out of the layer's range, this desire may be
12054 * visible - {Boolean} Display the layer (if in range)
12056 setVisibility: function(visible) {
12057 // sharing a map container, opacity has to be set per layer
12058 var opacity = this.opacity == null ? 1 : this.opacity;
12059 OpenLayers.Layer.EventPane.prototype.setVisibility.apply(this, arguments);
12060 this.setOpacity(opacity);
12064 * APIMethod: display
12065 * Hide or show the Layer
12068 * visible - {Boolean}
12070 display: function(visible) {
12071 if (!this._dragging) {
12072 this.setGMapVisibility(visible);
12074 OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments);
12081 * bounds - {<OpenLayers.Bounds>}
12082 * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to
12083 * do some init work in that case.
12084 * dragging - {Boolean}
12086 moveTo: function(bounds, zoomChanged, dragging) {
12087 this._dragging = dragging;
12088 OpenLayers.Layer.EventPane.prototype.moveTo.apply(this, arguments);
12089 delete this._dragging;
12093 * APIMethod: setOpacity
12094 * Sets the opacity for the entire layer (all images)
12097 * opacity - {Float}
12099 setOpacity: function(opacity) {
12100 if (opacity !== this.opacity) {
12101 if (this.map != null) {
12102 this.map.events.triggerEvent("changelayer", {
12104 property: "opacity"
12107 this.opacity = opacity;
12109 // Though this layer's opacity may not change, we're sharing a container
12110 // and need to update the opacity for the entire container.
12111 if (this.getVisibility()) {
12112 var container = this.getMapContainer();
12113 OpenLayers.Util.modifyDOMElement(
12114 container, null, null, null, null, null, null, opacity
12120 * APIMethod: destroy
12121 * Clean up this layer.
12123 destroy: function() {
12125 * We have to override this method because the event pane destroy
12126 * deletes the mapObject reference before removing this layer from
12130 this.setGMapVisibility(false);
12131 var cache = OpenLayers.Layer.Google.cache[this.map.id];
12132 if (cache && cache.count <= 1) {
12133 this.removeGMapElements();
12136 OpenLayers.Layer.EventPane.prototype.destroy.apply(this, arguments);
12140 * Method: removeGMapElements
12141 * Remove all elements added to the dom. This should only be called if
12142 * this is the last of the Google layers for the given map.
12144 removeGMapElements: function() {
12145 var cache = OpenLayers.Layer.Google.cache[this.map.id];
12147 // remove shared elements from dom
12148 var container = this.mapObject && this.getMapContainer();
12149 if (container && container.parentNode) {
12150 container.parentNode.removeChild(container);
12152 var termsOfUse = cache.termsOfUse;
12153 if (termsOfUse && termsOfUse.parentNode) {
12154 termsOfUse.parentNode.removeChild(termsOfUse);
12156 var poweredBy = cache.poweredBy;
12157 if (poweredBy && poweredBy.parentNode) {
12158 poweredBy.parentNode.removeChild(poweredBy);
12160 if (this.mapObject && window.google && google.maps &&
12161 google.maps.event && google.maps.event.clearListeners) {
12162 google.maps.event.clearListeners(this.mapObject, 'tilesloaded');
12168 * APIMethod: removeMap
12169 * On being removed from the map, also remove termsOfUse and poweredBy divs
12172 * map - {<OpenLayers.Map>}
12174 removeMap: function(map) {
12175 // hide layer before removing
12176 if (this.visibility && this.mapObject) {
12177 this.setGMapVisibility(false);
12179 // check to see if last Google layer in this map
12180 var cache = OpenLayers.Layer.Google.cache[map.id];
12182 if (cache.count <= 1) {
12183 this.removeGMapElements();
12184 delete OpenLayers.Layer.Google.cache[map.id];
12186 // decrement the layer count
12190 // remove references to gmap elements
12191 delete this.termsOfUse;
12192 delete this.poweredBy;
12193 delete this.mapObject;
12194 delete this.dragObject;
12195 OpenLayers.Layer.EventPane.prototype.removeMap.apply(this, arguments);
12199 // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds
12203 * APIMethod: getOLBoundsFromMapObjectBounds
12206 * moBounds - {Object}
12209 * {<OpenLayers.Bounds>} An <OpenLayers.Bounds>, translated from the
12210 * passed-in MapObject Bounds.
12211 * Returns null if null value is passed in.
12213 getOLBoundsFromMapObjectBounds: function(moBounds) {
12214 var olBounds = null;
12215 if (moBounds != null) {
12216 var sw = moBounds.getSouthWest();
12217 var ne = moBounds.getNorthEast();
12218 if (this.sphericalMercator) {
12219 sw = this.forwardMercator(sw.lng(), sw.lat());
12220 ne = this.forwardMercator(ne.lng(), ne.lat());
12222 sw = new OpenLayers.LonLat(sw.lng(), sw.lat());
12223 ne = new OpenLayers.LonLat(ne.lng(), ne.lat());
12225 olBounds = new OpenLayers.Bounds(sw.lon,
12234 * APIMethod: getWarningHTML
12237 * {String} String with information on why layer is broken, how to get
12240 getWarningHTML:function() {
12241 return OpenLayers.i18n("googleWarning");
12245 /************************************
12247 * MapObject Interface Controls *
12249 ************************************/
12252 // Get&Set Center, Zoom
12255 * APIMethod: getMapObjectCenter
12258 * {Object} The mapObject's current center in Map Object format
12260 getMapObjectCenter: function() {
12261 return this.mapObject.getCenter();
12265 * APIMethod: getMapObjectZoom
12268 * {Integer} The mapObject's current zoom, in Map Object format
12270 getMapObjectZoom: function() {
12271 return this.mapObject.getZoom();
12275 /************************************
12277 * MapObject Primitives *
12279 ************************************/
12285 * APIMethod: getLongitudeFromMapObjectLonLat
12288 * moLonLat - {Object} MapObject LonLat format
12291 * {Float} Longitude of the given MapObject LonLat
12293 getLongitudeFromMapObjectLonLat: function(moLonLat) {
12294 return this.sphericalMercator ?
12295 this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lon :
12300 * APIMethod: getLatitudeFromMapObjectLonLat
12303 * moLonLat - {Object} MapObject LonLat format
12306 * {Float} Latitude of the given MapObject LonLat
12308 getLatitudeFromMapObjectLonLat: function(moLonLat) {
12309 var lat = this.sphericalMercator ?
12310 this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lat :
12318 * APIMethod: getXFromMapObjectPixel
12321 * moPixel - {Object} MapObject Pixel format
12324 * {Integer} X value of the MapObject Pixel
12326 getXFromMapObjectPixel: function(moPixel) {
12331 * APIMethod: getYFromMapObjectPixel
12334 * moPixel - {Object} MapObject Pixel format
12337 * {Integer} Y value of the MapObject Pixel
12339 getYFromMapObjectPixel: function(moPixel) {
12343 CLASS_NAME: "OpenLayers.Layer.Google"
12347 * Property: OpenLayers.Layer.Google.cache
12348 * {Object} Cache for elements that should only be created once per map.
12350 OpenLayers.Layer.Google.cache = {};
12354 * Constant: OpenLayers.Layer.Google.v2
12356 * Mixin providing functionality specific to the Google Maps API v2.
12358 * This API has been deprecated by Google.
12359 * Developers are encouraged to migrate to v3 of the API; support for this
12360 * is provided by <OpenLayers.Layer.Google.v3>
12362 OpenLayers.Layer.Google.v2 = {
12365 * Property: termsOfUse
12366 * {DOMElement} Div for Google's copyright and terms of use link
12371 * Property: poweredBy
12372 * {DOMElement} Div for Google's powered by logo and link
12377 * Property: dragObject
12378 * {GDraggableObject} Since 2.93, Google has exposed the ability to get
12379 * the maps GDraggableObject. We can now use this for smooth panning
12384 * Method: loadMapObject
12385 * Load the GMap and register appropriate event listeners. If we can't
12386 * load GMap2, then display a warning message.
12388 loadMapObject:function() {
12390 this.type = G_NORMAL_MAP;
12392 var mapObject, termsOfUse, poweredBy;
12393 var cache = OpenLayers.Layer.Google.cache[this.map.id];
12395 // there are already Google layers added to this map
12396 mapObject = cache.mapObject;
12397 termsOfUse = cache.termsOfUse;
12398 poweredBy = cache.poweredBy;
12399 // increment the layer count
12402 // this is the first Google layer for this map
12404 var container = this.map.viewPortDiv;
12405 var div = document.createElement("div");
12406 div.id = this.map.id + "_GMap2Container";
12407 div.style.position = "absolute";
12408 div.style.width = "100%";
12409 div.style.height = "100%";
12410 container.appendChild(div);
12412 // create GMap and shuffle elements
12414 mapObject = new GMap2(div);
12416 // move the ToS and branding stuff up to the container div
12417 termsOfUse = div.lastChild;
12418 container.appendChild(termsOfUse);
12419 termsOfUse.style.zIndex = "1100";
12420 termsOfUse.style.right = "";
12421 termsOfUse.style.bottom = "";
12422 termsOfUse.className = "olLayerGoogleCopyright";
12424 poweredBy = div.lastChild;
12425 container.appendChild(poweredBy);
12426 poweredBy.style.zIndex = "1100";
12427 poweredBy.style.right = "";
12428 poweredBy.style.bottom = "";
12429 poweredBy.className = "olLayerGooglePoweredBy gmnoprint";
12434 // cache elements for use by any other google layers added to
12436 OpenLayers.Layer.Google.cache[this.map.id] = {
12437 mapObject: mapObject,
12438 termsOfUse: termsOfUse,
12439 poweredBy: poweredBy,
12444 this.mapObject = mapObject;
12445 this.termsOfUse = termsOfUse;
12446 this.poweredBy = poweredBy;
12448 // ensure this layer type is one of the mapObject types
12449 if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(),
12450 this.type) === -1) {
12451 this.mapObject.addMapType(this.type);
12454 //since v 2.93 getDragObject is now available.
12455 if(typeof mapObject.getDragObject == "function") {
12456 this.dragObject = mapObject.getDragObject();
12458 this.dragPanMapObject = null;
12461 if(this.isBaseLayer === false) {
12462 this.setGMapVisibility(this.div.style.display !== "none");
12468 * APIMethod: onMapResize
12470 onMapResize: function() {
12471 // workaround for resizing of invisible or not yet fully loaded layers
12472 // where GMap2.checkResize() does not work. We need to load the GMap
12473 // for the old div size, then checkResize(), and then call
12474 // layer.moveTo() to trigger GMap.setCenter() (which will finish
12475 // the GMap initialization).
12476 if(this.visibility && this.mapObject.isLoaded()) {
12477 this.mapObject.checkResize();
12479 if(!this._resized) {
12481 var handle = GEvent.addListener(this.mapObject, "load", function() {
12482 GEvent.removeListener(handle);
12483 delete layer._resized;
12484 layer.mapObject.checkResize();
12485 layer.moveTo(layer.map.getCenter(), layer.map.getZoom());
12488 this._resized = true;
12493 * Method: setGMapVisibility
12494 * Display the GMap container and associated elements.
12497 * visible - {Boolean} Display the GMap elements.
12499 setGMapVisibility: function(visible) {
12500 var cache = OpenLayers.Layer.Google.cache[this.map.id];
12502 var container = this.mapObject.getContainer();
12503 if (visible === true) {
12504 this.mapObject.setMapType(this.type);
12505 container.style.display = "";
12506 this.termsOfUse.style.left = "";
12507 this.termsOfUse.style.display = "";
12508 this.poweredBy.style.display = "";
12509 cache.displayed = this.id;
12511 if (cache.displayed === this.id) {
12512 delete cache.displayed;
12514 if (!cache.displayed) {
12515 container.style.display = "none";
12516 this.termsOfUse.style.display = "none";
12517 // move ToU far to the left in addition to setting display
12518 // to "none", because at the end of the GMap2 load
12519 // sequence, display: none will be unset and ToU would be
12520 // visible after loading a map with a google layer that is
12521 // initially hidden.
12522 this.termsOfUse.style.left = "-9999px";
12523 this.poweredBy.style.display = "none";
12530 * Method: getMapContainer
12533 * {DOMElement} the GMap container's div
12535 getMapContainer: function() {
12536 return this.mapObject.getContainer();
12540 // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds
12544 * APIMethod: getMapObjectBoundsFromOLBounds
12547 * olBounds - {<OpenLayers.Bounds>}
12550 * {Object} A MapObject Bounds, translated from olBounds
12551 * Returns null if null value is passed in
12553 getMapObjectBoundsFromOLBounds: function(olBounds) {
12554 var moBounds = null;
12555 if (olBounds != null) {
12556 var sw = this.sphericalMercator ?
12557 this.inverseMercator(olBounds.bottom, olBounds.left) :
12558 new OpenLayers.LonLat(olBounds.bottom, olBounds.left);
12559 var ne = this.sphericalMercator ?
12560 this.inverseMercator(olBounds.top, olBounds.right) :
12561 new OpenLayers.LonLat(olBounds.top, olBounds.right);
12562 moBounds = new GLatLngBounds(new GLatLng(sw.lat, sw.lon),
12563 new GLatLng(ne.lat, ne.lon));
12569 /************************************
12571 * MapObject Interface Controls *
12573 ************************************/
12576 // Get&Set Center, Zoom
12579 * APIMethod: setMapObjectCenter
12580 * Set the mapObject to the specified center and zoom
12583 * center - {Object} MapObject LonLat format
12584 * zoom - {int} MapObject zoom format
12586 setMapObjectCenter: function(center, zoom) {
12587 this.mapObject.setCenter(center, zoom);
12591 * APIMethod: dragPanMapObject
12597 dragPanMapObject: function(dX, dY) {
12598 this.dragObject.moveBy(new GSize(-dX, dY));
12602 // LonLat - Pixel Translation
12605 * APIMethod: getMapObjectLonLatFromMapObjectPixel
12608 * moPixel - {Object} MapObject Pixel format
12611 * {Object} MapObject LonLat translated from MapObject Pixel
12613 getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
12614 return this.mapObject.fromContainerPixelToLatLng(moPixel);
12618 * APIMethod: getMapObjectPixelFromMapObjectLonLat
12621 * moLonLat - {Object} MapObject LonLat format
12624 * {Object} MapObject Pixel transtlated from MapObject LonLat
12626 getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
12627 return this.mapObject.fromLatLngToContainerPixel(moLonLat);
12634 * APIMethod: getMapObjectZoomFromMapObjectBounds
12637 * moBounds - {Object} MapObject Bounds format
12640 * {Object} MapObject Zoom for specified MapObject Bounds
12642 getMapObjectZoomFromMapObjectBounds: function(moBounds) {
12643 return this.mapObject.getBoundsZoomLevel(moBounds);
12646 /************************************
12648 * MapObject Primitives *
12650 ************************************/
12656 * APIMethod: getMapObjectLonLatFromLonLat
12663 * {Object} MapObject LonLat built from lon and lat params
12665 getMapObjectLonLatFromLonLat: function(lon, lat) {
12667 if(this.sphericalMercator) {
12668 var lonlat = this.inverseMercator(lon, lat);
12669 gLatLng = new GLatLng(lonlat.lat, lonlat.lon);
12671 gLatLng = new GLatLng(lat, lon);
12679 * APIMethod: getMapObjectPixelFromXY
12686 * {Object} MapObject Pixel from x and y parameters
12688 getMapObjectPixelFromXY: function(x, y) {
12689 return new GPoint(x, y);
12693 /* ======================================================================
12694 OpenLayers/Control.js
12695 ====================================================================== */
12697 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
12698 * full list of contributors). Published under the 2-clause BSD license.
12699 * See license.txt in the OpenLayers distribution or repository for the
12700 * full text of the license. */
12703 * @requires OpenLayers/BaseTypes/Class.js
12707 * Class: OpenLayers.Control
12708 * Controls affect the display or behavior of the map. They allow everything
12709 * from panning and zooming to displaying a scale indicator. Controls by
12710 * default are added to the map they are contained within however it is
12711 * possible to add a control to an external div by passing the div in the
12712 * options parameter.
12715 * The following example shows how to add many of the common controls
12718 * > var map = new OpenLayers.Map('map', { controls: [] });
12720 * > map.addControl(new OpenLayers.Control.PanZoomBar());
12721 * > map.addControl(new OpenLayers.Control.LayerSwitcher({'ascending':false}));
12722 * > map.addControl(new OpenLayers.Control.Permalink());
12723 * > map.addControl(new OpenLayers.Control.Permalink('permalink'));
12724 * > map.addControl(new OpenLayers.Control.MousePosition());
12725 * > map.addControl(new OpenLayers.Control.OverviewMap());
12726 * > map.addControl(new OpenLayers.Control.KeyboardDefaults());
12728 * The next code fragment is a quick example of how to intercept
12729 * shift-mouse click to display the extent of the bounding box
12730 * dragged out by the user. Usually controls are not created
12731 * in exactly this manner. See the source for a more complete
12734 * > var control = new OpenLayers.Control();
12735 * > OpenLayers.Util.extend(control, {
12736 * > draw: function () {
12737 * > // this Handler.Box will intercept the shift-mousedown
12738 * > // before Control.MouseDefault gets to see it
12739 * > this.box = new OpenLayers.Handler.Box( control,
12740 * > {"done": this.notice},
12741 * > {keyMask: OpenLayers.Handler.MOD_SHIFT});
12742 * > this.box.activate();
12745 * > notice: function (bounds) {
12746 * > OpenLayers.Console.userError(bounds);
12749 * > map.addControl(control);
12752 OpenLayers.Control = OpenLayers.Class({
12762 * {<OpenLayers.Map>} this gets set in the addControl() function in
12769 * {DOMElement} The element that contains the control, if not present the
12770 * control is placed inside the map.
12775 * APIProperty: type
12776 * {Number} Controls can have a 'type'. The type determines the type of
12777 * interactions which are possible with them when they are placed in an
12778 * <OpenLayers.Control.Panel>.
12783 * Property: allowSelection
12784 * {Boolean} By default, controls do not allow selection, because
12785 * it may interfere with map dragging. If this is true, OpenLayers
12786 * will not prevent selection of the control.
12787 * Default is false.
12789 allowSelection: false,
12792 * Property: displayClass
12793 * {string} This property is used for CSS related to the drawing of the
12799 * APIProperty: title
12800 * {string} This property is used for showing a tooltip over the
12806 * APIProperty: autoActivate
12807 * {Boolean} Activate the control when it is added to a map. Default is
12810 autoActivate: false,
12813 * APIProperty: active
12814 * {Boolean} The control is active (read-only). Use <activate> and
12815 * <deactivate> to change control state.
12820 * Property: handlerOptions
12821 * {Object} Used to set non-default properties on the control's handler
12823 handlerOptions: null,
12826 * Property: handler
12827 * {<OpenLayers.Handler>} null
12832 * APIProperty: eventListeners
12833 * {Object} If set as an option at construction, the eventListeners
12834 * object will be registered with <OpenLayers.Events.on>. Object
12835 * structure must be a listeners object as shown in the example for
12836 * the events.on method.
12838 eventListeners: null,
12841 * APIProperty: events
12842 * {<OpenLayers.Events>} Events instance for listeners and triggering
12843 * control specific events.
12845 * Register a listener for a particular event with the following syntax:
12847 * control.events.register(type, obj, listener);
12850 * Listeners will be called with a reference to an event object. The
12851 * properties of this event depends on exactly what happened.
12853 * All event objects have at least the following properties:
12854 * object - {Object} A reference to control.events.object (a reference
12856 * element - {DOMElement} A reference to control.events.element (which
12857 * will be null unless documented otherwise).
12859 * Supported map event types:
12860 * activate - Triggered when activated.
12861 * deactivate - Triggered when deactivated.
12866 * Constructor: OpenLayers.Control
12867 * Create an OpenLayers Control. The options passed as a parameter
12868 * directly extend the control. For example passing the following:
12870 * > var control = new OpenLayers.Control({div: myDiv});
12872 * Overrides the default div attribute value of null.
12875 * options - {Object}
12877 initialize: function (options) {
12878 // We do this before the extend so that instances can override
12879 // className in options.
12880 this.displayClass =
12881 this.CLASS_NAME.replace("OpenLayers.", "ol").replace(/\./g, "");
12883 OpenLayers.Util.extend(this, options);
12885 this.events = new OpenLayers.Events(this);
12886 if(this.eventListeners instanceof Object) {
12887 this.events.on(this.eventListeners);
12889 if (this.id == null) {
12890 this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
12896 * The destroy method is used to perform any clean up before the control
12897 * is dereferenced. Typically this is where event listeners are removed
12898 * to prevent memory leaks.
12900 destroy: function () {
12902 if(this.eventListeners) {
12903 this.events.un(this.eventListeners);
12905 this.events.destroy();
12906 this.events = null;
12908 this.eventListeners = null;
12910 // eliminate circular references
12911 if (this.handler) {
12912 this.handler.destroy();
12913 this.handler = null;
12915 if(this.handlers) {
12916 for(var key in this.handlers) {
12917 if(this.handlers.hasOwnProperty(key) &&
12918 typeof this.handlers[key].destroy == "function") {
12919 this.handlers[key].destroy();
12922 this.handlers = null;
12925 this.map.removeControl(this);
12933 * Set the map property for the control. This is done through an accessor
12934 * so that subclasses can override this and take special action once
12935 * they have their map variable set.
12938 * map - {<OpenLayers.Map>}
12940 setMap: function(map) {
12942 if (this.handler) {
12943 this.handler.setMap(map);
12949 * The draw method is called when the control is ready to be displayed
12950 * on the page. If a div has not been created one is created. Controls
12951 * with a visual component will almost always want to override this method
12952 * to customize the look of control.
12955 * px - {<OpenLayers.Pixel>} The top-left pixel position of the control
12959 * {DOMElement} A reference to the DIV DOMElement containing the control
12961 draw: function (px) {
12962 if (this.div == null) {
12963 this.div = OpenLayers.Util.createDiv(this.id);
12964 this.div.className = this.displayClass;
12965 if (!this.allowSelection) {
12966 this.div.className += " olControlNoSelect";
12967 this.div.setAttribute("unselectable", "on", 0);
12968 this.div.onselectstart = OpenLayers.Function.False;
12970 if (this.title != "") {
12971 this.div.title = this.title;
12975 this.position = px.clone();
12977 this.moveTo(this.position);
12983 * Sets the left and top style attributes to the passed in pixel
12987 * px - {<OpenLayers.Pixel>}
12989 moveTo: function (px) {
12990 if ((px != null) && (this.div != null)) {
12991 this.div.style.left = px.x + "px";
12992 this.div.style.top = px.y + "px";
12997 * APIMethod: activate
12998 * Explicitly activates a control and it's associated
12999 * handler if one has been set. Controls can be
13000 * deactivated by calling the deactivate() method.
13003 * {Boolean} True if the control was successfully activated or
13004 * false if the control was already active.
13006 activate: function () {
13010 if (this.handler) {
13011 this.handler.activate();
13013 this.active = true;
13015 OpenLayers.Element.addClass(
13016 this.map.viewPortDiv,
13017 this.displayClass.replace(/ /g, "") + "Active"
13020 this.events.triggerEvent("activate");
13025 * APIMethod: deactivate
13026 * Deactivates a control and it's associated handler if any. The exact
13027 * effect of this depends on the control itself.
13030 * {Boolean} True if the control was effectively deactivated or false
13031 * if the control was already inactive.
13033 deactivate: function () {
13035 if (this.handler) {
13036 this.handler.deactivate();
13038 this.active = false;
13040 OpenLayers.Element.removeClass(
13041 this.map.viewPortDiv,
13042 this.displayClass.replace(/ /g, "") + "Active"
13045 this.events.triggerEvent("deactivate");
13051 CLASS_NAME: "OpenLayers.Control"
13055 * Constant: OpenLayers.Control.TYPE_BUTTON
13057 OpenLayers.Control.TYPE_BUTTON = 1;
13060 * Constant: OpenLayers.Control.TYPE_TOGGLE
13062 OpenLayers.Control.TYPE_TOGGLE = 2;
13065 * Constant: OpenLayers.Control.TYPE_TOOL
13067 OpenLayers.Control.TYPE_TOOL = 3;
13068 /* ======================================================================
13069 OpenLayers/Strategy.js
13070 ====================================================================== */
13072 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
13073 * full list of contributors). Published under the 2-clause BSD license.
13074 * See license.txt in the OpenLayers distribution or repository for the
13075 * full text of the license. */
13078 * @requires OpenLayers/BaseTypes/Class.js
13082 * Class: OpenLayers.Strategy
13083 * Abstract vector layer strategy class. Not to be instantiated directly. Use
13084 * one of the strategy subclasses instead.
13086 OpenLayers.Strategy = OpenLayers.Class({
13090 * {<OpenLayers.Layer.Vector>} The layer this strategy belongs to.
13095 * Property: options
13096 * {Object} Any options sent to the constructor.
13102 * {Boolean} The control is active.
13107 * Property: autoActivate
13108 * {Boolean} The creator of the strategy can set autoActivate to false
13109 * to fully control when the protocol is activated and deactivated.
13110 * Defaults to true.
13112 autoActivate: true,
13115 * Property: autoDestroy
13116 * {Boolean} The creator of the strategy can set autoDestroy to false
13117 * to fully control when the strategy is destroyed. Defaults to
13123 * Constructor: OpenLayers.Strategy
13124 * Abstract class for vector strategies. Create instances of a subclass.
13127 * options - {Object} Optional object whose properties will be set on the
13130 initialize: function(options) {
13131 OpenLayers.Util.extend(this, options);
13132 this.options = options;
13133 // set the active property here, so that user cannot override it
13134 this.active = false;
13138 * APIMethod: destroy
13139 * Clean up the strategy.
13141 destroy: function() {
13144 this.options = null;
13149 * Called to set the <layer> property.
13152 * layer - {<OpenLayers.Layer.Vector>}
13154 setLayer: function(layer) {
13155 this.layer = layer;
13160 * Activate the strategy. Register any listeners, do appropriate setup.
13163 * {Boolean} True if the strategy was successfully activated or false if
13164 * the strategy was already active.
13166 activate: function() {
13167 if (!this.active) {
13168 this.active = true;
13175 * Method: deactivate
13176 * Deactivate the strategy. Unregister any listeners, do appropriate
13180 * {Boolean} True if the strategy was successfully deactivated or false if
13181 * the strategy was already inactive.
13183 deactivate: function() {
13185 this.active = false;
13191 CLASS_NAME: "OpenLayers.Strategy"
13193 /* ======================================================================
13194 OpenLayers/Feature.js
13195 ====================================================================== */
13197 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
13198 * full list of contributors). Published under the 2-clause BSD license.
13199 * See license.txt in the OpenLayers distribution or repository for the
13200 * full text of the license. */
13204 * @requires OpenLayers/BaseTypes/Class.js
13205 * @requires OpenLayers/Util.js
13209 * Class: OpenLayers.Feature
13210 * Features are combinations of geography and attributes. The OpenLayers.Feature
13211 * class specifically combines a marker and a lonlat.
13213 OpenLayers.Feature = OpenLayers.Class({
13217 * {<OpenLayers.Layer>}
13229 * {<OpenLayers.LonLat>}
13241 * {<OpenLayers.Marker>}
13246 * APIProperty: popupClass
13247 * {<OpenLayers.Class>} The class which will be used to instantiate
13248 * a new Popup. Default is <OpenLayers.Popup.Anchored>.
13254 * {<OpenLayers.Popup>}
13259 * Constructor: OpenLayers.Feature
13260 * Constructor for features.
13263 * layer - {<OpenLayers.Layer>}
13264 * lonlat - {<OpenLayers.LonLat>}
13268 * {<OpenLayers.Feature>}
13270 initialize: function(layer, lonlat, data) {
13271 this.layer = layer;
13272 this.lonlat = lonlat;
13273 this.data = (data != null) ? data : {};
13274 this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
13279 * nullify references to prevent circular references and memory leaks
13281 destroy: function() {
13283 //remove the popup from the map
13284 if ((this.layer != null) && (this.layer.map != null)) {
13285 if (this.popup != null) {
13286 this.layer.map.removePopup(this.popup);
13289 // remove the marker from the layer
13290 if (this.layer != null && this.marker != null) {
13291 this.layer.removeMarker(this.marker);
13296 this.lonlat = null;
13298 if (this.marker != null) {
13299 this.destroyMarker(this.marker);
13300 this.marker = null;
13302 if (this.popup != null) {
13303 this.destroyPopup(this.popup);
13312 * {Boolean} Whether or not the feature is currently visible on screen
13313 * (based on its 'lonlat' property)
13315 onScreen:function() {
13317 var onScreen = false;
13318 if ((this.layer != null) && (this.layer.map != null)) {
13319 var screenBounds = this.layer.map.getExtent();
13320 onScreen = screenBounds.containsLonLat(this.lonlat);
13327 * Method: createMarker
13328 * Based on the data associated with the Feature, create and return a marker object.
13331 * {<OpenLayers.Marker>} A Marker Object created from the 'lonlat' and 'icon' properties
13332 * set in this.data. If no 'lonlat' is set, returns null. If no
13333 * 'icon' is set, OpenLayers.Marker() will load the default image.
13335 * Note - this.marker is set to return value
13338 createMarker: function() {
13340 if (this.lonlat != null) {
13341 this.marker = new OpenLayers.Marker(this.lonlat, this.data.icon);
13343 return this.marker;
13347 * Method: destroyMarker
13349 * If user overrides the createMarker() function, s/he should be able
13350 * to also specify an alternative function for destroying it
13352 destroyMarker: function() {
13353 this.marker.destroy();
13357 * Method: createPopup
13358 * Creates a popup object created from the 'lonlat', 'popupSize',
13359 * and 'popupContentHTML' properties set in this.data. It uses
13360 * this.marker.icon as default anchor.
13362 * If no 'lonlat' is set, returns null.
13363 * If no this.marker has been created, no anchor is sent.
13365 * Note - the returned popup object is 'owned' by the feature, so you
13366 * cannot use the popup's destroy method to discard the popup.
13367 * Instead, you must use the feature's destroyPopup
13369 * Note - this.popup is set to return value
13372 * closeBox - {Boolean} create popup with closebox or not
13375 * {<OpenLayers.Popup>} Returns the created popup, which is also set
13376 * as 'popup' property of this feature. Will be of whatever type
13377 * specified by this feature's 'popupClass' property, but must be
13378 * of type <OpenLayers.Popup>.
13381 createPopup: function(closeBox) {
13383 if (this.lonlat != null) {
13385 var anchor = (this.marker) ? this.marker.icon : null;
13386 var popupClass = this.popupClass ?
13387 this.popupClass : OpenLayers.Popup.Anchored;
13388 this.popup = new popupClass(this.id + "_popup",
13390 this.data.popupSize,
13391 this.data.popupContentHTML,
13395 if (this.data.overflow != null) {
13396 this.popup.contentDiv.style.overflow = this.data.overflow;
13399 this.popup.feature = this;
13406 * Method: destroyPopup
13407 * Destroys the popup created via createPopup.
13409 * As with the marker, if user overrides the createPopup() function, s/he
13410 * should also be able to override the destruction
13412 destroyPopup: function() {
13414 this.popup.feature = null;
13415 this.popup.destroy();
13420 CLASS_NAME: "OpenLayers.Feature"
13422 /* ======================================================================
13423 OpenLayers/Feature/Vector.js
13424 ====================================================================== */
13426 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
13427 * full list of contributors). Published under the 2-clause BSD license.
13428 * See license.txt in the OpenLayers distribution or repository for the
13429 * full text of the license. */
13432 OpenLayers.State = {
13434 UNKNOWN: 'Unknown',
13441 * @requires OpenLayers/Feature.js
13442 * @requires OpenLayers/Util.js
13446 * Class: OpenLayers.Feature.Vector
13447 * Vector features use the OpenLayers.Geometry classes as geometry description.
13448 * They have an 'attributes' property, which is the data object, and a 'style'
13449 * property, the default values of which are defined in the
13450 * <OpenLayers.Feature.Vector.style> objects.
13453 * - <OpenLayers.Feature>
13455 OpenLayers.Feature.Vector = OpenLayers.Class(OpenLayers.Feature, {
13464 * APIProperty: geometry
13465 * {<OpenLayers.Geometry>}
13470 * APIProperty: attributes
13471 * {Object} This object holds arbitrary, serializable properties that
13472 * describe the feature.
13478 * {<OpenLayers.Bounds>} The box bounding that feature's geometry, that
13479 * property can be set by an <OpenLayers.Format> object when
13480 * deserializing the feature, so in most cases it represents an
13481 * information set by the server.
13492 * APIProperty: style
13499 * {String} If this property is set it will be taken into account by
13500 * {<OpenLayers.HTTP>} when upadting or deleting the feature.
13505 * Property: renderIntent
13506 * {String} rendering intent currently being used
13508 renderIntent: "default",
13511 * APIProperty: modified
13512 * {Object} An object with the originals of the geometry and attributes of
13513 * the feature, if they were changed. Currently this property is only read
13514 * by <OpenLayers.Format.WFST.v1>, and written by
13515 * <OpenLayers.Control.ModifyFeature>, which sets the geometry property.
13516 * Applications can set the originals of modified attributes in the
13517 * attributes property. Note that applications have to check if this
13518 * object and the attributes property is already created before using it.
13519 * After a change made with ModifyFeature, this object could look like
13523 * geometry: >Object
13527 * When an application has made changes to feature attributes, it could
13528 * have set the attributes to something like this:
13533 * myAttribute: "original"
13538 * Note that <OpenLayers.Format.WFST.v1> only checks for truthy values in
13539 * *modified.geometry* and the attribute names in *modified.attributes*,
13540 * but it is recommended to set the original values (and not just true) as
13541 * attribute value, so applications could use this information to undo
13547 * Constructor: OpenLayers.Feature.Vector
13548 * Create a vector feature.
13551 * geometry - {<OpenLayers.Geometry>} The geometry that this feature
13553 * attributes - {Object} An optional object that will be mapped to the
13554 * <attributes> property.
13555 * style - {Object} An optional style object.
13557 initialize: function(geometry, attributes, style) {
13558 OpenLayers.Feature.prototype.initialize.apply(this,
13559 [null, null, attributes]);
13560 this.lonlat = null;
13561 this.geometry = geometry ? geometry : null;
13563 this.attributes = {};
13565 this.attributes = OpenLayers.Util.extend(this.attributes,
13568 this.style = style ? style : null;
13573 * nullify references to prevent circular references and memory leaks
13575 destroy: function() {
13577 this.layer.removeFeatures(this);
13581 this.geometry = null;
13582 this.modified = null;
13583 OpenLayers.Feature.prototype.destroy.apply(this, arguments);
13588 * Create a clone of this vector feature. Does not set any non-standard
13592 * {<OpenLayers.Feature.Vector>} An exact clone of this vector feature.
13594 clone: function () {
13595 return new OpenLayers.Feature.Vector(
13596 this.geometry ? this.geometry.clone() : null,
13603 * Determine whether the feature is within the map viewport. This method
13604 * tests for an intersection between the geometry and the viewport
13605 * bounds. If a more effecient but less precise geometry bounds
13606 * intersection is desired, call the method with the boundsOnly
13610 * boundsOnly - {Boolean} Only test whether a feature's bounds intersects
13611 * the viewport bounds. Default is false. If false, the feature's
13612 * geometry must intersect the viewport for onScreen to return true.
13615 * {Boolean} The feature is currently visible on screen (optionally
13616 * based on its bounds if boundsOnly is true).
13618 onScreen:function(boundsOnly) {
13619 var onScreen = false;
13620 if(this.layer && this.layer.map) {
13621 var screenBounds = this.layer.map.getExtent();
13623 var featureBounds = this.geometry.getBounds();
13624 onScreen = screenBounds.intersectsBounds(featureBounds);
13626 var screenPoly = screenBounds.toGeometry();
13627 onScreen = screenPoly.intersects(this.geometry);
13634 * Method: getVisibility
13635 * Determine whether the feature is displayed or not. It may not displayed
13637 * - its style display property is set to 'none',
13638 * - it doesn't belong to any layer,
13639 * - the styleMap creates a symbolizer with display property set to 'none'
13641 * - the layer which it belongs to is not visible.
13644 * {Boolean} The feature is currently displayed.
13646 getVisibility: function() {
13647 return !(this.style && this.style.display == 'none' ||
13649 this.layer && this.layer.styleMap &&
13650 this.layer.styleMap.createSymbolizer(this, this.renderIntent).display == 'none' ||
13651 this.layer && !this.layer.getVisibility());
13655 * Method: createMarker
13656 * HACK - we need to decide if all vector features should be able to
13660 * {<OpenLayers.Marker>} For now just returns null
13662 createMarker: function() {
13667 * Method: destroyMarker
13668 * HACK - we need to decide if all vector features should be able to
13671 * If user overrides the createMarker() function, s/he should be able
13672 * to also specify an alternative function for destroying it
13674 destroyMarker: function() {
13679 * Method: createPopup
13680 * HACK - we need to decide if all vector features should be able to
13684 * {<OpenLayers.Popup>} For now just returns null
13686 createPopup: function() {
13692 * Determins whether the feature intersects with the specified location.
13695 * lonlat - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an
13696 * object with a 'lon' and 'lat' properties.
13697 * toleranceLon - {float} Optional tolerance in Geometric Coords
13698 * toleranceLat - {float} Optional tolerance in Geographic Coords
13701 * {Boolean} Whether or not the feature is at the specified location
13703 atPoint: function(lonlat, toleranceLon, toleranceLat) {
13704 var atPoint = false;
13705 if(this.geometry) {
13706 atPoint = this.geometry.atPoint(lonlat, toleranceLon,
13713 * Method: destroyPopup
13714 * HACK - we need to decide if all vector features should be able to
13717 destroyPopup: function() {
13723 * Moves the feature and redraws it at its new location
13726 * location - {<OpenLayers.LonLat> or <OpenLayers.Pixel>} the
13727 * location to which to move the feature.
13729 move: function(location) {
13731 if(!this.layer || !this.geometry.move){
13732 //do nothing if no layer or immoveable geometry
13737 if (location.CLASS_NAME == "OpenLayers.LonLat") {
13738 pixel = this.layer.getViewPortPxFromLonLat(location);
13743 var lastPixel = this.layer.getViewPortPxFromLonLat(this.geometry.getBounds().getCenterLonLat());
13744 var res = this.layer.map.getResolution();
13745 this.geometry.move(res * (pixel.x - lastPixel.x),
13746 res * (lastPixel.y - pixel.y));
13747 this.layer.drawFeature(this);
13753 * Sets the new state
13758 toState: function(state) {
13759 if (state == OpenLayers.State.UPDATE) {
13760 switch (this.state) {
13761 case OpenLayers.State.UNKNOWN:
13762 case OpenLayers.State.DELETE:
13763 this.state = state;
13765 case OpenLayers.State.UPDATE:
13766 case OpenLayers.State.INSERT:
13769 } else if (state == OpenLayers.State.INSERT) {
13770 switch (this.state) {
13771 case OpenLayers.State.UNKNOWN:
13774 this.state = state;
13777 } else if (state == OpenLayers.State.DELETE) {
13778 switch (this.state) {
13779 case OpenLayers.State.INSERT:
13780 // the feature should be destroyed
13782 case OpenLayers.State.DELETE:
13784 case OpenLayers.State.UNKNOWN:
13785 case OpenLayers.State.UPDATE:
13786 this.state = state;
13789 } else if (state == OpenLayers.State.UNKNOWN) {
13790 this.state = state;
13794 CLASS_NAME: "OpenLayers.Feature.Vector"
13799 * Constant: OpenLayers.Feature.Vector.style
13800 * OpenLayers features can have a number of style attributes. The 'default'
13801 * style will typically be used if no other style is specified. These
13802 * styles correspond for the most part, to the styling properties defined
13803 * by the SVG standard.
13804 * Information on fill properties: http://www.w3.org/TR/SVG/painting.html#FillProperties
13805 * Information on stroke properties: http://www.w3.org/TR/SVG/painting.html#StrokeProperties
13807 * Symbolizer properties:
13808 * fill - {Boolean} Set to false if no fill is desired.
13809 * fillColor - {String} Hex fill color. Default is "#ee9900".
13810 * fillOpacity - {Number} Fill opacity (0-1). Default is 0.4
13811 * stroke - {Boolean} Set to false if no stroke is desired.
13812 * strokeColor - {String} Hex stroke color. Default is "#ee9900".
13813 * strokeOpacity - {Number} Stroke opacity (0-1). Default is 1.
13814 * strokeWidth - {Number} Pixel stroke width. Default is 1.
13815 * strokeLinecap - {String} Stroke cap type. Default is "round". [butt | round | square]
13816 * strokeDashstyle - {String} Stroke dash style. Default is "solid". [dot | dash | dashdot | longdash | longdashdot | solid]
13817 * graphic - {Boolean} Set to false if no graphic is desired.
13818 * pointRadius - {Number} Pixel point radius. Default is 6.
13819 * pointerEvents - {String} Default is "visiblePainted".
13820 * cursor - {String} Default is "".
13821 * externalGraphic - {String} Url to an external graphic that will be used for rendering points.
13822 * graphicWidth - {Number} Pixel width for sizing an external graphic.
13823 * graphicHeight - {Number} Pixel height for sizing an external graphic.
13824 * graphicOpacity - {Number} Opacity (0-1) for an external graphic.
13825 * graphicXOffset - {Number} Pixel offset along the positive x axis for displacing an external graphic.
13826 * graphicYOffset - {Number} Pixel offset along the positive y axis for displacing an external graphic.
13827 * rotation - {Number} For point symbolizers, this is the rotation of a graphic in the clockwise direction about its center point (or any point off center as specified by graphicXOffset and graphicYOffset).
13828 * graphicZIndex - {Number} The integer z-index value to use in rendering.
13829 * graphicName - {String} Named graphic to use when rendering points. Supported values include "circle" (default),
13830 * "square", "star", "x", "cross", "triangle".
13831 * graphicTitle - {String} Tooltip when hovering over a feature. *deprecated*, use title instead
13832 * title - {String} Tooltip when hovering over a feature. Not supported by the canvas renderer.
13833 * backgroundGraphic - {String} Url to a graphic to be used as the background under an externalGraphic.
13834 * backgroundGraphicZIndex - {Number} The integer z-index value to use in rendering the background graphic.
13835 * backgroundXOffset - {Number} The x offset (in pixels) for the background graphic.
13836 * backgroundYOffset - {Number} The y offset (in pixels) for the background graphic.
13837 * backgroundHeight - {Number} The height of the background graphic. If not provided, the graphicHeight will be used.
13838 * backgroundWidth - {Number} The width of the background width. If not provided, the graphicWidth will be used.
13839 * label - {String} The text for an optional label. For browsers that use the canvas renderer, this requires either
13840 * fillText or mozDrawText to be available.
13841 * labelAlign - {String} Label alignment. This specifies the insertion point relative to the text. It is a string
13842 * composed of two characters. The first character is for the horizontal alignment, the second for the vertical
13843 * alignment. Valid values for horizontal alignment: "l"=left, "c"=center, "r"=right. Valid values for vertical
13844 * alignment: "t"=top, "m"=middle, "b"=bottom. Example values: "lt", "cm", "rb". Default is "cm".
13845 * labelXOffset - {Number} Pixel offset along the positive x axis for displacing the label. Not supported by the canvas renderer.
13846 * labelYOffset - {Number} Pixel offset along the positive y axis for displacing the label. Not supported by the canvas renderer.
13847 * labelSelect - {Boolean} If set to true, labels will be selectable using SelectFeature or similar controls.
13848 * Default is false.
13849 * labelOutlineColor - {String} The color of the label outline. Default is 'white'. Only supported by the canvas & SVG renderers.
13850 * labelOutlineWidth - {Number} The width of the label outline. Default is 3, set to 0 or null to disable. Only supported by the SVG renderers.
13851 * labelOutlineOpacity - {Number} The opacity (0-1) of the label outline. Default is fontOpacity. Only supported by the canvas & SVG renderers.
13852 * fontColor - {String} The font color for the label, to be provided like CSS.
13853 * fontOpacity - {Number} Opacity (0-1) for the label
13854 * fontFamily - {String} The font family for the label, to be provided like in CSS.
13855 * fontSize - {String} The font size for the label, to be provided like in CSS.
13856 * fontStyle - {String} The font style for the label, to be provided like in CSS.
13857 * fontWeight - {String} The font weight for the label, to be provided like in CSS.
13858 * display - {String} Symbolizers will have no effect if display is set to "none". All other values have no effect.
13860 OpenLayers.Feature.Vector.style = {
13862 fillColor: "#ee9900",
13864 hoverFillColor: "white",
13865 hoverFillOpacity: 0.8,
13866 strokeColor: "#ee9900",
13869 strokeLinecap: "round",
13870 strokeDashstyle: "solid",
13871 hoverStrokeColor: "red",
13872 hoverStrokeOpacity: 1,
13873 hoverStrokeWidth: 0.2,
13875 hoverPointRadius: 1,
13876 hoverPointUnit: "%",
13877 pointerEvents: "visiblePainted",
13879 fontColor: "#000000",
13881 labelOutlineColor: "white",
13882 labelOutlineWidth: 3
13887 hoverFillColor: "white",
13888 hoverFillOpacity: 0.8,
13889 strokeColor: "blue",
13892 strokeLinecap: "round",
13893 strokeDashstyle: "solid",
13894 hoverStrokeColor: "red",
13895 hoverStrokeOpacity: 1,
13896 hoverStrokeWidth: 0.2,
13898 hoverPointRadius: 1,
13899 hoverPointUnit: "%",
13900 pointerEvents: "visiblePainted",
13902 fontColor: "#000000",
13904 labelOutlineColor: "white",
13905 labelOutlineWidth: 3
13909 fillColor: "#66cccc",
13911 hoverFillColor: "white",
13912 hoverFillOpacity: 0.8,
13913 strokeColor: "#66cccc",
13915 strokeLinecap: "round",
13917 strokeDashstyle: "solid",
13918 hoverStrokeColor: "red",
13919 hoverStrokeOpacity: 1,
13920 hoverStrokeWidth: 0.2,
13922 hoverPointRadius: 1,
13923 hoverPointUnit: "%",
13924 pointerEvents: "visiblePainted",
13926 fontColor: "#000000",
13928 labelOutlineColor: "white",
13929 labelOutlineWidth: 3
13936 /* ======================================================================
13937 OpenLayers/Style.js
13938 ====================================================================== */
13940 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
13941 * full list of contributors). Published under the 2-clause BSD license.
13942 * See license.txt in the OpenLayers distribution or repository for the
13943 * full text of the license. */
13947 * @requires OpenLayers/BaseTypes/Class.js
13948 * @requires OpenLayers/Util.js
13949 * @requires OpenLayers/Feature/Vector.js
13953 * Class: OpenLayers.Style
13954 * This class represents a UserStyle obtained
13955 * from a SLD, containing styling rules.
13957 OpenLayers.Style = OpenLayers.Class({
13961 * {String} A unique id for this session.
13966 * APIProperty: name
13973 * {String} Title of this style (set if included in SLD)
13978 * Property: description
13979 * {String} Description of this style (set if abstract is included in SLD)
13984 * APIProperty: layerName
13985 * {<String>} name of the layer that this style belongs to, usually
13986 * according to the NamedLayer attribute of an SLD document.
13991 * APIProperty: isDefault
13998 * {Array(<OpenLayers.Rule>)}
14003 * APIProperty: context
14004 * {Object} An optional object with properties that symbolizers' property
14005 * values should be evaluated against. If no context is specified,
14006 * feature.attributes will be used
14011 * Property: defaultStyle
14012 * {Object} hash of style properties to use as default for merging
14013 * rule-based style symbolizers onto. If no rules are defined,
14014 * createSymbolizer will return this style. If <defaultsPerSymbolizer> is set to
14015 * true, the defaultStyle will only be taken into account if there are
14018 defaultStyle: null,
14021 * Property: defaultsPerSymbolizer
14022 * {Boolean} If set to true, the <defaultStyle> will extend the symbolizer
14023 * of every rule. Properties of the <defaultStyle> will also be used to set
14024 * missing symbolizer properties if the symbolizer has stroke, fill or
14025 * graphic set to true. Default is false.
14027 defaultsPerSymbolizer: false,
14030 * Property: propertyStyles
14031 * {Hash of Boolean} cache of style properties that need to be parsed for
14032 * propertyNames. Property names are keys, values won't be used.
14034 propertyStyles: null,
14038 * Constructor: OpenLayers.Style
14039 * Creates a UserStyle.
14042 * style - {Object} Optional hash of style properties that will be
14043 * used as default style for this style object. This style
14044 * applies if no rules are specified. Symbolizers defined in
14045 * rules will extend this default style.
14046 * options - {Object} An optional object with properties to set on the
14050 * rules - {Array(<OpenLayers.Rule>)} List of rules to be added to the
14054 * {<OpenLayers.Style>}
14056 initialize: function(style, options) {
14058 OpenLayers.Util.extend(this, options);
14060 if(options && options.rules) {
14061 this.addRules(options.rules);
14064 // use the default style from OpenLayers.Feature.Vector if no style
14065 // was given in the constructor
14066 this.setDefaultStyle(style ||
14067 OpenLayers.Feature.Vector.style["default"]);
14069 this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
14073 * APIMethod: destroy
14074 * nullify references to prevent circular references and memory leaks
14076 destroy: function() {
14077 for (var i=0, len=this.rules.length; i<len; i++) {
14078 this.rules[i].destroy();
14079 this.rules[i] = null;
14082 this.defaultStyle = null;
14086 * Method: createSymbolizer
14087 * creates a style by applying all feature-dependent rules to the base
14091 * feature - {<OpenLayers.Feature>} feature to evaluate rules for
14094 * {Object} symbolizer hash
14096 createSymbolizer: function(feature) {
14097 var style = this.defaultsPerSymbolizer ? {} : this.createLiterals(
14098 OpenLayers.Util.extend({}, this.defaultStyle), feature);
14100 var rules = this.rules;
14103 var elseRules = [];
14104 var appliedRules = false;
14105 for(var i=0, len=rules.length; i<len; i++) {
14107 // does the rule apply?
14108 var applies = rule.evaluate(feature);
14111 if(rule instanceof OpenLayers.Rule && rule.elseFilter) {
14112 elseRules.push(rule);
14114 appliedRules = true;
14115 this.applySymbolizer(rule, style, feature);
14120 // if no other rules apply, apply the rules with else filters
14121 if(appliedRules == false && elseRules.length > 0) {
14122 appliedRules = true;
14123 for(var i=0, len=elseRules.length; i<len; i++) {
14124 this.applySymbolizer(elseRules[i], style, feature);
14128 // don't display if there were rules but none applied
14129 if(rules.length > 0 && appliedRules == false) {
14130 style.display = "none";
14133 if (style.label != null && typeof style.label !== "string") {
14134 style.label = String(style.label);
14141 * Method: applySymbolizer
14144 * rule - {<OpenLayers.Rule>}
14146 * feature - {<OpenLayer.Feature.Vector>}
14149 * {Object} A style with new symbolizer applied.
14151 applySymbolizer: function(rule, style, feature) {
14152 var symbolizerPrefix = feature.geometry ?
14153 this.getSymbolizerPrefix(feature.geometry) :
14154 OpenLayers.Style.SYMBOLIZER_PREFIXES[0];
14156 var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer;
14158 if(this.defaultsPerSymbolizer === true) {
14159 var defaults = this.defaultStyle;
14160 OpenLayers.Util.applyDefaults(symbolizer, {
14161 pointRadius: defaults.pointRadius
14163 if(symbolizer.stroke === true || symbolizer.graphic === true) {
14164 OpenLayers.Util.applyDefaults(symbolizer, {
14165 strokeWidth: defaults.strokeWidth,
14166 strokeColor: defaults.strokeColor,
14167 strokeOpacity: defaults.strokeOpacity,
14168 strokeDashstyle: defaults.strokeDashstyle,
14169 strokeLinecap: defaults.strokeLinecap
14172 if(symbolizer.fill === true || symbolizer.graphic === true) {
14173 OpenLayers.Util.applyDefaults(symbolizer, {
14174 fillColor: defaults.fillColor,
14175 fillOpacity: defaults.fillOpacity
14178 if(symbolizer.graphic === true) {
14179 OpenLayers.Util.applyDefaults(symbolizer, {
14180 pointRadius: this.defaultStyle.pointRadius,
14181 externalGraphic: this.defaultStyle.externalGraphic,
14182 graphicName: this.defaultStyle.graphicName,
14183 graphicOpacity: this.defaultStyle.graphicOpacity,
14184 graphicWidth: this.defaultStyle.graphicWidth,
14185 graphicHeight: this.defaultStyle.graphicHeight,
14186 graphicXOffset: this.defaultStyle.graphicXOffset,
14187 graphicYOffset: this.defaultStyle.graphicYOffset
14192 // merge the style with the current style
14193 return this.createLiterals(
14194 OpenLayers.Util.extend(style, symbolizer), feature);
14198 * Method: createLiterals
14199 * creates literals for all style properties that have an entry in
14200 * <this.propertyStyles>.
14203 * style - {Object} style to create literals for. Will be modified
14205 * feature - {Object}
14208 * {Object} the modified style
14210 createLiterals: function(style, feature) {
14211 var context = OpenLayers.Util.extend({}, feature.attributes || feature.data);
14212 OpenLayers.Util.extend(context, this.context);
14214 for (var i in this.propertyStyles) {
14215 style[i] = OpenLayers.Style.createLiteral(style[i], context, feature, i);
14221 * Method: findPropertyStyles
14222 * Looks into all rules for this style and the defaultStyle to collect
14223 * all the style hash property names containing ${...} strings that have
14224 * to be replaced using the createLiteral method before returning them.
14227 * {Object} hash of property names that need createLiteral parsing. The
14228 * name of the property is the key, and the value is true;
14230 findPropertyStyles: function() {
14231 var propertyStyles = {};
14233 // check the default style
14234 var style = this.defaultStyle;
14235 this.addPropertyStyles(propertyStyles, style);
14237 // walk through all rules to check for properties in their symbolizer
14238 var rules = this.rules;
14239 var symbolizer, value;
14240 for (var i=0, len=rules.length; i<len; i++) {
14241 symbolizer = rules[i].symbolizer;
14242 for (var key in symbolizer) {
14243 value = symbolizer[key];
14244 if (typeof value == "object") {
14245 // symbolizer key is "Point", "Line" or "Polygon"
14246 this.addPropertyStyles(propertyStyles, value);
14248 // symbolizer is a hash of style properties
14249 this.addPropertyStyles(propertyStyles, symbolizer);
14254 return propertyStyles;
14258 * Method: addPropertyStyles
14261 * propertyStyles - {Object} hash to add new property styles to. Will be
14263 * symbolizer - {Object} search this symbolizer for property styles
14266 * {Object} propertyStyles hash
14268 addPropertyStyles: function(propertyStyles, symbolizer) {
14270 for (var key in symbolizer) {
14271 property = symbolizer[key];
14272 if (typeof property == "string" &&
14273 property.match(/\$\{\w+\}/)) {
14274 propertyStyles[key] = true;
14277 return propertyStyles;
14281 * APIMethod: addRules
14282 * Adds rules to this style.
14285 * rules - {Array(<OpenLayers.Rule>)}
14287 addRules: function(rules) {
14288 Array.prototype.push.apply(this.rules, rules);
14289 this.propertyStyles = this.findPropertyStyles();
14293 * APIMethod: setDefaultStyle
14294 * Sets the default style for this style object.
14297 * style - {Object} Hash of style properties
14299 setDefaultStyle: function(style) {
14300 this.defaultStyle = style;
14301 this.propertyStyles = this.findPropertyStyles();
14305 * Method: getSymbolizerPrefix
14306 * Returns the correct symbolizer prefix according to the
14307 * geometry type of the passed geometry
14310 * geometry - {<OpenLayers.Geometry>}
14313 * {String} key of the according symbolizer
14315 getSymbolizerPrefix: function(geometry) {
14316 var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES;
14317 for (var i=0, len=prefixes.length; i<len; i++) {
14318 if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) {
14319 return prefixes[i];
14326 * Clones this style.
14329 * {<OpenLayers.Style>} Clone of this style.
14331 clone: function() {
14332 var options = OpenLayers.Util.extend({}, this);
14335 options.rules = [];
14336 for(var i=0, len=this.rules.length; i<len; ++i) {
14337 options.rules.push(this.rules[i].clone());
14341 options.context = this.context && OpenLayers.Util.extend({}, this.context);
14342 //clone default style
14343 var defaultStyle = OpenLayers.Util.extend({}, this.defaultStyle);
14344 return new OpenLayers.Style(defaultStyle, options);
14347 CLASS_NAME: "OpenLayers.Style"
14352 * Function: createLiteral
14353 * converts a style value holding a combination of PropertyName and Literal
14354 * into a Literal, taking the property values from the passed features.
14357 * value - {String} value to parse. If this string contains a construct like
14358 * "foo ${bar}", then "foo " will be taken as literal, and "${bar}"
14359 * will be replaced by the value of the "bar" attribute of the passed
14361 * context - {Object} context to take attribute values from
14362 * feature - {<OpenLayers.Feature.Vector>} optional feature to pass to
14363 * <OpenLayers.String.format> for evaluating functions in the
14365 * property - {String} optional, name of the property for which the literal is
14366 * being created for evaluating functions in the context.
14369 * {String} the parsed value. In the example of the value parameter above, the
14370 * result would be "foo valueOfBar", assuming that the passed feature has an
14371 * attribute named "bar" with the value "valueOfBar".
14373 OpenLayers.Style.createLiteral = function(value, context, feature, property) {
14374 if (typeof value == "string" && value.indexOf("${") != -1) {
14375 value = OpenLayers.String.format(value, context, [feature, property]);
14376 value = (isNaN(value) || !value) ? value : parseFloat(value);
14382 * Constant: OpenLayers.Style.SYMBOLIZER_PREFIXES
14383 * {Array} prefixes of the sld symbolizers. These are the
14384 * same as the main geometry types
14386 OpenLayers.Style.SYMBOLIZER_PREFIXES = ['Point', 'Line', 'Polygon', 'Text',
14388 /* ======================================================================
14389 OpenLayers/Filter.js
14390 ====================================================================== */
14392 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
14393 * full list of contributors). Published under the 2-clause BSD license.
14394 * See license.txt in the OpenLayers distribution or repository for the
14395 * full text of the license. */
14399 * @requires OpenLayers/BaseTypes/Class.js
14400 * @requires OpenLayers/Util.js
14401 * @requires OpenLayers/Style.js
14405 * Class: OpenLayers.Filter
14406 * This class represents an OGC Filter.
14408 OpenLayers.Filter = OpenLayers.Class({
14411 * Constructor: OpenLayers.Filter
14412 * This class represents a generic filter.
14415 * options - {Object} Optional object whose properties will be set on the
14419 * {<OpenLayers.Filter>}
14421 initialize: function(options) {
14422 OpenLayers.Util.extend(this, options);
14426 * APIMethod: destroy
14427 * Remove reference to anything added.
14429 destroy: function() {
14433 * APIMethod: evaluate
14434 * Evaluates this filter in a specific context. Instances or subclasses
14435 * are supposed to override this method.
14438 * context - {Object} Context to use in evaluating the filter. If a vector
14439 * feature is provided, the feature.attributes will be used as context.
14442 * {Boolean} The filter applies.
14444 evaluate: function(context) {
14450 * Clones this filter. Should be implemented by subclasses.
14453 * {<OpenLayers.Filter>} Clone of this filter.
14455 clone: function() {
14460 * APIMethod: toString
14463 * {String} Include <OpenLayers.Format.CQL> in your build to get a CQL
14464 * representation of the filter returned. Otherwise "[Object object]"
14465 * will be returned.
14467 toString: function() {
14469 if (OpenLayers.Format && OpenLayers.Format.CQL) {
14470 string = OpenLayers.Format.CQL.prototype.write(this);
14472 string = Object.prototype.toString.call(this);
14477 CLASS_NAME: "OpenLayers.Filter"
14479 /* ======================================================================
14480 OpenLayers/Strategy/Filter.js
14481 ====================================================================== */
14483 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
14484 * full list of contributors). Published under the 2-clause BSD license.
14485 * See license.txt in the OpenLayers distribution or repository for the
14486 * full text of the license. */
14489 * @requires OpenLayers/Strategy.js
14490 * @requires OpenLayers/Filter.js
14494 * Class: OpenLayers.Strategy.Filter
14495 * Strategy for limiting features that get added to a layer by
14496 * evaluating a filter. The strategy maintains a cache of
14497 * all features until removeFeatures is called on the layer.
14500 * - <OpenLayers.Strategy>
14502 OpenLayers.Strategy.Filter = OpenLayers.Class(OpenLayers.Strategy, {
14505 * APIProperty: filter
14506 * {<OpenLayers.Filter>} Filter for limiting features sent to the layer.
14507 * Use the <setFilter> method to update this filter after construction.
14513 * {Array(<OpenLayers.Feature.Vector>)} List of currently cached
14519 * Property: caching
14520 * {Boolean} The filter is currently caching features.
14525 * Constructor: OpenLayers.Strategy.Filter
14526 * Create a new filter strategy.
14529 * options - {Object} Optional object whose properties will be set on the
14534 * APIMethod: activate
14535 * Activate the strategy. Register any listeners, do appropriate setup.
14536 * By default, this strategy automatically activates itself when a layer
14537 * is added to a map.
14540 * {Boolean} True if the strategy was successfully activated or false if
14541 * the strategy was already active.
14543 activate: function() {
14544 var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments);
14547 this.layer.events.on({
14548 "beforefeaturesadded": this.handleAdd,
14549 "beforefeaturesremoved": this.handleRemove,
14557 * APIMethod: deactivate
14558 * Deactivate the strategy. Clear the feature cache.
14561 * {Boolean} True if the strategy was successfully deactivated or false if
14562 * the strategy was already inactive.
14564 deactivate: function() {
14566 if (this.layer && this.layer.events) {
14567 this.layer.events.un({
14568 "beforefeaturesadded": this.handleAdd,
14569 "beforefeaturesremoved": this.handleRemove,
14573 return OpenLayers.Strategy.prototype.deactivate.apply(this, arguments);
14577 * Method: handleAdd
14579 handleAdd: function(event) {
14580 if (!this.caching && this.filter) {
14581 var features = event.features;
14582 event.features = [];
14584 for (var i=0, ii=features.length; i<ii; ++i) {
14585 feature = features[i];
14586 if (this.filter.evaluate(feature)) {
14587 event.features.push(feature);
14589 this.cache.push(feature);
14596 * Method: handleRemove
14598 handleRemove: function(event) {
14599 if (!this.caching) {
14605 * APIMethod: setFilter
14606 * Update the filter for this strategy. This will re-evaluate
14607 * any features on the layer and in the cache. Only features
14608 * for which filter.evalute(feature) returns true will be
14609 * added to the layer. Others will be cached by the strategy.
14612 * filter - {<OpenLayers.Filter>} A filter for evaluating features.
14614 setFilter: function(filter) {
14615 this.filter = filter;
14616 var previousCache = this.cache;
14618 // look through layer for features to remove from layer
14619 this.handleAdd({features: this.layer.features});
14620 // cache now contains features to remove from layer
14621 if (this.cache.length > 0) {
14622 this.caching = true;
14623 this.layer.removeFeatures(this.cache.slice());
14624 this.caching = false;
14626 // now look through previous cache for features to add to layer
14627 if (previousCache.length > 0) {
14628 var event = {features: previousCache};
14629 this.handleAdd(event);
14630 if (event.features.length > 0) {
14631 // event has features to add to layer
14632 this.caching = true;
14633 this.layer.addFeatures(event.features);
14634 this.caching = false;
14639 CLASS_NAME: "OpenLayers.Strategy.Filter"
14642 /* ======================================================================
14644 ====================================================================== */
14646 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
14647 * full list of contributors). Published under the 2-clause BSD license.
14648 * See license.txt in the OpenLayers distribution or repository for the
14649 * full text of the license. */
14653 * @requires OpenLayers/BaseTypes/Class.js
14654 * @requires OpenLayers/Util.js
14658 * Class: OpenLayers.Tile
14659 * This is a class designed to designate a single tile, however
14660 * it is explicitly designed to do relatively little. Tiles store
14661 * information about themselves -- such as the URL that they are related
14662 * to, and their size - but do not add themselves to the layer div
14663 * automatically, for example. Create a new tile with the
14664 * <OpenLayers.Tile> constructor, or a subclass.
14666 * TBD 3.0 - remove reference to url in above paragraph
14669 OpenLayers.Tile = OpenLayers.Class({
14672 * APIProperty: events
14673 * {<OpenLayers.Events>} An events object that handles all
14674 * events on the tile.
14676 * Register a listener for a particular event with the following syntax:
14678 * tile.events.register(type, obj, listener);
14681 * Supported event types:
14682 * beforedraw - Triggered before the tile is drawn. Used to defer
14683 * drawing to an animation queue. To defer drawing, listeners need
14684 * to return false, which will abort drawing. The queue handler needs
14685 * to call <draw>(true) to actually draw the tile.
14686 * loadstart - Triggered when tile loading starts.
14687 * loadend - Triggered when tile loading ends.
14688 * loaderror - Triggered before the loadend event (i.e. when the tile is
14689 * still hidden) if the tile could not be loaded.
14690 * reload - Triggered when an already loading tile is reloaded.
14691 * unload - Triggered before a tile is unloaded.
14696 * APIProperty: eventListeners
14697 * {Object} If set as an option at construction, the eventListeners
14698 * object will be registered with <OpenLayers.Events.on>. Object
14699 * structure must be a listeners object as shown in the example for
14700 * the events.on method.
14702 * This options can be set in the ``tileOptions`` option from
14703 * <OpenLayers.Layer.Grid>. For example, to be notified of the
14704 * ``loadend`` event of each tiles:
14706 * new OpenLayers.Layer.OSM('osm', 'http://tile.openstreetmap.org/${z}/${x}/${y}.png', {
14708 * eventListeners: {
14709 * 'loadend': function(evt) {
14710 * // do something on loadend
14717 eventListeners: null,
14727 * {<OpenLayers.Layer>} layer the tile is attached to
14733 * {String} url of the request.
14736 * Deprecated. The base tile class does not need an url. This should be
14737 * handled in subclasses. Does not belong here.
14742 * APIProperty: bounds
14743 * {<OpenLayers.Bounds>} null
14749 * {<OpenLayers.Size>} null
14754 * Property: position
14755 * {<OpenLayers.Pixel>} Top Left pixel of the tile
14760 * Property: isLoading
14761 * {Boolean} Is the tile loading?
14765 /** TBD 3.0 -- remove 'url' from the list of parameters to the constructor.
14766 * there is no need for the base tile class to have a url.
14770 * Constructor: OpenLayers.Tile
14771 * Constructor for a new <OpenLayers.Tile> instance.
14774 * layer - {<OpenLayers.Layer>} layer that the tile will go in.
14775 * position - {<OpenLayers.Pixel>}
14776 * bounds - {<OpenLayers.Bounds>}
14778 * size - {<OpenLayers.Size>}
14779 * options - {Object}
14781 initialize: function(layer, position, bounds, url, size, options) {
14782 this.layer = layer;
14783 this.position = position.clone();
14784 this.setBounds(bounds);
14787 this.size = size.clone();
14790 //give the tile a unique id based on its BBOX.
14791 this.id = OpenLayers.Util.createUniqueID("Tile_");
14793 OpenLayers.Util.extend(this, options);
14795 this.events = new OpenLayers.Events(this);
14796 if (this.eventListeners instanceof Object) {
14797 this.events.on(this.eventListeners);
14803 * Call immediately before destroying if you are listening to tile
14804 * events, so that counters are properly handled if tile is still
14805 * loading at destroy-time. Will only fire an event if the tile is
14808 unload: function() {
14809 if (this.isLoading) {
14810 this.isLoading = false;
14811 this.events.triggerEvent("unload");
14816 * APIMethod: destroy
14817 * Nullify references to prevent circular references and memory leaks.
14819 destroy:function() {
14821 this.bounds = null;
14823 this.position = null;
14825 if (this.eventListeners) {
14826 this.events.un(this.eventListeners);
14828 this.events.destroy();
14829 this.eventListeners = null;
14830 this.events = null;
14835 * Clear whatever is currently in the tile, then return whether or not
14836 * it should actually be re-drawn. This is an example implementation
14837 * that can be overridden by subclasses. The minimum thing to do here
14838 * is to call <clear> and return the result from <shouldDraw>.
14841 * force - {Boolean} If true, the tile will not be cleared and no beforedraw
14842 * event will be fired. This is used for drawing tiles asynchronously
14843 * after drawing has been cancelled by returning false from a beforedraw
14847 * {Boolean} Whether or not the tile should actually be drawn. Returns null
14848 * if a beforedraw listener returned false.
14850 draw: function(force) {
14852 //clear tile's contents and mark as not drawn
14855 var draw = this.shouldDraw();
14856 if (draw && !force && this.events.triggerEvent("beforedraw") === false) {
14863 * Method: shouldDraw
14864 * Return whether or not the tile should actually be (re-)drawn. The only
14865 * case where we *wouldn't* want to draw the tile is if the tile is outside
14866 * its layer's maxExtent
14869 * {Boolean} Whether or not the tile should actually be drawn.
14871 shouldDraw: function() {
14872 var withinMaxExtent = false,
14873 maxExtent = this.layer.maxExtent;
14875 var map = this.layer.map;
14876 var worldBounds = map.baseLayer.wrapDateLine && map.getMaxExtent();
14877 if (this.bounds.intersectsBounds(maxExtent, {inclusive: false, worldBounds: worldBounds})) {
14878 withinMaxExtent = true;
14882 return withinMaxExtent || this.layer.displayOutsideMaxExtent;
14886 * Method: setBounds
14887 * Sets the bounds on this instance
14890 * bounds {<OpenLayers.Bounds>}
14892 setBounds: function(bounds) {
14893 bounds = bounds.clone();
14894 if (this.layer.map.baseLayer.wrapDateLine) {
14895 var worldExtent = this.layer.map.getMaxExtent(),
14896 tolerance = this.layer.map.getResolution();
14897 bounds = bounds.wrapDateLine(worldExtent, {
14898 leftTolerance: tolerance,
14899 rightTolerance: tolerance
14902 this.bounds = bounds;
14907 * Reposition the tile.
14910 * bounds - {<OpenLayers.Bounds>}
14911 * position - {<OpenLayers.Pixel>}
14912 * redraw - {Boolean} Call draw method on tile after moving.
14915 moveTo: function (bounds, position, redraw) {
14916 if (redraw == null) {
14920 this.setBounds(bounds);
14921 this.position = position.clone();
14929 * Clear the tile of any bounds/position-related data so that it can
14930 * be reused in a new location.
14932 clear: function(draw) {
14933 // to be extended by subclasses
14936 CLASS_NAME: "OpenLayers.Tile"
14938 /* ======================================================================
14939 OpenLayers/Tile/Image.js
14940 ====================================================================== */
14942 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
14943 * full list of contributors). Published under the 2-clause BSD license.
14944 * See license.txt in the OpenLayers distribution or repository for the
14945 * full text of the license. */
14949 * @requires OpenLayers/Tile.js
14950 * @requires OpenLayers/Animation.js
14951 * @requires OpenLayers/Util.js
14955 * Class: OpenLayers.Tile.Image
14956 * Instances of OpenLayers.Tile.Image are used to manage the image tiles
14957 * used by various layers. Create a new image tile with the
14958 * <OpenLayers.Tile.Image> constructor.
14961 * - <OpenLayers.Tile>
14963 OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, {
14966 * APIProperty: events
14967 * {<OpenLayers.Events>} An events object that handles all
14968 * events on the tile.
14970 * Register a listener for a particular event with the following syntax:
14972 * tile.events.register(type, obj, listener);
14975 * Supported event types (in addition to the <OpenLayers.Tile> events):
14976 * beforeload - Triggered before an image is prepared for loading, when the
14977 * url for the image is known already. Listeners may call <setImage> on
14978 * the tile instance. If they do so, that image will be used and no new
14979 * one will be created.
14984 * {String} The URL of the image being requested. No default. Filled in by
14985 * layer.getURL() function. May be modified by loadstart listeners.
14991 * {HTMLImageElement} The image for this tile.
14997 * {DOMElement} The image element is appended to the frame. Any gutter on
14998 * the image will be hidden behind the frame. If no gutter is set,
14999 * this will be null.
15004 * Property: imageReloadAttempts
15005 * {Integer} Attempts to load the image.
15007 imageReloadAttempts: null,
15010 * Property: layerAlphaHack
15011 * {Boolean} True if the png alpha hack needs to be applied on the layer's div.
15013 layerAlphaHack: null,
15016 * Property: asyncRequestId
15017 * {Integer} ID of an request to see if request is still valid. This is a
15018 * number which increments by 1 for each asynchronous request.
15020 asyncRequestId: null,
15023 * APIProperty: maxGetUrlLength
15024 * {Number} If set, requests that would result in GET urls with more
15025 * characters than the number provided will be made using form-encoded
15026 * HTTP POST. It is good practice to avoid urls that are longer than 2048
15030 * Older versions of Gecko based browsers (e.g. Firefox < 3.5) and most
15031 * Opera versions do not fully support this option. On all browsers,
15032 * transition effects are not supported if POST requests are used.
15034 maxGetUrlLength: null,
15037 * Property: canvasContext
15038 * {CanvasRenderingContext2D} A canvas context associated with
15041 canvasContext: null,
15044 * APIProperty: crossOriginKeyword
15045 * The value of the crossorigin keyword to use when loading images. This is
15046 * only relevant when using <getCanvasContext> for tiles from remote
15047 * origins and should be set to either 'anonymous' or 'use-credentials'
15048 * for servers that send Access-Control-Allow-Origin headers with their
15051 crossOriginKeyword: null,
15053 /** TBD 3.0 - reorder the parameters to the init function to remove
15054 * URL. the getUrl() function on the layer gets called on
15055 * each draw(), so no need to specify it here.
15059 * Constructor: OpenLayers.Tile.Image
15060 * Constructor for a new <OpenLayers.Tile.Image> instance.
15063 * layer - {<OpenLayers.Layer>} layer that the tile will go in.
15064 * position - {<OpenLayers.Pixel>}
15065 * bounds - {<OpenLayers.Bounds>}
15066 * url - {<String>} Deprecated. Remove me in 3.0.
15067 * size - {<OpenLayers.Size>}
15068 * options - {Object}
15070 initialize: function(layer, position, bounds, url, size, options) {
15071 OpenLayers.Tile.prototype.initialize.apply(this, arguments);
15073 this.url = url; //deprecated remove me
15075 this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack();
15077 if (this.maxGetUrlLength != null || this.layer.gutter || this.layerAlphaHack) {
15078 // only create frame if it's needed
15079 this.frame = document.createElement("div");
15080 this.frame.style.position = "absolute";
15081 this.frame.style.overflow = "hidden";
15083 if (this.maxGetUrlLength != null) {
15084 OpenLayers.Util.extend(this, OpenLayers.Tile.Image.IFrame);
15089 * APIMethod: destroy
15090 * nullify references to prevent circular references and memory leaks
15092 destroy: function() {
15095 this.imgDiv = null;
15098 // don't handle async requests any more
15099 this.asyncRequestId = null;
15100 OpenLayers.Tile.prototype.destroy.apply(this, arguments);
15105 * Check that a tile should be drawn, and draw it.
15108 * {Boolean} Was a tile drawn? Or null if a beforedraw listener returned
15112 var shouldDraw = OpenLayers.Tile.prototype.draw.apply(this, arguments);
15114 // The layer's reproject option is deprecated.
15115 if (this.layer != this.layer.map.baseLayer && this.layer.reproject) {
15116 // getBoundsFromBaseLayer is defined in deprecated.js.
15117 this.bounds = this.getBoundsFromBaseLayer(this.position);
15119 if (this.isLoading) {
15120 //if we're already loading, send 'reload' instead of 'loadstart'.
15121 this._loadEvent = "reload";
15123 this.isLoading = true;
15124 this._loadEvent = "loadstart";
15127 this.positionTile();
15128 } else if (shouldDraw === false) {
15135 * Method: renderTile
15136 * Internal function to actually initialize the image tile,
15137 * position it correctly, and set its url.
15139 renderTile: function() {
15140 if (this.layer.async) {
15141 // Asynchronous image requests call the asynchronous getURL method
15142 // on the layer to fetch an image that covers 'this.bounds'.
15143 var id = this.asyncRequestId = (this.asyncRequestId || 0) + 1;
15144 this.layer.getURLasync(this.bounds, function(url) {
15145 if (id == this.asyncRequestId) {
15151 // synchronous image requests get the url immediately.
15152 this.url = this.layer.getURL(this.bounds);
15158 * Method: positionTile
15159 * Using the properties currenty set on the layer, position the tile correctly.
15160 * This method is used both by the async and non-async versions of the Tile.Image
15163 positionTile: function() {
15164 var style = this.getTile().style,
15165 size = this.frame ? this.size :
15166 this.layer.getImageSize(this.bounds),
15168 if (this.layer instanceof OpenLayers.Layer.Grid) {
15169 ratio = this.layer.getServerResolution() / this.layer.map.getResolution();
15171 style.left = this.position.x + "px";
15172 style.top = this.position.y + "px";
15173 style.width = Math.round(ratio * size.w) + "px";
15174 style.height = Math.round(ratio * size.h) + "px";
15179 * Remove the tile from the DOM, clear it of any image related data so that
15180 * it can be reused in a new location.
15182 clear: function() {
15183 OpenLayers.Tile.prototype.clear.apply(this, arguments);
15184 var img = this.imgDiv;
15186 var tile = this.getTile();
15187 if (tile.parentNode === this.layer.div) {
15188 this.layer.div.removeChild(tile);
15191 if (this.layerAlphaHack === true) {
15192 img.style.filter = "";
15194 OpenLayers.Element.removeClass(img, "olImageLoadError");
15196 this.canvasContext = null;
15201 * Returns or creates and returns the tile image.
15203 getImage: function() {
15204 if (!this.imgDiv) {
15205 this.imgDiv = OpenLayers.Tile.Image.IMAGE.cloneNode(false);
15207 var style = this.imgDiv.style;
15209 var left = 0, top = 0;
15210 if (this.layer.gutter) {
15211 left = this.layer.gutter / this.layer.tileSize.w * 100;
15212 top = this.layer.gutter / this.layer.tileSize.h * 100;
15214 style.left = -left + "%";
15215 style.top = -top + "%";
15216 style.width = (2 * left + 100) + "%";
15217 style.height = (2 * top + 100) + "%";
15219 style.visibility = "hidden";
15221 if (this.layer.opacity < 1) {
15222 style.filter = 'alpha(opacity=' +
15223 (this.layer.opacity * 100) +
15226 style.position = "absolute";
15227 if (this.layerAlphaHack) {
15228 // move the image out of sight
15229 style.paddingTop = style.height;
15230 style.height = "0";
15231 style.width = "100%";
15234 this.frame.appendChild(this.imgDiv);
15238 return this.imgDiv;
15242 * APIMethod: setImage
15243 * Sets the image element for this tile. This method should only be called
15244 * from beforeload listeners.
15247 * img - {HTMLImageElement} The image to use for this tile.
15249 setImage: function(img) {
15254 * Method: initImage
15255 * Creates the content for the frame on the tile.
15257 initImage: function() {
15258 if (!this.url && !this.imgDiv) {
15259 // fast path out - if there is no tile url and no previous image
15260 this.isLoading = false;
15263 this.events.triggerEvent('beforeload');
15264 this.layer.div.appendChild(this.getTile());
15265 this.events.triggerEvent(this._loadEvent);
15266 var img = this.getImage();
15267 var src = img.getAttribute('src') || '';
15268 if (this.url && OpenLayers.Util.isEquivalentUrl(src, this.url)) {
15269 this._loadTimeout = window.setTimeout(
15270 OpenLayers.Function.bind(this.onImageLoad, this), 0
15273 this.stopLoading();
15274 if (this.crossOriginKeyword) {
15275 img.removeAttribute("crossorigin");
15277 OpenLayers.Event.observe(img, "load",
15278 OpenLayers.Function.bind(this.onImageLoad, this)
15280 OpenLayers.Event.observe(img, "error",
15281 OpenLayers.Function.bind(this.onImageError, this)
15283 this.imageReloadAttempts = 0;
15284 this.setImgSrc(this.url);
15289 * Method: setImgSrc
15290 * Sets the source for the tile image
15293 * url - {String} or undefined to hide the image
15295 setImgSrc: function(url) {
15296 var img = this.imgDiv;
15298 img.style.visibility = 'hidden';
15299 img.style.opacity = 0;
15300 // don't set crossOrigin if the url is a data URL
15301 if (this.crossOriginKeyword) {
15302 if (url.substr(0, 5) !== 'data:') {
15303 img.setAttribute("crossorigin", this.crossOriginKeyword);
15305 img.removeAttribute("crossorigin");
15310 // Remove reference to the image, and leave it to the browser's
15311 // caching and garbage collection.
15312 this.stopLoading();
15313 this.imgDiv = null;
15314 if (img.parentNode) {
15315 img.parentNode.removeChild(img);
15322 * Get the tile's markup.
15325 * {DOMElement} The tile's markup
15327 getTile: function() {
15328 return this.frame ? this.frame : this.getImage();
15332 * Method: createBackBuffer
15333 * Create a backbuffer for this tile. A backbuffer isn't exactly a clone
15334 * of the tile's markup, because we want to avoid the reloading of the
15335 * image. So we clone the frame, and steal the image from the tile.
15338 * {DOMElement} The markup, or undefined if the tile has no image
15339 * or if it's currently loading.
15341 createBackBuffer: function() {
15342 if (!this.imgDiv || this.isLoading) {
15347 backBuffer = this.frame.cloneNode(false);
15348 backBuffer.appendChild(this.imgDiv);
15350 backBuffer = this.imgDiv;
15352 this.imgDiv = null;
15357 * Method: onImageLoad
15358 * Handler for the image onload event
15360 onImageLoad: function() {
15361 var img = this.imgDiv;
15362 this.stopLoading();
15363 img.style.visibility = 'inherit';
15364 img.style.opacity = this.layer.opacity;
15365 this.isLoading = false;
15366 this.canvasContext = null;
15367 this.events.triggerEvent("loadend");
15369 if (this.layerAlphaHack === true) {
15371 "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" +
15372 img.src + "', sizingMethod='scale')";
15377 * Method: onImageError
15378 * Handler for the image onerror event
15380 onImageError: function() {
15381 var img = this.imgDiv;
15382 if (img.src != null) {
15383 this.imageReloadAttempts++;
15384 if (this.imageReloadAttempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) {
15385 this.setImgSrc(this.layer.getURL(this.bounds));
15387 OpenLayers.Element.addClass(img, "olImageLoadError");
15388 this.events.triggerEvent("loaderror");
15389 this.onImageLoad();
15395 * Method: stopLoading
15396 * Stops a loading sequence so <onImageLoad> won't be executed.
15398 stopLoading: function() {
15399 OpenLayers.Event.stopObservingElement(this.imgDiv);
15400 window.clearTimeout(this._loadTimeout);
15401 delete this._loadTimeout;
15405 * APIMethod: getCanvasContext
15406 * Returns a canvas context associated with the tile image (with
15407 * the image drawn on it).
15408 * Returns undefined if the browser does not support canvas, if
15409 * the tile has no image or if it's currently loading.
15411 * The function returns a canvas context instance but the
15412 * underlying canvas is still available in the 'canvas' property:
15414 * var context = tile.getCanvasContext();
15416 * var data = context.canvas.toDataURL('image/jpeg');
15423 getCanvasContext: function() {
15424 if (OpenLayers.CANVAS_SUPPORTED && this.imgDiv && !this.isLoading) {
15425 if (!this.canvasContext) {
15426 var canvas = document.createElement("canvas");
15427 canvas.width = this.size.w;
15428 canvas.height = this.size.h;
15429 this.canvasContext = canvas.getContext("2d");
15430 this.canvasContext.drawImage(this.imgDiv, 0, 0);
15432 return this.canvasContext;
15436 CLASS_NAME: "OpenLayers.Tile.Image"
15441 * Constant: OpenLayers.Tile.Image.IMAGE
15442 * {HTMLImageElement} The image for a tile.
15444 OpenLayers.Tile.Image.IMAGE = (function() {
15445 var img = new Image();
15446 img.className = "olTileImage";
15447 // avoid image gallery menu in IE6
15448 img.galleryImg = "no";
15452 /* ======================================================================
15453 OpenLayers/Layer/Image.js
15454 ====================================================================== */
15456 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
15457 * full list of contributors). Published under the 2-clause BSD license.
15458 * See license.txt in the OpenLayers distribution or repository for the
15459 * full text of the license. */
15462 * @requires OpenLayers/Layer.js
15463 * @requires OpenLayers/Tile/Image.js
15467 * Class: OpenLayers.Layer.Image
15468 * Instances of OpenLayers.Layer.Image are used to display data from a web
15469 * accessible image as a map layer. Create a new image layer with the
15470 * <OpenLayers.Layer.Image> constructor.
15473 * - <OpenLayers.Layer>
15475 OpenLayers.Layer.Image = OpenLayers.Class(OpenLayers.Layer, {
15478 * Property: isBaseLayer
15479 * {Boolean} The layer is a base layer. Default is true. Set this property
15480 * in the layer options
15486 * {String} URL of the image to use
15492 * {<OpenLayers.Bounds>} The image bounds in map units. This extent will
15493 * also be used as the default maxExtent for the layer. If you wish
15494 * to have a maxExtent that is different than the image extent, set the
15495 * maxExtent property of the options argument (as with any other layer).
15501 * {<OpenLayers.Size>} The image size in pixels
15507 * {<OpenLayers.Tile.Image>}
15512 * Property: aspectRatio
15513 * {Float} The ratio of height/width represented by a single pixel in the
15519 * Constructor: OpenLayers.Layer.Image
15520 * Create a new image layer
15523 * name - {String} A name for the layer.
15524 * url - {String} Relative or absolute path to the image
15525 * extent - {<OpenLayers.Bounds>} The extent represented by the image
15526 * size - {<OpenLayers.Size>} The size (in pixels) of the image
15527 * options - {Object} Hashtable of extra options to tag onto the layer
15529 initialize: function(name, url, extent, size, options) {
15531 this.extent = extent;
15532 this.maxExtent = extent;
15534 OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);
15536 this.aspectRatio = (this.extent.getHeight() / this.size.h) /
15537 (this.extent.getWidth() / this.size.w);
15542 * Destroy this layer
15544 destroy: function() {
15546 this.removeTileMonitoringHooks(this.tile);
15547 this.tile.destroy();
15550 OpenLayers.Layer.prototype.destroy.apply(this, arguments);
15555 * Create a clone of this layer
15558 * obj - {Object} An optional layer (is this ever used?)
15561 * {<OpenLayers.Layer.Image>} An exact copy of this layer
15563 clone: function(obj) {
15566 obj = new OpenLayers.Layer.Image(this.name,
15570 this.getOptions());
15573 //get all additions from superclasses
15574 obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
15576 // copy/set any non-init, non-simple values here
15582 * APIMethod: setMap
15585 * map - {<OpenLayers.Map>}
15587 setMap: function(map) {
15589 * If nothing to do with resolutions has been set, assume a single
15590 * resolution determined by ratio*extent/size - if an image has a
15591 * pixel aspect ratio different than one (as calculated above), the
15592 * image will be stretched in one dimension only.
15594 if( this.options.maxResolution == null ) {
15595 this.options.maxResolution = this.aspectRatio *
15596 this.extent.getWidth() /
15599 OpenLayers.Layer.prototype.setMap.apply(this, arguments);
15604 * Create the tile for the image or resize it for the new resolution
15607 * bounds - {<OpenLayers.Bounds>}
15608 * zoomChanged - {Boolean}
15609 * dragging - {Boolean}
15611 moveTo:function(bounds, zoomChanged, dragging) {
15612 OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
15614 var firstRendering = (this.tile == null);
15616 if(zoomChanged || firstRendering) {
15618 //determine new tile size
15619 this.setTileSize();
15621 //determine new position (upper left corner of new bounds)
15622 var ulPx = this.map.getLayerPxFromLonLat({
15623 lon: this.extent.left,
15624 lat: this.extent.top
15627 if(firstRendering) {
15628 //create the new tile
15629 this.tile = new OpenLayers.Tile.Image(this, ulPx, this.extent,
15630 null, this.tileSize);
15631 this.addTileMonitoringHooks(this.tile);
15633 //just resize the tile and set it's new position
15634 this.tile.size = this.tileSize.clone();
15635 this.tile.position = ulPx.clone();
15642 * Set the tile size based on the map size.
15644 setTileSize: function() {
15645 var tileWidth = this.extent.getWidth() / this.map.getResolution();
15646 var tileHeight = this.extent.getHeight() / this.map.getResolution();
15647 this.tileSize = new OpenLayers.Size(tileWidth, tileHeight);
15651 * Method: addTileMonitoringHooks
15652 * This function takes a tile as input and adds the appropriate hooks to
15653 * the tile so that the layer can keep track of the loading tiles.
15656 * tile - {<OpenLayers.Tile>}
15658 addTileMonitoringHooks: function(tile) {
15659 tile.onLoadStart = function() {
15660 this.events.triggerEvent("loadstart");
15662 tile.events.register("loadstart", this, tile.onLoadStart);
15664 tile.onLoadEnd = function() {
15665 this.events.triggerEvent("loadend");
15667 tile.events.register("loadend", this, tile.onLoadEnd);
15668 tile.events.register("unload", this, tile.onLoadEnd);
15672 * Method: removeTileMonitoringHooks
15673 * This function takes a tile as input and removes the tile hooks
15674 * that were added in <addTileMonitoringHooks>.
15677 * tile - {<OpenLayers.Tile>}
15679 removeTileMonitoringHooks: function(tile) {
15682 "loadstart": tile.onLoadStart,
15683 "loadend": tile.onLoadEnd,
15684 "unload": tile.onLoadEnd,
15690 * APIMethod: setUrl
15693 * newUrl - {String}
15695 setUrl: function(newUrl) {
15701 * APIMethod: getURL
15702 * The url we return is always the same (the image itself never changes)
15703 * so we can ignore the bounds parameter (it will always be the same,
15707 * bounds - {<OpenLayers.Bounds>}
15709 getURL: function(bounds) {
15713 CLASS_NAME: "OpenLayers.Layer.Image"
15715 /* ======================================================================
15716 OpenLayers/Geometry.js
15717 ====================================================================== */
15719 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
15720 * full list of contributors). Published under the 2-clause BSD license.
15721 * See license.txt in the OpenLayers distribution or repository for the
15722 * full text of the license. */
15725 * @requires OpenLayers/BaseTypes/Class.js
15729 * Class: OpenLayers.Geometry
15730 * A Geometry is a description of a geographic object. Create an instance of
15731 * this class with the <OpenLayers.Geometry> constructor. This is a base class,
15732 * typical geometry types are described by subclasses of this class.
15734 * Note that if you use the <OpenLayers.Geometry.fromWKT> method, you must
15735 * explicitly include the OpenLayers.Format.WKT in your build.
15737 OpenLayers.Geometry = OpenLayers.Class({
15741 * {String} A unique identifier for this geometry.
15747 * {<OpenLayers.Geometry>}This is set when a Geometry is added as component
15748 * of another geometry
15754 * {<OpenLayers.Bounds>} The bounds of this geometry
15759 * Constructor: OpenLayers.Geometry
15760 * Creates a geometry object.
15762 initialize: function() {
15763 this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME+ "_");
15768 * Destroy this geometry.
15770 destroy: function() {
15772 this.bounds = null;
15777 * Create a clone of this geometry. Does not set any non-standard
15778 * properties of the cloned geometry.
15781 * {<OpenLayers.Geometry>} An exact clone of this geometry.
15783 clone: function() {
15784 return new OpenLayers.Geometry();
15788 * Method: setBounds
15789 * Set the bounds for this Geometry.
15792 * bounds - {<OpenLayers.Bounds>}
15794 setBounds: function(bounds) {
15796 this.bounds = bounds.clone();
15801 * Method: clearBounds
15802 * Nullify this components bounds and that of its parent as well.
15804 clearBounds: function() {
15805 this.bounds = null;
15807 this.parent.clearBounds();
15812 * Method: extendBounds
15813 * Extend the existing bounds to include the new bounds.
15814 * If geometry's bounds is not yet set, then set a new Bounds.
15817 * newBounds - {<OpenLayers.Bounds>}
15819 extendBounds: function(newBounds){
15820 var bounds = this.getBounds();
15822 this.setBounds(newBounds);
15824 this.bounds.extend(newBounds);
15829 * APIMethod: getBounds
15830 * Get the bounds for this Geometry. If bounds is not set, it
15831 * is calculated again, this makes queries faster.
15834 * {<OpenLayers.Bounds>}
15836 getBounds: function() {
15837 if (this.bounds == null) {
15838 this.calculateBounds();
15840 return this.bounds;
15844 * APIMethod: calculateBounds
15845 * Recalculate the bounds for the geometry.
15847 calculateBounds: function() {
15849 // This should be overridden by subclasses.
15854 * APIMethod: distanceTo
15855 * Calculate the closest distance between two geometries (on the x-y plane).
15858 * geometry - {<OpenLayers.Geometry>} The target geometry.
15859 * options - {Object} Optional properties for configuring the distance
15862 * Valid options depend on the specific geometry type.
15865 * {Number | Object} The distance between this geometry and the target.
15866 * If details is true, the return will be an object with distance,
15867 * x0, y0, x1, and x2 properties. The x0 and y0 properties represent
15868 * the coordinates of the closest point on this geometry. The x1 and y1
15869 * properties represent the coordinates of the closest point on the
15872 distanceTo: function(geometry, options) {
15876 * APIMethod: getVertices
15877 * Return a list of all points in this geometry.
15880 * nodes - {Boolean} For lines, only return vertices that are
15881 * endpoints. If false, for lines, only vertices that are not
15882 * endpoints will be returned. If not provided, all vertices will
15886 * {Array} A list of all vertices in the geometry.
15888 getVertices: function(nodes) {
15893 * Note - This is only an approximation based on the bounds of the
15897 * lonlat - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an
15898 * object with a 'lon' and 'lat' properties.
15899 * toleranceLon - {float} Optional tolerance in Geometric Coords
15900 * toleranceLat - {float} Optional tolerance in Geographic Coords
15903 * {Boolean} Whether or not the geometry is at the specified location
15905 atPoint: function(lonlat, toleranceLon, toleranceLat) {
15906 var atPoint = false;
15907 var bounds = this.getBounds();
15908 if ((bounds != null) && (lonlat != null)) {
15910 var dX = (toleranceLon != null) ? toleranceLon : 0;
15911 var dY = (toleranceLat != null) ? toleranceLat : 0;
15913 var toleranceBounds =
15914 new OpenLayers.Bounds(this.bounds.left - dX,
15915 this.bounds.bottom - dY,
15916 this.bounds.right + dX,
15917 this.bounds.top + dY);
15919 atPoint = toleranceBounds.containsLonLat(lonlat);
15925 * Method: getLength
15926 * Calculate the length of this geometry. This method is defined in
15930 * {Float} The length of the collection by summing its parts
15932 getLength: function() {
15933 //to be overridden by geometries that actually have a length
15940 * Calculate the area of this geometry. This method is defined in subclasses.
15943 * {Float} The area of the collection by summing its parts
15945 getArea: function() {
15946 //to be overridden by geometries that actually have an area
15952 * APIMethod: getCentroid
15953 * Calculate the centroid of this geometry. This method is defined in subclasses.
15956 * {<OpenLayers.Geometry.Point>} The centroid of the collection
15958 getCentroid: function() {
15964 * Returns a text representation of the geometry. If the WKT format is
15965 * included in a build, this will be the Well-Known Text
15969 * {String} String representation of this geometry.
15971 toString: function() {
15973 if (OpenLayers.Format && OpenLayers.Format.WKT) {
15974 string = OpenLayers.Format.WKT.prototype.write(
15975 new OpenLayers.Feature.Vector(this)
15978 string = Object.prototype.toString.call(this);
15983 CLASS_NAME: "OpenLayers.Geometry"
15987 * Function: OpenLayers.Geometry.fromWKT
15988 * Generate a geometry given a Well-Known Text string. For this method to
15989 * work, you must include the OpenLayers.Format.WKT in your build
15993 * wkt - {String} A string representing the geometry in Well-Known Text.
15996 * {<OpenLayers.Geometry>} A geometry of the appropriate class.
15998 OpenLayers.Geometry.fromWKT = function(wkt) {
16000 if (OpenLayers.Format && OpenLayers.Format.WKT) {
16001 var format = OpenLayers.Geometry.fromWKT.format;
16003 format = new OpenLayers.Format.WKT();
16004 OpenLayers.Geometry.fromWKT.format = format;
16006 var result = format.read(wkt);
16007 if (result instanceof OpenLayers.Feature.Vector) {
16008 geom = result.geometry;
16009 } else if (OpenLayers.Util.isArray(result)) {
16010 var len = result.length;
16011 var components = new Array(len);
16012 for (var i=0; i<len; ++i) {
16013 components[i] = result[i].geometry;
16015 geom = new OpenLayers.Geometry.Collection(components);
16022 * Method: OpenLayers.Geometry.segmentsIntersect
16023 * Determine whether two line segments intersect. Optionally calculates
16024 * and returns the intersection point. This function is optimized for
16025 * cases where seg1.x2 >= seg2.x1 || seg2.x2 >= seg1.x1. In those
16026 * obvious cases where there is no intersection, the function should
16030 * seg1 - {Object} Object representing a segment with properties x1, y1, x2,
16031 * and y2. The start point is represented by x1 and y1. The end point
16032 * is represented by x2 and y2. Start and end are ordered so that x1 < x2.
16033 * seg2 - {Object} Object representing a segment with properties x1, y1, x2,
16034 * and y2. The start point is represented by x1 and y1. The end point
16035 * is represented by x2 and y2. Start and end are ordered so that x1 < x2.
16036 * options - {Object} Optional properties for calculating the intersection.
16039 * point - {Boolean} Return the intersection point. If false, the actual
16040 * intersection point will not be calculated. If true and the segments
16041 * intersect, the intersection point will be returned. If true and
16042 * the segments do not intersect, false will be returned. If true and
16043 * the segments are coincident, true will be returned.
16044 * tolerance - {Number} If a non-null value is provided, if the segments are
16045 * within the tolerance distance, this will be considered an intersection.
16046 * In addition, if the point option is true and the calculated intersection
16047 * is within the tolerance distance of an end point, the endpoint will be
16048 * returned instead of the calculated intersection. Further, if the
16049 * intersection is within the tolerance of endpoints on both segments, or
16050 * if two segment endpoints are within the tolerance distance of eachother
16051 * (but no intersection is otherwise calculated), an endpoint on the
16052 * first segment provided will be returned.
16055 * {Boolean | <OpenLayers.Geometry.Point>} The two segments intersect.
16056 * If the point argument is true, the return will be the intersection
16057 * point or false if none exists. If point is true and the segments
16058 * are coincident, return will be true (and the instersection is equal
16059 * to the shorter segment).
16061 OpenLayers.Geometry.segmentsIntersect = function(seg1, seg2, options) {
16062 var point = options && options.point;
16063 var tolerance = options && options.tolerance;
16064 var intersection = false;
16065 var x11_21 = seg1.x1 - seg2.x1;
16066 var y11_21 = seg1.y1 - seg2.y1;
16067 var x12_11 = seg1.x2 - seg1.x1;
16068 var y12_11 = seg1.y2 - seg1.y1;
16069 var y22_21 = seg2.y2 - seg2.y1;
16070 var x22_21 = seg2.x2 - seg2.x1;
16071 var d = (y22_21 * x12_11) - (x22_21 * y12_11);
16072 var n1 = (x22_21 * y11_21) - (y22_21 * x11_21);
16073 var n2 = (x12_11 * y11_21) - (y12_11 * x11_21);
16076 if(n1 == 0 && n2 == 0) {
16078 intersection = true;
16081 var along1 = n1 / d;
16082 var along2 = n2 / d;
16083 if(along1 >= 0 && along1 <= 1 && along2 >=0 && along2 <= 1) {
16086 intersection = true;
16088 // calculate the intersection point
16089 var x = seg1.x1 + (along1 * x12_11);
16090 var y = seg1.y1 + (along1 * y12_11);
16091 intersection = new OpenLayers.Geometry.Point(x, y);
16099 var segs = [seg1, seg2];
16101 // check segment endpoints for proximity to intersection
16102 // set intersection to first endpoint within the tolerance
16103 outer: for(var i=0; i<2; ++i) {
16105 for(var j=1; j<3; ++j) {
16109 Math.pow(x - intersection.x, 2) +
16110 Math.pow(y - intersection.y, 2)
16112 if(dist < tolerance) {
16113 intersection.x = x;
16114 intersection.y = y;
16122 // no calculated intersection, but segments could be within
16123 // the tolerance of one another
16124 var segs = [seg1, seg2];
16125 var source, target, x, y, p, result;
16126 // check segment endpoints for proximity to intersection
16127 // set intersection to first endpoint within the tolerance
16128 outer: for(var i=0; i<2; ++i) {
16130 target = segs[(i+1)%2];
16131 for(var j=1; j<3; ++j) {
16132 p = {x: source["x"+j], y: source["y"+j]};
16133 result = OpenLayers.Geometry.distanceToSegment(p, target);
16134 if(result.distance < tolerance) {
16136 intersection = new OpenLayers.Geometry.Point(p.x, p.y);
16138 intersection = true;
16146 return intersection;
16150 * Function: OpenLayers.Geometry.distanceToSegment
16153 * point - {Object} An object with x and y properties representing the
16154 * point coordinates.
16155 * segment - {Object} An object with x1, y1, x2, and y2 properties
16156 * representing endpoint coordinates.
16159 * {Object} An object with distance, along, x, and y properties. The distance
16160 * will be the shortest distance between the input point and segment.
16161 * The x and y properties represent the coordinates along the segment
16162 * where the shortest distance meets the segment. The along attribute
16163 * describes how far between the two segment points the given point is.
16165 OpenLayers.Geometry.distanceToSegment = function(point, segment) {
16166 var result = OpenLayers.Geometry.distanceSquaredToSegment(point, segment);
16167 result.distance = Math.sqrt(result.distance);
16172 * Function: OpenLayers.Geometry.distanceSquaredToSegment
16174 * Usually the distanceToSegment function should be used. This variant however
16175 * can be used for comparisons where the exact distance is not important.
16178 * point - {Object} An object with x and y properties representing the
16179 * point coordinates.
16180 * segment - {Object} An object with x1, y1, x2, and y2 properties
16181 * representing endpoint coordinates.
16184 * {Object} An object with squared distance, along, x, and y properties.
16185 * The distance will be the shortest distance between the input point and
16186 * segment. The x and y properties represent the coordinates along the
16187 * segment where the shortest distance meets the segment. The along
16188 * attribute describes how far between the two segment points the given
16191 OpenLayers.Geometry.distanceSquaredToSegment = function(point, segment) {
16194 var x1 = segment.x1;
16195 var y1 = segment.y1;
16196 var x2 = segment.x2;
16197 var y2 = segment.y2;
16200 var along = ((dx * (x0 - x1)) + (dy * (y0 - y1))) /
16201 (Math.pow(dx, 2) + Math.pow(dy, 2));
16206 } else if(along >= 1.0) {
16210 x = x1 + along * dx;
16211 y = y1 + along * dy;
16214 distance: Math.pow(x - x0, 2) + Math.pow(y - y0, 2),
16219 /* ======================================================================
16220 OpenLayers/Geometry/Collection.js
16221 ====================================================================== */
16223 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
16224 * full list of contributors). Published under the 2-clause BSD license.
16225 * See license.txt in the OpenLayers distribution or repository for the
16226 * full text of the license. */
16229 * @requires OpenLayers/Geometry.js
16233 * Class: OpenLayers.Geometry.Collection
16234 * A Collection is exactly what it sounds like: A collection of different
16235 * Geometries. These are stored in the local parameter <components> (which
16236 * can be passed as a parameter to the constructor).
16238 * As new geometries are added to the collection, they are NOT cloned.
16239 * When removing geometries, they need to be specified by reference (ie you
16240 * have to pass in the *exact* geometry to be removed).
16242 * The <getArea> and <getLength> functions here merely iterate through
16243 * the components, summing their respective areas and lengths.
16245 * Create a new instance with the <OpenLayers.Geometry.Collection> constructor.
16248 * - <OpenLayers.Geometry>
16250 OpenLayers.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, {
16253 * APIProperty: components
16254 * {Array(<OpenLayers.Geometry>)} The component parts of this geometry
16259 * Property: componentTypes
16260 * {Array(String)} An array of class names representing the types of
16261 * components that the collection can include. A null value means the
16262 * component types are not restricted.
16264 componentTypes: null,
16267 * Constructor: OpenLayers.Geometry.Collection
16268 * Creates a Geometry Collection -- a list of geoms.
16271 * components - {Array(<OpenLayers.Geometry>)} Optional array of geometries
16274 initialize: function (components) {
16275 OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
16276 this.components = [];
16277 if (components != null) {
16278 this.addComponents(components);
16283 * APIMethod: destroy
16284 * Destroy this geometry.
16286 destroy: function () {
16287 this.components.length = 0;
16288 this.components = null;
16289 OpenLayers.Geometry.prototype.destroy.apply(this, arguments);
16294 * Clone this geometry.
16297 * {<OpenLayers.Geometry.Collection>} An exact clone of this collection
16299 clone: function() {
16300 var geometry = eval("new " + this.CLASS_NAME + "()");
16301 for(var i=0, len=this.components.length; i<len; i++) {
16302 geometry.addComponent(this.components[i].clone());
16305 // catch any randomly tagged-on properties
16306 OpenLayers.Util.applyDefaults(geometry, this);
16312 * Method: getComponentsString
16313 * Get a string representing the components for this collection
16316 * {String} A string representation of the components of this geometry
16318 getComponentsString: function(){
16320 for(var i=0, len=this.components.length; i<len; i++) {
16321 strings.push(this.components[i].toShortString());
16323 return strings.join(",");
16327 * APIMethod: calculateBounds
16328 * Recalculate the bounds by iterating through the components and
16329 * calling calling extendBounds() on each item.
16331 calculateBounds: function() {
16332 this.bounds = null;
16333 var bounds = new OpenLayers.Bounds();
16334 var components = this.components;
16336 for (var i=0, len=components.length; i<len; i++) {
16337 bounds.extend(components[i].getBounds());
16340 // to preserve old behavior, we only set bounds if non-null
16341 // in the future, we could add bounds.isEmpty()
16342 if (bounds.left != null && bounds.bottom != null &&
16343 bounds.right != null && bounds.top != null) {
16344 this.setBounds(bounds);
16349 * APIMethod: addComponents
16350 * Add components to this geometry.
16353 * components - {Array(<OpenLayers.Geometry>)} An array of geometries to add
16355 addComponents: function(components){
16356 if(!(OpenLayers.Util.isArray(components))) {
16357 components = [components];
16359 for(var i=0, len=components.length; i<len; i++) {
16360 this.addComponent(components[i]);
16365 * Method: addComponent
16366 * Add a new component (geometry) to the collection. If this.componentTypes
16367 * is set, then the component class name must be in the componentTypes array.
16369 * The bounds cache is reset.
16372 * component - {<OpenLayers.Geometry>} A geometry to add
16373 * index - {int} Optional index into the array to insert the component
16376 * {Boolean} The component geometry was successfully added
16378 addComponent: function(component, index) {
16381 if(this.componentTypes == null ||
16382 (OpenLayers.Util.indexOf(this.componentTypes,
16383 component.CLASS_NAME) > -1)) {
16385 if(index != null && (index < this.components.length)) {
16386 var components1 = this.components.slice(0, index);
16387 var components2 = this.components.slice(index,
16388 this.components.length);
16389 components1.push(component);
16390 this.components = components1.concat(components2);
16392 this.components.push(component);
16394 component.parent = this;
16395 this.clearBounds();
16403 * APIMethod: removeComponents
16404 * Remove components from this geometry.
16407 * components - {Array(<OpenLayers.Geometry>)} The components to be removed
16410 * {Boolean} A component was removed.
16412 removeComponents: function(components) {
16413 var removed = false;
16415 if(!(OpenLayers.Util.isArray(components))) {
16416 components = [components];
16418 for(var i=components.length-1; i>=0; --i) {
16419 removed = this.removeComponent(components[i]) || removed;
16425 * Method: removeComponent
16426 * Remove a component from this geometry.
16429 * component - {<OpenLayers.Geometry>}
16432 * {Boolean} The component was removed.
16434 removeComponent: function(component) {
16436 OpenLayers.Util.removeItem(this.components, component);
16438 // clearBounds() so that it gets recalculated on the next call
16439 // to this.getBounds();
16440 this.clearBounds();
16445 * APIMethod: getLength
16446 * Calculate the length of this geometry
16449 * {Float} The length of the geometry
16451 getLength: function() {
16453 for (var i=0, len=this.components.length; i<len; i++) {
16454 length += this.components[i].getLength();
16460 * APIMethod: getArea
16461 * Calculate the area of this geometry. Note how this function is overridden
16462 * in <OpenLayers.Geometry.Polygon>.
16465 * {Float} The area of the collection by summing its parts
16467 getArea: function() {
16469 for (var i=0, len=this.components.length; i<len; i++) {
16470 area += this.components[i].getArea();
16476 * APIMethod: getGeodesicArea
16477 * Calculate the approximate area of the polygon were it projected onto
16481 * projection - {<OpenLayers.Projection>} The spatial reference system
16482 * for the geometry coordinates. If not provided, Geographic/WGS84 is
16486 * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
16487 * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
16488 * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
16491 * {float} The approximate geodesic area of the geometry in square meters.
16493 getGeodesicArea: function(projection) {
16495 for(var i=0, len=this.components.length; i<len; i++) {
16496 area += this.components[i].getGeodesicArea(projection);
16502 * APIMethod: getCentroid
16504 * Compute the centroid for this geometry collection.
16507 * weighted - {Boolean} Perform the getCentroid computation recursively,
16508 * returning an area weighted average of all geometries in this collection.
16511 * {<OpenLayers.Geometry.Point>} The centroid of the collection
16513 getCentroid: function(weighted) {
16515 return this.components.length && this.components[0].getCentroid();
16517 var len = this.components.length;
16523 var centroids = [];
16525 var minArea = Number.MAX_VALUE;
16527 for (var i=0; i<len; ++i) {
16528 component = this.components[i];
16529 var area = component.getArea();
16530 var centroid = component.getCentroid(true);
16531 if (isNaN(area) || isNaN(centroid.x) || isNaN(centroid.y)) {
16536 minArea = (area < minArea && area > 0) ? area : minArea;
16537 centroids.push(centroid);
16539 len = areas.length;
16540 if (areaSum === 0) {
16541 // all the components in this collection have 0 area
16542 // probably a collection of points -- weight all the points the same
16543 for (var i=0; i<len; ++i) {
16546 areaSum = areas.length;
16548 // normalize all the areas where the smallest area will get
16550 for (var i=0; i<len; ++i) {
16551 areas[i] /= minArea;
16553 areaSum /= minArea;
16556 var xSum = 0, ySum = 0, centroid, area;
16557 for (var i=0; i<len; ++i) {
16558 centroid = centroids[i];
16560 xSum += centroid.x * area;
16561 ySum += centroid.y * area;
16564 return new OpenLayers.Geometry.Point(xSum/areaSum, ySum/areaSum);
16568 * APIMethod: getGeodesicLength
16569 * Calculate the approximate length of the geometry were it projected onto
16572 * projection - {<OpenLayers.Projection>} The spatial reference system
16573 * for the geometry coordinates. If not provided, Geographic/WGS84 is
16577 * {Float} The appoximate geodesic length of the geometry in meters.
16579 getGeodesicLength: function(projection) {
16581 for(var i=0, len=this.components.length; i<len; i++) {
16582 length += this.components[i].getGeodesicLength(projection);
16589 * Moves a geometry by the given displacement along positive x and y axes.
16590 * This modifies the position of the geometry and clears the cached
16594 * x - {Float} Distance to move geometry in positive x direction.
16595 * y - {Float} Distance to move geometry in positive y direction.
16597 move: function(x, y) {
16598 for(var i=0, len=this.components.length; i<len; i++) {
16599 this.components[i].move(x, y);
16604 * APIMethod: rotate
16605 * Rotate a geometry around some origin
16608 * angle - {Float} Rotation angle in degrees (measured counterclockwise
16609 * from the positive x-axis)
16610 * origin - {<OpenLayers.Geometry.Point>} Center point for the rotation
16612 rotate: function(angle, origin) {
16613 for(var i=0, len=this.components.length; i<len; ++i) {
16614 this.components[i].rotate(angle, origin);
16619 * APIMethod: resize
16620 * Resize a geometry relative to some origin. Use this method to apply
16621 * a uniform scaling to a geometry.
16624 * scale - {Float} Factor by which to scale the geometry. A scale of 2
16625 * doubles the size of the geometry in each dimension
16626 * (lines, for example, will be twice as long, and polygons
16627 * will have four times the area).
16628 * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing
16629 * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1.
16632 * {<OpenLayers.Geometry>} - The current geometry.
16634 resize: function(scale, origin, ratio) {
16635 for(var i=0; i<this.components.length; ++i) {
16636 this.components[i].resize(scale, origin, ratio);
16642 * APIMethod: distanceTo
16643 * Calculate the closest distance between two geometries (on the x-y plane).
16646 * geometry - {<OpenLayers.Geometry>} The target geometry.
16647 * options - {Object} Optional properties for configuring the distance
16651 * details - {Boolean} Return details from the distance calculation.
16652 * Default is false.
16653 * edge - {Boolean} Calculate the distance from this geometry to the
16654 * nearest edge of the target geometry. Default is true. If true,
16655 * calling distanceTo from a geometry that is wholly contained within
16656 * the target will result in a non-zero distance. If false, whenever
16657 * geometries intersect, calling distanceTo will return 0. If false,
16658 * details cannot be returned.
16661 * {Number | Object} The distance between this geometry and the target.
16662 * If details is true, the return will be an object with distance,
16663 * x0, y0, x1, and y1 properties. The x0 and y0 properties represent
16664 * the coordinates of the closest point on this geometry. The x1 and y1
16665 * properties represent the coordinates of the closest point on the
16668 distanceTo: function(geometry, options) {
16669 var edge = !(options && options.edge === false);
16670 var details = edge && options && options.details;
16671 var result, best, distance;
16672 var min = Number.POSITIVE_INFINITY;
16673 for(var i=0, len=this.components.length; i<len; ++i) {
16674 result = this.components[i].distanceTo(geometry, options);
16675 distance = details ? result.distance : result;
16676 if(distance < min) {
16688 * APIMethod: equals
16689 * Determine whether another geometry is equivalent to this one. Geometries
16690 * are considered equivalent if all components have the same coordinates.
16693 * geometry - {<OpenLayers.Geometry>} The geometry to test.
16696 * {Boolean} The supplied geometry is equivalent to this geometry.
16698 equals: function(geometry) {
16699 var equivalent = true;
16700 if(!geometry || !geometry.CLASS_NAME ||
16701 (this.CLASS_NAME != geometry.CLASS_NAME)) {
16702 equivalent = false;
16703 } else if(!(OpenLayers.Util.isArray(geometry.components)) ||
16704 (geometry.components.length != this.components.length)) {
16705 equivalent = false;
16707 for(var i=0, len=this.components.length; i<len; ++i) {
16708 if(!this.components[i].equals(geometry.components[i])) {
16709 equivalent = false;
16718 * APIMethod: transform
16719 * Reproject the components geometry from source to dest.
16722 * source - {<OpenLayers.Projection>}
16723 * dest - {<OpenLayers.Projection>}
16726 * {<OpenLayers.Geometry>}
16728 transform: function(source, dest) {
16729 if (source && dest) {
16730 for (var i=0, len=this.components.length; i<len; i++) {
16731 var component = this.components[i];
16732 component.transform(source, dest);
16734 this.bounds = null;
16740 * APIMethod: intersects
16741 * Determine if the input geometry intersects this one.
16744 * geometry - {<OpenLayers.Geometry>} Any type of geometry.
16747 * {Boolean} The input geometry intersects this one.
16749 intersects: function(geometry) {
16750 var intersect = false;
16751 for(var i=0, len=this.components.length; i<len; ++ i) {
16752 intersect = geometry.intersects(this.components[i]);
16761 * APIMethod: getVertices
16762 * Return a list of all points in this geometry.
16765 * nodes - {Boolean} For lines, only return vertices that are
16766 * endpoints. If false, for lines, only vertices that are not
16767 * endpoints will be returned. If not provided, all vertices will
16771 * {Array} A list of all vertices in the geometry.
16773 getVertices: function(nodes) {
16775 for(var i=0, len=this.components.length; i<len; ++i) {
16776 Array.prototype.push.apply(
16777 vertices, this.components[i].getVertices(nodes)
16784 CLASS_NAME: "OpenLayers.Geometry.Collection"
16786 /* ======================================================================
16787 OpenLayers/Geometry/Point.js
16788 ====================================================================== */
16790 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
16791 * full list of contributors). Published under the 2-clause BSD license.
16792 * See license.txt in the OpenLayers distribution or repository for the
16793 * full text of the license. */
16796 * @requires OpenLayers/Geometry.js
16800 * Class: OpenLayers.Geometry.Point
16801 * Point geometry class.
16804 * - <OpenLayers.Geometry>
16806 OpenLayers.Geometry.Point = OpenLayers.Class(OpenLayers.Geometry, {
16821 * Constructor: OpenLayers.Geometry.Point
16822 * Construct a point geometry.
16829 initialize: function(x, y) {
16830 OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
16832 this.x = parseFloat(x);
16833 this.y = parseFloat(y);
16840 * {<OpenLayers.Geometry.Point>} An exact clone of this OpenLayers.Geometry.Point
16842 clone: function(obj) {
16844 obj = new OpenLayers.Geometry.Point(this.x, this.y);
16847 // catch any randomly tagged-on properties
16848 OpenLayers.Util.applyDefaults(obj, this);
16854 * Method: calculateBounds
16855 * Create a new Bounds based on the lon/lat
16857 calculateBounds: function () {
16858 this.bounds = new OpenLayers.Bounds(this.x, this.y,
16863 * APIMethod: distanceTo
16864 * Calculate the closest distance between two geometries (on the x-y plane).
16867 * geometry - {<OpenLayers.Geometry>} The target geometry.
16868 * options - {Object} Optional properties for configuring the distance
16872 * details - {Boolean} Return details from the distance calculation.
16873 * Default is false.
16874 * edge - {Boolean} Calculate the distance from this geometry to the
16875 * nearest edge of the target geometry. Default is true. If true,
16876 * calling distanceTo from a geometry that is wholly contained within
16877 * the target will result in a non-zero distance. If false, whenever
16878 * geometries intersect, calling distanceTo will return 0. If false,
16879 * details cannot be returned.
16882 * {Number | Object} The distance between this geometry and the target.
16883 * If details is true, the return will be an object with distance,
16884 * x0, y0, x1, and x2 properties. The x0 and y0 properties represent
16885 * the coordinates of the closest point on this geometry. The x1 and y1
16886 * properties represent the coordinates of the closest point on the
16889 distanceTo: function(geometry, options) {
16890 var edge = !(options && options.edge === false);
16891 var details = edge && options && options.details;
16892 var distance, x0, y0, x1, y1, result;
16893 if(geometry instanceof OpenLayers.Geometry.Point) {
16898 distance = Math.sqrt(Math.pow(x0 - x1, 2) + Math.pow(y0 - y1, 2));
16899 result = !details ?
16900 distance : {x0: x0, y0: y0, x1: x1, y1: y1, distance: distance};
16902 result = geometry.distanceTo(this, options);
16904 // switch coord order since this geom is target
16906 x0: result.x1, y0: result.y1,
16907 x1: result.x0, y1: result.y0,
16908 distance: result.distance
16916 * APIMethod: equals
16917 * Determine whether another geometry is equivalent to this one. Geometries
16918 * are considered equivalent if all components have the same coordinates.
16921 * geom - {<OpenLayers.Geometry.Point>} The geometry to test.
16924 * {Boolean} The supplied geometry is equivalent to this geometry.
16926 equals: function(geom) {
16927 var equals = false;
16928 if (geom != null) {
16929 equals = ((this.x == geom.x && this.y == geom.y) ||
16930 (isNaN(this.x) && isNaN(this.y) && isNaN(geom.x) && isNaN(geom.y)));
16936 * Method: toShortString
16939 * {String} Shortened String representation of Point object.
16940 * (ex. <i>"5, 42"</i>)
16942 toShortString: function() {
16943 return (this.x + ", " + this.y);
16948 * Moves a geometry by the given displacement along positive x and y axes.
16949 * This modifies the position of the geometry and clears the cached
16953 * x - {Float} Distance to move geometry in positive x direction.
16954 * y - {Float} Distance to move geometry in positive y direction.
16956 move: function(x, y) {
16957 this.x = this.x + x;
16958 this.y = this.y + y;
16959 this.clearBounds();
16963 * APIMethod: rotate
16964 * Rotate a point around another.
16967 * angle - {Float} Rotation angle in degrees (measured counterclockwise
16968 * from the positive x-axis)
16969 * origin - {<OpenLayers.Geometry.Point>} Center point for the rotation
16971 rotate: function(angle, origin) {
16972 angle *= Math.PI / 180;
16973 var radius = this.distanceTo(origin);
16974 var theta = angle + Math.atan2(this.y - origin.y, this.x - origin.x);
16975 this.x = origin.x + (radius * Math.cos(theta));
16976 this.y = origin.y + (radius * Math.sin(theta));
16977 this.clearBounds();
16981 * APIMethod: getCentroid
16984 * {<OpenLayers.Geometry.Point>} The centroid of the collection
16986 getCentroid: function() {
16987 return new OpenLayers.Geometry.Point(this.x, this.y);
16991 * APIMethod: resize
16992 * Resize a point relative to some origin. For points, this has the effect
16993 * of scaling a vector (from the origin to the point). This method is
16994 * more useful on geometry collection subclasses.
16997 * scale - {Float} Ratio of the new distance from the origin to the old
16998 * distance from the origin. A scale of 2 doubles the
16999 * distance between the point and origin.
17000 * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing
17001 * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1.
17004 * {<OpenLayers.Geometry>} - The current geometry.
17006 resize: function(scale, origin, ratio) {
17007 ratio = (ratio == undefined) ? 1 : ratio;
17008 this.x = origin.x + (scale * ratio * (this.x - origin.x));
17009 this.y = origin.y + (scale * (this.y - origin.y));
17010 this.clearBounds();
17015 * APIMethod: intersects
17016 * Determine if the input geometry intersects this one.
17019 * geometry - {<OpenLayers.Geometry>} Any type of geometry.
17022 * {Boolean} The input geometry intersects this one.
17024 intersects: function(geometry) {
17025 var intersect = false;
17026 if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
17027 intersect = this.equals(geometry);
17029 intersect = geometry.intersects(this);
17035 * APIMethod: transform
17036 * Translate the x,y properties of the point from source to dest.
17039 * source - {<OpenLayers.Projection>}
17040 * dest - {<OpenLayers.Projection>}
17043 * {<OpenLayers.Geometry>}
17045 transform: function(source, dest) {
17046 if ((source && dest)) {
17047 OpenLayers.Projection.transform(
17048 this, source, dest);
17049 this.bounds = null;
17055 * APIMethod: getVertices
17056 * Return a list of all points in this geometry.
17059 * nodes - {Boolean} For lines, only return vertices that are
17060 * endpoints. If false, for lines, only vertices that are not
17061 * endpoints will be returned. If not provided, all vertices will
17065 * {Array} A list of all vertices in the geometry.
17067 getVertices: function(nodes) {
17071 CLASS_NAME: "OpenLayers.Geometry.Point"
17073 /* ======================================================================
17074 OpenLayers/Geometry/MultiPoint.js
17075 ====================================================================== */
17077 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
17078 * full list of contributors). Published under the 2-clause BSD license.
17079 * See license.txt in the OpenLayers distribution or repository for the
17080 * full text of the license. */
17083 * @requires OpenLayers/Geometry/Collection.js
17084 * @requires OpenLayers/Geometry/Point.js
17088 * Class: OpenLayers.Geometry.MultiPoint
17089 * MultiPoint is a collection of Points. Create a new instance with the
17090 * <OpenLayers.Geometry.MultiPoint> constructor.
17093 * - <OpenLayers.Geometry.Collection>
17094 * - <OpenLayers.Geometry>
17096 OpenLayers.Geometry.MultiPoint = OpenLayers.Class(
17097 OpenLayers.Geometry.Collection, {
17100 * Property: componentTypes
17101 * {Array(String)} An array of class names representing the types of
17102 * components that the collection can include. A null value means the
17103 * component types are not restricted.
17105 componentTypes: ["OpenLayers.Geometry.Point"],
17108 * Constructor: OpenLayers.Geometry.MultiPoint
17109 * Create a new MultiPoint Geometry
17112 * components - {Array(<OpenLayers.Geometry.Point>)}
17115 * {<OpenLayers.Geometry.MultiPoint>}
17119 * APIMethod: addPoint
17120 * Wrapper for <OpenLayers.Geometry.Collection.addComponent>
17123 * point - {<OpenLayers.Geometry.Point>} Point to be added
17124 * index - {Integer} Optional index
17126 addPoint: function(point, index) {
17127 this.addComponent(point, index);
17131 * APIMethod: removePoint
17132 * Wrapper for <OpenLayers.Geometry.Collection.removeComponent>
17135 * point - {<OpenLayers.Geometry.Point>} Point to be removed
17137 removePoint: function(point){
17138 this.removeComponent(point);
17141 CLASS_NAME: "OpenLayers.Geometry.MultiPoint"
17143 /* ======================================================================
17144 OpenLayers/Geometry/Curve.js
17145 ====================================================================== */
17147 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
17148 * full list of contributors). Published under the 2-clause BSD license.
17149 * See license.txt in the OpenLayers distribution or repository for the
17150 * full text of the license. */
17153 * @requires OpenLayers/Geometry/MultiPoint.js
17157 * Class: OpenLayers.Geometry.Curve
17158 * A Curve is a MultiPoint, whose points are assumed to be connected. To
17159 * this end, we provide a "getLength()" function, which iterates through
17160 * the points, summing the distances between them.
17163 * - <OpenLayers.Geometry.MultiPoint>
17165 OpenLayers.Geometry.Curve = OpenLayers.Class(OpenLayers.Geometry.MultiPoint, {
17168 * Property: componentTypes
17169 * {Array(String)} An array of class names representing the types of
17170 * components that the collection can include. A null
17171 * value means the component types are not restricted.
17173 componentTypes: ["OpenLayers.Geometry.Point"],
17176 * Constructor: OpenLayers.Geometry.Curve
17179 * point - {Array(<OpenLayers.Geometry.Point>)}
17183 * APIMethod: getLength
17186 * {Float} The length of the curve
17188 getLength: function() {
17190 if ( this.components && (this.components.length > 1)) {
17191 for(var i=1, len=this.components.length; i<len; i++) {
17192 length += this.components[i-1].distanceTo(this.components[i]);
17199 * APIMethod: getGeodesicLength
17200 * Calculate the approximate length of the geometry were it projected onto
17203 * projection - {<OpenLayers.Projection>} The spatial reference system
17204 * for the geometry coordinates. If not provided, Geographic/WGS84 is
17208 * {Float} The appoximate geodesic length of the geometry in meters.
17210 getGeodesicLength: function(projection) {
17211 var geom = this; // so we can work with a clone if needed
17213 var gg = new OpenLayers.Projection("EPSG:4326");
17214 if(!gg.equals(projection)) {
17215 geom = this.clone().transform(projection, gg);
17219 if(geom.components && (geom.components.length > 1)) {
17221 for(var i=1, len=geom.components.length; i<len; i++) {
17222 p1 = geom.components[i-1];
17223 p2 = geom.components[i];
17224 // this returns km and requires lon/lat properties
17225 length += OpenLayers.Util.distVincenty(
17226 {lon: p1.x, lat: p1.y}, {lon: p2.x, lat: p2.y}
17231 return length * 1000;
17234 CLASS_NAME: "OpenLayers.Geometry.Curve"
17236 /* ======================================================================
17237 OpenLayers/Geometry/LineString.js
17238 ====================================================================== */
17240 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
17241 * full list of contributors). Published under the 2-clause BSD license.
17242 * See license.txt in the OpenLayers distribution or repository for the
17243 * full text of the license. */
17246 * @requires OpenLayers/Geometry/Curve.js
17250 * Class: OpenLayers.Geometry.LineString
17251 * A LineString is a Curve which, once two points have been added to it, can
17252 * never be less than two points long.
17255 * - <OpenLayers.Geometry.Curve>
17257 OpenLayers.Geometry.LineString = OpenLayers.Class(OpenLayers.Geometry.Curve, {
17260 * Constructor: OpenLayers.Geometry.LineString
17261 * Create a new LineString geometry
17264 * points - {Array(<OpenLayers.Geometry.Point>)} An array of points used to
17265 * generate the linestring
17270 * APIMethod: removeComponent
17271 * Only allows removal of a point if there are three or more points in
17272 * the linestring. (otherwise the result would be just a single point)
17275 * point - {<OpenLayers.Geometry.Point>} The point to be removed
17278 * {Boolean} The component was removed.
17280 removeComponent: function(point) {
17281 var removed = this.components && (this.components.length > 2);
17283 OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this,
17290 * APIMethod: intersects
17291 * Test for instersection between two geometries. This is a cheapo
17292 * implementation of the Bently-Ottmann algorigithm. It doesn't
17293 * really keep track of a sweep line data structure. It is closer
17294 * to the brute force method, except that segments are sorted and
17295 * potential intersections are only calculated when bounding boxes
17299 * geometry - {<OpenLayers.Geometry>}
17302 * {Boolean} The input geometry intersects this geometry.
17304 intersects: function(geometry) {
17305 var intersect = false;
17306 var type = geometry.CLASS_NAME;
17307 if(type == "OpenLayers.Geometry.LineString" ||
17308 type == "OpenLayers.Geometry.LinearRing" ||
17309 type == "OpenLayers.Geometry.Point") {
17310 var segs1 = this.getSortedSegments();
17312 if(type == "OpenLayers.Geometry.Point") {
17314 x1: geometry.x, y1: geometry.y,
17315 x2: geometry.x, y2: geometry.y
17318 segs2 = geometry.getSortedSegments();
17320 var seg1, seg1x1, seg1x2, seg1y1, seg1y2,
17321 seg2, seg2y1, seg2y2;
17323 outer: for(var i=0, len=segs1.length; i<len; ++i) {
17329 inner: for(var j=0, jlen=segs2.length; j<jlen; ++j) {
17331 if(seg2.x1 > seg1x2) {
17332 // seg1 still left of seg2
17335 if(seg2.x2 < seg1x1) {
17336 // seg2 still left of seg1
17341 if(Math.min(seg2y1, seg2y2) > Math.max(seg1y1, seg1y2)) {
17345 if(Math.max(seg2y1, seg2y2) < Math.min(seg1y1, seg1y2)) {
17349 if(OpenLayers.Geometry.segmentsIntersect(seg1, seg2)) {
17356 intersect = geometry.intersects(this);
17362 * Method: getSortedSegments
17365 * {Array} An array of segment objects. Segment objects have properties
17366 * x1, y1, x2, and y2. The start point is represented by x1 and y1.
17367 * The end point is represented by x2 and y2. Start and end are
17368 * ordered so that x1 < x2.
17370 getSortedSegments: function() {
17371 var numSeg = this.components.length - 1;
17372 var segments = new Array(numSeg), point1, point2;
17373 for(var i=0; i<numSeg; ++i) {
17374 point1 = this.components[i];
17375 point2 = this.components[i + 1];
17376 if(point1.x < point2.x) {
17392 // more efficient to define this somewhere static
17393 function byX1(seg1, seg2) {
17394 return seg1.x1 - seg2.x1;
17396 return segments.sort(byX1);
17400 * Method: splitWithSegment
17401 * Split this geometry with the given segment.
17404 * seg - {Object} An object with x1, y1, x2, and y2 properties referencing
17405 * segment endpoint coordinates.
17406 * options - {Object} Properties of this object will be used to determine
17407 * how the split is conducted.
17410 * edge - {Boolean} Allow splitting when only edges intersect. Default is
17411 * true. If false, a vertex on the source segment must be within the
17412 * tolerance distance of the intersection to be considered a split.
17413 * tolerance - {Number} If a non-null value is provided, intersections
17414 * within the tolerance distance of one of the source segment's
17415 * endpoints will be assumed to occur at the endpoint.
17418 * {Object} An object with *lines* and *points* properties. If the given
17419 * segment intersects this linestring, the lines array will reference
17420 * geometries that result from the split. The points array will contain
17421 * all intersection points. Intersection points are sorted along the
17422 * segment (in order from x1,y1 to x2,y2).
17424 splitWithSegment: function(seg, options) {
17425 var edge = !(options && options.edge === false);
17426 var tolerance = options && options.tolerance;
17428 var verts = this.getVertices();
17430 var intersections = [];
17432 var vert1, vert2, point;
17433 var node, vertex, target;
17434 var interOptions = {point: true, tolerance: tolerance};
17436 for(var i=0, stop=verts.length-2; i<=stop; ++i) {
17438 points.push(vert1.clone());
17439 vert2 = verts[i+1];
17440 target = {x1: vert1.x, y1: vert1.y, x2: vert2.x, y2: vert2.y};
17441 point = OpenLayers.Geometry.segmentsIntersect(
17442 seg, target, interOptions
17444 if(point instanceof OpenLayers.Geometry.Point) {
17445 if((point.x === seg.x1 && point.y === seg.y1) ||
17446 (point.x === seg.x2 && point.y === seg.y2) ||
17447 point.equals(vert1) || point.equals(vert2)) {
17452 if(vertex || edge) {
17453 // push intersections different than the previous
17454 if(!point.equals(intersections[intersections.length-1])) {
17455 intersections.push(point.clone());
17458 if(point.equals(vert1)) {
17462 if(point.equals(vert2)) {
17466 if(!point.equals(vert1)) {
17467 points.push(point);
17469 lines.push(new OpenLayers.Geometry.LineString(points));
17470 points = [point.clone()];
17475 points.push(vert2.clone());
17476 lines.push(new OpenLayers.Geometry.LineString(points));
17478 if(intersections.length > 0) {
17479 // sort intersections along segment
17480 var xDir = seg.x1 < seg.x2 ? 1 : -1;
17481 var yDir = seg.y1 < seg.y2 ? 1 : -1;
17484 points: intersections.sort(function(p1, p2) {
17485 return (xDir * p1.x - xDir * p2.x) || (yDir * p1.y - yDir * p2.y);
17494 * Use this geometry (the source) to attempt to split a target geometry.
17497 * target - {<OpenLayers.Geometry>} The target geometry.
17498 * options - {Object} Properties of this object will be used to determine
17499 * how the split is conducted.
17502 * mutual - {Boolean} Split the source geometry in addition to the target
17503 * geometry. Default is false.
17504 * edge - {Boolean} Allow splitting when only edges intersect. Default is
17505 * true. If false, a vertex on the source must be within the tolerance
17506 * distance of the intersection to be considered a split.
17507 * tolerance - {Number} If a non-null value is provided, intersections
17508 * within the tolerance distance of an existing vertex on the source
17509 * will be assumed to occur at the vertex.
17512 * {Array} A list of geometries (of this same type as the target) that
17513 * result from splitting the target with the source geometry. The
17514 * source and target geometry will remain unmodified. If no split
17515 * results, null will be returned. If mutual is true and a split
17516 * results, return will be an array of two arrays - the first will be
17517 * all geometries that result from splitting the source geometry and
17518 * the second will be all geometries that result from splitting the
17521 split: function(target, options) {
17522 var results = null;
17523 var mutual = options && options.mutual;
17524 var sourceSplit, targetSplit, sourceParts, targetParts;
17525 if(target instanceof OpenLayers.Geometry.LineString) {
17526 var verts = this.getVertices();
17527 var vert1, vert2, seg, splits, lines, point;
17530 for(var i=0, stop=verts.length-2; i<=stop; ++i) {
17532 vert2 = verts[i+1];
17534 x1: vert1.x, y1: vert1.y,
17535 x2: vert2.x, y2: vert2.y
17537 targetParts = targetParts || [target];
17539 points.push(vert1.clone());
17541 for(var j=0; j<targetParts.length; ++j) {
17542 splits = targetParts[j].splitWithSegment(seg, options);
17544 // splice in new features
17545 lines = splits.lines;
17546 if(lines.length > 0) {
17547 lines.unshift(j, 1);
17548 Array.prototype.splice.apply(targetParts, lines);
17549 j += lines.length - 2;
17552 for(var k=0, len=splits.points.length; k<len; ++k) {
17553 point = splits.points[k];
17554 if(!point.equals(vert1)) {
17555 points.push(point);
17556 sourceParts.push(new OpenLayers.Geometry.LineString(points));
17557 if(point.equals(vert2)) {
17560 points = [point.clone()];
17568 if(mutual && sourceParts.length > 0 && points.length > 0) {
17569 points.push(vert2.clone());
17570 sourceParts.push(new OpenLayers.Geometry.LineString(points));
17573 results = target.splitWith(this, options);
17575 if(targetParts && targetParts.length > 1) {
17576 targetSplit = true;
17580 if(sourceParts && sourceParts.length > 1) {
17581 sourceSplit = true;
17585 if(targetSplit || sourceSplit) {
17587 results = [sourceParts, targetParts];
17589 results = targetParts;
17596 * Method: splitWith
17597 * Split this geometry (the target) with the given geometry (the source).
17600 * geometry - {<OpenLayers.Geometry>} A geometry used to split this
17601 * geometry (the source).
17602 * options - {Object} Properties of this object will be used to determine
17603 * how the split is conducted.
17606 * mutual - {Boolean} Split the source geometry in addition to the target
17607 * geometry. Default is false.
17608 * edge - {Boolean} Allow splitting when only edges intersect. Default is
17609 * true. If false, a vertex on the source must be within the tolerance
17610 * distance of the intersection to be considered a split.
17611 * tolerance - {Number} If a non-null value is provided, intersections
17612 * within the tolerance distance of an existing vertex on the source
17613 * will be assumed to occur at the vertex.
17616 * {Array} A list of geometries (of this same type as the target) that
17617 * result from splitting the target with the source geometry. The
17618 * source and target geometry will remain unmodified. If no split
17619 * results, null will be returned. If mutual is true and a split
17620 * results, return will be an array of two arrays - the first will be
17621 * all geometries that result from splitting the source geometry and
17622 * the second will be all geometries that result from splitting the
17625 splitWith: function(geometry, options) {
17626 return geometry.split(this, options);
17631 * APIMethod: getVertices
17632 * Return a list of all points in this geometry.
17635 * nodes - {Boolean} For lines, only return vertices that are
17636 * endpoints. If false, for lines, only vertices that are not
17637 * endpoints will be returned. If not provided, all vertices will
17641 * {Array} A list of all vertices in the geometry.
17643 getVertices: function(nodes) {
17645 if(nodes === true) {
17647 this.components[0],
17648 this.components[this.components.length-1]
17650 } else if (nodes === false) {
17651 vertices = this.components.slice(1, this.components.length-1);
17653 vertices = this.components.slice();
17659 * APIMethod: distanceTo
17660 * Calculate the closest distance between two geometries (on the x-y plane).
17663 * geometry - {<OpenLayers.Geometry>} The target geometry.
17664 * options - {Object} Optional properties for configuring the distance
17668 * details - {Boolean} Return details from the distance calculation.
17669 * Default is false.
17670 * edge - {Boolean} Calculate the distance from this geometry to the
17671 * nearest edge of the target geometry. Default is true. If true,
17672 * calling distanceTo from a geometry that is wholly contained within
17673 * the target will result in a non-zero distance. If false, whenever
17674 * geometries intersect, calling distanceTo will return 0. If false,
17675 * details cannot be returned.
17678 * {Number | Object} The distance between this geometry and the target.
17679 * If details is true, the return will be an object with distance,
17680 * x0, y0, x1, and x2 properties. The x0 and y0 properties represent
17681 * the coordinates of the closest point on this geometry. The x1 and y1
17682 * properties represent the coordinates of the closest point on the
17685 distanceTo: function(geometry, options) {
17686 var edge = !(options && options.edge === false);
17687 var details = edge && options && options.details;
17688 var result, best = {};
17689 var min = Number.POSITIVE_INFINITY;
17690 if(geometry instanceof OpenLayers.Geometry.Point) {
17691 var segs = this.getSortedSegments();
17692 var x = geometry.x;
17693 var y = geometry.y;
17695 for(var i=0, len=segs.length; i<len; ++i) {
17697 result = OpenLayers.Geometry.distanceToSegment(geometry, seg);
17698 if(result.distance < min) {
17699 min = result.distance;
17705 // if distance increases and we cross y0 to the right of x0, no need to keep looking.
17706 if(seg.x2 > x && ((y > seg.y1 && y < seg.y2) || (y < seg.y1 && y > seg.y2))) {
17713 distance: best.distance,
17714 x0: best.x, y0: best.y,
17718 best = best.distance;
17720 } else if(geometry instanceof OpenLayers.Geometry.LineString) {
17721 var segs0 = this.getSortedSegments();
17722 var segs1 = geometry.getSortedSegments();
17723 var seg0, seg1, intersection, x0, y0;
17724 var len1 = segs1.length;
17725 var interOptions = {point: true};
17726 outer: for(var i=0, len=segs0.length; i<len; ++i) {
17730 for(var j=0; j<len1; ++j) {
17732 intersection = OpenLayers.Geometry.segmentsIntersect(seg0, seg1, interOptions);
17737 x0: intersection.x, y0: intersection.y,
17738 x1: intersection.x, y1: intersection.y
17742 result = OpenLayers.Geometry.distanceToSegment({x: x0, y: y0}, seg1);
17743 if(result.distance < min) {
17744 min = result.distance;
17748 x1: result.x, y1: result.y
17755 best = best.distance;
17758 // check the final vertex in this line's sorted segments
17760 result = geometry.distanceTo(
17761 new OpenLayers.Geometry.Point(seg0.x2, seg0.y2),
17764 var dist = details ? result.distance : result;
17769 x0: result.x1, y0: result.y1,
17770 x1: result.x0, y1: result.y0
17779 best = geometry.distanceTo(this, options);
17780 // swap since target comes from this line
17783 distance: best.distance,
17784 x0: best.x1, y0: best.y1,
17785 x1: best.x0, y1: best.y0
17793 * APIMethod: simplify
17794 * This function will return a simplified LineString.
17795 * Simplification is based on the Douglas-Peucker algorithm.
17799 * tolerance - {number} threshhold for simplification in map units
17802 * {OpenLayers.Geometry.LineString} the simplified LineString
17804 simplify: function(tolerance){
17805 if (this && this !== null) {
17806 var points = this.getVertices();
17807 if (points.length < 3) {
17811 var compareNumbers = function(a, b){
17816 * Private function doing the Douglas-Peucker reduction
17818 var douglasPeuckerReduction = function(points, firstPoint, lastPoint, tolerance){
17819 var maxDistance = 0;
17820 var indexFarthest = 0;
17822 for (var index = firstPoint, distance; index < lastPoint; index++) {
17823 distance = perpendicularDistance(points[firstPoint], points[lastPoint], points[index]);
17824 if (distance > maxDistance) {
17825 maxDistance = distance;
17826 indexFarthest = index;
17830 if (maxDistance > tolerance && indexFarthest != firstPoint) {
17831 //Add the largest point that exceeds the tolerance
17832 pointIndexsToKeep.push(indexFarthest);
17833 douglasPeuckerReduction(points, firstPoint, indexFarthest, tolerance);
17834 douglasPeuckerReduction(points, indexFarthest, lastPoint, tolerance);
17839 * Private function calculating the perpendicular distance
17840 * TODO: check whether OpenLayers.Geometry.LineString::distanceTo() is faster or slower
17842 var perpendicularDistance = function(point1, point2, point){
17843 //Area = |(1/2)(x1y2 + x2y3 + x3y1 - x2y1 - x3y2 - x1y3)| *Area of triangle
17844 //Base = v((x1-x2)²+(x1-x2)²) *Base of Triangle*
17845 //Area = .5*Base*H *Solve for height
17846 //Height = Area/.5/Base
17848 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));
17849 var bottom = Math.sqrt(Math.pow(point1.x - point2.x, 2) + Math.pow(point1.y - point2.y, 2));
17850 var height = area / bottom * 2;
17855 var firstPoint = 0;
17856 var lastPoint = points.length - 1;
17857 var pointIndexsToKeep = [];
17859 //Add the first and last index to the keepers
17860 pointIndexsToKeep.push(firstPoint);
17861 pointIndexsToKeep.push(lastPoint);
17863 //The first and the last point cannot be the same
17864 while (points[firstPoint].equals(points[lastPoint])) {
17866 //Addition: the first point not equal to first point in the LineString is kept as well
17867 pointIndexsToKeep.push(lastPoint);
17870 douglasPeuckerReduction(points, firstPoint, lastPoint, tolerance);
17871 var returnPoints = [];
17872 pointIndexsToKeep.sort(compareNumbers);
17873 for (var index = 0; index < pointIndexsToKeep.length; index++) {
17874 returnPoints.push(points[pointIndexsToKeep[index]]);
17876 return new OpenLayers.Geometry.LineString(returnPoints);
17884 CLASS_NAME: "OpenLayers.Geometry.LineString"
17886 /* ======================================================================
17887 OpenLayers/Geometry/LinearRing.js
17888 ====================================================================== */
17890 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
17891 * full list of contributors). Published under the 2-clause BSD license.
17892 * See license.txt in the OpenLayers distribution or repository for the
17893 * full text of the license. */
17896 * @requires OpenLayers/Geometry/LineString.js
17900 * Class: OpenLayers.Geometry.LinearRing
17902 * A Linear Ring is a special LineString which is closed. It closes itself
17903 * automatically on every addPoint/removePoint by adding a copy of the first
17904 * point as the last point.
17906 * Also, as it is the first in the line family to close itself, a getArea()
17907 * function is defined to calculate the enclosed area of the linearRing
17910 * - <OpenLayers.Geometry.LineString>
17912 OpenLayers.Geometry.LinearRing = OpenLayers.Class(
17913 OpenLayers.Geometry.LineString, {
17916 * Property: componentTypes
17917 * {Array(String)} An array of class names representing the types of
17918 * components that the collection can include. A null
17919 * value means the component types are not restricted.
17921 componentTypes: ["OpenLayers.Geometry.Point"],
17924 * Constructor: OpenLayers.Geometry.LinearRing
17925 * Linear rings are constructed with an array of points. This array
17926 * can represent a closed or open ring. If the ring is open (the last
17927 * point does not equal the first point), the constructor will close
17928 * the ring. If the ring is already closed (the last point does equal
17929 * the first point), it will be left closed.
17932 * points - {Array(<OpenLayers.Geometry.Point>)} points
17936 * APIMethod: addComponent
17937 * Adds a point to geometry components. If the point is to be added to
17938 * the end of the components array and it is the same as the last point
17939 * already in that array, the duplicate point is not added. This has
17940 * the effect of closing the ring if it is not already closed, and
17941 * doing the right thing if it is already closed. This behavior can
17942 * be overridden by calling the method with a non-null index as the
17946 * point - {<OpenLayers.Geometry.Point>}
17947 * index - {Integer} Index into the array to insert the component
17950 * {Boolean} Was the Point successfully added?
17952 addComponent: function(point, index) {
17955 //remove last point
17956 var lastPoint = this.components.pop();
17958 // given an index, add the point
17959 // without an index only add non-duplicate points
17960 if(index != null || !point.equals(lastPoint)) {
17961 added = OpenLayers.Geometry.Collection.prototype.addComponent.apply(this,
17965 //append copy of first point
17966 var firstPoint = this.components[0];
17967 OpenLayers.Geometry.Collection.prototype.addComponent.apply(this,
17974 * APIMethod: removeComponent
17975 * Removes a point from geometry components.
17978 * point - {<OpenLayers.Geometry.Point>}
17981 * {Boolean} The component was removed.
17983 removeComponent: function(point) {
17984 var removed = this.components && (this.components.length > 3);
17986 //remove last point
17987 this.components.pop();
17990 OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this,
17992 //append copy of first point
17993 var firstPoint = this.components[0];
17994 OpenLayers.Geometry.Collection.prototype.addComponent.apply(this,
18002 * Moves a geometry by the given displacement along positive x and y axes.
18003 * This modifies the position of the geometry and clears the cached
18007 * x - {Float} Distance to move geometry in positive x direction.
18008 * y - {Float} Distance to move geometry in positive y direction.
18010 move: function(x, y) {
18011 for(var i = 0, len=this.components.length; i<len - 1; i++) {
18012 this.components[i].move(x, y);
18017 * APIMethod: rotate
18018 * Rotate a geometry around some origin
18021 * angle - {Float} Rotation angle in degrees (measured counterclockwise
18022 * from the positive x-axis)
18023 * origin - {<OpenLayers.Geometry.Point>} Center point for the rotation
18025 rotate: function(angle, origin) {
18026 for(var i=0, len=this.components.length; i<len - 1; ++i) {
18027 this.components[i].rotate(angle, origin);
18032 * APIMethod: resize
18033 * Resize a geometry relative to some origin. Use this method to apply
18034 * a uniform scaling to a geometry.
18037 * scale - {Float} Factor by which to scale the geometry. A scale of 2
18038 * doubles the size of the geometry in each dimension
18039 * (lines, for example, will be twice as long, and polygons
18040 * will have four times the area).
18041 * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing
18042 * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1.
18045 * {<OpenLayers.Geometry>} - The current geometry.
18047 resize: function(scale, origin, ratio) {
18048 for(var i=0, len=this.components.length; i<len - 1; ++i) {
18049 this.components[i].resize(scale, origin, ratio);
18055 * APIMethod: transform
18056 * Reproject the components geometry from source to dest.
18059 * source - {<OpenLayers.Projection>}
18060 * dest - {<OpenLayers.Projection>}
18063 * {<OpenLayers.Geometry>}
18065 transform: function(source, dest) {
18066 if (source && dest) {
18067 for (var i=0, len=this.components.length; i<len - 1; i++) {
18068 var component = this.components[i];
18069 component.transform(source, dest);
18071 this.bounds = null;
18077 * APIMethod: getCentroid
18080 * {<OpenLayers.Geometry.Point>} The centroid of the collection
18082 getCentroid: function() {
18083 if (this.components) {
18084 var len = this.components.length;
18085 if (len > 0 && len <= 2) {
18086 return this.components[0].clone();
18087 } else if (len > 2) {
18090 var x0 = this.components[0].x;
18091 var y0 = this.components[0].y;
18092 var area = -1 * this.getArea();
18094 for (var i = 0; i < len - 1; i++) {
18095 var b = this.components[i];
18096 var c = this.components[i+1];
18097 sumX += (b.x + c.x - 2 * x0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0));
18098 sumY += (b.y + c.y - 2 * y0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0));
18100 var x = x0 + sumX / (6 * area);
18101 var y = y0 + sumY / (6 * area);
18103 for (var i = 0; i < len - 1; i++) {
18104 sumX += this.components[i].x;
18105 sumY += this.components[i].y;
18107 var x = sumX / (len - 1);
18108 var y = sumY / (len - 1);
18110 return new OpenLayers.Geometry.Point(x, y);
18118 * APIMethod: getArea
18119 * Note - The area is positive if the ring is oriented CW, otherwise
18120 * it will be negative.
18123 * {Float} The signed area for a ring.
18125 getArea: function() {
18127 if ( this.components && (this.components.length > 2)) {
18129 for (var i=0, len=this.components.length; i<len - 1; i++) {
18130 var b = this.components[i];
18131 var c = this.components[i+1];
18132 sum += (b.x + c.x) * (c.y - b.y);
18134 area = - sum / 2.0;
18140 * APIMethod: getGeodesicArea
18141 * Calculate the approximate area of the polygon were it projected onto
18142 * the earth. Note that this area will be positive if ring is oriented
18143 * clockwise, otherwise it will be negative.
18146 * projection - {<OpenLayers.Projection>} The spatial reference system
18147 * for the geometry coordinates. If not provided, Geographic/WGS84 is
18151 * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
18152 * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
18153 * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
18156 * {float} The approximate signed geodesic area of the polygon in square
18159 getGeodesicArea: function(projection) {
18160 var ring = this; // so we can work with a clone if needed
18162 var gg = new OpenLayers.Projection("EPSG:4326");
18163 if(!gg.equals(projection)) {
18164 ring = this.clone().transform(projection, gg);
18168 var len = ring.components && ring.components.length;
18171 for(var i=0; i<len-1; i++) {
18172 p1 = ring.components[i];
18173 p2 = ring.components[i+1];
18174 area += OpenLayers.Util.rad(p2.x - p1.x) *
18175 (2 + Math.sin(OpenLayers.Util.rad(p1.y)) +
18176 Math.sin(OpenLayers.Util.rad(p2.y)));
18178 area = area * 6378137.0 * 6378137.0 / 2.0;
18184 * Method: containsPoint
18185 * Test if a point is inside a linear ring. For the case where a point
18186 * is coincident with a linear ring edge, returns 1. Otherwise,
18190 * point - {<OpenLayers.Geometry.Point>}
18193 * {Boolean | Number} The point is inside the linear ring. Returns 1 if
18194 * the point is coincident with an edge. Returns boolean otherwise.
18196 containsPoint: function(point) {
18197 var approx = OpenLayers.Number.limitSigDigs;
18199 var px = approx(point.x, digs);
18200 var py = approx(point.y, digs);
18201 function getX(y, x1, y1, x2, y2) {
18202 return (y - y2) * ((x2 - x1) / (y2 - y1)) + x2;
18204 var numSeg = this.components.length - 1;
18205 var start, end, x1, y1, x2, y2, cx, cy;
18207 for(var i=0; i<numSeg; ++i) {
18208 start = this.components[i];
18209 x1 = approx(start.x, digs);
18210 y1 = approx(start.y, digs);
18211 end = this.components[i + 1];
18212 x2 = approx(end.x, digs);
18213 y2 = approx(end.y, digs);
18216 * The following conditions enforce five edge-crossing rules:
18217 * 1. points coincident with edges are considered contained;
18218 * 2. an upward edge includes its starting endpoint, and
18219 * excludes its final endpoint;
18220 * 3. a downward edge excludes its starting endpoint, and
18221 * includes its final endpoint;
18222 * 4. horizontal edges are excluded; and
18223 * 5. the edge-ray intersection point must be strictly right
18229 // point on horizontal line
18230 if(x1 <= x2 && (px >= x1 && px <= x2) || // right or vert
18231 x1 >= x2 && (px <= x1 && px >= x2)) { // left or vert
18237 // ignore other horizontal edges
18240 cx = approx(getX(py, x1, y1, x2, y2), digs);
18243 if(y1 < y2 && (py >= y1 && py <= y2) || // upward
18244 y1 > y2 && (py <= y1 && py >= y2)) { // downward
18251 // no crossing to the right
18254 if(x1 != x2 && (cx < Math.min(x1, x2) || cx > Math.max(x1, x2))) {
18258 if(y1 < y2 && (py >= y1 && py < y2) || // upward
18259 y1 > y2 && (py < y1 && py >= y2)) { // downward
18263 var contained = (crosses == -1) ?
18266 // even (out) or odd (in)
18273 * APIMethod: intersects
18274 * Determine if the input geometry intersects this one.
18277 * geometry - {<OpenLayers.Geometry>} Any type of geometry.
18280 * {Boolean} The input geometry intersects this one.
18282 intersects: function(geometry) {
18283 var intersect = false;
18284 if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
18285 intersect = this.containsPoint(geometry);
18286 } else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LineString") {
18287 intersect = geometry.intersects(this);
18288 } else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
18289 intersect = OpenLayers.Geometry.LineString.prototype.intersects.apply(
18293 // check for component intersections
18294 for(var i=0, len=geometry.components.length; i<len; ++ i) {
18295 intersect = geometry.components[i].intersects(this);
18305 * APIMethod: getVertices
18306 * Return a list of all points in this geometry.
18309 * nodes - {Boolean} For lines, only return vertices that are
18310 * endpoints. If false, for lines, only vertices that are not
18311 * endpoints will be returned. If not provided, all vertices will
18315 * {Array} A list of all vertices in the geometry.
18317 getVertices: function(nodes) {
18318 return (nodes === true) ? [] : this.components.slice(0, this.components.length-1);
18321 CLASS_NAME: "OpenLayers.Geometry.LinearRing"
18323 /* ======================================================================
18324 OpenLayers/Layer/HTTPRequest.js
18325 ====================================================================== */
18327 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
18328 * full list of contributors). Published under the 2-clause BSD license.
18329 * See license.txt in the OpenLayers distribution or repository for the
18330 * full text of the license. */
18334 * @requires OpenLayers/Layer.js
18338 * Class: OpenLayers.Layer.HTTPRequest
18341 * - <OpenLayers.Layer>
18343 OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, {
18346 * Constant: URL_HASH_FACTOR
18347 * {Float} Used to hash URL param strings for multi-WMS server selection.
18348 * Set to the Golden Ratio per Knuth's recommendation.
18350 URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2,
18354 * {Array(String) or String} This is either an array of url strings or
18355 * a single url string.
18361 * {Object} Hashtable of key/value parameters
18366 * APIProperty: reproject
18367 * *Deprecated*. See http://docs.openlayers.org/library/spherical_mercator.html
18368 * for information on the replacement for this functionality.
18369 * {Boolean} Whether layer should reproject itself based on base layer
18370 * locations. This allows reprojection onto commercial layers.
18371 * Default is false: Most layers can't reproject, but layers
18372 * which can create non-square geographic pixels can, like WMS.
18378 * Constructor: OpenLayers.Layer.HTTPRequest
18382 * url - {Array(String) or String}
18383 * params - {Object}
18384 * options - {Object} Hashtable of extra options to tag onto the layer
18386 initialize: function(name, url, params, options) {
18387 OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);
18389 if (!this.params) {
18390 this.params = OpenLayers.Util.extend({}, params);
18395 * APIMethod: destroy
18397 destroy: function() {
18399 this.params = null;
18400 OpenLayers.Layer.prototype.destroy.apply(this, arguments);
18410 * {<OpenLayers.Layer.HTTPRequest>} An exact clone of this
18411 * <OpenLayers.Layer.HTTPRequest>
18413 clone: function (obj) {
18416 obj = new OpenLayers.Layer.HTTPRequest(this.name,
18419 this.getOptions());
18422 //get all additions from superclasses
18423 obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
18425 // copy/set any non-init, non-simple values here
18431 * APIMethod: setUrl
18434 * newUrl - {String}
18436 setUrl: function(newUrl) {
18441 * APIMethod: mergeNewParams
18444 * newParams - {Object}
18447 * redrawn: {Boolean} whether the layer was actually redrawn.
18449 mergeNewParams:function(newParams) {
18450 this.params = OpenLayers.Util.extend(this.params, newParams);
18451 var ret = this.redraw();
18452 if(this.map != null) {
18453 this.map.events.triggerEvent("changelayer", {
18462 * APIMethod: redraw
18463 * Redraws the layer. Returns true if the layer was redrawn, false if not.
18466 * force - {Boolean} Force redraw by adding random parameter.
18469 * {Boolean} The layer was redrawn.
18471 redraw: function(force) {
18473 return this.mergeNewParams({"_olSalt": Math.random()});
18475 return OpenLayers.Layer.prototype.redraw.apply(this, []);
18480 * Method: selectUrl
18481 * selectUrl() implements the standard floating-point multiplicative
18482 * hash function described by Knuth, and hashes the contents of the
18483 * given param string into a float between 0 and 1. This float is then
18484 * scaled to the size of the provided urls array, and used to select
18488 * paramString - {String}
18489 * urls - {Array(String)}
18492 * {String} An entry from the urls array, deterministically selected based
18493 * on the paramString.
18495 selectUrl: function(paramString, urls) {
18497 for (var i=0, len=paramString.length; i<len; i++) {
18498 product *= paramString.charCodeAt(i) * this.URL_HASH_FACTOR;
18499 product -= Math.floor(product);
18501 return urls[Math.floor(product * urls.length)];
18505 * Method: getFullRequestString
18506 * Combine url with layer's params and these newParams.
18508 * does checking on the serverPath variable, allowing for cases when it
18509 * is supplied with trailing ? or &, as well as cases where not.
18511 * return in formatted string like this:
18512 * "server?key1=value1&key2=value2&key3=value3"
18514 * WARNING: The altUrl parameter is deprecated and will be removed in 3.0.
18517 * newParams - {Object}
18518 * altUrl - {String} Use this as the url instead of the layer's url
18523 getFullRequestString:function(newParams, altUrl) {
18525 // if not altUrl passed in, use layer's url
18526 var url = altUrl || this.url;
18528 // create a new params hashtable with all the layer params and the
18529 // new params together. then convert to string
18530 var allParams = OpenLayers.Util.extend({}, this.params);
18531 allParams = OpenLayers.Util.extend(allParams, newParams);
18532 var paramsString = OpenLayers.Util.getParameterString(allParams);
18534 // if url is not a string, it should be an array of strings,
18535 // in which case we will deterministically select one of them in
18536 // order to evenly distribute requests to different urls.
18538 if (OpenLayers.Util.isArray(url)) {
18539 url = this.selectUrl(paramsString, url);
18542 // ignore parameters that are already in the url search string
18544 OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));
18545 for(var key in allParams) {
18546 if(key.toUpperCase() in urlParams) {
18547 delete allParams[key];
18550 paramsString = OpenLayers.Util.getParameterString(allParams);
18552 return OpenLayers.Util.urlAppend(url, paramsString);
18555 CLASS_NAME: "OpenLayers.Layer.HTTPRequest"
18557 /* ======================================================================
18558 OpenLayers/Layer/Grid.js
18559 ====================================================================== */
18561 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
18562 * full list of contributors). Published under the 2-clause BSD license.
18563 * See license.txt in the OpenLayers distribution or repository for the
18564 * full text of the license. */
18568 * @requires OpenLayers/Layer/HTTPRequest.js
18569 * @requires OpenLayers/Tile/Image.js
18573 * Class: OpenLayers.Layer.Grid
18574 * Base class for layers that use a lattice of tiles. Create a new grid
18575 * layer with the <OpenLayers.Layer.Grid> constructor.
18578 * - <OpenLayers.Layer.HTTPRequest>
18580 OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
18583 * APIProperty: tileSize
18584 * {<OpenLayers.Size>}
18589 * Property: tileOriginCorner
18590 * {String} If the <tileOrigin> property is not provided, the tile origin
18591 * will be derived from the layer's <maxExtent>. The corner of the
18592 * <maxExtent> used is determined by this property. Acceptable values
18593 * are "tl" (top left), "tr" (top right), "bl" (bottom left), and "br"
18594 * (bottom right). Default is "bl".
18596 tileOriginCorner: "bl",
18599 * APIProperty: tileOrigin
18600 * {<OpenLayers.LonLat>} Optional origin for aligning the grid of tiles.
18601 * If provided, requests for tiles at all resolutions will be aligned
18602 * with this location (no tiles shall overlap this location). If
18603 * not provided, the grid of tiles will be aligned with the layer's
18604 * <maxExtent>. Default is ``null``.
18608 /** APIProperty: tileOptions
18609 * {Object} optional configuration options for <OpenLayers.Tile> instances
18610 * created by this Layer, if supported by the tile class.
18615 * APIProperty: tileClass
18616 * {<OpenLayers.Tile>} The tile class to use for this layer.
18617 * Defaults is OpenLayers.Tile.Image.
18619 tileClass: OpenLayers.Tile.Image,
18623 * {Array(Array(<OpenLayers.Tile>))} This is an array of rows, each row is
18624 * an array of tiles.
18629 * APIProperty: singleTile
18630 * {Boolean} Moves the layer into single-tile mode, meaning that one tile
18631 * will be loaded. The tile's size will be determined by the 'ratio'
18632 * property. When the tile is dragged such that it does not cover the
18633 * entire viewport, it is reloaded.
18637 /** APIProperty: ratio
18638 * {Float} Used only when in single-tile mode, this specifies the
18639 * ratio of the size of the single tile to the size of the map.
18640 * Default value is 1.5.
18645 * APIProperty: buffer
18646 * {Integer} Used only when in gridded mode, this specifies the number of
18647 * extra rows and colums of tiles on each side which will
18648 * surround the minimum grid tiles to cover the map.
18649 * For very slow loading layers, a larger value may increase
18650 * performance somewhat when dragging, but will increase bandwidth
18651 * use significantly.
18656 * APIProperty: transitionEffect
18657 * {String} The transition effect to use when the map is zoomed.
18658 * Two posible values:
18660 * "resize" - Existing tiles are resized on zoom to provide a visual
18661 * effect of the zoom having taken place immediately. As the
18662 * new tiles become available, they are drawn on top of the
18663 * resized tiles (this is the default setting).
18664 * "map-resize" - Existing tiles are resized on zoom and placed below the
18665 * base layer. New tiles for the base layer will cover existing tiles.
18666 * This setting is recommended when having an overlay duplicated during
18667 * the transition is undesirable (e.g. street labels or big transparent
18669 * null - No transition effect.
18671 * Using "resize" on non-opaque layers can cause undesired visual
18672 * effects. Set transitionEffect to null in this case.
18674 transitionEffect: "resize",
18677 * APIProperty: numLoadingTiles
18678 * {Integer} How many tiles are still loading?
18680 numLoadingTiles: 0,
18683 * Property: serverResolutions
18684 * {Array(Number}} This property is documented in subclasses as
18687 serverResolutions: null,
18690 * Property: loading
18691 * {Boolean} Indicates if tiles are being loaded.
18696 * Property: backBuffer
18697 * {DOMElement} The back buffer.
18702 * Property: gridResolution
18703 * {Number} The resolution of the current grid. Used for backbuffer and
18704 * client zoom. This property is updated every time the grid is
18707 gridResolution: null,
18710 * Property: backBufferResolution
18711 * {Number} The resolution of the current back buffer. This property is
18712 * updated each time a back buffer is created.
18714 backBufferResolution: null,
18717 * Property: backBufferLonLat
18718 * {Object} The top-left corner of the current back buffer. Includes lon
18719 * and lat properties. This object is updated each time a back buffer
18722 backBufferLonLat: null,
18725 * Property: backBufferTimerId
18726 * {Number} The id of the back buffer timer. This timer is used to
18727 * delay the removal of the back buffer, thereby preventing
18728 * flash effects caused by tile animation.
18730 backBufferTimerId: null,
18733 * APIProperty: removeBackBufferDelay
18734 * {Number} Delay for removing the backbuffer when all tiles have finished
18735 * loading. Can be set to 0 when no css opacity transitions for the
18736 * olTileImage class are used. Default is 0 for <singleTile> layers,
18737 * 2500 for tiled layers. See <className> for more information on
18740 removeBackBufferDelay: null,
18743 * APIProperty: className
18744 * {String} Name of the class added to the layer div. If not set in the
18745 * options passed to the constructor then className defaults to
18746 * "olLayerGridSingleTile" for single tile layers (see <singleTile>),
18747 * and "olLayerGrid" for non single tile layers.
18751 * The displaying of tiles is not animated by default for single tile
18752 * layers - OpenLayers' default theme (style.css) includes this:
18754 * .olLayerGrid .olTileImage {
18755 * -webkit-transition: opacity 0.2s linear;
18756 * -moz-transition: opacity 0.2s linear;
18757 * -o-transition: opacity 0.2s linear;
18758 * transition: opacity 0.2s linear;
18761 * To animate tile displaying for any grid layer the following
18762 * CSS rule can be used:
18765 * -webkit-transition: opacity 0.2s linear;
18766 * -moz-transition: opacity 0.2s linear;
18767 * -o-transition: opacity 0.2s linear;
18768 * transition: opacity 0.2s linear;
18771 * In that case, to avoid flash effects, <removeBackBufferDelay>
18772 * should not be zero.
18777 * Register a listener for a particular event with the following syntax:
18779 * layer.events.register(type, obj, listener);
18782 * Listeners will be called with a reference to an event object. The
18783 * properties of this event depends on exactly what happened.
18785 * All event objects have at least the following properties:
18786 * object - {Object} A reference to layer.events.object.
18787 * element - {DOMElement} A reference to layer.events.element.
18789 * Supported event types:
18790 * addtile - Triggered when a tile is added to this layer. Listeners receive
18791 * an object as first argument, which has a tile property that
18792 * references the tile that has been added.
18793 * tileloadstart - Triggered when a tile starts loading. Listeners receive
18794 * an object as first argument, which has a tile property that
18795 * references the tile that starts loading.
18796 * tileloaded - Triggered when each new tile is
18797 * loaded, as a means of progress update to listeners.
18798 * listeners can access 'numLoadingTiles' if they wish to keep
18799 * track of the loading progress. Listeners are called with an object
18800 * with a 'tile' property as first argument, making the loaded tile
18801 * available to the listener, and an 'aborted' property, which will be
18802 * true when loading was aborted and no tile data is available.
18803 * tileerror - Triggered before the tileloaded event (i.e. when the tile is
18804 * still hidden) if a tile failed to load. Listeners receive an object
18805 * as first argument, which has a tile property that references the
18806 * tile that could not be loaded.
18807 * retile - Triggered when the layer recreates its tile grid.
18811 * Property: gridLayout
18812 * {Object} Object containing properties tilelon, tilelat, startcol,
18818 * Property: rowSign
18819 * {Number} 1 for grids starting at the top, -1 for grids starting at the
18820 * bottom. This is used for several grid index and offset calculations.
18825 * Property: transitionendEvents
18826 * {Array} Event names for transitionend
18828 transitionendEvents: [
18829 'transitionend', 'webkitTransitionEnd', 'otransitionend',
18834 * Constructor: OpenLayers.Layer.Grid
18835 * Create a new grid layer
18840 * params - {Object}
18841 * options - {Object} Hashtable of extra options to tag onto the layer
18843 initialize: function(name, url, params, options) {
18844 OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this,
18847 this._removeBackBuffer = OpenLayers.Function.bind(this.removeBackBuffer, this);
18849 this.initProperties();
18851 this.rowSign = this.tileOriginCorner.substr(0, 1) === "t" ? 1 : -1;
18855 * Method: initProperties
18856 * Set any properties that depend on the value of singleTile.
18857 * Currently sets removeBackBufferDelay and className
18859 initProperties: function() {
18860 if (this.options.removeBackBufferDelay === undefined) {
18861 this.removeBackBufferDelay = this.singleTile ? 0 : 2500;
18864 if (this.options.className === undefined) {
18865 this.className = this.singleTile ? 'olLayerGridSingleTile' :
18874 * map - {<OpenLayers.Map>} The map.
18876 setMap: function(map) {
18877 OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this, map);
18878 OpenLayers.Element.addClass(this.div, this.className);
18882 * Method: removeMap
18883 * Called when the layer is removed from the map.
18886 * map - {<OpenLayers.Map>} The map.
18888 removeMap: function(map) {
18889 this.removeBackBuffer();
18893 * APIMethod: destroy
18894 * Deconstruct the layer and clear the grid.
18896 destroy: function() {
18897 this.removeBackBuffer();
18901 this.tileSize = null;
18902 OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments);
18906 * APIMethod: mergeNewParams
18907 * Refetches tiles with new params merged, keeping a backbuffer. Each
18908 * loading new tile will have a css class of '.olTileReplacing'. If a
18909 * stylesheet applies a 'display: none' style to that class, any fade-in
18910 * transition will not apply, and backbuffers for each tile will be removed
18911 * as soon as the tile is loaded.
18914 * newParams - {Object}
18917 * redrawn: {Boolean} whether the layer was actually redrawn.
18921 * Method: clearGrid
18922 * Go through and remove all tiles from the grid, calling
18923 * destroy() on each of them to kill circular references
18925 clearGrid:function() {
18927 for(var iRow=0, len=this.grid.length; iRow<len; iRow++) {
18928 var row = this.grid[iRow];
18929 for(var iCol=0, clen=row.length; iCol<clen; iCol++) {
18930 var tile = row[iCol];
18931 this.destroyTile(tile);
18935 this.gridResolution = null;
18936 this.gridLayout = null;
18941 * APIMethod: addOptions
18944 * newOptions - {Object}
18945 * reinitialize - {Boolean} If set to true, and if resolution options of the
18946 * current baseLayer were changed, the map will be recentered to make
18947 * sure that it is displayed with a valid resolution, and a
18948 * changebaselayer event will be triggered.
18950 addOptions: function (newOptions, reinitialize) {
18951 var singleTileChanged = newOptions.singleTile !== undefined &&
18952 newOptions.singleTile !== this.singleTile;
18953 OpenLayers.Layer.HTTPRequest.prototype.addOptions.apply(this, arguments);
18954 if (this.map && singleTileChanged) {
18955 this.initProperties();
18957 this.tileSize = this.options.tileSize;
18958 this.setTileSize();
18959 this.moveTo(null, true);
18965 * Create a clone of this layer
18968 * obj - {Object} Is this ever used?
18971 * {<OpenLayers.Layer.Grid>} An exact clone of this OpenLayers.Layer.Grid
18973 clone: function (obj) {
18976 obj = new OpenLayers.Layer.Grid(this.name,
18979 this.getOptions());
18982 //get all additions from superclasses
18983 obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]);
18985 // copy/set any non-init, non-simple values here
18986 if (this.tileSize != null) {
18987 obj.tileSize = this.tileSize.clone();
18990 // we do not want to copy reference to grid, so we make a new array
18992 obj.gridResolution = null;
18993 // same for backbuffer
18994 obj.backBuffer = null;
18995 obj.backBufferTimerId = null;
18996 obj.loading = false;
18997 obj.numLoadingTiles = 0;
19004 * This function is called whenever the map is moved. All the moving
19005 * of actual 'tiles' is done by the map, but moveTo's role is to accept
19006 * a bounds and make sure the data that that bounds requires is pre-loaded.
19009 * bounds - {<OpenLayers.Bounds>}
19010 * zoomChanged - {Boolean}
19011 * dragging - {Boolean}
19013 moveTo:function(bounds, zoomChanged, dragging) {
19015 OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments);
19017 bounds = bounds || this.map.getExtent();
19019 if (bounds != null) {
19021 // if grid is empty or zoom has changed, we *must* re-tile
19022 var forceReTile = !this.grid.length || zoomChanged;
19024 // total bounds of the tiles
19025 var tilesBounds = this.getTilesBounds();
19027 // the new map resolution
19028 var resolution = this.map.getResolution();
19030 // the server-supported resolution for the new map resolution
19031 var serverResolution = this.getServerResolution(resolution);
19033 if (this.singleTile) {
19035 // We want to redraw whenever even the slightest part of the
19036 // current bounds is not contained by our tile.
19037 // (thus, we do not specify partial -- its default is false)
19039 if ( forceReTile ||
19040 (!dragging && !tilesBounds.containsBounds(bounds))) {
19042 // In single tile mode with no transition effect, we insert
19043 // a non-scaled backbuffer when the layer is moved. But if
19044 // a zoom occurs right after a move, i.e. before the new
19045 // image is received, we need to remove the backbuffer, or
19046 // an ill-positioned image will be visible during the zoom
19049 if(zoomChanged && this.transitionEffect !== 'resize') {
19050 this.removeBackBuffer();
19053 if(!zoomChanged || this.transitionEffect === 'resize') {
19054 this.applyBackBuffer(resolution);
19057 this.initSingleTile(bounds);
19061 // if the bounds have changed such that they are not even
19062 // *partially* contained by our tiles (e.g. when user has
19063 // programmatically panned to the other side of the earth on
19064 // zoom level 18), then moveGriddedTiles could potentially have
19065 // to run through thousands of cycles, so we want to reTile
19066 // instead (thus, partial true).
19067 forceReTile = forceReTile ||
19068 !tilesBounds.intersectsBounds(bounds, {
19069 worldBounds: this.map.baseLayer.wrapDateLine &&
19070 this.map.getMaxExtent()
19074 if(zoomChanged && (this.transitionEffect === 'resize' ||
19075 this.gridResolution === resolution)) {
19076 this.applyBackBuffer(resolution);
19078 this.initGriddedTiles(bounds);
19080 this.moveGriddedTiles();
19087 * Method: getTileData
19088 * Given a map location, retrieve a tile and the pixel offset within that
19089 * tile corresponding to the location. If there is not an existing
19090 * tile in the grid that covers the given location, null will be
19094 * loc - {<OpenLayers.LonLat>} map location
19097 * {Object} Object with the following properties: tile ({<OpenLayers.Tile>}),
19098 * i ({Number} x-pixel offset from top left), and j ({Integer} y-pixel
19099 * offset from top left).
19101 getTileData: function(loc) {
19105 numRows = this.grid.length;
19107 if (this.map && numRows) {
19108 var res = this.map.getResolution(),
19109 tileWidth = this.tileSize.w,
19110 tileHeight = this.tileSize.h,
19111 bounds = this.grid[0][0].bounds,
19112 left = bounds.left,
19116 // deal with multiple worlds
19117 if (this.map.baseLayer.wrapDateLine) {
19118 var worldWidth = this.map.getMaxExtent().getWidth();
19119 var worldsAway = Math.ceil((left - x) / worldWidth);
19120 x += worldWidth * worldsAway;
19123 // tile distance to location (fractional number of tiles);
19124 var dtx = (x - left) / (res * tileWidth);
19125 var dty = (top - y) / (res * tileHeight);
19126 // index of tile in grid
19127 var col = Math.floor(dtx);
19128 var row = Math.floor(dty);
19129 if (row >= 0 && row < numRows) {
19130 var tile = this.grid[row][col];
19134 // pixel index within tile
19135 i: Math.floor((dtx - col) * tileWidth),
19136 j: Math.floor((dty - row) * tileHeight)
19145 * Method: destroyTile
19148 * tile - {<OpenLayers.Tile>}
19150 destroyTile: function(tile) {
19151 this.removeTileMonitoringHooks(tile);
19156 * Method: getServerResolution
19157 * Return the closest server-supported resolution.
19160 * resolution - {Number} The base resolution. If undefined the
19161 * map resolution is used.
19164 * {Number} The closest server resolution value.
19166 getServerResolution: function(resolution) {
19167 var distance = Number.POSITIVE_INFINITY;
19168 resolution = resolution || this.map.getResolution();
19169 if(this.serverResolutions &&
19170 OpenLayers.Util.indexOf(this.serverResolutions, resolution) === -1) {
19171 var i, newDistance, newResolution, serverResolution;
19172 for(i=this.serverResolutions.length-1; i>= 0; i--) {
19173 newResolution = this.serverResolutions[i];
19174 newDistance = Math.abs(newResolution - resolution);
19175 if (newDistance > distance) {
19178 distance = newDistance;
19179 serverResolution = newResolution;
19181 resolution = serverResolution;
19187 * Method: getServerZoom
19188 * Return the zoom value corresponding to the best matching server
19189 * resolution, taking into account <serverResolutions> and <zoomOffset>.
19192 * {Number} The closest server supported zoom. This is not the map zoom
19193 * level, but an index of the server's resolutions array.
19195 getServerZoom: function() {
19196 var resolution = this.getServerResolution();
19197 return this.serverResolutions ?
19198 OpenLayers.Util.indexOf(this.serverResolutions, resolution) :
19199 this.map.getZoomForResolution(resolution) + (this.zoomOffset || 0);
19203 * Method: applyBackBuffer
19204 * Create, insert, scale and position a back buffer for the layer.
19207 * resolution - {Number} The resolution to transition to.
19209 applyBackBuffer: function(resolution) {
19210 if(this.backBufferTimerId !== null) {
19211 this.removeBackBuffer();
19213 var backBuffer = this.backBuffer;
19215 backBuffer = this.createBackBuffer();
19219 if (resolution === this.gridResolution) {
19220 this.div.insertBefore(backBuffer, this.div.firstChild);
19222 this.map.baseLayer.div.parentNode.insertBefore(backBuffer, this.map.baseLayer.div);
19224 this.backBuffer = backBuffer;
19226 // set some information in the instance for subsequent
19227 // calls to applyBackBuffer where the same back buffer
19229 var topLeftTileBounds = this.grid[0][0].bounds;
19230 this.backBufferLonLat = {
19231 lon: topLeftTileBounds.left,
19232 lat: topLeftTileBounds.top
19234 this.backBufferResolution = this.gridResolution;
19237 var ratio = this.backBufferResolution / resolution;
19239 // scale the tiles inside the back buffer
19240 var tiles = backBuffer.childNodes, tile;
19241 for (var i=tiles.length-1; i>=0; --i) {
19243 tile.style.top = ((ratio * tile._i * tile._h) | 0) + 'px';
19244 tile.style.left = ((ratio * tile._j * tile._w) | 0) + 'px';
19245 tile.style.width = Math.round(ratio * tile._w) + 'px';
19246 tile.style.height = Math.round(ratio * tile._h) + 'px';
19249 // and position it (based on the grid's top-left corner)
19250 var position = this.getViewPortPxFromLonLat(
19251 this.backBufferLonLat, resolution);
19252 var leftOffset = this.map.layerContainerOriginPx.x;
19253 var topOffset = this.map.layerContainerOriginPx.y;
19254 backBuffer.style.left = Math.round(position.x - leftOffset) + 'px';
19255 backBuffer.style.top = Math.round(position.y - topOffset) + 'px';
19259 * Method: createBackBuffer
19260 * Create a back buffer.
19263 * {DOMElement} The DOM element for the back buffer, undefined if the
19264 * grid isn't initialized yet.
19266 createBackBuffer: function() {
19268 if(this.grid.length > 0) {
19269 backBuffer = document.createElement('div');
19270 backBuffer.id = this.div.id + '_bb';
19271 backBuffer.className = 'olBackBuffer';
19272 backBuffer.style.position = 'absolute';
19273 var map = this.map;
19274 backBuffer.style.zIndex = this.transitionEffect === 'resize' ?
19275 this.getZIndex() - 1 :
19277 map.Z_INDEX_BASE.BaseLayer -
19278 (map.getNumLayers() - map.getLayerIndex(this));
19279 for(var i=0, lenI=this.grid.length; i<lenI; i++) {
19280 for(var j=0, lenJ=this.grid[i].length; j<lenJ; j++) {
19281 var tile = this.grid[i][j],
19282 markup = this.grid[i][j].createBackBuffer();
19286 markup._w = tile.size.w;
19287 markup._h = tile.size.h;
19288 markup.id = tile.id + '_bb';
19289 backBuffer.appendChild(markup);
19298 * Method: removeBackBuffer
19299 * Remove back buffer from DOM.
19301 removeBackBuffer: function() {
19302 if (this._transitionElement) {
19303 for (var i=this.transitionendEvents.length-1; i>=0; --i) {
19304 OpenLayers.Event.stopObserving(this._transitionElement,
19305 this.transitionendEvents[i], this._removeBackBuffer);
19307 delete this._transitionElement;
19309 if(this.backBuffer) {
19310 if (this.backBuffer.parentNode) {
19311 this.backBuffer.parentNode.removeChild(this.backBuffer);
19313 this.backBuffer = null;
19314 this.backBufferResolution = null;
19315 if(this.backBufferTimerId !== null) {
19316 window.clearTimeout(this.backBufferTimerId);
19317 this.backBufferTimerId = null;
19324 * Move the layer based on pixel vector.
19330 moveByPx: function(dx, dy) {
19331 if (!this.singleTile) {
19332 this.moveGriddedTiles();
19337 * APIMethod: setTileSize
19338 * Check if we are in singleTile mode and if so, set the size as a ratio
19339 * of the map size (as specified by the layer's 'ratio' property).
19342 * size - {<OpenLayers.Size>}
19344 setTileSize: function(size) {
19345 if (this.singleTile) {
19346 size = this.map.getSize();
19347 size.h = parseInt(size.h * this.ratio, 10);
19348 size.w = parseInt(size.w * this.ratio, 10);
19350 OpenLayers.Layer.HTTPRequest.prototype.setTileSize.apply(this, [size]);
19354 * APIMethod: getTilesBounds
19355 * Return the bounds of the tile grid.
19358 * {<OpenLayers.Bounds>} A Bounds object representing the bounds of all the
19359 * currently loaded tiles (including those partially or not at all seen
19362 getTilesBounds: function() {
19365 var length = this.grid.length;
19367 var bottomLeftTileBounds = this.grid[length - 1][0].bounds,
19368 width = this.grid[0].length * bottomLeftTileBounds.getWidth(),
19369 height = this.grid.length * bottomLeftTileBounds.getHeight();
19371 bounds = new OpenLayers.Bounds(bottomLeftTileBounds.left,
19372 bottomLeftTileBounds.bottom,
19373 bottomLeftTileBounds.left + width,
19374 bottomLeftTileBounds.bottom + height);
19380 * Method: initSingleTile
19383 * bounds - {<OpenLayers.Bounds>}
19385 initSingleTile: function(bounds) {
19386 this.events.triggerEvent("retile");
19388 //determine new tile bounds
19389 var center = bounds.getCenterLonLat();
19390 var tileWidth = bounds.getWidth() * this.ratio;
19391 var tileHeight = bounds.getHeight() * this.ratio;
19394 new OpenLayers.Bounds(center.lon - (tileWidth/2),
19395 center.lat - (tileHeight/2),
19396 center.lon + (tileWidth/2),
19397 center.lat + (tileHeight/2));
19399 var px = this.map.getLayerPxFromLonLat({
19400 lon: tileBounds.left,
19401 lat: tileBounds.top
19404 if (!this.grid.length) {
19408 var tile = this.grid[0][0];
19410 tile = this.addTile(tileBounds, px);
19412 this.addTileMonitoringHooks(tile);
19414 this.grid[0][0] = tile;
19416 tile.moveTo(tileBounds, px);
19419 //remove all but our single tile
19420 this.removeExcessTiles(1,1);
19422 // store the resolution of the grid
19423 this.gridResolution = this.getServerResolution();
19427 * Method: calculateGridLayout
19428 * Generate parameters for the grid layout.
19431 * bounds - {<OpenLayers.Bound>|Object} OpenLayers.Bounds or an
19432 * object with a 'left' and 'top' properties.
19433 * origin - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an
19434 * object with a 'lon' and 'lat' properties.
19435 * resolution - {Number}
19438 * {Object} Object containing properties tilelon, tilelat, startcol,
19441 calculateGridLayout: function(bounds, origin, resolution) {
19442 var tilelon = resolution * this.tileSize.w;
19443 var tilelat = resolution * this.tileSize.h;
19445 var offsetlon = bounds.left - origin.lon;
19446 var tilecol = Math.floor(offsetlon/tilelon) - this.buffer;
19448 var rowSign = this.rowSign;
19450 var offsetlat = rowSign * (origin.lat - bounds.top + tilelat);
19451 var tilerow = Math[~rowSign ? 'floor' : 'ceil'](offsetlat/tilelat) - this.buffer * rowSign;
19454 tilelon: tilelon, tilelat: tilelat,
19455 startcol: tilecol, startrow: tilerow
19461 * Method: getTileOrigin
19462 * Determine the origin for aligning the grid of tiles. If a <tileOrigin>
19463 * property is supplied, that will be returned. Otherwise, the origin
19464 * will be derived from the layer's <maxExtent> property. In this case,
19465 * the tile origin will be the corner of the <maxExtent> given by the
19466 * <tileOriginCorner> property.
19469 * {<OpenLayers.LonLat>} The tile origin.
19471 getTileOrigin: function() {
19472 var origin = this.tileOrigin;
19474 var extent = this.getMaxExtent();
19476 "tl": ["left", "top"],
19477 "tr": ["right", "top"],
19478 "bl": ["left", "bottom"],
19479 "br": ["right", "bottom"]
19480 })[this.tileOriginCorner];
19481 origin = new OpenLayers.LonLat(extent[edges[0]], extent[edges[1]]);
19487 * Method: getTileBoundsForGridIndex
19490 * row - {Number} The row of the grid
19491 * col - {Number} The column of the grid
19494 * {<OpenLayers.Bounds>} The bounds for the tile at (row, col)
19496 getTileBoundsForGridIndex: function(row, col) {
19497 var origin = this.getTileOrigin();
19498 var tileLayout = this.gridLayout;
19499 var tilelon = tileLayout.tilelon;
19500 var tilelat = tileLayout.tilelat;
19501 var startcol = tileLayout.startcol;
19502 var startrow = tileLayout.startrow;
19503 var rowSign = this.rowSign;
19504 return new OpenLayers.Bounds(
19505 origin.lon + (startcol + col) * tilelon,
19506 origin.lat - (startrow + row * rowSign) * tilelat * rowSign,
19507 origin.lon + (startcol + col + 1) * tilelon,
19508 origin.lat - (startrow + (row - 1) * rowSign) * tilelat * rowSign
19513 * Method: initGriddedTiles
19516 * bounds - {<OpenLayers.Bounds>}
19518 initGriddedTiles:function(bounds) {
19519 this.events.triggerEvent("retile");
19521 // work out mininum number of rows and columns; this is the number of
19522 // tiles required to cover the viewport plus at least one for panning
19524 var viewSize = this.map.getSize();
19526 var origin = this.getTileOrigin();
19527 var resolution = this.map.getResolution(),
19528 serverResolution = this.getServerResolution(),
19529 ratio = resolution / serverResolution,
19531 w: this.tileSize.w / ratio,
19532 h: this.tileSize.h / ratio
19535 var minRows = Math.ceil(viewSize.h/tileSize.h) +
19536 2 * this.buffer + 1;
19537 var minCols = Math.ceil(viewSize.w/tileSize.w) +
19538 2 * this.buffer + 1;
19540 var tileLayout = this.calculateGridLayout(bounds, origin, serverResolution);
19541 this.gridLayout = tileLayout;
19543 var tilelon = tileLayout.tilelon;
19544 var tilelat = tileLayout.tilelat;
19546 var layerContainerDivLeft = this.map.layerContainerOriginPx.x;
19547 var layerContainerDivTop = this.map.layerContainerOriginPx.y;
19549 var tileBounds = this.getTileBoundsForGridIndex(0, 0);
19550 var startPx = this.map.getViewPortPxFromLonLat(
19551 new OpenLayers.LonLat(tileBounds.left, tileBounds.top)
19553 startPx.x = Math.round(startPx.x) - layerContainerDivLeft;
19554 startPx.y = Math.round(startPx.y) - layerContainerDivTop;
19556 var tileData = [], center = this.map.getCenter();
19560 var row = this.grid[rowidx];
19563 this.grid.push(row);
19568 tileBounds = this.getTileBoundsForGridIndex(rowidx, colidx);
19569 var px = startPx.clone();
19570 px.x = px.x + colidx * Math.round(tileSize.w);
19571 px.y = px.y + rowidx * Math.round(tileSize.h);
19572 var tile = row[colidx];
19574 tile = this.addTile(tileBounds, px);
19575 this.addTileMonitoringHooks(tile);
19578 tile.moveTo(tileBounds, px, false);
19580 var tileCenter = tileBounds.getCenterLonLat();
19583 distance: Math.pow(tileCenter.lon - center.lon, 2) +
19584 Math.pow(tileCenter.lat - center.lat, 2)
19588 } while ((tileBounds.right <= bounds.right + tilelon * this.buffer)
19589 || colidx < minCols);
19592 } while((tileBounds.bottom >= bounds.bottom - tilelat * this.buffer)
19593 || rowidx < minRows);
19595 //shave off exceess rows and colums
19596 this.removeExcessTiles(rowidx, colidx);
19598 var resolution = this.getServerResolution();
19599 // store the resolution of the grid
19600 this.gridResolution = resolution;
19602 //now actually draw the tiles
19603 tileData.sort(function(a, b) {
19604 return a.distance - b.distance;
19606 for (var i=0, ii=tileData.length; i<ii; ++i) {
19607 tileData[i].tile.draw();
19612 * Method: getMaxExtent
19613 * Get this layer's maximum extent. (Implemented as a getter for
19614 * potential specific implementations in sub-classes.)
19617 * {<OpenLayers.Bounds>}
19619 getMaxExtent: function() {
19620 return this.maxExtent;
19624 * APIMethod: addTile
19625 * Create a tile, initialize it, and add it to the layer div.
19628 * bounds - {<OpenLayers.Bounds>}
19629 * position - {<OpenLayers.Pixel>}
19632 * {<OpenLayers.Tile>} The added OpenLayers.Tile
19634 addTile: function(bounds, position) {
19635 var tile = new this.tileClass(
19636 this, position, bounds, null, this.tileSize, this.tileOptions
19638 this.events.triggerEvent("addtile", {tile: tile});
19643 * Method: addTileMonitoringHooks
19644 * This function takes a tile as input and adds the appropriate hooks to
19645 * the tile so that the layer can keep track of the loading tiles.
19648 * tile - {<OpenLayers.Tile>}
19650 addTileMonitoringHooks: function(tile) {
19652 var replacingCls = 'olTileReplacing';
19654 tile.onLoadStart = function() {
19655 //if that was first tile then trigger a 'loadstart' on the layer
19656 if (this.loading === false) {
19657 this.loading = true;
19658 this.events.triggerEvent("loadstart");
19660 this.events.triggerEvent("tileloadstart", {tile: tile});
19661 this.numLoadingTiles++;
19662 if (!this.singleTile && this.backBuffer && this.gridResolution === this.backBufferResolution) {
19663 OpenLayers.Element.addClass(tile.getTile(), replacingCls);
19667 tile.onLoadEnd = function(evt) {
19668 this.numLoadingTiles--;
19669 var aborted = evt.type === 'unload';
19670 this.events.triggerEvent("tileloaded", {
19674 if (!this.singleTile && !aborted && this.backBuffer && this.gridResolution === this.backBufferResolution) {
19675 var tileDiv = tile.getTile();
19676 if (OpenLayers.Element.getStyle(tileDiv, 'display') === 'none') {
19677 var bufferTile = document.getElementById(tile.id + '_bb');
19679 bufferTile.parentNode.removeChild(bufferTile);
19682 OpenLayers.Element.removeClass(tileDiv, replacingCls);
19684 //if that was the last tile, then trigger a 'loadend' on the layer
19685 if (this.numLoadingTiles === 0) {
19686 if (this.backBuffer) {
19687 if (this.backBuffer.childNodes.length === 0) {
19688 // no tiles transitioning, remove immediately
19689 this.removeBackBuffer();
19691 // wait until transition has ended or delay has passed
19692 this._transitionElement = aborted ?
19693 this.div.lastChild : tile.imgDiv;
19694 var transitionendEvents = this.transitionendEvents;
19695 for (var i=transitionendEvents.length-1; i>=0; --i) {
19696 OpenLayers.Event.observe(this._transitionElement,
19697 transitionendEvents[i],
19698 this._removeBackBuffer);
19700 // the removal of the back buffer is delayed to prevent
19701 // flash effects due to the animation of tile displaying
19702 this.backBufferTimerId = window.setTimeout(
19703 this._removeBackBuffer, this.removeBackBufferDelay
19707 this.loading = false;
19708 this.events.triggerEvent("loadend");
19712 tile.onLoadError = function() {
19713 this.events.triggerEvent("tileerror", {tile: tile});
19717 "loadstart": tile.onLoadStart,
19718 "loadend": tile.onLoadEnd,
19719 "unload": tile.onLoadEnd,
19720 "loaderror": tile.onLoadError,
19726 * Method: removeTileMonitoringHooks
19727 * This function takes a tile as input and removes the tile hooks
19728 * that were added in addTileMonitoringHooks()
19731 * tile - {<OpenLayers.Tile>}
19733 removeTileMonitoringHooks: function(tile) {
19736 "loadstart": tile.onLoadStart,
19737 "loadend": tile.onLoadEnd,
19738 "unload": tile.onLoadEnd,
19739 "loaderror": tile.onLoadError,
19745 * Method: moveGriddedTiles
19747 moveGriddedTiles: function() {
19748 var buffer = this.buffer + 1;
19750 var tlTile = this.grid[0][0];
19752 x: tlTile.position.x +
19753 this.map.layerContainerOriginPx.x,
19754 y: tlTile.position.y +
19755 this.map.layerContainerOriginPx.y
19757 var ratio = this.getServerResolution() / this.map.getResolution();
19759 w: Math.round(this.tileSize.w * ratio),
19760 h: Math.round(this.tileSize.h * ratio)
19762 if (tlViewPort.x > -tileSize.w * (buffer - 1)) {
19763 this.shiftColumn(true, tileSize);
19764 } else if (tlViewPort.x < -tileSize.w * buffer) {
19765 this.shiftColumn(false, tileSize);
19766 } else if (tlViewPort.y > -tileSize.h * (buffer - 1)) {
19767 this.shiftRow(true, tileSize);
19768 } else if (tlViewPort.y < -tileSize.h * buffer) {
19769 this.shiftRow(false, tileSize);
19781 * prepend - {Boolean} if true, prepend to beginning.
19782 * if false, then append to end
19783 * tileSize - {Object} rendered tile size; object with w and h properties
19785 shiftRow: function(prepend, tileSize) {
19786 var grid = this.grid;
19787 var rowIndex = prepend ? 0 : (grid.length - 1);
19788 var sign = prepend ? -1 : 1;
19789 var rowSign = this.rowSign;
19790 var tileLayout = this.gridLayout;
19791 tileLayout.startrow += sign * rowSign;
19793 var modelRow = grid[rowIndex];
19794 var row = grid[prepend ? 'pop' : 'shift']();
19795 for (var i=0, len=row.length; i<len; i++) {
19797 var position = modelRow[i].position.clone();
19798 position.y += tileSize.h * sign;
19799 tile.moveTo(this.getTileBoundsForGridIndex(rowIndex, i), position);
19801 grid[prepend ? 'unshift' : 'push'](row);
19805 * Method: shiftColumn
19806 * Shift grid work in the other dimension
19809 * prepend - {Boolean} if true, prepend to beginning.
19810 * if false, then append to end
19811 * tileSize - {Object} rendered tile size; object with w and h properties
19813 shiftColumn: function(prepend, tileSize) {
19814 var grid = this.grid;
19815 var colIndex = prepend ? 0 : (grid[0].length - 1);
19816 var sign = prepend ? -1 : 1;
19817 var tileLayout = this.gridLayout;
19818 tileLayout.startcol += sign;
19820 for (var i=0, len=grid.length; i<len; i++) {
19822 var position = row[colIndex].position.clone();
19823 var tile = row[prepend ? 'pop' : 'shift']();
19824 position.x += tileSize.w * sign;
19825 tile.moveTo(this.getTileBoundsForGridIndex(i, colIndex), position);
19826 row[prepend ? 'unshift' : 'push'](tile);
19831 * Method: removeExcessTiles
19832 * When the size of the map or the buffer changes, we may need to
19833 * remove some excess rows and columns.
19836 * rows - {Integer} Maximum number of rows we want our grid to have.
19837 * columns - {Integer} Maximum number of columns we want our grid to have.
19839 removeExcessTiles: function(rows, columns) {
19842 // remove extra rows
19843 while (this.grid.length > rows) {
19844 var row = this.grid.pop();
19845 for (i=0, l=row.length; i<l; i++) {
19847 this.destroyTile(tile);
19851 // remove extra columns
19852 for (i=0, l=this.grid.length; i<l; i++) {
19853 while (this.grid[i].length > columns) {
19854 var row = this.grid[i];
19855 var tile = row.pop();
19856 this.destroyTile(tile);
19862 * Method: onMapResize
19863 * For singleTile layers, this will set a new tile size according to the
19864 * dimensions of the map pane.
19866 onMapResize: function() {
19867 if (this.singleTile) {
19869 this.setTileSize();
19874 * APIMethod: getTileBounds
19875 * Returns The tile bounds for a layer given a pixel location.
19878 * viewPortPx - {<OpenLayers.Pixel>} The location in the viewport.
19881 * {<OpenLayers.Bounds>} Bounds of the tile at the given pixel location.
19883 getTileBounds: function(viewPortPx) {
19884 var maxExtent = this.maxExtent;
19885 var resolution = this.getResolution();
19886 var tileMapWidth = resolution * this.tileSize.w;
19887 var tileMapHeight = resolution * this.tileSize.h;
19888 var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);
19889 var tileLeft = maxExtent.left + (tileMapWidth *
19890 Math.floor((mapPoint.lon -
19893 var tileBottom = maxExtent.bottom + (tileMapHeight *
19894 Math.floor((mapPoint.lat -
19895 maxExtent.bottom) /
19897 return new OpenLayers.Bounds(tileLeft, tileBottom,
19898 tileLeft + tileMapWidth,
19899 tileBottom + tileMapHeight);
19902 CLASS_NAME: "OpenLayers.Layer.Grid"
19904 /* ======================================================================
19905 OpenLayers/Layer/XYZ.js
19906 ====================================================================== */
19908 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
19909 * full list of contributors). Published under the 2-clause BSD license.
19910 * See license.txt in the OpenLayers distribution or repository for the
19911 * full text of the license. */
19914 * @requires OpenLayers/Layer/Grid.js
19918 * Class: OpenLayers.Layer.XYZ
19919 * The XYZ class is designed to make it easier for people who have tiles
19920 * arranged by a standard XYZ grid.
19923 * - <OpenLayers.Layer.Grid>
19925 OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, {
19928 * APIProperty: isBaseLayer
19929 * Default is true, as this is designed to be a base tile source.
19934 * APIProperty: sphericalMercator
19935 * Whether the tile extents should be set to the defaults for
19936 * spherical mercator. Useful for things like OpenStreetMap.
19937 * Default is false, except for the OSM subclass.
19939 sphericalMercator: false,
19942 * APIProperty: zoomOffset
19943 * {Number} If your cache has more zoom levels than you want to provide
19944 * access to with this layer, supply a zoomOffset. This zoom offset
19945 * is added to the current map zoom level to determine the level
19946 * for a requested tile. For example, if you supply a zoomOffset
19947 * of 3, when the map is at the zoom 0, tiles will be requested from
19948 * level 3 of your cache. Default is 0 (assumes cache level and map
19949 * zoom are equivalent). Using <zoomOffset> is an alternative to
19950 * setting <serverResolutions> if you only want to expose a subset
19951 * of the server resolutions.
19956 * APIProperty: serverResolutions
19957 * {Array} A list of all resolutions available on the server. Only set this
19958 * property if the map resolutions differ from the server. This
19959 * property serves two purposes. (a) <serverResolutions> can include
19960 * resolutions that the server supports and that you don't want to
19961 * provide with this layer; you can also look at <zoomOffset>, which is
19962 * an alternative to <serverResolutions> for that specific purpose.
19963 * (b) The map can work with resolutions that aren't supported by
19964 * the server, i.e. that aren't in <serverResolutions>. When the
19965 * map is displayed in such a resolution data for the closest
19966 * server-supported resolution is loaded and the layer div is
19967 * stretched as necessary.
19969 serverResolutions: null,
19972 * Constructor: OpenLayers.Layer.XYZ
19977 * options - {Object} Hashtable of extra options to tag onto the layer
19979 initialize: function(name, url, options) {
19980 if (options && options.sphericalMercator || this.sphericalMercator) {
19981 options = OpenLayers.Util.extend({
19982 projection: "EPSG:900913",
19986 OpenLayers.Layer.Grid.prototype.initialize.apply(this, [
19987 name || this.name, url || this.url, {}, options
19993 * Create a clone of this layer
19996 * obj - {Object} Is this ever used?
19999 * {<OpenLayers.Layer.XYZ>} An exact clone of this OpenLayers.Layer.XYZ
20001 clone: function (obj) {
20004 obj = new OpenLayers.Layer.XYZ(this.name,
20006 this.getOptions());
20009 //get all additions from superclasses
20010 obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
20019 * bounds - {<OpenLayers.Bounds>}
20022 * {String} A string with the layer's url and parameters and also the
20023 * passed-in bounds and appropriate tile size specified as
20026 getURL: function (bounds) {
20027 var xyz = this.getXYZ(bounds);
20028 var url = this.url;
20029 if (OpenLayers.Util.isArray(url)) {
20030 var s = '' + xyz.x + xyz.y + xyz.z;
20031 url = this.selectUrl(s, url);
20034 return OpenLayers.String.format(url, xyz);
20039 * Calculates x, y and z for the given bounds.
20042 * bounds - {<OpenLayers.Bounds>}
20045 * {Object} - an object with x, y and z properties.
20047 getXYZ: function(bounds) {
20048 var res = this.getServerResolution();
20049 var x = Math.round((bounds.left - this.maxExtent.left) /
20050 (res * this.tileSize.w));
20051 var y = Math.round((this.maxExtent.top - bounds.top) /
20052 (res * this.tileSize.h));
20053 var z = this.getServerZoom();
20055 if (this.wrapDateLine) {
20056 var limit = Math.pow(2, z);
20057 x = ((x % limit) + limit) % limit;
20060 return {'x': x, 'y': y, 'z': z};
20063 /* APIMethod: setMap
20064 * When the layer is added to a map, then we can fetch our origin
20065 * (if we don't have one.)
20068 * map - {<OpenLayers.Map>}
20070 setMap: function(map) {
20071 OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);
20072 if (!this.tileOrigin) {
20073 this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left,
20074 this.maxExtent.bottom);
20078 CLASS_NAME: "OpenLayers.Layer.XYZ"
20080 /* ======================================================================
20081 OpenLayers/Layer/OSM.js
20082 ====================================================================== */
20084 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
20085 * full list of contributors). Published under the 2-clause BSD license.
20086 * See license.txt in the OpenLayers distribution or repository for the
20087 * full text of the license. */
20090 * @requires OpenLayers/Layer/XYZ.js
20094 * Class: OpenLayers.Layer.OSM
20095 * This layer allows accessing OpenStreetMap tiles. By default the OpenStreetMap
20096 * hosted tile.openstreetmap.org Mapnik tileset is used. If you wish to use
20097 * a different layer instead, you need to provide a different
20098 * URL to the constructor. Here's an example for using OpenCycleMap:
20101 * new OpenLayers.Layer.OSM("OpenCycleMap",
20102 * ["http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png",
20103 * "http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png",
20104 * "http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"]);
20108 * - <OpenLayers.Layer.XYZ>
20110 OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {
20113 * APIProperty: name
20114 * {String} The layer name. Defaults to "OpenStreetMap" if the first
20115 * argument to the constructor is null or undefined.
20117 name: "OpenStreetMap",
20121 * {String} The tileset URL scheme. Defaults to
20122 * : http://[a|b|c].tile.openstreetmap.org/${z}/${x}/${y}.png
20123 * (the official OSM tileset) if the second argument to the constructor
20124 * is null or undefined. To use another tileset you can have something
20127 * new OpenLayers.Layer.OSM("OpenCycleMap",
20128 * ["http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png",
20129 * "http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png",
20130 * "http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"]);
20134 'http://a.tile.openstreetmap.org/${z}/${x}/${y}.png',
20135 'http://b.tile.openstreetmap.org/${z}/${x}/${y}.png',
20136 'http://c.tile.openstreetmap.org/${z}/${x}/${y}.png'
20140 * Property: attribution
20141 * {String} The layer attribution.
20143 attribution: "© <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors",
20146 * Property: sphericalMercator
20149 sphericalMercator: true,
20152 * Property: wrapDateLine
20155 wrapDateLine: true,
20157 /** APIProperty: tileOptions
20158 * {Object} optional configuration options for <OpenLayers.Tile> instances
20159 * created by this Layer. Default is
20162 * {crossOriginKeyword: 'anonymous'}
20165 * When using OSM tilesets other than the default ones, it may be
20166 * necessary to set this to
20169 * {crossOriginKeyword: null}
20172 * if the server does not send Access-Control-Allow-Origin headers.
20177 * Constructor: OpenLayers.Layer.OSM
20180 * name - {String} The layer name.
20181 * url - {String} The tileset URL scheme.
20182 * options - {Object} Configuration options for the layer. Any inherited
20183 * layer option can be set in this object (e.g.
20184 * <OpenLayers.Layer.Grid.buffer>).
20186 initialize: function(name, url, options) {
20187 OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments);
20188 this.tileOptions = OpenLayers.Util.extend({
20189 crossOriginKeyword: 'anonymous'
20190 }, this.options && this.options.tileOptions);
20196 clone: function(obj) {
20198 obj = new OpenLayers.Layer.OSM(
20199 this.name, this.url, this.getOptions());
20201 obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);
20205 CLASS_NAME: "OpenLayers.Layer.OSM"
20207 /* ======================================================================
20208 OpenLayers/Renderer.js
20209 ====================================================================== */
20211 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
20212 * full list of contributors). Published under the 2-clause BSD license.
20213 * See license.txt in the OpenLayers distribution or repository for the
20214 * full text of the license. */
20217 * @requires OpenLayers/BaseTypes/Class.js
20221 * Class: OpenLayers.Renderer
20222 * This is the base class for all renderers.
20224 * This is based on a merger code written by Paul Spencer and Bertil Chapuis.
20225 * It is largely composed of virtual functions that are to be implemented
20226 * in technology-specific subclasses, but there is some generic code too.
20228 * The functions that *are* implemented here merely deal with the maintenance
20229 * of the size and extent variables, as well as the cached 'resolution'
20232 * A note to the user that all subclasses should use getResolution() instead
20233 * of directly accessing this.resolution in order to correctly use the
20237 OpenLayers.Renderer = OpenLayers.Class({
20240 * Property: container
20253 * {<OpenLayers.Bounds>}
20259 * {Boolean} If the renderer is currently in a state where many things
20260 * are changing, the 'locked' property is set to true. This means
20261 * that renderers can expect at least one more drawFeature event to be
20262 * called with the 'locked' property set to 'true': In some renderers,
20263 * this might make sense to use as a 'only update local information'
20270 * {<OpenLayers.Size>}
20275 * Property: resolution
20276 * {Float} cache of current map resolution
20282 * {<OpenLayers.Map>} Reference to the map -- this is set in Vector's setMap()
20287 * Property: featureDx
20288 * {Number} Feature offset in x direction. Will be calculated for and
20289 * applied to the current feature while rendering (see
20290 * <calculateFeatureDx>).
20295 * Constructor: OpenLayers.Renderer
20298 * containerID - {<String>}
20299 * options - {Object} options for this renderer. See sublcasses for
20300 * supported options.
20302 initialize: function(containerID, options) {
20303 this.container = OpenLayers.Util.getElement(containerID);
20304 OpenLayers.Util.extend(this, options);
20308 * APIMethod: destroy
20310 destroy: function() {
20311 this.container = null;
20312 this.extent = null;
20314 this.resolution = null;
20319 * APIMethod: supported
20320 * This should be overridden by specific subclasses
20323 * {Boolean} Whether or not the browser supports the renderer class
20325 supported: function() {
20330 * Method: setExtent
20331 * Set the visible part of the layer.
20333 * Resolution has probably changed, so we nullify the resolution
20334 * cache (this.resolution) -- this way it will be re-computed when
20335 * next it is needed.
20336 * We nullify the resolution cache (this.resolution) if resolutionChanged
20337 * is set to true - this way it will be re-computed on the next
20338 * getResolution() request.
20341 * extent - {<OpenLayers.Bounds>}
20342 * resolutionChanged - {Boolean}
20345 * {Boolean} true to notify the layer that the new extent does not exceed
20346 * the coordinate range, and the features will not need to be redrawn.
20349 setExtent: function(extent, resolutionChanged) {
20350 this.extent = extent.clone();
20351 if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
20352 var ratio = extent.getWidth() / this.map.getExtent().getWidth(),
20353 extent = extent.scale(1 / ratio);
20354 this.extent = extent.wrapDateLine(this.map.getMaxExtent()).scale(ratio);
20356 if (resolutionChanged) {
20357 this.resolution = null;
20364 * Sets the size of the drawing surface.
20366 * Resolution has probably changed, so we nullify the resolution
20367 * cache (this.resolution) -- this way it will be re-computed when
20368 * next it is needed.
20371 * size - {<OpenLayers.Size>}
20373 setSize: function(size) {
20374 this.size = size.clone();
20375 this.resolution = null;
20379 * Method: getResolution
20380 * Uses cached copy of resolution if available to minimize computing
20383 * {Float} The current map's resolution
20385 getResolution: function() {
20386 this.resolution = this.resolution || this.map.getResolution();
20387 return this.resolution;
20391 * Method: drawFeature
20392 * Draw the feature. The optional style argument can be used
20393 * to override the feature's own style. This method should only
20394 * be called from layer.drawFeature().
20397 * feature - {<OpenLayers.Feature.Vector>}
20398 * style - {<Object>}
20401 * {Boolean} true if the feature has been drawn completely, false if not,
20402 * undefined if the feature had no geometry
20404 drawFeature: function(feature, style) {
20405 if(style == null) {
20406 style = feature.style;
20408 if (feature.geometry) {
20409 var bounds = feature.geometry.getBounds();
20412 if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
20413 worldBounds = this.map.getMaxExtent();
20415 if (!bounds.intersectsBounds(this.extent, {worldBounds: worldBounds})) {
20416 style = {display: "none"};
20418 this.calculateFeatureDx(bounds, worldBounds);
20420 var rendered = this.drawGeometry(feature.geometry, style, feature.id);
20421 if(style.display != "none" && style.label && rendered !== false) {
20423 var location = feature.geometry.getCentroid();
20424 if(style.labelXOffset || style.labelYOffset) {
20425 var xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset;
20426 var yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset;
20427 var res = this.getResolution();
20428 location.move(xOffset*res, yOffset*res);
20430 this.drawText(feature.id, style, location);
20432 this.removeText(feature.id);
20440 * Method: calculateFeatureDx
20441 * {Number} Calculates the feature offset in x direction. Looking at the
20442 * center of the feature bounds and the renderer extent, we calculate how
20443 * many world widths the two are away from each other. This distance is
20444 * used to shift the feature as close as possible to the center of the
20445 * current enderer extent, which ensures that the feature is visible in the
20446 * current viewport.
20449 * bounds - {<OpenLayers.Bounds>} Bounds of the feature
20450 * worldBounds - {<OpenLayers.Bounds>} Bounds of the world
20452 calculateFeatureDx: function(bounds, worldBounds) {
20453 this.featureDx = 0;
20455 var worldWidth = worldBounds.getWidth(),
20456 rendererCenterX = (this.extent.left + this.extent.right) / 2,
20457 featureCenterX = (bounds.left + bounds.right) / 2,
20458 worldsAway = Math.round((featureCenterX - rendererCenterX) / worldWidth);
20459 this.featureDx = worldsAway * worldWidth;
20464 * Method: drawGeometry
20466 * Draw a geometry. This should only be called from the renderer itself.
20467 * Use layer.drawFeature() from outside the renderer.
20471 * geometry - {<OpenLayers.Geometry>}
20473 * featureId - {<String>}
20475 drawGeometry: function(geometry, style, featureId) {},
20479 * Function for drawing text labels.
20480 * This method is only called by the renderer itself.
20483 * featureId - {String}
20485 * location - {<OpenLayers.Geometry.Point>}
20487 drawText: function(featureId, style, location) {},
20490 * Method: removeText
20491 * Function for removing text labels.
20492 * This method is only called by the renderer itself.
20495 * featureId - {String}
20497 removeText: function(featureId) {},
20501 * Clear all vectors from the renderer.
20502 * virtual function.
20504 clear: function() {},
20507 * Method: getFeatureIdFromEvent
20508 * Returns a feature id from an event on the renderer.
20509 * How this happens is specific to the renderer. This should be
20510 * called from layer.getFeatureFromEvent().
20511 * Virtual function.
20514 * evt - {<OpenLayers.Event>}
20517 * {String} A feature id or undefined.
20519 getFeatureIdFromEvent: function(evt) {},
20522 * Method: eraseFeatures
20523 * This is called by the layer to erase features
20526 * features - {Array(<OpenLayers.Feature.Vector>)}
20528 eraseFeatures: function(features) {
20529 if(!(OpenLayers.Util.isArray(features))) {
20530 features = [features];
20532 for(var i=0, len=features.length; i<len; ++i) {
20533 var feature = features[i];
20534 this.eraseGeometry(feature.geometry, feature.id);
20535 this.removeText(feature.id);
20540 * Method: eraseGeometry
20541 * Remove a geometry from the renderer (by id).
20542 * virtual function.
20545 * geometry - {<OpenLayers.Geometry>}
20546 * featureId - {String}
20548 eraseGeometry: function(geometry, featureId) {},
20552 * moves this renderer's root to a (different) renderer.
20553 * To be implemented by subclasses that require a common renderer root for
20554 * feature selection.
20557 * renderer - {<OpenLayers.Renderer>} target renderer for the moved root
20559 moveRoot: function(renderer) {},
20562 * Method: getRenderLayerId
20563 * Gets the layer that this renderer's output appears on. If moveRoot was
20564 * used, this will be different from the id of the layer containing the
20565 * features rendered by this renderer.
20568 * {String} the id of the output layer.
20570 getRenderLayerId: function() {
20571 return this.container.id;
20575 * Method: applyDefaultSymbolizer
20578 * symbolizer - {Object}
20583 applyDefaultSymbolizer: function(symbolizer) {
20584 var result = OpenLayers.Util.extend({},
20585 OpenLayers.Renderer.defaultSymbolizer);
20586 if(symbolizer.stroke === false) {
20587 delete result.strokeWidth;
20588 delete result.strokeColor;
20590 if(symbolizer.fill === false) {
20591 delete result.fillColor;
20593 OpenLayers.Util.extend(result, symbolizer);
20597 CLASS_NAME: "OpenLayers.Renderer"
20601 * Constant: OpenLayers.Renderer.defaultSymbolizer
20602 * {Object} Properties from this symbolizer will be applied to symbolizers
20603 * with missing properties. This can also be used to set a global
20604 * symbolizer default in OpenLayers. To be SLD 1.x compliant, add the
20605 * following code before rendering any vector features:
20607 * OpenLayers.Renderer.defaultSymbolizer = {
20608 * fillColor: "#808080",
20610 * strokeColor: "#000000",
20611 * strokeOpacity: 1,
20614 * graphicName: "square"
20618 OpenLayers.Renderer.defaultSymbolizer = {
20619 fillColor: "#000000",
20620 strokeColor: "#000000",
20631 * Constant: OpenLayers.Renderer.symbol
20632 * Coordinate arrays for well known (named) symbols.
20634 OpenLayers.Renderer.symbol = {
20635 "star": [350,75, 379,161, 469,161, 397,215, 423,301, 350,250, 277,301,
20636 303,215, 231,161, 321,161, 350,75],
20637 "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,
20639 "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],
20640 "square": [0,0, 0,1, 1,1, 1,0, 0,0],
20641 "triangle": [0,10, 10,10, 5,0, 0,10]
20643 /* ======================================================================
20644 OpenLayers/Renderer/Canvas.js
20645 ====================================================================== */
20647 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
20648 * full list of contributors). Published under the 2-clause BSD license.
20649 * See license.txt in the OpenLayers distribution or repository for the
20650 * full text of the license. */
20653 * @requires OpenLayers/Renderer.js
20657 * Class: OpenLayers.Renderer.Canvas
20658 * A renderer based on the 2D 'canvas' drawing element.
20661 * - <OpenLayers.Renderer>
20663 OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {
20666 * APIProperty: hitDetection
20667 * {Boolean} Allow for hit detection of features. Default is true.
20669 hitDetection: true,
20672 * Property: hitOverflow
20673 * {Number} The method for converting feature identifiers to color values
20674 * supports 16777215 sequential values. Two features cannot be
20675 * predictably detected if their identifiers differ by more than this
20676 * value. The hitOverflow allows for bigger numbers (but the
20677 * difference in values is still limited).
20683 * {Canvas} The canvas context object.
20688 * Property: features
20689 * {Object} Internal object of feature/style pairs for use in redrawing the layer.
20694 * Property: pendingRedraw
20695 * {Boolean} The renderer needs a redraw call to render features added while
20696 * the renderer was locked.
20698 pendingRedraw: false,
20701 * Property: cachedSymbolBounds
20702 * {Object} Internal cache of calculated symbol extents.
20704 cachedSymbolBounds: {},
20707 * Constructor: OpenLayers.Renderer.Canvas
20710 * containerID - {<String>}
20711 * options - {Object} Optional properties to be set on the renderer.
20713 initialize: function(containerID, options) {
20714 OpenLayers.Renderer.prototype.initialize.apply(this, arguments);
20715 this.root = document.createElement("canvas");
20716 this.container.appendChild(this.root);
20717 this.canvas = this.root.getContext("2d");
20718 this.features = {};
20719 if (this.hitDetection) {
20720 this.hitCanvas = document.createElement("canvas");
20721 this.hitContext = this.hitCanvas.getContext("2d");
20726 * Method: setExtent
20727 * Set the visible part of the layer.
20730 * extent - {<OpenLayers.Bounds>}
20731 * resolutionChanged - {Boolean}
20734 * {Boolean} true to notify the layer that the new extent does not exceed
20735 * the coordinate range, and the features will not need to be redrawn.
20738 setExtent: function() {
20739 OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);
20740 // always redraw features
20745 * Method: eraseGeometry
20746 * Erase a geometry from the renderer. Because the Canvas renderer has
20747 * 'memory' of the features that it has drawn, we have to remove the
20748 * feature so it doesn't redraw.
20751 * geometry - {<OpenLayers.Geometry>}
20752 * featureId - {String}
20754 eraseGeometry: function(geometry, featureId) {
20755 this.eraseFeatures(this.features[featureId][0]);
20759 * APIMethod: supported
20762 * {Boolean} Whether or not the browser supports the renderer class
20764 supported: function() {
20765 return OpenLayers.CANVAS_SUPPORTED;
20770 * Sets the size of the drawing surface.
20772 * Once the size is updated, redraw the canvas.
20775 * size - {<OpenLayers.Size>}
20777 setSize: function(size) {
20778 this.size = size.clone();
20779 var root = this.root;
20780 root.style.width = size.w + "px";
20781 root.style.height = size.h + "px";
20782 root.width = size.w;
20783 root.height = size.h;
20784 this.resolution = null;
20785 if (this.hitDetection) {
20786 var hitCanvas = this.hitCanvas;
20787 hitCanvas.style.width = size.w + "px";
20788 hitCanvas.style.height = size.h + "px";
20789 hitCanvas.width = size.w;
20790 hitCanvas.height = size.h;
20795 * Method: drawFeature
20796 * Draw the feature. Stores the feature in the features list,
20797 * then redraws the layer.
20800 * feature - {<OpenLayers.Feature.Vector>}
20801 * style - {<Object>}
20804 * {Boolean} The feature has been drawn completely. If the feature has no
20805 * geometry, undefined will be returned. If the feature is not rendered
20806 * for other reasons, false will be returned.
20808 drawFeature: function(feature, style) {
20810 if (feature.geometry) {
20811 style = this.applyDefaultSymbolizer(style || feature.style);
20812 // don't render if display none or feature outside extent
20813 var bounds = feature.geometry.getBounds();
20816 if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
20817 worldBounds = this.map.getMaxExtent();
20820 var intersects = bounds && bounds.intersectsBounds(this.extent, {worldBounds: worldBounds});
20822 rendered = (style.display !== "none") && !!bounds && intersects;
20824 // keep track of what we have rendered for redraw
20825 this.features[feature.id] = [feature, style];
20828 // remove from features tracked for redraw
20829 delete(this.features[feature.id]);
20831 this.pendingRedraw = true;
20833 if (this.pendingRedraw && !this.locked) {
20835 this.pendingRedraw = false;
20841 * Method: drawGeometry
20842 * Used when looping (in redraw) over the features; draws
20846 * geometry - {<OpenLayers.Geometry>}
20849 drawGeometry: function(geometry, style, featureId) {
20850 var className = geometry.CLASS_NAME;
20851 if ((className == "OpenLayers.Geometry.Collection") ||
20852 (className == "OpenLayers.Geometry.MultiPoint") ||
20853 (className == "OpenLayers.Geometry.MultiLineString") ||
20854 (className == "OpenLayers.Geometry.MultiPolygon")) {
20855 for (var i = 0; i < geometry.components.length; i++) {
20856 this.drawGeometry(geometry.components[i], style, featureId);
20860 switch (geometry.CLASS_NAME) {
20861 case "OpenLayers.Geometry.Point":
20862 this.drawPoint(geometry, style, featureId);
20864 case "OpenLayers.Geometry.LineString":
20865 this.drawLineString(geometry, style, featureId);
20867 case "OpenLayers.Geometry.LinearRing":
20868 this.drawLinearRing(geometry, style, featureId);
20870 case "OpenLayers.Geometry.Polygon":
20871 this.drawPolygon(geometry, style, featureId);
20879 * Method: drawExternalGraphic
20880 * Called to draw External graphics.
20883 * geometry - {<OpenLayers.Geometry>}
20885 * featureId - {String}
20887 drawExternalGraphic: function(geometry, style, featureId) {
20888 var img = new Image();
20890 var title = style.title || style.graphicTitle;
20895 var width = style.graphicWidth || style.graphicHeight;
20896 var height = style.graphicHeight || style.graphicWidth;
20897 width = width ? width : style.pointRadius * 2;
20898 height = height ? height : style.pointRadius * 2;
20899 var xOffset = (style.graphicXOffset != undefined) ?
20900 style.graphicXOffset : -(0.5 * width);
20901 var yOffset = (style.graphicYOffset != undefined) ?
20902 style.graphicYOffset : -(0.5 * height);
20904 var opacity = style.graphicOpacity || style.fillOpacity;
20906 var onLoad = function() {
20907 if(!this.features[featureId]) {
20910 var pt = this.getLocalXY(geometry);
20913 if(!isNaN(p0) && !isNaN(p1)) {
20914 var x = (p0 + xOffset) | 0;
20915 var y = (p1 + yOffset) | 0;
20916 var canvas = this.canvas;
20917 canvas.globalAlpha = opacity;
20918 var factor = OpenLayers.Renderer.Canvas.drawImageScaleFactor ||
20919 (OpenLayers.Renderer.Canvas.drawImageScaleFactor =
20920 /android 2.1/.test(navigator.userAgent.toLowerCase()) ?
20921 // 320 is the screen width of the G1 phone, for
20922 // which drawImage works out of the box.
20923 320 / window.screen.width : 1
20926 img, x*factor, y*factor, width*factor, height*factor
20928 if (this.hitDetection) {
20929 this.setHitContextStyle("fill", featureId);
20930 this.hitContext.fillRect(x, y, width, height);
20935 img.onload = OpenLayers.Function.bind(onLoad, this);
20936 img.src = style.externalGraphic;
20940 * Method: drawNamedSymbol
20941 * Called to draw Well Known Graphic Symbol Name.
20942 * This method is only called by the renderer itself.
20945 * geometry - {<OpenLayers.Geometry>}
20947 * featureId - {String}
20949 drawNamedSymbol: function(geometry, style, featureId) {
20950 var x, y, cx, cy, i, symbolBounds, scaling, angle;
20951 var unscaledStrokeWidth;
20952 var deg2rad = Math.PI / 180.0;
20954 var symbol = OpenLayers.Renderer.symbol[style.graphicName];
20957 throw new Error(style.graphicName + ' is not a valid symbol name');
20960 if (!symbol.length || symbol.length < 2) return;
20962 var pt = this.getLocalXY(geometry);
20966 if (isNaN(p0) || isNaN(p1)) return;
20968 // Use rounded line caps
20969 this.canvas.lineCap = "round";
20970 this.canvas.lineJoin = "round";
20972 if (this.hitDetection) {
20973 this.hitContext.lineCap = "round";
20974 this.hitContext.lineJoin = "round";
20977 // Scale and rotate symbols, using precalculated bounds whenever possible.
20978 if (style.graphicName in this.cachedSymbolBounds) {
20979 symbolBounds = this.cachedSymbolBounds[style.graphicName];
20981 symbolBounds = new OpenLayers.Bounds();
20982 for(i = 0; i < symbol.length; i+=2) {
20983 symbolBounds.extend(new OpenLayers.LonLat(symbol[i], symbol[i+1]));
20985 this.cachedSymbolBounds[style.graphicName] = symbolBounds;
20988 // Push symbol scaling, translation and rotation onto the transformation stack in reverse order.
20989 // Don't forget to apply all canvas transformations to the hitContext canvas as well(!)
20990 this.canvas.save();
20991 if (this.hitDetection) { this.hitContext.save(); }
20993 // Step 3: place symbol at the desired location
20994 this.canvas.translate(p0,p1);
20995 if (this.hitDetection) { this.hitContext.translate(p0,p1); }
20997 // Step 2a. rotate the symbol if necessary
20998 angle = deg2rad * style.rotation; // will be NaN when style.rotation is undefined.
20999 if (!isNaN(angle)) {
21000 this.canvas.rotate(angle);
21001 if (this.hitDetection) { this.hitContext.rotate(angle); }
21004 // // Step 2: scale symbol such that pointRadius equals half the maximum symbol dimension.
21005 scaling = 2.0 * style.pointRadius / Math.max(symbolBounds.getWidth(), symbolBounds.getHeight());
21006 this.canvas.scale(scaling,scaling);
21007 if (this.hitDetection) { this.hitContext.scale(scaling,scaling); }
21009 // Step 1: center the symbol at the origin
21010 cx = symbolBounds.getCenterLonLat().lon;
21011 cy = symbolBounds.getCenterLonLat().lat;
21012 this.canvas.translate(-cx,-cy);
21013 if (this.hitDetection) { this.hitContext.translate(-cx,-cy); }
21015 // Don't forget to scale stroke widths, because they are affected by canvas scale transformations as well(!)
21016 // Alternative: scale symbol coordinates manually, so stroke width scaling is not needed anymore.
21017 unscaledStrokeWidth = style.strokeWidth;
21018 style.strokeWidth = unscaledStrokeWidth / scaling;
21020 if (style.fill !== false) {
21021 this.setCanvasStyle("fill", style);
21022 this.canvas.beginPath();
21023 for (i=0; i<symbol.length; i=i+2) {
21026 if (i == 0) this.canvas.moveTo(x,y);
21027 this.canvas.lineTo(x,y);
21029 this.canvas.closePath();
21030 this.canvas.fill();
21032 if (this.hitDetection) {
21033 this.setHitContextStyle("fill", featureId, style);
21034 this.hitContext.beginPath();
21035 for (i=0; i<symbol.length; i=i+2) {
21038 if (i == 0) this.canvas.moveTo(x,y);
21039 this.hitContext.lineTo(x,y);
21041 this.hitContext.closePath();
21042 this.hitContext.fill();
21046 if (style.stroke !== false) {
21047 this.setCanvasStyle("stroke", style);
21048 this.canvas.beginPath();
21049 for (i=0; i<symbol.length; i=i+2) {
21052 if (i == 0) this.canvas.moveTo(x,y);
21053 this.canvas.lineTo(x,y);
21055 this.canvas.closePath();
21056 this.canvas.stroke();
21059 if (this.hitDetection) {
21060 this.setHitContextStyle("stroke", featureId, style, scaling);
21061 this.hitContext.beginPath();
21062 for (i=0; i<symbol.length; i=i+2) {
21065 if (i == 0) this.hitContext.moveTo(x,y);
21066 this.hitContext.lineTo(x,y);
21068 this.hitContext.closePath();
21069 this.hitContext.stroke();
21074 style.strokeWidth = unscaledStrokeWidth;
21075 this.canvas.restore();
21076 if (this.hitDetection) { this.hitContext.restore(); }
21077 this.setCanvasStyle("reset");
21081 * Method: setCanvasStyle
21082 * Prepare the canvas for drawing by setting various global settings.
21085 * type - {String} one of 'stroke', 'fill', or 'reset'
21086 * style - {Object} Symbolizer hash
21088 setCanvasStyle: function(type, style) {
21089 if (type === "fill") {
21090 this.canvas.globalAlpha = style['fillOpacity'];
21091 this.canvas.fillStyle = style['fillColor'];
21092 } else if (type === "stroke") {
21093 this.canvas.globalAlpha = style['strokeOpacity'];
21094 this.canvas.strokeStyle = style['strokeColor'];
21095 this.canvas.lineWidth = style['strokeWidth'];
21097 this.canvas.globalAlpha = 0;
21098 this.canvas.lineWidth = 1;
21103 * Method: featureIdToHex
21104 * Convert a feature ID string into an RGB hex string.
21107 * featureId - {String} Feature id
21110 * {String} RGB hex string.
21112 featureIdToHex: function(featureId) {
21113 var id = Number(featureId.split("_").pop()) + 1; // zero for no feature
21114 if (id >= 16777216) {
21115 this.hitOverflow = id - 16777215;
21116 id = id % 16777216 + 1;
21118 var hex = "000000" + id.toString(16);
21119 var len = hex.length;
21120 hex = "#" + hex.substring(len-6, len);
21125 * Method: setHitContextStyle
21126 * Prepare the hit canvas for drawing by setting various global settings.
21129 * type - {String} one of 'stroke', 'fill', or 'reset'
21130 * featureId - {String} The feature id.
21131 * symbolizer - {<OpenLayers.Symbolizer>} The symbolizer.
21133 setHitContextStyle: function(type, featureId, symbolizer, strokeScaling) {
21134 var hex = this.featureIdToHex(featureId);
21135 if (type == "fill") {
21136 this.hitContext.globalAlpha = 1.0;
21137 this.hitContext.fillStyle = hex;
21138 } else if (type == "stroke") {
21139 this.hitContext.globalAlpha = 1.0;
21140 this.hitContext.strokeStyle = hex;
21141 // bump up stroke width to deal with antialiasing. If strokeScaling is defined, we're rendering a symbol
21142 // on a transformed canvas, so the antialias width bump has to scale as well.
21143 if (typeof strokeScaling === "undefined") {
21144 this.hitContext.lineWidth = symbolizer.strokeWidth + 2;
21146 if (!isNaN(strokeScaling)) { this.hitContext.lineWidth = symbolizer.strokeWidth + 2.0 / strokeScaling; }
21149 this.hitContext.globalAlpha = 0;
21150 this.hitContext.lineWidth = 1;
21155 * Method: drawPoint
21156 * This method is only called by the renderer itself.
21159 * geometry - {<OpenLayers.Geometry>}
21161 * featureId - {String}
21163 drawPoint: function(geometry, style, featureId) {
21164 if(style.graphic !== false) {
21165 if(style.externalGraphic) {
21166 this.drawExternalGraphic(geometry, style, featureId);
21167 } else if (style.graphicName && (style.graphicName != "circle")) {
21168 this.drawNamedSymbol(geometry, style, featureId);
21170 var pt = this.getLocalXY(geometry);
21173 if(!isNaN(p0) && !isNaN(p1)) {
21174 var twoPi = Math.PI*2;
21175 var radius = style.pointRadius;
21176 if(style.fill !== false) {
21177 this.setCanvasStyle("fill", style);
21178 this.canvas.beginPath();
21179 this.canvas.arc(p0, p1, radius, 0, twoPi, true);
21180 this.canvas.fill();
21181 if (this.hitDetection) {
21182 this.setHitContextStyle("fill", featureId, style);
21183 this.hitContext.beginPath();
21184 this.hitContext.arc(p0, p1, radius, 0, twoPi, true);
21185 this.hitContext.fill();
21189 if(style.stroke !== false) {
21190 this.setCanvasStyle("stroke", style);
21191 this.canvas.beginPath();
21192 this.canvas.arc(p0, p1, radius, 0, twoPi, true);
21193 this.canvas.stroke();
21194 if (this.hitDetection) {
21195 this.setHitContextStyle("stroke", featureId, style);
21196 this.hitContext.beginPath();
21197 this.hitContext.arc(p0, p1, radius, 0, twoPi, true);
21198 this.hitContext.stroke();
21200 this.setCanvasStyle("reset");
21208 * Method: drawLineString
21209 * This method is only called by the renderer itself.
21212 * geometry - {<OpenLayers.Geometry>}
21214 * featureId - {String}
21216 drawLineString: function(geometry, style, featureId) {
21217 style = OpenLayers.Util.applyDefaults({fill: false}, style);
21218 this.drawLinearRing(geometry, style, featureId);
21222 * Method: drawLinearRing
21223 * This method is only called by the renderer itself.
21226 * geometry - {<OpenLayers.Geometry>}
21228 * featureId - {String}
21230 drawLinearRing: function(geometry, style, featureId) {
21231 if (style.fill !== false) {
21232 this.setCanvasStyle("fill", style);
21233 this.renderPath(this.canvas, geometry, style, featureId, "fill");
21234 if (this.hitDetection) {
21235 this.setHitContextStyle("fill", featureId, style);
21236 this.renderPath(this.hitContext, geometry, style, featureId, "fill");
21239 if (style.stroke !== false) {
21240 this.setCanvasStyle("stroke", style);
21241 this.renderPath(this.canvas, geometry, style, featureId, "stroke");
21242 if (this.hitDetection) {
21243 this.setHitContextStyle("stroke", featureId, style);
21244 this.renderPath(this.hitContext, geometry, style, featureId, "stroke");
21247 this.setCanvasStyle("reset");
21251 * Method: renderPath
21252 * Render a path with stroke and optional fill.
21254 renderPath: function(context, geometry, style, featureId, type) {
21255 var components = geometry.components;
21256 var len = components.length;
21257 context.beginPath();
21258 var start = this.getLocalXY(components[0]);
21261 if (!isNaN(x) && !isNaN(y)) {
21262 context.moveTo(start[0], start[1]);
21263 for (var i=1; i<len; ++i) {
21264 var pt = this.getLocalXY(components[i]);
21265 context.lineTo(pt[0], pt[1]);
21267 if (type === "fill") {
21276 * Method: drawPolygon
21277 * This method is only called by the renderer itself.
21280 * geometry - {<OpenLayers.Geometry>}
21282 * featureId - {String}
21284 drawPolygon: function(geometry, style, featureId) {
21285 var components = geometry.components;
21286 var len = components.length;
21287 this.drawLinearRing(components[0], style, featureId);
21288 // erase inner rings
21289 for (var i=1; i<len; ++i) {
21291 * Note that this is overly agressive. Here we punch holes through
21292 * all previously rendered features on the same canvas. A better
21293 * solution for polygons with interior rings would be to draw the
21294 * polygon on a sketch canvas first. We could erase all holes
21295 * there and then copy the drawing to the layer canvas.
21296 * TODO: http://trac.osgeo.org/openlayers/ticket/3130
21298 this.canvas.globalCompositeOperation = "destination-out";
21299 if (this.hitDetection) {
21300 this.hitContext.globalCompositeOperation = "destination-out";
21302 this.drawLinearRing(
21304 OpenLayers.Util.applyDefaults({stroke: false, fillOpacity: 1.0}, style),
21307 this.canvas.globalCompositeOperation = "source-over";
21308 if (this.hitDetection) {
21309 this.hitContext.globalCompositeOperation = "source-over";
21311 this.drawLinearRing(
21313 OpenLayers.Util.applyDefaults({fill: false}, style),
21321 * This method is only called by the renderer itself.
21324 * location - {<OpenLayers.Point>}
21327 drawText: function(location, style) {
21328 var pt = this.getLocalXY(location);
21330 this.setCanvasStyle("reset");
21331 this.canvas.fillStyle = style.fontColor;
21332 this.canvas.globalAlpha = style.fontOpacity || 1.0;
21333 var fontStyle = [style.fontStyle ? style.fontStyle : "normal",
21334 "normal", // "font-variant" not supported
21335 style.fontWeight ? style.fontWeight : "normal",
21336 style.fontSize ? style.fontSize : "1em",
21337 style.fontFamily ? style.fontFamily : "sans-serif"].join(" ");
21338 var labelRows = style.label.split('\n');
21339 var numRows = labelRows.length;
21340 if (this.canvas.fillText) {
21342 this.canvas.font = fontStyle;
21343 this.canvas.textAlign =
21344 OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[0]] ||
21346 this.canvas.textBaseline =
21347 OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[1]] ||
21350 OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];
21351 if (vfactor == null) {
21355 this.canvas.measureText('Mg').height ||
21356 this.canvas.measureText('xx').width;
21357 pt[1] += lineHeight*vfactor*(numRows-1);
21358 for (var i = 0; i < numRows; i++) {
21359 if (style.labelOutlineWidth) {
21360 this.canvas.save();
21361 this.canvas.globalAlpha = style.labelOutlineOpacity || style.fontOpacity || 1.0;
21362 this.canvas.strokeStyle = style.labelOutlineColor;
21363 this.canvas.lineWidth = style.labelOutlineWidth;
21364 this.canvas.strokeText(labelRows[i], pt[0], pt[1] + (lineHeight*i) + 1);
21365 this.canvas.restore();
21367 this.canvas.fillText(labelRows[i], pt[0], pt[1] + (lineHeight*i));
21369 } else if (this.canvas.mozDrawText) {
21370 // Mozilla pre-Gecko1.9.1 (<FF3.1)
21371 this.canvas.mozTextStyle = fontStyle;
21372 // No built-in text alignment, so we measure and adjust the position
21374 OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[0]];
21375 if (hfactor == null) {
21379 OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];
21380 if (vfactor == null) {
21383 var lineHeight = this.canvas.mozMeasureText('xx');
21384 pt[1] += lineHeight*(1 + (vfactor*numRows));
21385 for (var i = 0; i < numRows; i++) {
21386 var x = pt[0] + (hfactor*this.canvas.mozMeasureText(labelRows[i]));
21387 var y = pt[1] + (i*lineHeight);
21388 this.canvas.translate(x, y);
21389 this.canvas.mozDrawText(labelRows[i]);
21390 this.canvas.translate(-x, -y);
21393 this.setCanvasStyle("reset");
21397 * Method: getLocalXY
21398 * transform geographic xy into pixel xy
21401 * point - {<OpenLayers.Geometry.Point>}
21403 getLocalXY: function(point) {
21404 var resolution = this.getResolution();
21405 var extent = this.extent;
21406 var x = ((point.x - this.featureDx) / resolution + (-extent.left / resolution));
21407 var y = ((extent.top / resolution) - point.y / resolution);
21413 * Clear all vectors from the renderer.
21415 clear: function() {
21416 var height = this.root.height;
21417 var width = this.root.width;
21418 this.canvas.clearRect(0, 0, width, height);
21419 this.features = {};
21420 if (this.hitDetection) {
21421 this.hitContext.clearRect(0, 0, width, height);
21426 * Method: getFeatureIdFromEvent
21427 * Returns a feature id from an event on the renderer.
21430 * evt - {<OpenLayers.Event>}
21433 * {<OpenLayers.Feature.Vector} A feature or undefined. This method returns a
21434 * feature instead of a feature id to avoid an unnecessary lookup on the
21437 getFeatureIdFromEvent: function(evt) {
21438 var featureId, feature;
21440 if (this.hitDetection && this.root.style.display !== "none") {
21441 // this dragging check should go in the feature handler
21442 if (!this.map.dragging) {
21446 var data = this.hitContext.getImageData(x, y, 1, 1).data;
21447 if (data[3] === 255) { // antialiased
21448 var id = data[2] + (256 * (data[1] + (256 * data[0])));
21450 featureId = "OpenLayers_Feature_Vector_" + (id - 1 + this.hitOverflow);
21452 feature = this.features[featureId][0];
21454 // Because of antialiasing on the canvas, when the hit location is at a point where the edge of
21455 // one symbol intersects the interior of another symbol, a wrong hit color (and therefore id) results.
21456 // todo: set Antialiasing = 'off' on the hitContext as soon as browsers allow it.
21466 * Method: eraseFeatures
21467 * This is called by the layer to erase features; removes the feature from
21468 * the list, then redraws the layer.
21471 * features - {Array(<OpenLayers.Feature.Vector>)}
21473 eraseFeatures: function(features) {
21474 if(!(OpenLayers.Util.isArray(features))) {
21475 features = [features];
21477 for(var i=0; i<features.length; ++i) {
21478 delete this.features[features[i].id];
21485 * The real 'meat' of the function: any time things have changed,
21486 * redraw() can be called to loop over all the data and (you guessed
21487 * it) redraw it. Unlike Elements-based Renderers, we can't interact
21488 * with things once they're drawn, to remove them, for example, so
21489 * instead we have to just clear everything and draw from scratch.
21491 redraw: function() {
21492 if (!this.locked) {
21493 var height = this.root.height;
21494 var width = this.root.width;
21495 this.canvas.clearRect(0, 0, width, height);
21496 if (this.hitDetection) {
21497 this.hitContext.clearRect(0, 0, width, height);
21500 var feature, geometry, style;
21501 var worldBounds = (this.map.baseLayer && this.map.baseLayer.wrapDateLine) && this.map.getMaxExtent();
21502 for (var id in this.features) {
21503 if (!this.features.hasOwnProperty(id)) { continue; }
21504 feature = this.features[id][0];
21505 geometry = feature.geometry;
21506 this.calculateFeatureDx(geometry.getBounds(), worldBounds);
21507 style = this.features[id][1];
21508 this.drawGeometry(geometry, style, feature.id);
21510 labelMap.push([feature, style]);
21514 for (var i=0, len=labelMap.length; i<len; ++i) {
21515 item = labelMap[i];
21516 this.drawText(item[0].geometry.getCentroid(), item[1]);
21521 CLASS_NAME: "OpenLayers.Renderer.Canvas"
21525 * Constant: OpenLayers.Renderer.Canvas.LABEL_ALIGN
21528 OpenLayers.Renderer.Canvas.LABEL_ALIGN = {
21536 * Constant: OpenLayers.Renderer.Canvas.LABEL_FACTOR
21539 OpenLayers.Renderer.Canvas.LABEL_FACTOR = {
21547 * Constant: OpenLayers.Renderer.Canvas.drawImageScaleFactor
21548 * {Number} Scale factor to apply to the canvas drawImage arguments. This
21549 * is always 1 except for Android 2.1 devices, to work around
21550 * http://code.google.com/p/android/issues/detail?id=5141.
21552 OpenLayers.Renderer.Canvas.drawImageScaleFactor = null;
21553 /* ======================================================================
21554 OpenLayers/Layer/Bing.js
21555 ====================================================================== */
21557 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
21558 * full list of contributors). Published under the 2-clause BSD license.
21559 * See license.txt in the OpenLayers distribution or repository for the
21560 * full text of the license. */
21563 * @requires OpenLayers/Layer/XYZ.js
21567 * Class: OpenLayers.Layer.Bing
21568 * Bing layer using direct tile access as provided by Bing Maps REST Services.
21569 * See http://msdn.microsoft.com/en-us/library/ff701713.aspx for more
21570 * information. Note: Terms of Service compliant use requires the map to be
21571 * configured with an <OpenLayers.Control.Attribution> control and the
21572 * attribution placed on or near the map.
21575 * - <OpenLayers.Layer.XYZ>
21577 OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, {
21581 * {String} API key for Bing maps, get your own key
21582 * at http://bingmapsportal.com/ .
21587 * Property: serverResolutions
21588 * {Array} the resolutions provided by the Bing servers.
21590 serverResolutions: [
21591 156543.03390625, 78271.516953125, 39135.7584765625,
21592 19567.87923828125, 9783.939619140625, 4891.9698095703125,
21593 2445.9849047851562, 1222.9924523925781, 611.4962261962891,
21594 305.74811309814453, 152.87405654907226, 76.43702827453613,
21595 38.218514137268066, 19.109257068634033, 9.554628534317017,
21596 4.777314267158508, 2.388657133579254, 1.194328566789627,
21597 0.5971642833948135, 0.29858214169740677, 0.14929107084870338,
21598 0.07464553542435169
21602 * Property: attributionTemplate
21605 attributionTemplate: '<span class="olBingAttribution ${type}">' +
21606 '<div><a target="_blank" href="http://www.bing.com/maps/">' +
21607 '<img src="${logo}" /></a></div>${copyrights}' +
21608 '<a style="white-space: nowrap" target="_blank" '+
21609 'href="http://www.microsoft.com/maps/product/terms.html">' +
21610 'Terms of Use</a></span>',
21613 * Property: metadata
21614 * {Object} Metadata for this layer, as returned by the callback script
21619 * Property: protocolRegex
21620 * {RegExp} Regular expression to match and replace http: in bing urls
21622 protocolRegex: /^http:/i,
21625 * APIProperty: type
21626 * {String} The layer identifier. Any non-birdseye imageryType
21627 * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be
21628 * used. Default is "Road".
21633 * APIProperty: culture
21634 * {String} The culture identifier. See http://msdn.microsoft.com/en-us/library/ff701709.aspx
21635 * for the definition and the possible values. Default is "en-US".
21640 * APIProperty: metadataParams
21641 * {Object} Optional url parameters for the Get Imagery Metadata request
21642 * as described here: http://msdn.microsoft.com/en-us/library/ff701716.aspx
21644 metadataParams: null,
21646 /** APIProperty: tileOptions
21647 * {Object} optional configuration options for <OpenLayers.Tile> instances
21648 * created by this Layer. Default is
21651 * {crossOriginKeyword: 'anonymous'}
21656 /** APIProperty: protocol
21657 * {String} Protocol to use to fetch Imagery Metadata, tiles and bing logo
21658 * Can be 'http:' 'https:' or ''
21660 * Warning: tiles may not be available under both HTTP and HTTPS protocols.
21661 * Microsoft approved use of both HTTP and HTTPS urls for tiles. However
21662 * this is undocumented and the Imagery Metadata API always returns HTTP
21665 * Default is '', unless when executed from a file:/// uri, in which case
21668 protocol: ~window.location.href.indexOf('http') ? '' : 'http:',
21671 * Constructor: OpenLayers.Layer.Bing
21672 * Create a new Bing layer.
21676 * var road = new OpenLayers.Layer.Bing({
21677 * name: "My Bing Aerial Layer",
21679 * key: "my-api-key-here",
21684 * options - {Object} Configuration properties for the layer.
21686 * Required configuration properties:
21687 * key - {String} Bing Maps API key for your application. Get one at
21688 * http://bingmapsportal.com/.
21689 * type - {String} The layer identifier. Any non-birdseye imageryType
21690 * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be
21693 * Any other documented layer properties can be provided in the config object.
21695 initialize: function(options) {
21696 options = OpenLayers.Util.applyDefaults({
21697 sphericalMercator: true
21699 var name = options.name || "Bing " + (options.type || this.type);
21701 var newArgs = [name, null, options];
21702 OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs);
21703 this.tileOptions = OpenLayers.Util.extend({
21704 crossOriginKeyword: 'anonymous'
21705 }, this.options.tileOptions);
21706 this.loadMetadata();
21710 * Method: loadMetadata
21712 loadMetadata: function() {
21713 this._callbackId = "_callback_" + this.id.replace(/\./g, "_");
21714 // link the processMetadata method to the global scope and bind it
21715 // to this instance
21716 window[this._callbackId] = OpenLayers.Function.bind(
21717 OpenLayers.Layer.Bing.processMetadata, this
21719 var params = OpenLayers.Util.applyDefaults({
21721 jsonp: this._callbackId,
21722 include: "ImageryProviders"
21723 }, this.metadataParams);
21724 var url = this.protocol + "//dev.virtualearth.net/REST/v1/Imagery/Metadata/" +
21725 this.type + "?" + OpenLayers.Util.getParameterString(params);
21726 var script = document.createElement("script");
21727 script.type = "text/javascript";
21729 script.id = this._callbackId;
21730 document.getElementsByTagName("head")[0].appendChild(script);
21734 * Method: initLayer
21736 * Sets layer properties according to the metadata provided by the API
21738 initLayer: function() {
21739 var res = this.metadata.resourceSets[0].resources[0];
21740 var url = res.imageUrl.replace("{quadkey}", "${quadkey}");
21741 url = url.replace("{culture}", this.culture);
21742 url = url.replace(this.protocolRegex, this.protocol);
21744 for (var i=0; i<res.imageUrlSubdomains.length; ++i) {
21745 this.url.push(url.replace("{subdomain}", res.imageUrlSubdomains[i]));
21748 maxResolution: Math.min(
21749 this.serverResolutions[res.zoomMin],
21750 this.maxResolution || Number.POSITIVE_INFINITY
21752 numZoomLevels: Math.min(
21753 res.zoomMax + 1 - res.zoomMin, this.numZoomLevels
21756 if (!this.isBaseLayer) {
21759 this.updateAttribution();
21766 * bounds - {<OpenLayers.Bounds>}
21768 getURL: function(bounds) {
21772 var xyz = this.getXYZ(bounds), x = xyz.x, y = xyz.y, z = xyz.z;
21773 var quadDigits = [];
21774 for (var i = z; i > 0; --i) {
21776 var mask = 1 << (i - 1);
21777 if ((x & mask) != 0) {
21780 if ((y & mask) != 0) {
21784 quadDigits.push(digit);
21786 var quadKey = quadDigits.join("");
21787 var url = this.selectUrl('' + x + y + z, this.url);
21789 return OpenLayers.String.format(url, {'quadkey': quadKey});
21793 * Method: updateAttribution
21794 * Updates the attribution according to the requirements outlined in
21795 * http://gis.638310.n2.nabble.com/Bing-imagery-td5789168.html
21797 updateAttribution: function() {
21798 var metadata = this.metadata;
21799 if (!metadata.resourceSets || !this.map || !this.map.center) {
21802 var res = metadata.resourceSets[0].resources[0];
21803 var extent = this.map.getExtent().transform(
21804 this.map.getProjectionObject(),
21805 new OpenLayers.Projection("EPSG:4326")
21807 var providers = res.imageryProviders || [],
21808 zoom = OpenLayers.Util.indexOf(this.serverResolutions,
21809 this.getServerResolution()),
21810 copyrights = "", provider, i, ii, j, jj, bbox, coverage;
21811 for (i=0,ii=providers.length; i<ii; ++i) {
21812 provider = providers[i];
21813 for (j=0,jj=provider.coverageAreas.length; j<jj; ++j) {
21814 coverage = provider.coverageAreas[j];
21815 // axis order provided is Y,X
21816 bbox = OpenLayers.Bounds.fromArray(coverage.bbox, true);
21817 if (extent.intersectsBounds(bbox) &&
21818 zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) {
21819 copyrights += provider.attribution + " ";
21823 var logo = metadata.brandLogoUri.replace(this.protocolRegex, this.protocol);
21824 this.attribution = OpenLayers.String.format(this.attributionTemplate, {
21825 type: this.type.toLowerCase(),
21827 copyrights: copyrights
21829 this.map && this.map.events.triggerEvent("changelayer", {
21831 property: "attribution"
21838 setMap: function() {
21839 OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments);
21840 this.map.events.register("moveend", this, this.updateAttribution);
21850 * {<OpenLayers.Layer.Bing>} An exact clone of this <OpenLayers.Layer.Bing>
21852 clone: function(obj) {
21854 obj = new OpenLayers.Layer.Bing(this.options);
21856 //get all additions from superclasses
21857 obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);
21858 // copy/set any non-init, non-simple values here
21865 destroy: function() {
21867 this.map.events.unregister("moveend", this, this.updateAttribution);
21868 OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments);
21871 CLASS_NAME: "OpenLayers.Layer.Bing"
21875 * Function: OpenLayers.Layer.Bing.processMetadata
21876 * This function will be bound to an instance, linked to the global scope with
21877 * an id, and called by the JSONP script returned by the API.
21880 * metadata - {Object} metadata as returned by the API
21882 OpenLayers.Layer.Bing.processMetadata = function(metadata) {
21883 this.metadata = metadata;
21885 var script = document.getElementById(this._callbackId);
21886 script.parentNode.removeChild(script);
21887 window[this._callbackId] = undefined; // cannot delete from window in IE
21888 delete this._callbackId;
21890 /* ======================================================================
21891 OpenLayers/Handler.js
21892 ====================================================================== */
21894 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
21895 * full list of contributors). Published under the 2-clause BSD license.
21896 * See license.txt in the OpenLayers distribution or repository for the
21897 * full text of the license. */
21900 * @requires OpenLayers/BaseTypes/Class.js
21901 * @requires OpenLayers/Events.js
21905 * Class: OpenLayers.Handler
21906 * Base class to construct a higher-level handler for event sequences. All
21907 * handlers have activate and deactivate methods. In addition, they have
21908 * methods named like browser events. When a handler is activated, any
21909 * additional methods named like a browser event is registered as a
21910 * listener for the corresponding event. When a handler is deactivated,
21911 * those same methods are unregistered as event listeners.
21913 * Handlers also typically have a callbacks object with keys named like
21914 * the abstracted events or event sequences that they are in charge of
21915 * handling. The controls that wrap handlers define the methods that
21916 * correspond to these abstract events - so instead of listening for
21917 * individual browser events, they only listen for the abstract events
21918 * defined by the handler.
21920 * Handlers are created by controls, which ultimately have the responsibility
21921 * of making changes to the the state of the application. Handlers
21922 * themselves may make temporary changes, but in general are expected to
21923 * return the application in the same state that they found it.
21925 OpenLayers.Handler = OpenLayers.Class({
21934 * APIProperty: control
21935 * {<OpenLayers.Control>}. The control that initialized this handler. The
21936 * control is assumed to have a valid map property - that map is used
21937 * in the handler's own setMap method.
21943 * {<OpenLayers.Map>}
21948 * APIProperty: keyMask
21949 * {Integer} Use bitwise operators and one or more of the OpenLayers.Handler
21950 * constants to construct a keyMask. The keyMask is used by
21951 * <checkModifiers>. If the keyMask matches the combination of keys
21952 * down on an event, checkModifiers returns true.
21956 * // handler only responds if the Shift key is down
21957 * handler.keyMask = OpenLayers.Handler.MOD_SHIFT;
21959 * // handler only responds if Ctrl-Shift is down
21960 * handler.keyMask = OpenLayers.Handler.MOD_SHIFT |
21961 * OpenLayers.Handler.MOD_CTRL;
21974 * {Event} This property references the last event handled by the handler.
21975 * Note that this property is not part of the stable API. Use of the
21976 * evt property should be restricted to controls in the library
21977 * or other applications that are willing to update with changes to
21978 * the OpenLayers code.
21984 * {Boolean} Indicates the support of touch events. When touch events are
21985 * started touch will be true and all mouse related listeners will do
21991 * Constructor: OpenLayers.Handler
21992 * Construct a handler.
21995 * control - {<OpenLayers.Control>} The control that initialized this
21996 * handler. The control is assumed to have a valid map property; that
21997 * map is used in the handler's own setMap method. If a map property
21998 * is present in the options argument it will be used instead.
21999 * callbacks - {Object} An object whose properties correspond to abstracted
22000 * events or sequences of browser events. The values for these
22001 * properties are functions defined by the control that get called by
22003 * options - {Object} An optional object whose properties will be set on
22006 initialize: function(control, callbacks, options) {
22007 OpenLayers.Util.extend(this, options);
22008 this.control = control;
22009 this.callbacks = callbacks;
22011 var map = this.map || control.map;
22016 this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
22022 setMap: function (map) {
22027 * Method: checkModifiers
22028 * Check the keyMask on the handler. If no <keyMask> is set, this always
22029 * returns true. If a <keyMask> is set and it matches the combination
22030 * of keys down on an event, this returns true.
22033 * {Boolean} The keyMask matches the keys down on an event.
22035 checkModifiers: function (evt) {
22036 if(this.keyMask == null) {
22039 /* calculate the keyboard modifier mask for this event */
22041 (evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) |
22042 (evt.ctrlKey ? OpenLayers.Handler.MOD_CTRL : 0) |
22043 (evt.altKey ? OpenLayers.Handler.MOD_ALT : 0) |
22044 (evt.metaKey ? OpenLayers.Handler.MOD_META : 0);
22046 /* if it differs from the handler object's key mask,
22047 bail out of the event handler */
22048 return (keyModifiers == this.keyMask);
22052 * APIMethod: activate
22053 * Turn on the handler. Returns false if the handler was already active.
22056 * {Boolean} The handler was activated.
22058 activate: function() {
22062 // register for event handlers defined on this class.
22063 var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
22064 for (var i=0, len=events.length; i<len; i++) {
22065 if (this[events[i]]) {
22066 this.register(events[i], this[events[i]]);
22069 this.active = true;
22074 * APIMethod: deactivate
22075 * Turn off the handler. Returns false if the handler was already inactive.
22078 * {Boolean} The handler was deactivated.
22080 deactivate: function() {
22084 // unregister event handlers defined on this class.
22085 var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
22086 for (var i=0, len=events.length; i<len; i++) {
22087 if (this[events[i]]) {
22088 this.unregister(events[i], this[events[i]]);
22091 this.touch = false;
22092 this.active = false;
22097 * Method: startTouch
22098 * Start touch events, this method must be called by subclasses in
22099 * "touchstart" method. When touch events are started <touch> will be
22100 * true and all mouse related listeners will do nothing.
22102 startTouch: function() {
22106 "mousedown", "mouseup", "mousemove", "click", "dblclick",
22109 for (var i=0, len=events.length; i<len; i++) {
22110 if (this[events[i]]) {
22111 this.unregister(events[i], this[events[i]]);
22119 * Trigger the control's named callback with the given arguments
22122 * name - {String} The key for the callback that is one of the properties
22123 * of the handler's callbacks object.
22124 * args - {Array(*)} An array of arguments (any type) with which to call
22125 * the callback (defined by the control).
22127 callback: function (name, args) {
22128 if (name && this.callbacks[name]) {
22129 this.callbacks[name].apply(this.control, args);
22135 * register an event on the map
22137 register: function (name, method) {
22138 // TODO: deal with registerPriority in 3.0
22139 this.map.events.registerPriority(name, this, method);
22140 this.map.events.registerPriority(name, this, this.setEvent);
22144 * Method: unregister
22145 * unregister an event from the map
22147 unregister: function (name, method) {
22148 this.map.events.unregister(name, this, method);
22149 this.map.events.unregister(name, this, this.setEvent);
22154 * With each registered browser event, the handler sets its own evt
22155 * property. This property can be accessed by controls if needed
22156 * to get more information about the event that the handler is
22159 * This allows modifier keys on the event to be checked (alt, shift, ctrl,
22160 * and meta cannot be checked with the keyboard handler). For a
22161 * control to determine which modifier keys are associated with the
22162 * event that a handler is currently processing, it should access
22163 * (code)handler.evt.altKey || handler.evt.shiftKey ||
22164 * handler.evt.ctrlKey || handler.evt.metaKey(end).
22167 * evt - {Event} The browser event.
22169 setEvent: function(evt) {
22176 * Deconstruct the handler.
22178 destroy: function () {
22179 // unregister event listeners
22181 // eliminate circular references
22182 this.control = this.map = null;
22185 CLASS_NAME: "OpenLayers.Handler"
22189 * Constant: OpenLayers.Handler.MOD_NONE
22190 * If set as the <keyMask>, <checkModifiers> returns false if any key is down.
22192 OpenLayers.Handler.MOD_NONE = 0;
22195 * Constant: OpenLayers.Handler.MOD_SHIFT
22196 * If set as the <keyMask>, <checkModifiers> returns false if Shift is down.
22198 OpenLayers.Handler.MOD_SHIFT = 1;
22201 * Constant: OpenLayers.Handler.MOD_CTRL
22202 * If set as the <keyMask>, <checkModifiers> returns false if Ctrl is down.
22204 OpenLayers.Handler.MOD_CTRL = 2;
22207 * Constant: OpenLayers.Handler.MOD_ALT
22208 * If set as the <keyMask>, <checkModifiers> returns false if Alt is down.
22210 OpenLayers.Handler.MOD_ALT = 4;
22213 * Constant: OpenLayers.Handler.MOD_META
22214 * If set as the <keyMask>, <checkModifiers> returns false if Cmd is down.
22216 OpenLayers.Handler.MOD_META = 8;
22219 /* ======================================================================
22220 OpenLayers/Handler/MouseWheel.js
22221 ====================================================================== */
22223 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
22224 * full list of contributors). Published under the 2-clause BSD license.
22225 * See license.txt in the OpenLayers distribution or repository for the
22226 * full text of the license. */
22229 * @requires OpenLayers/Handler.js
22233 * Class: OpenLayers.Handler.MouseWheel
22234 * Handler for wheel up/down events.
22237 * - <OpenLayers.Handler>
22239 OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, {
22241 * Property: wheelListener
22244 wheelListener: null,
22247 * Property: interval
22248 * {Integer} In order to increase server performance, an interval (in
22249 * milliseconds) can be set to reduce the number of up/down events
22250 * called. If set, a new up/down event will not be set until the
22251 * interval has passed.
22252 * Defaults to 0, meaning no interval.
22257 * Property: maxDelta
22258 * {Integer} Maximum delta to collect before breaking from the current
22259 * interval. In cumulative mode, this also limits the maximum delta
22260 * returned from the handler. Default is Number.POSITIVE_INFINITY.
22262 maxDelta: Number.POSITIVE_INFINITY,
22266 * {Integer} When interval is set, delta collects the mousewheel z-deltas
22267 * of the events that occur within the interval.
22268 * See also the cumulative option
22273 * Property: cumulative
22274 * {Boolean} When interval is set: true to collect all the mousewheel
22275 * z-deltas, false to only record the delta direction (positive or
22281 * Constructor: OpenLayers.Handler.MouseWheel
22284 * control - {<OpenLayers.Control>}
22285 * callbacks - {Object} An object containing a single function to be
22286 * called when the drag operation is finished.
22287 * The callback should expect to recieve a single
22288 * argument, the point geometry.
22289 * options - {Object}
22291 initialize: function(control, callbacks, options) {
22292 OpenLayers.Handler.prototype.initialize.apply(this, arguments);
22293 this.wheelListener = OpenLayers.Function.bindAsEventListener(
22294 this.onWheelEvent, this
22301 destroy: function() {
22302 OpenLayers.Handler.prototype.destroy.apply(this, arguments);
22303 this.wheelListener = null;
22307 * Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/
22311 * Method: onWheelEvent
22312 * Catch the wheel event and handle it xbrowserly
22317 onWheelEvent: function(e){
22319 // make sure we have a map and check keyboard modifiers
22320 if (!this.map || !this.checkModifiers(e)) {
22324 // Ride up the element's DOM hierarchy to determine if it or any of
22325 // its ancestors was:
22326 // * specifically marked as scrollable (CSS overflow property)
22327 // * one of our layer divs or a div marked as scrollable
22328 // ('olScrollable' CSS class)
22331 var overScrollableDiv = false;
22332 var allowScroll = false;
22333 var overMapDiv = false;
22335 var elem = OpenLayers.Event.element(e);
22336 while((elem != null) && !overMapDiv && !overScrollableDiv) {
22338 if (!overScrollableDiv) {
22341 if (elem.currentStyle) {
22342 overflow = elem.currentStyle["overflow"];
22345 document.defaultView.getComputedStyle(elem, null);
22346 overflow = style.getPropertyValue("overflow");
22348 overScrollableDiv = ( overflow &&
22349 (overflow == "auto") || (overflow == "scroll") );
22351 //sometimes when scrolling in a popup, this causes
22352 // obscure browser error
22356 if (!allowScroll) {
22357 allowScroll = OpenLayers.Element.hasClass(elem, 'olScrollable');
22358 if (!allowScroll) {
22359 for (var i = 0, len = this.map.layers.length; i < len; i++) {
22360 // Are we in the layer div? Note that we have two cases
22361 // here: one is to catch EventPane layers, which have a
22362 // pane above the layer (layer.pane)
22363 var layer = this.map.layers[i];
22364 if (elem == layer.div || elem == layer.pane) {
22365 allowScroll = true;
22371 overMapDiv = (elem == this.map.div);
22373 elem = elem.parentNode;
22376 // Logic below is the following:
22378 // If we are over a scrollable div or not over the map div:
22379 // * do nothing (let the browser handle scrolling)
22383 // If we are over the layer div or a 'olScrollable' div:
22386 // * kill event (so as not to also scroll the page after zooming)
22390 // Kill the event (dont scroll the page if we wheel over the
22391 // layerswitcher or the pan/zoom control)
22393 if (!overScrollableDiv && overMapDiv) {
22397 if (e.wheelDelta) {
22398 delta = e.wheelDelta;
22399 if (delta % 160 === 0) {
22400 // opera have steps of 160 instead of 120
22401 delta = delta * 0.75;
22403 delta = delta / 120;
22404 } else if (e.detail) {
22405 // detail in Firefox on OS X is 1/3 of Windows
22406 // so force delta 1 / -1
22407 delta = - (e.detail / Math.abs(e.detail));
22409 this.delta += delta;
22411 window.clearTimeout(this._timeoutId);
22412 if(this.interval && Math.abs(this.delta) < this.maxDelta) {
22413 // store e because window.event might change during delay
22414 var evt = OpenLayers.Util.extend({}, e);
22415 this._timeoutId = window.setTimeout(
22416 OpenLayers.Function.bind(function(){
22417 this.wheelZoom(evt);
22425 OpenLayers.Event.stop(e);
22430 * Method: wheelZoom
22431 * Given the wheel event, we carry out the appropriate zooming in or out,
22432 * based on the 'wheelDelta' or 'detail' property of the event.
22437 wheelZoom: function(e) {
22438 var delta = this.delta;
22442 e.xy = this.map.events.getMousePosition(e);
22444 this.callback("down",
22445 [e, this.cumulative ? Math.max(-this.maxDelta, delta) : -1]);
22447 this.callback("up",
22448 [e, this.cumulative ? Math.min(this.maxDelta, delta) : 1]);
22456 activate: function (evt) {
22457 if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
22458 //register mousewheel events specifically on the window and document
22459 var wheelListener = this.wheelListener;
22460 OpenLayers.Event.observe(window, "DOMMouseScroll", wheelListener);
22461 OpenLayers.Event.observe(window, "mousewheel", wheelListener);
22462 OpenLayers.Event.observe(document, "mousewheel", wheelListener);
22470 * Method: deactivate
22472 deactivate: function (evt) {
22473 if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
22474 // unregister mousewheel events specifically on the window and document
22475 var wheelListener = this.wheelListener;
22476 OpenLayers.Event.stopObserving(window, "DOMMouseScroll", wheelListener);
22477 OpenLayers.Event.stopObserving(window, "mousewheel", wheelListener);
22478 OpenLayers.Event.stopObserving(document, "mousewheel", wheelListener);
22485 CLASS_NAME: "OpenLayers.Handler.MouseWheel"
22487 /* ======================================================================
22488 OpenLayers/Geometry/MultiLineString.js
22489 ====================================================================== */
22491 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
22492 * full list of contributors). Published under the 2-clause BSD license.
22493 * See license.txt in the OpenLayers distribution or repository for the
22494 * full text of the license. */
22497 * @requires OpenLayers/Geometry/Collection.js
22498 * @requires OpenLayers/Geometry/LineString.js
22502 * Class: OpenLayers.Geometry.MultiLineString
22503 * A MultiLineString is a geometry with multiple <OpenLayers.Geometry.LineString>
22507 * - <OpenLayers.Geometry.Collection>
22508 * - <OpenLayers.Geometry>
22510 OpenLayers.Geometry.MultiLineString = OpenLayers.Class(
22511 OpenLayers.Geometry.Collection, {
22514 * Property: componentTypes
22515 * {Array(String)} An array of class names representing the types of
22516 * components that the collection can include. A null value means the
22517 * component types are not restricted.
22519 componentTypes: ["OpenLayers.Geometry.LineString"],
22522 * Constructor: OpenLayers.Geometry.MultiLineString
22523 * Constructor for a MultiLineString Geometry.
22526 * components - {Array(<OpenLayers.Geometry.LineString>)}
22532 * Use this geometry (the source) to attempt to split a target geometry.
22535 * geometry - {<OpenLayers.Geometry>} The target geometry.
22536 * options - {Object} Properties of this object will be used to determine
22537 * how the split is conducted.
22540 * mutual - {Boolean} Split the source geometry in addition to the target
22541 * geometry. Default is false.
22542 * edge - {Boolean} Allow splitting when only edges intersect. Default is
22543 * true. If false, a vertex on the source must be within the tolerance
22544 * distance of the intersection to be considered a split.
22545 * tolerance - {Number} If a non-null value is provided, intersections
22546 * within the tolerance distance of an existing vertex on the source
22547 * will be assumed to occur at the vertex.
22550 * {Array} A list of geometries (of this same type as the target) that
22551 * result from splitting the target with the source geometry. The
22552 * source and target geometry will remain unmodified. If no split
22553 * results, null will be returned. If mutual is true and a split
22554 * results, return will be an array of two arrays - the first will be
22555 * all geometries that result from splitting the source geometry and
22556 * the second will be all geometries that result from splitting the
22559 split: function(geometry, options) {
22560 var results = null;
22561 var mutual = options && options.mutual;
22562 var splits, sourceLine, sourceLines, sourceSplit, targetSplit;
22563 var sourceParts = [];
22564 var targetParts = [geometry];
22565 for(var i=0, len=this.components.length; i<len; ++i) {
22566 sourceLine = this.components[i];
22567 sourceSplit = false;
22568 for(var j=0; j < targetParts.length; ++j) {
22569 splits = sourceLine.split(targetParts[j], options);
22572 sourceLines = splits[0];
22573 for(var k=0, klen=sourceLines.length; k<klen; ++k) {
22574 if(k===0 && sourceParts.length) {
22575 sourceParts[sourceParts.length-1].addComponent(
22580 new OpenLayers.Geometry.MultiLineString([
22586 sourceSplit = true;
22587 splits = splits[1];
22589 if(splits.length) {
22590 // splice in new target parts
22591 splits.unshift(j, 1);
22592 Array.prototype.splice.apply(targetParts, splits);
22598 // source line was not hit
22599 if(sourceParts.length) {
22600 // add line to existing multi
22601 sourceParts[sourceParts.length-1].addComponent(
22605 // create a fresh multi
22607 new OpenLayers.Geometry.MultiLineString(
22614 if(sourceParts && sourceParts.length > 1) {
22615 sourceSplit = true;
22619 if(targetParts && targetParts.length > 1) {
22620 targetSplit = true;
22624 if(sourceSplit || targetSplit) {
22626 results = [sourceParts, targetParts];
22628 results = targetParts;
22635 * Method: splitWith
22636 * Split this geometry (the target) with the given geometry (the source).
22639 * geometry - {<OpenLayers.Geometry>} A geometry used to split this
22640 * geometry (the source).
22641 * options - {Object} Properties of this object will be used to determine
22642 * how the split is conducted.
22645 * mutual - {Boolean} Split the source geometry in addition to the target
22646 * geometry. Default is false.
22647 * edge - {Boolean} Allow splitting when only edges intersect. Default is
22648 * true. If false, a vertex on the source must be within the tolerance
22649 * distance of the intersection to be considered a split.
22650 * tolerance - {Number} If a non-null value is provided, intersections
22651 * within the tolerance distance of an existing vertex on the source
22652 * will be assumed to occur at the vertex.
22655 * {Array} A list of geometries (of this same type as the target) that
22656 * result from splitting the target with the source geometry. The
22657 * source and target geometry will remain unmodified. If no split
22658 * results, null will be returned. If mutual is true and a split
22659 * results, return will be an array of two arrays - the first will be
22660 * all geometries that result from splitting the source geometry and
22661 * the second will be all geometries that result from splitting the
22664 splitWith: function(geometry, options) {
22665 var results = null;
22666 var mutual = options && options.mutual;
22667 var splits, targetLine, sourceLines, sourceSplit, targetSplit, sourceParts, targetParts;
22668 if(geometry instanceof OpenLayers.Geometry.LineString) {
22670 sourceParts = [geometry];
22671 for(var i=0, len=this.components.length; i<len; ++i) {
22672 targetSplit = false;
22673 targetLine = this.components[i];
22674 for(var j=0; j<sourceParts.length; ++j) {
22675 splits = sourceParts[j].split(targetLine, options);
22678 sourceLines = splits[0];
22679 if(sourceLines.length) {
22680 // splice in new source parts
22681 sourceLines.unshift(j, 1);
22682 Array.prototype.splice.apply(sourceParts, sourceLines);
22683 j += sourceLines.length - 2;
22685 splits = splits[1];
22686 if(splits.length === 0) {
22687 splits = [targetLine.clone()];
22690 for(var k=0, klen=splits.length; k<klen; ++k) {
22691 if(k===0 && targetParts.length) {
22692 targetParts[targetParts.length-1].addComponent(
22697 new OpenLayers.Geometry.MultiLineString([
22703 targetSplit = true;
22707 // target component was not hit
22708 if(targetParts.length) {
22709 // add it to any existing multi-line
22710 targetParts[targetParts.length-1].addComponent(
22714 // or start with a fresh multi-line
22716 new OpenLayers.Geometry.MultiLineString([
22725 results = geometry.split(this);
22727 if(sourceParts && sourceParts.length > 1) {
22728 sourceSplit = true;
22732 if(targetParts && targetParts.length > 1) {
22733 targetSplit = true;
22737 if(sourceSplit || targetSplit) {
22739 results = [sourceParts, targetParts];
22741 results = targetParts;
22747 CLASS_NAME: "OpenLayers.Geometry.MultiLineString"
22749 /* ======================================================================
22750 OpenLayers/Format.js
22751 ====================================================================== */
22753 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
22754 * full list of contributors). Published under the 2-clause BSD license.
22755 * See license.txt in the OpenLayers distribution or repository for the
22756 * full text of the license. */
22759 * @requires OpenLayers/BaseTypes/Class.js
22760 * @requires OpenLayers/Util.js
22764 * Class: OpenLayers.Format
22765 * Base class for format reading/writing a variety of formats. Subclasses
22766 * of OpenLayers.Format are expected to have read and write methods.
22768 OpenLayers.Format = OpenLayers.Class({
22771 * Property: options
22772 * {Object} A reference to options passed to the constructor.
22777 * APIProperty: externalProjection
22778 * {<OpenLayers.Projection>} When passed a externalProjection and
22779 * internalProjection, the format will reproject the geometries it
22780 * reads or writes. The externalProjection is the projection used by
22781 * the content which is passed into read or which comes out of write.
22782 * In order to reproject, a projection transformation function for the
22783 * specified projections must be available. This support may be
22784 * provided via proj4js or via a custom transformation function. See
22785 * {<OpenLayers.Projection.addTransform>} for more information on
22786 * custom transformations.
22788 externalProjection: null,
22791 * APIProperty: internalProjection
22792 * {<OpenLayers.Projection>} When passed a externalProjection and
22793 * internalProjection, the format will reproject the geometries it
22794 * reads or writes. The internalProjection is the projection used by
22795 * the geometries which are returned by read or which are passed into
22796 * write. In order to reproject, a projection transformation function
22797 * for the specified projections must be available. This support may be
22798 * provided via proj4js or via a custom transformation function. See
22799 * {<OpenLayers.Projection.addTransform>} for more information on
22800 * custom transformations.
22802 internalProjection: null,
22805 * APIProperty: data
22806 * {Object} When <keepData> is true, this is the parsed string sent to
22812 * APIProperty: keepData
22813 * {Object} Maintain a reference (<data>) to the most recently read data.
22814 * Default is false.
22819 * Constructor: OpenLayers.Format
22820 * Instances of this class are not useful. See one of the subclasses.
22823 * options - {Object} An optional object with properties to set on the
22827 * keepData - {Boolean} If true, upon <read>, the data property will be
22828 * set to the parsed object (e.g. the json or xml object).
22831 * An instance of OpenLayers.Format
22833 initialize: function(options) {
22834 OpenLayers.Util.extend(this, options);
22835 this.options = options;
22839 * APIMethod: destroy
22842 destroy: function() {
22847 * Read data from a string, and return an object whose type depends on the
22851 * data - {string} Data to read/parse.
22854 * Depends on the subclass
22856 read: function(data) {
22857 throw new Error('Read not implemented.');
22862 * Accept an object, and return a string.
22865 * object - {Object} Object to be serialized
22868 * {String} A string representation of the object.
22870 write: function(object) {
22871 throw new Error('Write not implemented.');
22874 CLASS_NAME: "OpenLayers.Format"
22876 /* ======================================================================
22877 OpenLayers/Format/XML.js
22878 ====================================================================== */
22880 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
22881 * full list of contributors). Published under the 2-clause BSD license.
22882 * See license.txt in the OpenLayers distribution or repository for the
22883 * full text of the license. */
22886 * @requires OpenLayers/Format.js
22890 * Class: OpenLayers.Format.XML
22891 * Read and write XML. For cross-browser XML generation, use methods on an
22892 * instance of the XML format class instead of on <code>document<end>.
22893 * The DOM creation and traversing methods exposed here all mimic the
22894 * W3C XML DOM methods. Create a new parser with the
22895 * <OpenLayers.Format.XML> constructor.
22898 * - <OpenLayers.Format>
22900 OpenLayers.Format.XML = OpenLayers.Class(OpenLayers.Format, {
22903 * Property: namespaces
22904 * {Object} Mapping of namespace aliases to namespace URIs. Properties
22905 * of this object should not be set individually. Read-only. All
22906 * XML subclasses should have their own namespaces object. Use
22907 * <setNamespace> to add or set a namespace alias after construction.
22912 * Property: namespaceAlias
22913 * {Object} Mapping of namespace URI to namespace alias. This object
22914 * is read-only. Use <setNamespace> to add or set a namespace alias.
22916 namespaceAlias: null,
22919 * Property: defaultPrefix
22920 * {String} The default namespace alias for creating element nodes.
22922 defaultPrefix: null,
22925 * Property: readers
22926 * Contains public functions, grouped by namespace prefix, that will
22927 * be applied when a namespaced node is found matching the function
22928 * name. The function will be applied in the scope of this parser
22929 * with two arguments: the node being read and a context object passed
22935 * Property: writers
22936 * As a compliment to the <readers> property, this structure contains public
22937 * writing functions grouped by namespace alias and named like the
22938 * node names they produce.
22944 * {XMLDom} If this browser uses ActiveX, this will be set to a XMLDOM
22945 * object. It is not intended to be a browser sniffing property.
22946 * Instead, the xmldom property is used instead of <code>document<end>
22947 * where namespaced node creation methods are not supported. In all
22948 * other browsers, this remains null.
22953 * Constructor: OpenLayers.Format.XML
22954 * Construct an XML parser. The parser is used to read and write XML.
22955 * Reading XML from a string returns a DOM element. Writing XML from
22956 * a DOM element returns a string.
22959 * options - {Object} Optional object whose properties will be set on
22962 initialize: function(options) {
22963 if(window.ActiveXObject) {
22964 this.xmldom = new ActiveXObject("Microsoft.XMLDOM");
22966 OpenLayers.Format.prototype.initialize.apply(this, [options]);
22967 // clone the namespace object and set all namespace aliases
22968 this.namespaces = OpenLayers.Util.extend({}, this.namespaces);
22969 this.namespaceAlias = {};
22970 for(var alias in this.namespaces) {
22971 this.namespaceAlias[this.namespaces[alias]] = alias;
22976 * APIMethod: destroy
22979 destroy: function() {
22980 this.xmldom = null;
22981 OpenLayers.Format.prototype.destroy.apply(this, arguments);
22985 * Method: setNamespace
22986 * Set a namespace alias and URI for the format.
22989 * alias - {String} The namespace alias (prefix).
22990 * uri - {String} The namespace URI.
22992 setNamespace: function(alias, uri) {
22993 this.namespaces[alias] = uri;
22994 this.namespaceAlias[uri] = alias;
22999 * Deserialize a XML string and return a DOM node.
23002 * text - {String} A XML string
23005 * {DOMElement} A DOM node
23007 read: function(text) {
23008 var index = text.indexOf('<');
23010 text = text.substring(index);
23012 var node = OpenLayers.Util.Try(
23013 OpenLayers.Function.bind((
23017 * Since we want to be able to call this method on the prototype
23018 * itself, this.xmldom may not exist even if in IE.
23020 if(window.ActiveXObject && !this.xmldom) {
23021 xmldom = new ActiveXObject("Microsoft.XMLDOM");
23023 xmldom = this.xmldom;
23026 xmldom.loadXML(text);
23031 return new DOMParser().parseFromString(text, 'text/xml');
23034 var req = new XMLHttpRequest();
23035 req.open("GET", "data:" + "text/xml" +
23036 ";charset=utf-8," + encodeURIComponent(text), false);
23037 if(req.overrideMimeType) {
23038 req.overrideMimeType("text/xml");
23041 return req.responseXML;
23045 if(this.keepData) {
23054 * Serialize a DOM node into a XML string.
23057 * node - {DOMElement} A DOM node.
23060 * {String} The XML string representation of the input node.
23062 write: function(node) {
23067 var serializer = new XMLSerializer();
23068 if (node.nodeType == 1) {
23069 // Add nodes to a document before serializing. Everything else
23070 // is serialized as is. This may need more work. See #1218 .
23071 var doc = document.implementation.createDocument("", "", null);
23072 if (doc.importNode) {
23073 node = doc.importNode(node, true);
23075 doc.appendChild(node);
23076 data = serializer.serializeToString(doc);
23078 data = serializer.serializeToString(node);
23085 * APIMethod: createElementNS
23086 * Create a new element with namespace. This node can be appended to
23087 * another node with the standard node.appendChild method. For
23088 * cross-browser support, this method must be used instead of
23089 * document.createElementNS.
23092 * uri - {String} Namespace URI for the element.
23093 * name - {String} The qualified name of the element (prefix:localname).
23096 * {Element} A DOM element with namespace.
23098 createElementNS: function(uri, name) {
23101 if(typeof uri == "string") {
23102 element = this.xmldom.createNode(1, name, uri);
23104 element = this.xmldom.createNode(1, name, "");
23107 element = document.createElementNS(uri, name);
23113 * APIMethod: createDocumentFragment
23114 * Create a document fragment node that can be appended to another node
23115 * created by createElementNS. This will call
23116 * document.createDocumentFragment outside of IE. In IE, the ActiveX
23117 * object's createDocumentFragment method is used.
23120 * {Element} A document fragment.
23122 createDocumentFragment: function() {
23125 element = this.xmldom.createDocumentFragment();
23127 element = document.createDocumentFragment();
23133 * APIMethod: createTextNode
23134 * Create a text node. This node can be appended to another node with
23135 * the standard node.appendChild method. For cross-browser support,
23136 * this method must be used instead of document.createTextNode.
23139 * text - {String} The text of the node.
23142 * {DOMElement} A DOM text node.
23144 createTextNode: function(text) {
23146 if (typeof text !== "string") {
23147 text = String(text);
23150 node = this.xmldom.createTextNode(text);
23152 node = document.createTextNode(text);
23158 * APIMethod: getElementsByTagNameNS
23159 * Get a list of elements on a node given the namespace URI and local name.
23160 * To return all nodes in a given namespace, use '*' for the name
23161 * argument. To return all nodes of a given (local) name, regardless
23162 * of namespace, use '*' for the uri argument.
23165 * node - {Element} Node on which to search for other nodes.
23166 * uri - {String} Namespace URI.
23167 * name - {String} Local name of the tag (without the prefix).
23170 * {NodeList} A node list or array of elements.
23172 getElementsByTagNameNS: function(node, uri, name) {
23174 if(node.getElementsByTagNameNS) {
23175 elements = node.getElementsByTagNameNS(uri, name);
23177 // brute force method
23178 var allNodes = node.getElementsByTagName("*");
23179 var potentialNode, fullName;
23180 for(var i=0, len=allNodes.length; i<len; ++i) {
23181 potentialNode = allNodes[i];
23182 fullName = (potentialNode.prefix) ?
23183 (potentialNode.prefix + ":" + name) : name;
23184 if((name == "*") || (fullName == potentialNode.nodeName)) {
23185 if((uri == "*") || (uri == potentialNode.namespaceURI)) {
23186 elements.push(potentialNode);
23195 * APIMethod: getAttributeNodeNS
23196 * Get an attribute node given the namespace URI and local name.
23199 * node - {Element} Node on which to search for attribute nodes.
23200 * uri - {String} Namespace URI.
23201 * name - {String} Local name of the attribute (without the prefix).
23204 * {DOMElement} An attribute node or null if none found.
23206 getAttributeNodeNS: function(node, uri, name) {
23207 var attributeNode = null;
23208 if(node.getAttributeNodeNS) {
23209 attributeNode = node.getAttributeNodeNS(uri, name);
23211 var attributes = node.attributes;
23212 var potentialNode, fullName;
23213 for(var i=0, len=attributes.length; i<len; ++i) {
23214 potentialNode = attributes[i];
23215 if(potentialNode.namespaceURI == uri) {
23216 fullName = (potentialNode.prefix) ?
23217 (potentialNode.prefix + ":" + name) : name;
23218 if(fullName == potentialNode.nodeName) {
23219 attributeNode = potentialNode;
23225 return attributeNode;
23229 * APIMethod: getAttributeNS
23230 * Get an attribute value given the namespace URI and local name.
23233 * node - {Element} Node on which to search for an attribute.
23234 * uri - {String} Namespace URI.
23235 * name - {String} Local name of the attribute (without the prefix).
23238 * {String} An attribute value or and empty string if none found.
23240 getAttributeNS: function(node, uri, name) {
23241 var attributeValue = "";
23242 if(node.getAttributeNS) {
23243 attributeValue = node.getAttributeNS(uri, name) || "";
23245 var attributeNode = this.getAttributeNodeNS(node, uri, name);
23246 if(attributeNode) {
23247 attributeValue = attributeNode.nodeValue;
23250 return attributeValue;
23254 * APIMethod: getChildValue
23255 * Get the textual value of the node if it exists, or return an
23256 * optional default string. Returns an empty string if no first child
23257 * exists and no default value is supplied.
23260 * node - {DOMElement} The element used to look for a first child value.
23261 * def - {String} Optional string to return in the event that no
23262 * first child value exists.
23265 * {String} The value of the first child of the given node.
23267 getChildValue: function(node, def) {
23268 var value = def || "";
23270 for(var child=node.firstChild; child; child=child.nextSibling) {
23271 switch(child.nodeType) {
23272 case 3: // text node
23273 case 4: // cdata section
23274 value += child.nodeValue;
23282 * APIMethod: isSimpleContent
23283 * Test if the given node has only simple content (i.e. no child element
23287 * node - {DOMElement} An element node.
23290 * {Boolean} The node has no child element nodes (nodes of type 1).
23292 isSimpleContent: function(node) {
23294 for(var child=node.firstChild; child; child=child.nextSibling) {
23295 if(child.nodeType === 1) {
23304 * APIMethod: contentType
23305 * Determine the content type for a given node.
23308 * node - {DOMElement}
23311 * {Integer} One of OpenLayers.Format.XML.CONTENT_TYPE.{EMPTY,SIMPLE,COMPLEX,MIXED}
23312 * if the node has no, simple, complex, or mixed content.
23314 contentType: function(node) {
23315 var simple = false,
23318 var type = OpenLayers.Format.XML.CONTENT_TYPE.EMPTY;
23320 for(var child=node.firstChild; child; child=child.nextSibling) {
23321 switch(child.nodeType) {
23330 if(complex && simple) {
23335 if(complex && simple) {
23336 type = OpenLayers.Format.XML.CONTENT_TYPE.MIXED;
23337 } else if(complex) {
23338 return OpenLayers.Format.XML.CONTENT_TYPE.COMPLEX;
23339 } else if(simple) {
23340 return OpenLayers.Format.XML.CONTENT_TYPE.SIMPLE;
23346 * APIMethod: hasAttributeNS
23347 * Determine whether a node has a particular attribute matching the given
23348 * name and namespace.
23351 * node - {Element} Node on which to search for an attribute.
23352 * uri - {String} Namespace URI.
23353 * name - {String} Local name of the attribute (without the prefix).
23356 * {Boolean} The node has an attribute matching the name and namespace.
23358 hasAttributeNS: function(node, uri, name) {
23360 if(node.hasAttributeNS) {
23361 found = node.hasAttributeNS(uri, name);
23363 found = !!this.getAttributeNodeNS(node, uri, name);
23369 * APIMethod: setAttributeNS
23370 * Adds a new attribute or changes the value of an attribute with the given
23371 * namespace and name.
23374 * node - {Element} Element node on which to set the attribute.
23375 * uri - {String} Namespace URI for the attribute.
23376 * name - {String} Qualified name (prefix:localname) for the attribute.
23377 * value - {String} Attribute value.
23379 setAttributeNS: function(node, uri, name, value) {
23380 if(node.setAttributeNS) {
23381 node.setAttributeNS(uri, name, value);
23385 var attribute = node.ownerDocument.createNode(
23388 attribute.nodeValue = value;
23389 node.setAttributeNode(attribute);
23391 node.setAttribute(name, value);
23394 throw "setAttributeNS not implemented";
23400 * Method: createElementNSPlus
23401 * Shorthand for creating namespaced elements with optional attributes and
23402 * child text nodes.
23405 * name - {String} The qualified node name.
23406 * options - {Object} Optional object for node configuration.
23409 * uri - {String} Optional namespace uri for the element - supply a prefix
23410 * instead if the namespace uri is a property of the format's namespace
23412 * attributes - {Object} Optional attributes to be set using the
23413 * <setAttributes> method.
23414 * value - {String} Optional text to be appended as a text node.
23417 * {Element} An element node.
23419 createElementNSPlus: function(name, options) {
23420 options = options || {};
23421 // order of prefix preference
23422 // 1. in the uri option
23423 // 2. in the prefix option
23424 // 3. in the qualified name
23425 // 4. from the defaultPrefix
23426 var uri = options.uri || this.namespaces[options.prefix];
23428 var loc = name.indexOf(":");
23429 uri = this.namespaces[name.substring(0, loc)];
23432 uri = this.namespaces[this.defaultPrefix];
23434 var node = this.createElementNS(uri, name);
23435 if(options.attributes) {
23436 this.setAttributes(node, options.attributes);
23438 var value = options.value;
23439 if(value != null) {
23440 node.appendChild(this.createTextNode(value));
23446 * Method: setAttributes
23447 * Set multiple attributes given key value pairs from an object.
23450 * node - {Element} An element node.
23451 * obj - {Object || Array} An object whose properties represent attribute
23452 * names and values represent attribute values. If an attribute name
23453 * is a qualified name ("prefix:local"), the prefix will be looked up
23454 * in the parsers {namespaces} object. If the prefix is found,
23455 * setAttributeNS will be used instead of setAttribute.
23457 setAttributes: function(node, obj) {
23459 for(var name in obj) {
23460 if(obj[name] != null && obj[name].toString) {
23461 value = obj[name].toString();
23462 // check for qualified attribute name ("prefix:local")
23463 uri = this.namespaces[name.substring(0, name.indexOf(":"))] || null;
23464 this.setAttributeNS(node, uri, name, value);
23471 * Shorthand for applying one of the named readers given the node
23472 * namespace and local name. Readers take two args (node, obj) and
23473 * generally extend or modify the second.
23476 * node - {DOMElement} The node to be read (required).
23477 * obj - {Object} The object to be modified (optional).
23480 * {Object} The input object, modified (or a new one if none was provided).
23482 readNode: function(node, obj) {
23486 var group = this.readers[node.namespaceURI ? this.namespaceAlias[node.namespaceURI]: this.defaultPrefix];
23488 var local = node.localName || node.nodeName.split(":").pop();
23489 var reader = group[local] || group["*"];
23491 reader.apply(this, [node, obj]);
23498 * Method: readChildNodes
23499 * Shorthand for applying the named readers to all children of a node.
23500 * For each child of type 1 (element), <readSelf> is called.
23503 * node - {DOMElement} The node to be read (required).
23504 * obj - {Object} The object to be modified (optional).
23507 * {Object} The input object, modified.
23509 readChildNodes: function(node, obj) {
23513 var children = node.childNodes;
23515 for(var i=0, len=children.length; i<len; ++i) {
23516 child = children[i];
23517 if(child.nodeType == 1) {
23518 this.readNode(child, obj);
23525 * Method: writeNode
23526 * Shorthand for applying one of the named writers and appending the
23527 * results to a node. If a qualified name is not provided for the
23528 * second argument (and a local name is used instead), the namespace
23529 * of the parent node will be assumed.
23532 * name - {String} The name of a node to generate. If a qualified name
23533 * (e.g. "pre:Name") is used, the namespace prefix is assumed to be
23534 * in the <writers> group. If a local name is used (e.g. "Name") then
23535 * the namespace of the parent is assumed. If a local name is used
23536 * and no parent is supplied, then the default namespace is assumed.
23537 * obj - {Object} Structure containing data for the writer.
23538 * parent - {DOMElement} Result will be appended to this node. If no parent
23539 * is supplied, the node will not be appended to anything.
23542 * {DOMElement} The child node.
23544 writeNode: function(name, obj, parent) {
23546 var split = name.indexOf(":");
23548 prefix = name.substring(0, split);
23549 local = name.substring(split + 1);
23552 prefix = this.namespaceAlias[parent.namespaceURI];
23554 prefix = this.defaultPrefix;
23558 var child = this.writers[prefix][local].apply(this, [obj]);
23560 parent.appendChild(child);
23566 * APIMethod: getChildEl
23567 * Get the first child element. Optionally only return the first child
23568 * if it matches the given name and namespace URI.
23571 * node - {DOMElement} The parent node.
23572 * name - {String} Optional node name (local) to search for.
23573 * uri - {String} Optional namespace URI to search for.
23576 * {DOMElement} The first child. Returns null if no element is found, if
23577 * something significant besides an element is found, or if the element
23578 * found does not match the optional name and uri.
23580 getChildEl: function(node, name, uri) {
23581 return node && this.getThisOrNextEl(node.firstChild, name, uri);
23585 * APIMethod: getNextEl
23586 * Get the next sibling element. Optionally get the first sibling only
23587 * if it matches the given local name and namespace URI.
23590 * node - {DOMElement} The node.
23591 * name - {String} Optional local name of the sibling to search for.
23592 * uri - {String} Optional namespace URI of the sibling to search for.
23595 * {DOMElement} The next sibling element. Returns null if no element is
23596 * found, something significant besides an element is found, or the
23597 * found element does not match the optional name and uri.
23599 getNextEl: function(node, name, uri) {
23600 return node && this.getThisOrNextEl(node.nextSibling, name, uri);
23604 * Method: getThisOrNextEl
23605 * Return this node or the next element node. Optionally get the first
23606 * sibling with the given local name or namespace URI.
23609 * node - {DOMElement} The node.
23610 * name - {String} Optional local name of the sibling to search for.
23611 * uri - {String} Optional namespace URI of the sibling to search for.
23614 * {DOMElement} The next sibling element. Returns null if no element is
23615 * found, something significant besides an element is found, or the
23616 * found element does not match the query.
23618 getThisOrNextEl: function(node, name, uri) {
23619 outer: for(var sibling=node; sibling; sibling=sibling.nextSibling) {
23620 switch(sibling.nodeType) {
23622 if((!name || name === (sibling.localName || sibling.nodeName.split(":").pop())) &&
23623 (!uri || uri === sibling.namespaceURI)) {
23630 if(/^\s*$/.test(sibling.nodeValue)) {
23634 case 6: // ENTITY_NODE
23635 case 12: // NOTATION_NODE
23636 case 10: // DOCUMENT_TYPE_NODE
23637 case 11: // DOCUMENT_FRAGMENT_NODE
23640 } // ignore comments and processing instructions
23642 return sibling || null;
23646 * APIMethod: lookupNamespaceURI
23647 * Takes a prefix and returns the namespace URI associated with it on the given
23648 * node if found (and null if not). Supplying null for the prefix will
23649 * return the default namespace.
23651 * For browsers that support it, this calls the native lookupNamesapceURI
23652 * function. In other browsers, this is an implementation of
23653 * http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI.
23655 * For browsers that don't support the attribute.ownerElement property, this
23656 * method cannot be called on attribute nodes.
23659 * node - {DOMElement} The node from which to start looking.
23660 * prefix - {String} The prefix to lookup or null to lookup the default namespace.
23663 * {String} The namespace URI for the given prefix. Returns null if the prefix
23664 * cannot be found or the node is the wrong type.
23666 lookupNamespaceURI: function(node, prefix) {
23669 if(node.lookupNamespaceURI) {
23670 uri = node.lookupNamespaceURI(prefix);
23672 outer: switch(node.nodeType) {
23673 case 1: // ELEMENT_NODE
23674 if(node.namespaceURI !== null && node.prefix === prefix) {
23675 uri = node.namespaceURI;
23678 var len = node.attributes.length;
23681 for(var i=0; i<len; ++i) {
23682 attr = node.attributes[i];
23683 if(attr.prefix === "xmlns" && attr.name === "xmlns:" + prefix) {
23684 uri = attr.value || null;
23686 } else if(attr.name === "xmlns" && prefix === null) {
23687 uri = attr.value || null;
23692 uri = this.lookupNamespaceURI(node.parentNode, prefix);
23694 case 2: // ATTRIBUTE_NODE
23695 uri = this.lookupNamespaceURI(node.ownerElement, prefix);
23697 case 9: // DOCUMENT_NODE
23698 uri = this.lookupNamespaceURI(node.documentElement, prefix);
23700 case 6: // ENTITY_NODE
23701 case 12: // NOTATION_NODE
23702 case 10: // DOCUMENT_TYPE_NODE
23703 case 11: // DOCUMENT_FRAGMENT_NODE
23706 // TEXT_NODE (3), CDATA_SECTION_NODE (4), ENTITY_REFERENCE_NODE (5),
23707 // PROCESSING_INSTRUCTION_NODE (7), COMMENT_NODE (8)
23708 uri = this.lookupNamespaceURI(node.parentNode, prefix);
23717 * Method: getXMLDoc
23718 * Get an XML document for nodes that are not supported in HTML (e.g.
23719 * createCDATASection). On IE, this will either return an existing or
23720 * create a new <xmldom> on the instance. On other browsers, this will
23721 * either return an existing or create a new shared document (see
23722 * <OpenLayers.Format.XML.document>).
23727 getXMLDoc: function() {
23728 if (!OpenLayers.Format.XML.document && !this.xmldom) {
23729 if (document.implementation && document.implementation.createDocument) {
23730 OpenLayers.Format.XML.document =
23731 document.implementation.createDocument("", "", null);
23732 } else if (!this.xmldom && window.ActiveXObject) {
23733 this.xmldom = new ActiveXObject("Microsoft.XMLDOM");
23736 return OpenLayers.Format.XML.document || this.xmldom;
23739 CLASS_NAME: "OpenLayers.Format.XML"
23743 OpenLayers.Format.XML.CONTENT_TYPE = {EMPTY: 0, SIMPLE: 1, COMPLEX: 2, MIXED: 3};
23746 * APIFunction: OpenLayers.Format.XML.lookupNamespaceURI
23747 * Takes a prefix and returns the namespace URI associated with it on the given
23748 * node if found (and null if not). Supplying null for the prefix will
23749 * return the default namespace.
23751 * For browsers that support it, this calls the native lookupNamesapceURI
23752 * function. In other browsers, this is an implementation of
23753 * http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI.
23755 * For browsers that don't support the attribute.ownerElement property, this
23756 * method cannot be called on attribute nodes.
23759 * node - {DOMElement} The node from which to start looking.
23760 * prefix - {String} The prefix to lookup or null to lookup the default namespace.
23763 * {String} The namespace URI for the given prefix. Returns null if the prefix
23764 * cannot be found or the node is the wrong type.
23766 OpenLayers.Format.XML.lookupNamespaceURI = OpenLayers.Function.bind(
23767 OpenLayers.Format.XML.prototype.lookupNamespaceURI,
23768 OpenLayers.Format.XML.prototype
23772 * Property: OpenLayers.Format.XML.document
23773 * {XMLDocument} XML document to reuse for creating non-HTML compliant nodes,
23774 * like document.createCDATASection.
23776 OpenLayers.Format.XML.document = null;
23777 /* ======================================================================
23778 OpenLayers/Format/OGCExceptionReport.js
23779 ====================================================================== */
23781 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
23782 * full list of contributors). Published under the 2-clause BSD license.
23783 * See license.txt in the OpenLayers distribution or repository for the
23784 * full text of the license. */
23787 * @requires OpenLayers/Format/XML.js
23791 * Class: OpenLayers.Format.OGCExceptionReport
23792 * Class to read exception reports for various OGC services and versions.
23795 * - <OpenLayers.Format.XML>
23797 OpenLayers.Format.OGCExceptionReport = OpenLayers.Class(OpenLayers.Format.XML, {
23800 * Property: namespaces
23801 * {Object} Mapping of namespace aliases to namespace URIs.
23804 ogc: "http://www.opengis.net/ogc"
23808 * Property: regExes
23809 * Compiled regular expressions for manipulating strings.
23812 trimSpace: (/^\s*|\s*$/g),
23813 removeSpace: (/\s*/g),
23814 splitSpace: (/\s+/),
23815 trimComma: (/\s*,\s*/g)
23819 * Property: defaultPrefix
23821 defaultPrefix: "ogc",
23824 * Constructor: OpenLayers.Format.OGCExceptionReport
23825 * Create a new parser for OGC exception reports.
23828 * options - {Object} An optional object whose properties will be set on
23834 * Read OGC exception report data from a string, and return an object with
23835 * information about the exceptions.
23838 * data - {String} or {DOMElement} data to read/parse.
23841 * {Object} Information about the exceptions that occurred.
23843 read: function(data) {
23845 if(typeof data == "string") {
23846 data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
23848 var root = data.documentElement;
23849 var exceptionInfo = {exceptionReport: null};
23851 this.readChildNodes(data, exceptionInfo);
23852 if (exceptionInfo.exceptionReport === null) {
23853 // fall-back to OWSCommon since this is a common output format for exceptions
23854 // we cannot easily use the ows readers directly since they differ for 1.0 and 1.1
23855 exceptionInfo = new OpenLayers.Format.OWSCommon().read(data);
23858 return exceptionInfo;
23862 * Property: readers
23863 * Contains public functions, grouped by namespace prefix, that will
23864 * be applied when a namespaced node is found matching the function
23865 * name. The function will be applied in the scope of this parser
23866 * with two arguments: the node being read and a context object passed
23871 "ServiceExceptionReport": function(node, obj) {
23872 obj.exceptionReport = {exceptions: []};
23873 this.readChildNodes(node, obj.exceptionReport);
23875 "ServiceException": function(node, exceptionReport) {
23877 code: node.getAttribute("code"),
23878 locator: node.getAttribute("locator"),
23879 text: this.getChildValue(node)
23881 exceptionReport.exceptions.push(exception);
23886 CLASS_NAME: "OpenLayers.Format.OGCExceptionReport"
23889 /* ======================================================================
23890 OpenLayers/Format/XML/VersionedOGC.js
23891 ====================================================================== */
23893 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
23894 * full list of contributors). Published under the 2-clause BSD license.
23895 * See license.txt in the OpenLayers distribution or repository for the
23896 * full text of the license. */
23899 * @requires OpenLayers/Format/XML.js
23900 * @requires OpenLayers/Format/OGCExceptionReport.js
23904 * Class: OpenLayers.Format.XML.VersionedOGC
23905 * Base class for versioned formats, i.e. a format which supports multiple
23908 * To enable checking if parsing succeeded, you will need to define a property
23909 * called errorProperty on the parser you want to check. The parser will then
23910 * check the returned object to see if that property is present. If it is, it
23911 * assumes the parsing was successful. If it is not present (or is null), it will
23912 * pass the document through an OGCExceptionReport parser.
23914 * If errorProperty is undefined for the parser, this error checking mechanism
23915 * will be disabled.
23920 * - <OpenLayers.Format.XML>
23922 OpenLayers.Format.XML.VersionedOGC = OpenLayers.Class(OpenLayers.Format.XML, {
23925 * APIProperty: defaultVersion
23926 * {String} Version number to assume if none found.
23928 defaultVersion: null,
23931 * APIProperty: version
23932 * {String} Specify a version string if one is known.
23937 * APIProperty: profile
23938 * {String} If provided, use a custom profile.
23943 * APIProperty: allowFallback
23944 * {Boolean} If a profiled parser cannot be found for the returned version,
23945 * use a non-profiled parser as the fallback. Application code using this
23946 * should take into account that the return object structure might be
23947 * missing the specifics of the profile. Defaults to false.
23949 allowFallback: false,
23953 * {String} The name of this parser, this is the part of the CLASS_NAME
23954 * except for "OpenLayers.Format."
23959 * APIProperty: stringifyOutput
23960 * {Boolean} If true, write will return a string otherwise a DOMElement.
23961 * Default is false.
23963 stringifyOutput: false,
23967 * {Object} Instance of the versioned parser. Cached for multiple read and
23968 * write calls of the same version.
23973 * Constructor: OpenLayers.Format.XML.VersionedOGC.
23977 * options - {Object} Optional object whose properties will be set on
23980 initialize: function(options) {
23981 OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
23982 var className = this.CLASS_NAME;
23983 this.name = className.substring(className.lastIndexOf(".")+1);
23987 * Method: getVersion
23988 * Returns the version to use. Subclasses can override this function
23989 * if a different version detection is needed.
23992 * root - {DOMElement}
23993 * options - {Object} Optional configuration object.
23996 * {String} The version to use.
23998 getVersion: function(root, options) {
24002 version = this.version;
24004 version = root.getAttribute("version");
24006 version = this.defaultVersion;
24010 version = (options && options.version) ||
24011 this.version || this.defaultVersion;
24017 * Method: getParser
24018 * Get an instance of the cached parser if available, otherwise create one.
24021 * version - {String}
24024 * {<OpenLayers.Format>}
24026 getParser: function(version) {
24027 version = version || this.defaultVersion;
24028 var profile = this.profile ? "_" + this.profile : "";
24029 if(!this.parser || this.parser.VERSION != version) {
24030 var format = OpenLayers.Format[this.name][
24031 "v" + version.replace(/\./g, "_") + profile
24034 if (profile !== "" && this.allowFallback) {
24035 // fallback to the non-profiled version of the parser
24037 format = OpenLayers.Format[this.name][
24038 "v" + version.replace(/\./g, "_")
24042 throw "Can't find a " + this.name + " parser for version " +
24046 this.parser = new format(this.options);
24048 return this.parser;
24053 * Write a document.
24056 * obj - {Object} An object representing the document.
24057 * options - {Object} Optional configuration object.
24060 * {String} The document as a string
24062 write: function(obj, options) {
24063 var version = this.getVersion(null, options);
24064 this.parser = this.getParser(version);
24065 var root = this.parser.write(obj, options);
24066 if (this.stringifyOutput === false) {
24069 return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
24075 * Read a doc and return an object representing the document.
24078 * data - {String | DOMElement} Data to read.
24079 * options - {Object} Options for the reader.
24082 * {Object} An object representing the document.
24084 read: function(data, options) {
24085 if(typeof data == "string") {
24086 data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
24088 var root = data.documentElement;
24089 var version = this.getVersion(root);
24090 this.parser = this.getParser(version); // Select the parser
24091 var obj = this.parser.read(data, options); // Parse the data
24093 var errorProperty = this.parser.errorProperty || null;
24094 if (errorProperty !== null && obj[errorProperty] === undefined) {
24095 // an error must have happened, so parse it and report back
24096 var format = new OpenLayers.Format.OGCExceptionReport();
24097 obj.error = format.read(data);
24099 obj.version = version;
24103 CLASS_NAME: "OpenLayers.Format.XML.VersionedOGC"
24105 /* ======================================================================
24106 OpenLayers/Filter/FeatureId.js
24107 ====================================================================== */
24109 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
24110 * full list of contributors). Published under the 2-clause BSD license.
24111 * See license.txt in the OpenLayers distribution or repository for the
24112 * full text of the license. */
24116 * @requires OpenLayers/Filter.js
24120 * Class: OpenLayers.Filter.FeatureId
24121 * This class represents a ogc:FeatureId Filter, as being used for rule-based SLD
24125 * - <OpenLayers.Filter>
24127 OpenLayers.Filter.FeatureId = OpenLayers.Class(OpenLayers.Filter, {
24130 * APIProperty: fids
24131 * {Array(String)} Feature Ids to evaluate this rule against.
24132 * To be passed inside the params object.
24138 * {String} Type to identify this filter.
24143 * Constructor: OpenLayers.Filter.FeatureId
24144 * Creates an ogc:FeatureId rule.
24147 * options - {Object} An optional object with properties to set on the
24151 * {<OpenLayers.Filter.FeatureId>}
24153 initialize: function(options) {
24155 OpenLayers.Filter.prototype.initialize.apply(this, [options]);
24159 * APIMethod: evaluate
24160 * evaluates this rule for a specific feature
24163 * feature - {<OpenLayers.Feature>} feature to apply the rule to.
24164 * For vector features, the check is run against the fid,
24165 * for plain features against the id.
24168 * {Boolean} true if the rule applies, false if it does not
24170 evaluate: function(feature) {
24171 for (var i=0, len=this.fids.length; i<len; i++) {
24172 var fid = feature.fid || feature.id;
24173 if (fid == this.fids[i]) {
24182 * Clones this filter.
24185 * {<OpenLayers.Filter.FeatureId>} Clone of this filter.
24187 clone: function() {
24188 var filter = new OpenLayers.Filter.FeatureId();
24189 OpenLayers.Util.extend(filter, this);
24190 filter.fids = this.fids.slice();
24194 CLASS_NAME: "OpenLayers.Filter.FeatureId"
24196 /* ======================================================================
24197 OpenLayers/Filter/Logical.js
24198 ====================================================================== */
24200 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
24201 * full list of contributors). Published under the 2-clause BSD license.
24202 * See license.txt in the OpenLayers distribution or repository for the
24203 * full text of the license. */
24207 * @requires OpenLayers/Filter.js
24211 * Class: OpenLayers.Filter.Logical
24212 * This class represents ogc:And, ogc:Or and ogc:Not rules.
24215 * - <OpenLayers.Filter>
24217 OpenLayers.Filter.Logical = OpenLayers.Class(OpenLayers.Filter, {
24220 * APIProperty: filters
24221 * {Array(<OpenLayers.Filter>)} Child filters for this filter.
24226 * APIProperty: type
24227 * {String} type of logical operator. Available types are:
24228 * - OpenLayers.Filter.Logical.AND = "&&";
24229 * - OpenLayers.Filter.Logical.OR = "||";
24230 * - OpenLayers.Filter.Logical.NOT = "!";
24235 * Constructor: OpenLayers.Filter.Logical
24236 * Creates a logical filter (And, Or, Not).
24239 * options - {Object} An optional object with properties to set on the
24243 * {<OpenLayers.Filter.Logical>}
24245 initialize: function(options) {
24247 OpenLayers.Filter.prototype.initialize.apply(this, [options]);
24251 * APIMethod: destroy
24252 * Remove reference to child filters.
24254 destroy: function() {
24255 this.filters = null;
24256 OpenLayers.Filter.prototype.destroy.apply(this);
24260 * APIMethod: evaluate
24261 * Evaluates this filter in a specific context.
24264 * context - {Object} Context to use in evaluating the filter. A vector
24265 * feature may also be provided to evaluate feature attributes in
24266 * comparison filters or geometries in spatial filters.
24269 * {Boolean} The filter applies.
24271 evaluate: function(context) {
24273 switch(this.type) {
24274 case OpenLayers.Filter.Logical.AND:
24275 for (i=0, len=this.filters.length; i<len; i++) {
24276 if (this.filters[i].evaluate(context) == false) {
24282 case OpenLayers.Filter.Logical.OR:
24283 for (i=0, len=this.filters.length; i<len; i++) {
24284 if (this.filters[i].evaluate(context) == true) {
24290 case OpenLayers.Filter.Logical.NOT:
24291 return (!this.filters[0].evaluate(context));
24298 * Clones this filter.
24301 * {<OpenLayers.Filter.Logical>} Clone of this filter.
24303 clone: function() {
24305 for(var i=0, len=this.filters.length; i<len; ++i) {
24306 filters.push(this.filters[i].clone());
24308 return new OpenLayers.Filter.Logical({
24314 CLASS_NAME: "OpenLayers.Filter.Logical"
24318 OpenLayers.Filter.Logical.AND = "&&";
24319 OpenLayers.Filter.Logical.OR = "||";
24320 OpenLayers.Filter.Logical.NOT = "!";
24321 /* ======================================================================
24322 OpenLayers/Filter/Comparison.js
24323 ====================================================================== */
24325 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
24326 * full list of contributors). Published under the 2-clause BSD license.
24327 * See license.txt in the OpenLayers distribution or repository for the
24328 * full text of the license. */
24331 * @requires OpenLayers/Filter.js
24335 * Class: OpenLayers.Filter.Comparison
24336 * This class represents a comparison filter.
24339 * - <OpenLayers.Filter>
24341 OpenLayers.Filter.Comparison = OpenLayers.Class(OpenLayers.Filter, {
24344 * APIProperty: type
24345 * {String} type: type of the comparison. This is one of
24346 * - OpenLayers.Filter.Comparison.EQUAL_TO = "==";
24347 * - OpenLayers.Filter.Comparison.NOT_EQUAL_TO = "!=";
24348 * - OpenLayers.Filter.Comparison.LESS_THAN = "<";
24349 * - OpenLayers.Filter.Comparison.GREATER_THAN = ">";
24350 * - OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = "<=";
24351 * - OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">=";
24352 * - OpenLayers.Filter.Comparison.BETWEEN = "..";
24353 * - OpenLayers.Filter.Comparison.LIKE = "~";
24354 * - OpenLayers.Filter.Comparison.IS_NULL = "NULL";
24359 * APIProperty: property
24361 * name of the context property to compare
24366 * APIProperty: value
24367 * {Number} or {String}
24368 * comparison value for binary comparisons. In the case of a String, this
24369 * can be a combination of text and propertyNames in the form
24370 * "literal ${propertyName}"
24375 * Property: matchCase
24376 * {Boolean} Force case sensitive searches for EQUAL_TO and NOT_EQUAL_TO
24377 * comparisons. The Filter Encoding 1.1 specification added a matchCase
24378 * attribute to ogc:PropertyIsEqualTo and ogc:PropertyIsNotEqualTo
24379 * elements. This property will be serialized with those elements only
24380 * if using the v1.1.0 filter format. However, when evaluating filters
24381 * here, the matchCase property will always be respected (for EQUAL_TO
24382 * and NOT_EQUAL_TO). Default is true.
24387 * APIProperty: lowerBoundary
24388 * {Number} or {String}
24389 * lower boundary for between comparisons. In the case of a String, this
24390 * can be a combination of text and propertyNames in the form
24391 * "literal ${propertyName}"
24393 lowerBoundary: null,
24396 * APIProperty: upperBoundary
24397 * {Number} or {String}
24398 * upper boundary for between comparisons. In the case of a String, this
24399 * can be a combination of text and propertyNames in the form
24400 * "literal ${propertyName}"
24402 upperBoundary: null,
24405 * Constructor: OpenLayers.Filter.Comparison
24406 * Creates a comparison rule.
24409 * options - {Object} An optional object with properties to set on the
24413 * {<OpenLayers.Filter.Comparison>}
24415 initialize: function(options) {
24416 OpenLayers.Filter.prototype.initialize.apply(this, [options]);
24417 // since matchCase on PropertyIsLike is not schema compliant, we only
24418 // want to use this if explicitly asked for
24419 if (this.type === OpenLayers.Filter.Comparison.LIKE
24420 && options.matchCase === undefined) {
24421 this.matchCase = null;
24426 * APIMethod: evaluate
24427 * Evaluates this filter in a specific context.
24430 * context - {Object} Context to use in evaluating the filter. If a vector
24431 * feature is provided, the feature.attributes will be used as context.
24434 * {Boolean} The filter applies.
24436 evaluate: function(context) {
24437 if (context instanceof OpenLayers.Feature.Vector) {
24438 context = context.attributes;
24440 var result = false;
24441 var got = context[this.property];
24443 switch(this.type) {
24444 case OpenLayers.Filter.Comparison.EQUAL_TO:
24446 if(!this.matchCase &&
24447 typeof got == "string" && typeof exp == "string") {
24448 result = (got.toUpperCase() == exp.toUpperCase());
24450 result = (got == exp);
24453 case OpenLayers.Filter.Comparison.NOT_EQUAL_TO:
24455 if(!this.matchCase &&
24456 typeof got == "string" && typeof exp == "string") {
24457 result = (got.toUpperCase() != exp.toUpperCase());
24459 result = (got != exp);
24462 case OpenLayers.Filter.Comparison.LESS_THAN:
24463 result = got < this.value;
24465 case OpenLayers.Filter.Comparison.GREATER_THAN:
24466 result = got > this.value;
24468 case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO:
24469 result = got <= this.value;
24471 case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO:
24472 result = got >= this.value;
24474 case OpenLayers.Filter.Comparison.BETWEEN:
24475 result = (got >= this.lowerBoundary) &&
24476 (got <= this.upperBoundary);
24478 case OpenLayers.Filter.Comparison.LIKE:
24479 var regexp = new RegExp(this.value, "gi");
24480 result = regexp.test(got);
24482 case OpenLayers.Filter.Comparison.IS_NULL:
24483 result = (got === null);
24490 * APIMethod: value2regex
24491 * Converts the value of this rule into a regular expression string,
24492 * according to the wildcard characters specified. This method has to
24493 * be called after instantiation of this class, if the value is not a
24494 * regular expression already.
24497 * wildCard - {Char} wildcard character in the above value, default
24499 * singleChar - {Char} single-character wildcard in the above value
24501 * escapeChar - {Char} escape character in the above value, default is
24505 * {String} regular expression string
24507 value2regex: function(wildCard, singleChar, escapeChar) {
24508 if (wildCard == ".") {
24509 throw new Error("'.' is an unsupported wildCard character for " +
24510 "OpenLayers.Filter.Comparison");
24514 // set UMN MapServer defaults for unspecified parameters
24515 wildCard = wildCard ? wildCard : "*";
24516 singleChar = singleChar ? singleChar : ".";
24517 escapeChar = escapeChar ? escapeChar : "!";
24519 this.value = this.value.replace(
24520 new RegExp("\\"+escapeChar+"(.|$)", "g"), "\\$1");
24521 this.value = this.value.replace(
24522 new RegExp("\\"+singleChar, "g"), ".");
24523 this.value = this.value.replace(
24524 new RegExp("\\"+wildCard, "g"), ".*");
24525 this.value = this.value.replace(
24526 new RegExp("\\\\.\\*", "g"), "\\"+wildCard);
24527 this.value = this.value.replace(
24528 new RegExp("\\\\\\.", "g"), "\\"+singleChar);
24534 * Method: regex2value
24535 * Convert the value of this rule from a regular expression string into an
24536 * ogc literal string using a wildCard of *, a singleChar of ., and an
24537 * escape of !. Leaves the <value> property unmodified.
24540 * {String} A string value.
24542 regex2value: function() {
24544 var value = this.value;
24546 // replace ! with !!
24547 value = value.replace(/!/g, "!!");
24549 // replace \. with !. (watching out for \\.)
24550 value = value.replace(/(\\)?\\\./g, function($0, $1) {
24551 return $1 ? $0 : "!.";
24554 // replace \* with #* (watching out for \\*)
24555 value = value.replace(/(\\)?\\\*/g, function($0, $1) {
24556 return $1 ? $0 : "!*";
24559 // replace \\ with \
24560 value = value.replace(/\\\\/g, "\\");
24562 // convert .* to * (the sequence #.* is not allowed)
24563 value = value.replace(/\.\*/g, "*");
24570 * Clones this filter.
24573 * {<OpenLayers.Filter.Comparison>} Clone of this filter.
24575 clone: function() {
24576 return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison(), this);
24579 CLASS_NAME: "OpenLayers.Filter.Comparison"
24583 OpenLayers.Filter.Comparison.EQUAL_TO = "==";
24584 OpenLayers.Filter.Comparison.NOT_EQUAL_TO = "!=";
24585 OpenLayers.Filter.Comparison.LESS_THAN = "<";
24586 OpenLayers.Filter.Comparison.GREATER_THAN = ">";
24587 OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = "<=";
24588 OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">=";
24589 OpenLayers.Filter.Comparison.BETWEEN = "..";
24590 OpenLayers.Filter.Comparison.LIKE = "~";
24591 OpenLayers.Filter.Comparison.IS_NULL = "NULL";
24592 /* ======================================================================
24593 OpenLayers/Format/Filter.js
24594 ====================================================================== */
24596 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
24597 * full list of contributors). Published under the 2-clause BSD license.
24598 * See license.txt in the OpenLayers distribution or repository for the
24599 * full text of the license. */
24602 * @requires OpenLayers/Format/XML/VersionedOGC.js
24603 * @requires OpenLayers/Filter/FeatureId.js
24604 * @requires OpenLayers/Filter/Logical.js
24605 * @requires OpenLayers/Filter/Comparison.js
24609 * Class: OpenLayers.Format.Filter
24610 * Read/Write ogc:Filter. Create a new instance with the <OpenLayers.Format.Filter>
24614 * - <OpenLayers.Format.XML.VersionedOGC>
24616 OpenLayers.Format.Filter = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
24619 * APIProperty: defaultVersion
24620 * {String} Version number to assume if none found. Default is "1.0.0".
24622 defaultVersion: "1.0.0",
24626 * Write an ogc:Filter given a filter object.
24629 * filter - {<OpenLayers.Filter>} An filter.
24630 * options - {Object} Optional configuration object.
24633 * {Elment} An ogc:Filter element node.
24638 * Read and Filter doc and return an object representing the Filter.
24641 * data - {String | DOMElement} Data to read.
24644 * {<OpenLayers.Filter>} A filter object.
24647 CLASS_NAME: "OpenLayers.Format.Filter"
24649 /* ======================================================================
24650 OpenLayers/Format/WFST.js
24651 ====================================================================== */
24653 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
24654 * full list of contributors). Published under the 2-clause BSD license.
24655 * See license.txt in the OpenLayers distribution or repository for the
24656 * full text of the license. */
24659 * @requires OpenLayers/Format.js
24663 * Function: OpenLayers.Format.WFST
24664 * Used to create a versioned WFS protocol. Default version is 1.0.0.
24667 * {<OpenLayers.Format>} A WFST format of the given version.
24669 OpenLayers.Format.WFST = function(options) {
24670 options = OpenLayers.Util.applyDefaults(
24671 options, OpenLayers.Format.WFST.DEFAULTS
24673 var cls = OpenLayers.Format.WFST["v"+options.version.replace(/\./g, "_")];
24675 throw "Unsupported WFST version: " + options.version;
24677 return new cls(options);
24681 * Constant: OpenLayers.Format.WFST.DEFAULTS
24682 * {Object} Default properties for the WFST format.
24684 OpenLayers.Format.WFST.DEFAULTS = {
24687 /* ======================================================================
24688 OpenLayers/Filter/Spatial.js
24689 ====================================================================== */
24691 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
24692 * full list of contributors). Published under the 2-clause BSD license.
24693 * See license.txt in the OpenLayers distribution or repository for the
24694 * full text of the license. */
24697 * @requires OpenLayers/Filter.js
24701 * Class: OpenLayers.Filter.Spatial
24702 * This class represents a spatial filter.
24703 * Currently implemented: BBOX, DWithin and Intersects
24706 * - <OpenLayers.Filter>
24708 OpenLayers.Filter.Spatial = OpenLayers.Class(OpenLayers.Filter, {
24711 * APIProperty: type
24712 * {String} Type of spatial filter.
24714 * The type should be one of:
24715 * - OpenLayers.Filter.Spatial.BBOX
24716 * - OpenLayers.Filter.Spatial.INTERSECTS
24717 * - OpenLayers.Filter.Spatial.DWITHIN
24718 * - OpenLayers.Filter.Spatial.WITHIN
24719 * - OpenLayers.Filter.Spatial.CONTAINS
24724 * APIProperty: property
24725 * {String} Name of the context property to compare.
24730 * APIProperty: value
24731 * {<OpenLayers.Bounds> || <OpenLayers.Geometry>} The bounds or geometry
24732 * to be used by the filter. Use bounds for BBOX filters and geometry
24733 * for INTERSECTS or DWITHIN filters.
24738 * APIProperty: distance
24739 * {Number} The distance to use in a DWithin spatial filter.
24744 * APIProperty: distanceUnits
24745 * {String} The units to use for the distance, e.g. 'm'.
24747 distanceUnits: null,
24750 * Constructor: OpenLayers.Filter.Spatial
24751 * Creates a spatial filter.
24754 * options - {Object} An optional object with properties to set on the
24758 * {<OpenLayers.Filter.Spatial>}
24763 * Evaluates this filter for a specific feature.
24766 * feature - {<OpenLayers.Feature.Vector>} feature to apply the filter to.
24769 * {Boolean} The feature meets filter criteria.
24771 evaluate: function(feature) {
24772 var intersect = false;
24773 switch(this.type) {
24774 case OpenLayers.Filter.Spatial.BBOX:
24775 case OpenLayers.Filter.Spatial.INTERSECTS:
24776 if(feature.geometry) {
24777 var geom = this.value;
24778 if(this.value.CLASS_NAME == "OpenLayers.Bounds") {
24779 geom = this.value.toGeometry();
24781 if(feature.geometry.intersects(geom)) {
24787 throw new Error('evaluate is not implemented for this filter type.');
24794 * Clones this filter.
24797 * {<OpenLayers.Filter.Spatial>} Clone of this filter.
24799 clone: function() {
24800 var options = OpenLayers.Util.applyDefaults({
24801 value: this.value && this.value.clone && this.value.clone()
24803 return new OpenLayers.Filter.Spatial(options);
24805 CLASS_NAME: "OpenLayers.Filter.Spatial"
24808 OpenLayers.Filter.Spatial.BBOX = "BBOX";
24809 OpenLayers.Filter.Spatial.INTERSECTS = "INTERSECTS";
24810 OpenLayers.Filter.Spatial.DWITHIN = "DWITHIN";
24811 OpenLayers.Filter.Spatial.WITHIN = "WITHIN";
24812 OpenLayers.Filter.Spatial.CONTAINS = "CONTAINS";
24813 /* ======================================================================
24814 OpenLayers/Format/WFST/v1.js
24815 ====================================================================== */
24817 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
24818 * full list of contributors). Published under the 2-clause BSD license.
24819 * See license.txt in the OpenLayers distribution or repository for the
24820 * full text of the license. */
24823 * @requires OpenLayers/Format/XML.js
24824 * @requires OpenLayers/Format/WFST.js
24825 * @requires OpenLayers/Filter/Spatial.js
24826 * @requires OpenLayers/Filter/FeatureId.js
24830 * Class: OpenLayers.Format.WFST.v1
24831 * Superclass for WFST parsers.
24834 * - <OpenLayers.Format.XML>
24836 OpenLayers.Format.WFST.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
24839 * Property: namespaces
24840 * {Object} Mapping of namespace aliases to namespace URIs.
24843 xlink: "http://www.w3.org/1999/xlink",
24844 xsi: "http://www.w3.org/2001/XMLSchema-instance",
24845 wfs: "http://www.opengis.net/wfs",
24846 gml: "http://www.opengis.net/gml",
24847 ogc: "http://www.opengis.net/ogc",
24848 ows: "http://www.opengis.net/ows"
24852 * Property: defaultPrefix
24854 defaultPrefix: "wfs",
24857 * Property: version
24858 * {String} WFS version number.
24863 * Property: schemaLocation
24864 * {String} Schema location for a particular minor version.
24866 schemaLocations: null,
24869 * APIProperty: srsName
24870 * {String} URI for spatial reference system.
24875 * APIProperty: extractAttributes
24876 * {Boolean} Extract attributes from GML. Default is true.
24878 extractAttributes: true,
24882 * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
24883 * Changing is not recommended, a new Format should be instantiated.
24888 * Property: stateName
24889 * {Object} Maps feature states to node names.
24894 * Constructor: OpenLayers.Format.WFST.v1
24895 * Instances of this class are not created directly. Use the
24896 * <OpenLayers.Format.WFST.v1_0_0> or <OpenLayers.Format.WFST.v1_1_0>
24897 * constructor instead.
24900 * options - {Object} An optional object whose properties will be set on
24903 initialize: function(options) {
24904 // set state name mapping
24905 this.stateName = {};
24906 this.stateName[OpenLayers.State.INSERT] = "wfs:Insert";
24907 this.stateName[OpenLayers.State.UPDATE] = "wfs:Update";
24908 this.stateName[OpenLayers.State.DELETE] = "wfs:Delete";
24909 OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
24913 * Method: getSrsName
24915 getSrsName: function(feature, options) {
24916 var srsName = options && options.srsName;
24918 if(feature && feature.layer) {
24919 srsName = feature.layer.projection.getCode();
24921 srsName = this.srsName;
24929 * Parse the response from a transaction. Because WFS is split into
24930 * Transaction requests (create, update, and delete) and GetFeature
24931 * requests (read), this method handles parsing of both types of
24935 * data - {String | Document} The WFST document to read
24936 * options - {Object} Options for the reader
24938 * Valid options properties:
24939 * output - {String} either "features" or "object". The default is
24940 * "features", which means that the method will return an array of
24941 * features. If set to "object", an object with a "features" property
24942 * and other properties read by the parser will be returned.
24945 * {Array | Object} Output depending on the output option.
24947 read: function(data, options) {
24948 options = options || {};
24949 OpenLayers.Util.applyDefaults(options, {
24953 if(typeof data == "string") {
24954 data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
24956 if(data && data.nodeType == 9) {
24957 data = data.documentElement;
24961 this.readNode(data, obj, true);
24963 if(obj.features && options.output === "features") {
24964 obj = obj.features;
24970 * Property: readers
24971 * Contains public functions, grouped by namespace prefix, that will
24972 * be applied when a namespaced node is found matching the function
24973 * name. The function will be applied in the scope of this parser
24974 * with two arguments: the node being read and a context object passed
24979 "FeatureCollection": function(node, obj) {
24981 this.readChildNodes(node, obj);
24988 * Given an array of features, write a WFS transaction. This assumes
24989 * the features have a state property that determines the operation
24990 * type - insert, update, or delete.
24993 * features - {Array(<OpenLayers.Feature.Vector>)} A list of features. See
24994 * below for a more detailed description of the influence of the
24995 * feature's *modified* property.
24996 * options - {Object}
24998 * feature.modified rules:
24999 * If a feature has a modified property set, the following checks will be
25000 * made before a feature's geometry or attribute is included in an Update
25002 * - *modified* is not set at all: The geometry and all attributes will be
25004 * - *modified.geometry* is set (null or a geometry): The geometry will be
25005 * included. If *modified.attributes* is not set, all attributes will
25007 * - *modified.attributes* is set: Only the attributes set (i.e. to null or
25008 * a value) in *modified.attributes* will be included.
25009 * If *modified.geometry* is not set, the geometry will not be included.
25011 * Valid options include:
25012 * - *multi* {Boolean} If set to true, geometries will be casted to
25013 * Multi geometries before writing.
25016 * {String} A serialized WFS transaction.
25018 write: function(features, options) {
25019 var node = this.writeNode("wfs:Transaction", {
25023 var value = this.schemaLocationAttr();
25025 this.setAttributeNS(
25026 node, this.namespaces["xsi"], "xsi:schemaLocation", value
25029 return OpenLayers.Format.XML.prototype.write.apply(this, [node]);
25033 * Property: writers
25034 * As a compliment to the readers property, this structure contains public
25035 * writing functions grouped by namespace alias and named like the
25036 * node names they produce.
25040 "GetFeature": function(options) {
25041 var node = this.createElementNSPlus("wfs:GetFeature", {
25044 version: this.version,
25045 handle: options && options.handle,
25046 outputFormat: options && options.outputFormat,
25047 maxFeatures: options && options.maxFeatures,
25048 "xsi:schemaLocation": this.schemaLocationAttr(options)
25051 if (typeof this.featureType == "string") {
25052 this.writeNode("Query", options, node);
25054 for (var i=0,len = this.featureType.length; i<len; i++) {
25055 options.featureType = this.featureType[i];
25056 this.writeNode("Query", options, node);
25061 "Transaction": function(obj) {
25063 var options = obj.options || {};
25064 var node = this.createElementNSPlus("wfs:Transaction", {
25067 version: this.version,
25068 handle: options.handle
25072 var features = obj.features;
25074 // temporarily re-assigning geometry types
25075 if (options.multi === true) {
25076 OpenLayers.Util.extend(this.geometryTypes, {
25077 "OpenLayers.Geometry.Point": "MultiPoint",
25078 "OpenLayers.Geometry.LineString": (this.multiCurve === true) ? "MultiCurve": "MultiLineString",
25079 "OpenLayers.Geometry.Polygon": (this.multiSurface === true) ? "MultiSurface" : "MultiPolygon"
25083 for(i=0, len=features.length; i<len; ++i) {
25084 feature = features[i];
25085 name = this.stateName[feature.state];
25087 this.writeNode(name, {
25093 // switch back to original geometry types assignment
25094 if (options.multi === true) {
25095 this.setGeometryTypes();
25098 if (options.nativeElements) {
25099 for (i=0, len=options.nativeElements.length; i<len; ++i) {
25100 this.writeNode("wfs:Native",
25101 options.nativeElements[i], node);
25106 "Native": function(nativeElement) {
25107 var node = this.createElementNSPlus("wfs:Native", {
25109 vendorId: nativeElement.vendorId,
25110 safeToIgnore: nativeElement.safeToIgnore
25112 value: nativeElement.value
25116 "Insert": function(obj) {
25117 var feature = obj.feature;
25118 var options = obj.options;
25119 var node = this.createElementNSPlus("wfs:Insert", {
25121 handle: options && options.handle
25124 this.srsName = this.getSrsName(feature);
25125 this.writeNode("feature:_typeName", feature, node);
25128 "Update": function(obj) {
25129 var feature = obj.feature;
25130 var options = obj.options;
25131 var node = this.createElementNSPlus("wfs:Update", {
25133 handle: options && options.handle,
25134 typeName: (this.featureNS ? this.featurePrefix + ":" : "") +
25138 if(this.featureNS) {
25139 node.setAttribute("xmlns:" + this.featurePrefix, this.featureNS);
25143 var modified = feature.modified;
25144 if (this.geometryName !== null && (!modified || modified.geometry !== undefined)) {
25145 this.srsName = this.getSrsName(feature);
25147 "Property", {name: this.geometryName, value: feature.geometry}, node
25151 // add in attributes
25152 for(var key in feature.attributes) {
25153 if(feature.attributes[key] !== undefined &&
25154 (!modified || !modified.attributes ||
25155 (modified.attributes && modified.attributes[key] !== undefined))) {
25157 "Property", {name: key, value: feature.attributes[key]}, node
25162 // add feature id filter
25163 this.writeNode("ogc:Filter", new OpenLayers.Filter.FeatureId({
25164 fids: [feature.fid]
25169 "Property": function(obj) {
25170 var node = this.createElementNSPlus("wfs:Property");
25171 this.writeNode("Name", obj.name, node);
25172 if(obj.value !== null) {
25173 this.writeNode("Value", obj.value, node);
25177 "Name": function(name) {
25178 return this.createElementNSPlus("wfs:Name", {value: name});
25180 "Value": function(obj) {
25182 if(obj instanceof OpenLayers.Geometry) {
25183 node = this.createElementNSPlus("wfs:Value");
25184 var geom = this.writeNode("feature:_geometry", obj).firstChild;
25185 node.appendChild(geom);
25187 node = this.createElementNSPlus("wfs:Value", {value: obj});
25191 "Delete": function(obj) {
25192 var feature = obj.feature;
25193 var options = obj.options;
25194 var node = this.createElementNSPlus("wfs:Delete", {
25196 handle: options && options.handle,
25197 typeName: (this.featureNS ? this.featurePrefix + ":" : "") +
25201 if(this.featureNS) {
25202 node.setAttribute("xmlns:" + this.featurePrefix, this.featureNS);
25204 this.writeNode("ogc:Filter", new OpenLayers.Filter.FeatureId({
25205 fids: [feature.fid]
25213 * Method: schemaLocationAttr
25214 * Generate the xsi:schemaLocation attribute value.
25217 * {String} The xsi:schemaLocation attribute or undefined if none.
25219 schemaLocationAttr: function(options) {
25220 options = OpenLayers.Util.extend({
25221 featurePrefix: this.featurePrefix,
25222 schema: this.schema
25224 var schemaLocations = OpenLayers.Util.extend({}, this.schemaLocations);
25225 if(options.schema) {
25226 schemaLocations[options.featurePrefix] = options.schema;
25230 for(var key in schemaLocations) {
25231 uri = this.namespaces[key];
25233 parts.push(uri + " " + schemaLocations[key]);
25236 var value = parts.join(" ") || undefined;
25241 * Method: setFilterProperty
25242 * Set the property of each spatial filter.
25245 * filter - {<OpenLayers.Filter>}
25247 setFilterProperty: function(filter) {
25248 if(filter.filters) {
25249 for(var i=0, len=filter.filters.length; i<len; ++i) {
25250 OpenLayers.Format.WFST.v1.prototype.setFilterProperty.call(this, filter.filters[i]);
25253 if(filter instanceof OpenLayers.Filter.Spatial && !filter.property) {
25254 // got a spatial filter without property, so set it
25255 filter.property = this.geometryName;
25260 CLASS_NAME: "OpenLayers.Format.WFST.v1"
25263 /* ======================================================================
25264 OpenLayers/Geometry/Polygon.js
25265 ====================================================================== */
25267 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
25268 * full list of contributors). Published under the 2-clause BSD license.
25269 * See license.txt in the OpenLayers distribution or repository for the
25270 * full text of the license. */
25273 * @requires OpenLayers/Geometry/Collection.js
25274 * @requires OpenLayers/Geometry/LinearRing.js
25278 * Class: OpenLayers.Geometry.Polygon
25279 * Polygon is a collection of Geometry.LinearRings.
25282 * - <OpenLayers.Geometry.Collection>
25283 * - <OpenLayers.Geometry>
25285 OpenLayers.Geometry.Polygon = OpenLayers.Class(
25286 OpenLayers.Geometry.Collection, {
25289 * Property: componentTypes
25290 * {Array(String)} An array of class names representing the types of
25291 * components that the collection can include. A null value means the
25292 * component types are not restricted.
25294 componentTypes: ["OpenLayers.Geometry.LinearRing"],
25297 * Constructor: OpenLayers.Geometry.Polygon
25298 * Constructor for a Polygon geometry.
25299 * The first ring (this.component[0])is the outer bounds of the polygon and
25300 * all subsequent rings (this.component[1-n]) are internal holes.
25304 * components - {Array(<OpenLayers.Geometry.LinearRing>)}
25308 * APIMethod: getArea
25309 * Calculated by subtracting the areas of the internal holes from the
25310 * area of the outer hole.
25313 * {float} The area of the geometry
25315 getArea: function() {
25317 if ( this.components && (this.components.length > 0)) {
25318 area += Math.abs(this.components[0].getArea());
25319 for (var i=1, len=this.components.length; i<len; i++) {
25320 area -= Math.abs(this.components[i].getArea());
25327 * APIMethod: getGeodesicArea
25328 * Calculate the approximate area of the polygon were it projected onto
25332 * projection - {<OpenLayers.Projection>} The spatial reference system
25333 * for the geometry coordinates. If not provided, Geographic/WGS84 is
25337 * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
25338 * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
25339 * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
25342 * {float} The approximate geodesic area of the polygon in square meters.
25344 getGeodesicArea: function(projection) {
25346 if(this.components && (this.components.length > 0)) {
25347 area += Math.abs(this.components[0].getGeodesicArea(projection));
25348 for(var i=1, len=this.components.length; i<len; i++) {
25349 area -= Math.abs(this.components[i].getGeodesicArea(projection));
25356 * Method: containsPoint
25357 * Test if a point is inside a polygon. Points on a polygon edge are
25358 * considered inside.
25361 * point - {<OpenLayers.Geometry.Point>}
25364 * {Boolean | Number} The point is inside the polygon. Returns 1 if the
25365 * point is on an edge. Returns boolean otherwise.
25367 containsPoint: function(point) {
25368 var numRings = this.components.length;
25369 var contained = false;
25371 // check exterior ring - 1 means on edge, boolean otherwise
25372 contained = this.components[0].containsPoint(point);
25373 if(contained !== 1) {
25374 if(contained && numRings > 1) {
25375 // check interior rings
25377 for(var i=1; i<numRings; ++i) {
25378 hole = this.components[i].containsPoint(point);
25397 * APIMethod: intersects
25398 * Determine if the input geometry intersects this one.
25401 * geometry - {<OpenLayers.Geometry>} Any type of geometry.
25404 * {Boolean} The input geometry intersects this one.
25406 intersects: function(geometry) {
25407 var intersect = false;
25409 if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
25410 intersect = this.containsPoint(geometry);
25411 } else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LineString" ||
25412 geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
25413 // check if rings/linestrings intersect
25414 for(i=0, len=this.components.length; i<len; ++i) {
25415 intersect = geometry.intersects(this.components[i]);
25421 // check if this poly contains points of the ring/linestring
25422 for(i=0, len=geometry.components.length; i<len; ++i) {
25423 intersect = this.containsPoint(geometry.components[i]);
25430 for(i=0, len=geometry.components.length; i<len; ++ i) {
25431 intersect = this.intersects(geometry.components[i]);
25437 // check case where this poly is wholly contained by another
25438 if(!intersect && geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") {
25439 // exterior ring points will be contained in the other geometry
25440 var ring = this.components[0];
25441 for(i=0, len=ring.components.length; i<len; ++i) {
25442 intersect = geometry.containsPoint(ring.components[i]);
25452 * APIMethod: distanceTo
25453 * Calculate the closest distance between two geometries (on the x-y plane).
25456 * geometry - {<OpenLayers.Geometry>} The target geometry.
25457 * options - {Object} Optional properties for configuring the distance
25461 * details - {Boolean} Return details from the distance calculation.
25462 * Default is false.
25463 * edge - {Boolean} Calculate the distance from this geometry to the
25464 * nearest edge of the target geometry. Default is true. If true,
25465 * calling distanceTo from a geometry that is wholly contained within
25466 * the target will result in a non-zero distance. If false, whenever
25467 * geometries intersect, calling distanceTo will return 0. If false,
25468 * details cannot be returned.
25471 * {Number | Object} The distance between this geometry and the target.
25472 * If details is true, the return will be an object with distance,
25473 * x0, y0, x1, and y1 properties. The x0 and y0 properties represent
25474 * the coordinates of the closest point on this geometry. The x1 and y1
25475 * properties represent the coordinates of the closest point on the
25478 distanceTo: function(geometry, options) {
25479 var edge = !(options && options.edge === false);
25481 // this is the case where we might not be looking for distance to edge
25482 if(!edge && this.intersects(geometry)) {
25485 result = OpenLayers.Geometry.Collection.prototype.distanceTo.apply(
25486 this, [geometry, options]
25492 CLASS_NAME: "OpenLayers.Geometry.Polygon"
25496 * APIMethod: createRegularPolygon
25497 * Create a regular polygon around a radius. Useful for creating circles
25501 * origin - {<OpenLayers.Geometry.Point>} center of polygon.
25502 * radius - {Float} distance to vertex, in map units.
25503 * sides - {Integer} Number of sides. 20 approximates a circle.
25504 * rotation - {Float} original angle of rotation, in degrees.
25506 OpenLayers.Geometry.Polygon.createRegularPolygon = function(origin, radius, sides, rotation) {
25507 var angle = Math.PI * ((1/sides) - (1/2));
25509 angle += (rotation / 180) * Math.PI;
25511 var rotatedAngle, x, y;
25513 for(var i=0; i<sides; ++i) {
25514 rotatedAngle = angle + (i * 2 * Math.PI / sides);
25515 x = origin.x + (radius * Math.cos(rotatedAngle));
25516 y = origin.y + (radius * Math.sin(rotatedAngle));
25517 points.push(new OpenLayers.Geometry.Point(x, y));
25519 var ring = new OpenLayers.Geometry.LinearRing(points);
25520 return new OpenLayers.Geometry.Polygon([ring]);
25522 /* ======================================================================
25523 OpenLayers/Geometry/MultiPolygon.js
25524 ====================================================================== */
25526 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
25527 * full list of contributors). Published under the 2-clause BSD license.
25528 * See license.txt in the OpenLayers distribution or repository for the
25529 * full text of the license. */
25532 * @requires OpenLayers/Geometry/Collection.js
25533 * @requires OpenLayers/Geometry/Polygon.js
25537 * Class: OpenLayers.Geometry.MultiPolygon
25538 * MultiPolygon is a geometry with multiple <OpenLayers.Geometry.Polygon>
25539 * components. Create a new instance with the <OpenLayers.Geometry.MultiPolygon>
25543 * - <OpenLayers.Geometry.Collection>
25545 OpenLayers.Geometry.MultiPolygon = OpenLayers.Class(
25546 OpenLayers.Geometry.Collection, {
25549 * Property: componentTypes
25550 * {Array(String)} An array of class names representing the types of
25551 * components that the collection can include. A null value means the
25552 * component types are not restricted.
25554 componentTypes: ["OpenLayers.Geometry.Polygon"],
25557 * Constructor: OpenLayers.Geometry.MultiPolygon
25558 * Create a new MultiPolygon geometry
25561 * components - {Array(<OpenLayers.Geometry.Polygon>)} An array of polygons
25562 * used to generate the MultiPolygon
25566 CLASS_NAME: "OpenLayers.Geometry.MultiPolygon"
25568 /* ======================================================================
25569 OpenLayers/Format/GML.js
25570 ====================================================================== */
25572 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
25573 * full list of contributors). Published under the 2-clause BSD license.
25574 * See license.txt in the OpenLayers distribution or repository for the
25575 * full text of the license. */
25578 * @requires OpenLayers/Format/XML.js
25579 * @requires OpenLayers/Feature/Vector.js
25580 * @requires OpenLayers/Geometry/Point.js
25581 * @requires OpenLayers/Geometry/MultiPoint.js
25582 * @requires OpenLayers/Geometry/LineString.js
25583 * @requires OpenLayers/Geometry/MultiLineString.js
25584 * @requires OpenLayers/Geometry/Polygon.js
25585 * @requires OpenLayers/Geometry/MultiPolygon.js
25589 * Class: OpenLayers.Format.GML
25590 * Read/Write GML. Create a new instance with the <OpenLayers.Format.GML>
25591 * constructor. Supports the GML simple features profile.
25594 * - <OpenLayers.Format.XML>
25596 OpenLayers.Format.GML = OpenLayers.Class(OpenLayers.Format.XML, {
25599 * APIProperty: featureNS
25600 * {String} Namespace used for feature attributes. Default is
25601 * "http://mapserver.gis.umn.edu/mapserver".
25603 featureNS: "http://mapserver.gis.umn.edu/mapserver",
25606 * APIProperty: featurePrefix
25607 * {String} Namespace alias (or prefix) for feature nodes. Default is
25610 featurePrefix: "feature",
25613 * APIProperty: featureName
25614 * {String} Element name for features. Default is "featureMember".
25616 featureName: "featureMember",
25619 * APIProperty: layerName
25620 * {String} Name of data layer. Default is "features".
25622 layerName: "features",
25625 * APIProperty: geometryName
25626 * {String} Name of geometry element. Defaults to "geometry".
25628 geometryName: "geometry",
25631 * APIProperty: collectionName
25632 * {String} Name of featureCollection element.
25634 collectionName: "FeatureCollection",
25637 * APIProperty: gmlns
25638 * {String} GML Namespace.
25640 gmlns: "http://www.opengis.net/gml",
25643 * APIProperty: extractAttributes
25644 * {Boolean} Extract attributes from GML.
25646 extractAttributes: true,
25650 * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
25651 * Changing is not recommended, a new Format should be instantiated.
25656 * Constructor: OpenLayers.Format.GML
25657 * Create a new parser for GML.
25660 * options - {Object} An optional object whose properties will be set on
25663 initialize: function(options) {
25664 // compile regular expressions once instead of every time they are used
25666 trimSpace: (/^\s*|\s*$/g),
25667 removeSpace: (/\s*/g),
25668 splitSpace: (/\s+/),
25669 trimComma: (/\s*,\s*/g)
25671 OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
25676 * Read data from a string, and return a list of features.
25679 * data - {String} or {DOMElement} data to read/parse.
25682 * {Array(<OpenLayers.Feature.Vector>)} An array of features.
25684 read: function(data) {
25685 if(typeof data == "string") {
25686 data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
25688 var featureNodes = this.getElementsByTagNameNS(data.documentElement,
25692 for(var i=0; i<featureNodes.length; i++) {
25693 var feature = this.parseFeature(featureNodes[i]);
25695 features.push(feature);
25702 * Method: parseFeature
25703 * This function is the core of the GML parsing code in OpenLayers.
25704 * It creates the geometries that are then attached to the returned
25705 * feature, and calls parseAttributes() to get attribute data out.
25708 * node - {DOMElement} A GML feature node.
25710 parseFeature: function(node) {
25711 // only accept one geometry per feature - look for highest "order"
25712 var order = ["MultiPolygon", "Polygon",
25713 "MultiLineString", "LineString",
25714 "MultiPoint", "Point", "Envelope"];
25715 // FIXME: In case we parse a feature with no geometry, but boundedBy an Envelope,
25716 // this code creates a geometry derived from the Envelope. This is not correct.
25717 var type, nodeList, geometry, parser;
25718 for(var i=0; i<order.length; ++i) {
25720 nodeList = this.getElementsByTagNameNS(node, this.gmlns, type);
25721 if(nodeList.length > 0) {
25722 // only deal with first geometry of this type
25723 parser = this.parseGeometry[type.toLowerCase()];
25725 geometry = parser.apply(this, [nodeList[0]]);
25726 if (this.internalProjection && this.externalProjection) {
25727 geometry.transform(this.externalProjection,
25728 this.internalProjection);
25731 throw new TypeError("Unsupported geometry type: " + type);
25733 // stop looking for different geometry types
25739 var boxNodes = this.getElementsByTagNameNS(node, this.gmlns, "Box");
25740 for(i=0; i<boxNodes.length; ++i) {
25741 var boxNode = boxNodes[i];
25742 var box = this.parseGeometry["box"].apply(this, [boxNode]);
25743 var parentNode = boxNode.parentNode;
25744 var parentName = parentNode.localName ||
25745 parentNode.nodeName.split(":").pop();
25746 if(parentName === "boundedBy") {
25749 geometry = box.toGeometry();
25753 // construct feature (optionally with attributes)
25755 if(this.extractAttributes) {
25756 attributes = this.parseAttributes(node);
25758 var feature = new OpenLayers.Feature.Vector(geometry, attributes);
25759 feature.bounds = bounds;
25762 featureType: node.firstChild.nodeName.split(":")[1],
25763 featureNS: node.firstChild.namespaceURI,
25764 featureNSPrefix: node.firstChild.prefix
25767 // assign fid - this can come from a "fid" or "id" attribute
25768 var childNode = node.firstChild;
25771 if(childNode.nodeType == 1) {
25772 fid = childNode.getAttribute("fid") ||
25773 childNode.getAttribute("id");
25778 childNode = childNode.nextSibling;
25785 * Property: parseGeometry
25786 * Properties of this object are the functions that parse geometries based
25792 * Method: parseGeometry.point
25793 * Given a GML node representing a point geometry, create an OpenLayers
25797 * node - {DOMElement} A GML node.
25800 * {<OpenLayers.Geometry.Point>} A point geometry.
25802 point: function(node) {
25804 * Three coordinate variations to consider:
25805 * 1) <gml:pos>x y z</gml:pos>
25806 * 2) <gml:coordinates>x, y, z</gml:coordinates>
25807 * 3) <gml:coord><gml:X>x</gml:X><gml:Y>y</gml:Y></gml:coord>
25809 var nodeList, coordString;
25812 // look for <gml:pos>
25813 var nodeList = this.getElementsByTagNameNS(node, this.gmlns, "pos");
25814 if(nodeList.length > 0) {
25815 coordString = nodeList[0].firstChild.nodeValue;
25816 coordString = coordString.replace(this.regExes.trimSpace, "");
25817 coords = coordString.split(this.regExes.splitSpace);
25820 // look for <gml:coordinates>
25821 if(coords.length == 0) {
25822 nodeList = this.getElementsByTagNameNS(node, this.gmlns,
25824 if(nodeList.length > 0) {
25825 coordString = nodeList[0].firstChild.nodeValue;
25826 coordString = coordString.replace(this.regExes.removeSpace,
25828 coords = coordString.split(",");
25832 // look for <gml:coord>
25833 if(coords.length == 0) {
25834 nodeList = this.getElementsByTagNameNS(node, this.gmlns,
25836 if(nodeList.length > 0) {
25837 var xList = this.getElementsByTagNameNS(nodeList[0],
25839 var yList = this.getElementsByTagNameNS(nodeList[0],
25841 if(xList.length > 0 && yList.length > 0) {
25842 coords = [xList[0].firstChild.nodeValue,
25843 yList[0].firstChild.nodeValue];
25848 // preserve third dimension
25849 if(coords.length == 2) {
25854 return new OpenLayers.Geometry.Point(coords[0], coords[1],
25858 return new OpenLayers.Geometry.Point(coords[1], coords[0],
25864 * Method: parseGeometry.multipoint
25865 * Given a GML node representing a multipoint geometry, create an
25866 * OpenLayers multipoint geometry.
25869 * node - {DOMElement} A GML node.
25872 * {<OpenLayers.Geometry.MultiPoint>} A multipoint geometry.
25874 multipoint: function(node) {
25875 var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
25877 var components = [];
25878 if(nodeList.length > 0) {
25880 for(var i=0; i<nodeList.length; ++i) {
25881 point = this.parseGeometry.point.apply(this, [nodeList[i]]);
25883 components.push(point);
25887 return new OpenLayers.Geometry.MultiPoint(components);
25891 * Method: parseGeometry.linestring
25892 * Given a GML node representing a linestring geometry, create an
25893 * OpenLayers linestring geometry.
25896 * node - {DOMElement} A GML node.
25899 * {<OpenLayers.Geometry.LineString>} A linestring geometry.
25901 linestring: function(node, ring) {
25903 * Two coordinate variations to consider:
25904 * 1) <gml:posList dimension="d">x0 y0 z0 x1 y1 z1</gml:posList>
25905 * 2) <gml:coordinates>x0, y0, z0 x1, y1, z1</gml:coordinates>
25907 var nodeList, coordString;
25911 // look for <gml:posList>
25912 nodeList = this.getElementsByTagNameNS(node, this.gmlns, "posList");
25913 if(nodeList.length > 0) {
25914 coordString = this.getChildValue(nodeList[0]);
25915 coordString = coordString.replace(this.regExes.trimSpace, "");
25916 coords = coordString.split(this.regExes.splitSpace);
25917 var dim = parseInt(nodeList[0].getAttribute("dimension"));
25919 for(var i=0; i<coords.length/dim; ++i) {
25923 z = (dim == 2) ? null : coords[j+2];
25925 points.push(new OpenLayers.Geometry.Point(x, y, z));
25927 points.push(new OpenLayers.Geometry.Point(y, x, z));
25932 // look for <gml:coordinates>
25933 if(coords.length == 0) {
25934 nodeList = this.getElementsByTagNameNS(node, this.gmlns,
25936 if(nodeList.length > 0) {
25937 coordString = this.getChildValue(nodeList[0]);
25938 coordString = coordString.replace(this.regExes.trimSpace,
25940 coordString = coordString.replace(this.regExes.trimComma,
25942 var pointList = coordString.split(this.regExes.splitSpace);
25943 for(var i=0; i<pointList.length; ++i) {
25944 coords = pointList[i].split(",");
25945 if(coords.length == 2) {
25949 points.push(new OpenLayers.Geometry.Point(coords[0],
25953 points.push(new OpenLayers.Geometry.Point(coords[1],
25962 if(points.length != 0) {
25964 line = new OpenLayers.Geometry.LinearRing(points);
25966 line = new OpenLayers.Geometry.LineString(points);
25973 * Method: parseGeometry.multilinestring
25974 * Given a GML node representing a multilinestring geometry, create an
25975 * OpenLayers multilinestring geometry.
25978 * node - {DOMElement} A GML node.
25981 * {<OpenLayers.Geometry.MultiLineString>} A multilinestring geometry.
25983 multilinestring: function(node) {
25984 var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
25986 var components = [];
25987 if(nodeList.length > 0) {
25989 for(var i=0; i<nodeList.length; ++i) {
25990 line = this.parseGeometry.linestring.apply(this,
25993 components.push(line);
25997 return new OpenLayers.Geometry.MultiLineString(components);
26001 * Method: parseGeometry.polygon
26002 * Given a GML node representing a polygon geometry, create an
26003 * OpenLayers polygon geometry.
26006 * node - {DOMElement} A GML node.
26009 * {<OpenLayers.Geometry.Polygon>} A polygon geometry.
26011 polygon: function(node) {
26012 var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
26014 var components = [];
26015 if(nodeList.length > 0) {
26016 // this assumes exterior ring first, inner rings after
26018 for(var i=0; i<nodeList.length; ++i) {
26019 ring = this.parseGeometry.linestring.apply(this,
26020 [nodeList[i], true]);
26022 components.push(ring);
26026 return new OpenLayers.Geometry.Polygon(components);
26030 * Method: parseGeometry.multipolygon
26031 * Given a GML node representing a multipolygon geometry, create an
26032 * OpenLayers multipolygon geometry.
26035 * node - {DOMElement} A GML node.
26038 * {<OpenLayers.Geometry.MultiPolygon>} A multipolygon geometry.
26040 multipolygon: function(node) {
26041 var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
26043 var components = [];
26044 if(nodeList.length > 0) {
26046 for(var i=0; i<nodeList.length; ++i) {
26047 polygon = this.parseGeometry.polygon.apply(this,
26050 components.push(polygon);
26054 return new OpenLayers.Geometry.MultiPolygon(components);
26057 envelope: function(node) {
26058 var components = [];
26062 var lpoint = this.getElementsByTagNameNS(node, this.gmlns, "lowerCorner");
26063 if (lpoint.length > 0) {
26066 if(lpoint.length > 0) {
26067 coordString = lpoint[0].firstChild.nodeValue;
26068 coordString = coordString.replace(this.regExes.trimSpace, "");
26069 coords = coordString.split(this.regExes.splitSpace);
26072 if(coords.length == 2) {
26076 var lowerPoint = new OpenLayers.Geometry.Point(coords[0], coords[1],coords[2]);
26078 var lowerPoint = new OpenLayers.Geometry.Point(coords[1], coords[0],coords[2]);
26082 var upoint = this.getElementsByTagNameNS(node, this.gmlns, "upperCorner");
26083 if (upoint.length > 0) {
26086 if(upoint.length > 0) {
26087 coordString = upoint[0].firstChild.nodeValue;
26088 coordString = coordString.replace(this.regExes.trimSpace, "");
26089 coords = coordString.split(this.regExes.splitSpace);
26092 if(coords.length == 2) {
26096 var upperPoint = new OpenLayers.Geometry.Point(coords[0], coords[1],coords[2]);
26098 var upperPoint = new OpenLayers.Geometry.Point(coords[1], coords[0],coords[2]);
26102 if (lowerPoint && upperPoint) {
26103 components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y));
26104 components.push(new OpenLayers.Geometry.Point(upperPoint.x, lowerPoint.y));
26105 components.push(new OpenLayers.Geometry.Point(upperPoint.x, upperPoint.y));
26106 components.push(new OpenLayers.Geometry.Point(lowerPoint.x, upperPoint.y));
26107 components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y));
26109 var ring = new OpenLayers.Geometry.LinearRing(components);
26110 envelope = new OpenLayers.Geometry.Polygon([ring]);
26116 * Method: parseGeometry.box
26117 * Given a GML node representing a box geometry, create an
26118 * OpenLayers.Bounds.
26121 * node - {DOMElement} A GML node.
26124 * {<OpenLayers.Bounds>} A bounds representing the box.
26126 box: function(node) {
26127 var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
26130 var coords, beginPoint = null, endPoint = null;
26131 if (nodeList.length > 0) {
26132 coordString = nodeList[0].firstChild.nodeValue;
26133 coords = coordString.split(" ");
26134 if (coords.length == 2) {
26135 beginPoint = coords[0].split(",");
26136 endPoint = coords[1].split(",");
26139 if (beginPoint !== null && endPoint !== null) {
26140 return new OpenLayers.Bounds(parseFloat(beginPoint[0]),
26141 parseFloat(beginPoint[1]),
26142 parseFloat(endPoint[0]),
26143 parseFloat(endPoint[1]) );
26150 * Method: parseAttributes
26153 * node - {DOMElement}
26156 * {Object} An attributes object.
26158 parseAttributes: function(node) {
26159 var attributes = {};
26160 // assume attributes are children of the first type 1 child
26161 var childNode = node.firstChild;
26162 var children, i, child, grandchildren, grandchild, name, value;
26164 if(childNode.nodeType == 1) {
26165 // attributes are type 1 children with one type 3 child
26166 children = childNode.childNodes;
26167 for(i=0; i<children.length; ++i) {
26168 child = children[i];
26169 if(child.nodeType == 1) {
26170 grandchildren = child.childNodes;
26171 if(grandchildren.length == 1) {
26172 grandchild = grandchildren[0];
26173 if(grandchild.nodeType == 3 ||
26174 grandchild.nodeType == 4) {
26175 name = (child.prefix) ?
26176 child.nodeName.split(":")[1] :
26178 value = grandchild.nodeValue.replace(
26179 this.regExes.trimSpace, "");
26180 attributes[name] = value;
26183 // If child has no childNodes (grandchildren),
26184 // set an attribute with null value.
26185 // e.g. <prefix:fieldname/> becomes
26186 // {fieldname: null}
26187 attributes[child.nodeName.split(":").pop()] = null;
26193 childNode = childNode.nextSibling;
26200 * Generate a GML document string given a list of features.
26203 * features - {Array(<OpenLayers.Feature.Vector>)} List of features to
26204 * serialize into a string.
26207 * {String} A string representing the GML document.
26209 write: function(features) {
26210 if(!(OpenLayers.Util.isArray(features))) {
26211 features = [features];
26213 var gml = this.createElementNS("http://www.opengis.net/wfs",
26214 "wfs:" + this.collectionName);
26215 for(var i=0; i<features.length; i++) {
26216 gml.appendChild(this.createFeatureXML(features[i]));
26218 return OpenLayers.Format.XML.prototype.write.apply(this, [gml]);
26222 * Method: createFeatureXML
26223 * Accept an OpenLayers.Feature.Vector, and build a GML node for it.
26226 * feature - {<OpenLayers.Feature.Vector>} The feature to be built as GML.
26229 * {DOMElement} A node reprensting the feature in GML.
26231 createFeatureXML: function(feature) {
26232 var geometry = feature.geometry;
26233 var geometryNode = this.buildGeometryNode(geometry);
26234 var geomContainer = this.createElementNS(this.featureNS,
26235 this.featurePrefix + ":" +
26236 this.geometryName);
26237 geomContainer.appendChild(geometryNode);
26238 var featureNode = this.createElementNS(this.gmlns,
26239 "gml:" + this.featureName);
26240 var featureContainer = this.createElementNS(this.featureNS,
26241 this.featurePrefix + ":" +
26243 var fid = feature.fid || feature.id;
26244 featureContainer.setAttribute("fid", fid);
26245 featureContainer.appendChild(geomContainer);
26246 for(var attr in feature.attributes) {
26247 var attrText = this.createTextNode(feature.attributes[attr]);
26248 var nodename = attr.substring(attr.lastIndexOf(":") + 1);
26249 var attrContainer = this.createElementNS(this.featureNS,
26250 this.featurePrefix + ":" +
26252 attrContainer.appendChild(attrText);
26253 featureContainer.appendChild(attrContainer);
26255 featureNode.appendChild(featureContainer);
26256 return featureNode;
26260 * APIMethod: buildGeometryNode
26262 buildGeometryNode: function(geometry) {
26263 if (this.externalProjection && this.internalProjection) {
26264 geometry = geometry.clone();
26265 geometry.transform(this.internalProjection,
26266 this.externalProjection);
26268 var className = geometry.CLASS_NAME;
26269 var type = className.substring(className.lastIndexOf(".") + 1);
26270 var builder = this.buildGeometry[type.toLowerCase()];
26271 return builder.apply(this, [geometry]);
26275 * Property: buildGeometry
26276 * Object containing methods to do the actual geometry node building
26277 * based on geometry type.
26280 // TBD retrieve the srs from layer
26281 // srsName is non-standard, so not including it until it's right.
26282 // gml.setAttribute("srsName",
26283 // "http://www.opengis.net/gml/srs/epsg.xml#4326");
26286 * Method: buildGeometry.point
26287 * Given an OpenLayers point geometry, create a GML point.
26290 * geometry - {<OpenLayers.Geometry.Point>} A point geometry.
26293 * {DOMElement} A GML point node.
26295 point: function(geometry) {
26296 var gml = this.createElementNS(this.gmlns, "gml:Point");
26297 gml.appendChild(this.buildCoordinatesNode(geometry));
26302 * Method: buildGeometry.multipoint
26303 * Given an OpenLayers multipoint geometry, create a GML multipoint.
26306 * geometry - {<OpenLayers.Geometry.MultiPoint>} A multipoint geometry.
26309 * {DOMElement} A GML multipoint node.
26311 multipoint: function(geometry) {
26312 var gml = this.createElementNS(this.gmlns, "gml:MultiPoint");
26313 var points = geometry.components;
26314 var pointMember, pointGeom;
26315 for(var i=0; i<points.length; i++) {
26316 pointMember = this.createElementNS(this.gmlns,
26317 "gml:pointMember");
26318 pointGeom = this.buildGeometry.point.apply(this,
26320 pointMember.appendChild(pointGeom);
26321 gml.appendChild(pointMember);
26327 * Method: buildGeometry.linestring
26328 * Given an OpenLayers linestring geometry, create a GML linestring.
26331 * geometry - {<OpenLayers.Geometry.LineString>} A linestring geometry.
26334 * {DOMElement} A GML linestring node.
26336 linestring: function(geometry) {
26337 var gml = this.createElementNS(this.gmlns, "gml:LineString");
26338 gml.appendChild(this.buildCoordinatesNode(geometry));
26343 * Method: buildGeometry.multilinestring
26344 * Given an OpenLayers multilinestring geometry, create a GML
26348 * geometry - {<OpenLayers.Geometry.MultiLineString>} A multilinestring
26352 * {DOMElement} A GML multilinestring node.
26354 multilinestring: function(geometry) {
26355 var gml = this.createElementNS(this.gmlns, "gml:MultiLineString");
26356 var lines = geometry.components;
26357 var lineMember, lineGeom;
26358 for(var i=0; i<lines.length; ++i) {
26359 lineMember = this.createElementNS(this.gmlns,
26360 "gml:lineStringMember");
26361 lineGeom = this.buildGeometry.linestring.apply(this,
26363 lineMember.appendChild(lineGeom);
26364 gml.appendChild(lineMember);
26370 * Method: buildGeometry.linearring
26371 * Given an OpenLayers linearring geometry, create a GML linearring.
26374 * geometry - {<OpenLayers.Geometry.LinearRing>} A linearring geometry.
26377 * {DOMElement} A GML linearring node.
26379 linearring: function(geometry) {
26380 var gml = this.createElementNS(this.gmlns, "gml:LinearRing");
26381 gml.appendChild(this.buildCoordinatesNode(geometry));
26386 * Method: buildGeometry.polygon
26387 * Given an OpenLayers polygon geometry, create a GML polygon.
26390 * geometry - {<OpenLayers.Geometry.Polygon>} A polygon geometry.
26393 * {DOMElement} A GML polygon node.
26395 polygon: function(geometry) {
26396 var gml = this.createElementNS(this.gmlns, "gml:Polygon");
26397 var rings = geometry.components;
26398 var ringMember, ringGeom, type;
26399 for(var i=0; i<rings.length; ++i) {
26400 type = (i==0) ? "outerBoundaryIs" : "innerBoundaryIs";
26401 ringMember = this.createElementNS(this.gmlns,
26403 ringGeom = this.buildGeometry.linearring.apply(this,
26405 ringMember.appendChild(ringGeom);
26406 gml.appendChild(ringMember);
26412 * Method: buildGeometry.multipolygon
26413 * Given an OpenLayers multipolygon geometry, create a GML multipolygon.
26416 * geometry - {<OpenLayers.Geometry.MultiPolygon>} A multipolygon
26420 * {DOMElement} A GML multipolygon node.
26422 multipolygon: function(geometry) {
26423 var gml = this.createElementNS(this.gmlns, "gml:MultiPolygon");
26424 var polys = geometry.components;
26425 var polyMember, polyGeom;
26426 for(var i=0; i<polys.length; ++i) {
26427 polyMember = this.createElementNS(this.gmlns,
26428 "gml:polygonMember");
26429 polyGeom = this.buildGeometry.polygon.apply(this,
26431 polyMember.appendChild(polyGeom);
26432 gml.appendChild(polyMember);
26439 * Method: buildGeometry.bounds
26440 * Given an OpenLayers bounds, create a GML box.
26443 * bounds - {<OpenLayers.Geometry.Bounds>} A bounds object.
26446 * {DOMElement} A GML box node.
26448 bounds: function(bounds) {
26449 var gml = this.createElementNS(this.gmlns, "gml:Box");
26450 gml.appendChild(this.buildCoordinatesNode(bounds));
26456 * Method: buildCoordinates
26457 * builds the coordinates XmlNode
26459 * <gml:coordinates decimal="." cs="," ts=" ">...</gml:coordinates>
26463 * geometry - {<OpenLayers.Geometry>}
26466 * {XmlNode} created xmlNode
26468 buildCoordinatesNode: function(geometry) {
26469 var coordinatesNode = this.createElementNS(this.gmlns,
26470 "gml:coordinates");
26471 coordinatesNode.setAttribute("decimal", ".");
26472 coordinatesNode.setAttribute("cs", ",");
26473 coordinatesNode.setAttribute("ts", " ");
26477 if(geometry instanceof OpenLayers.Bounds){
26478 parts.push(geometry.left + "," + geometry.bottom);
26479 parts.push(geometry.right + "," + geometry.top);
26481 var points = (geometry.components) ? geometry.components : [geometry];
26482 for(var i=0; i<points.length; i++) {
26483 parts.push(points[i].x + "," + points[i].y);
26487 var txtNode = this.createTextNode(parts.join(" "));
26488 coordinatesNode.appendChild(txtNode);
26490 return coordinatesNode;
26493 CLASS_NAME: "OpenLayers.Format.GML"
26495 /* ======================================================================
26496 OpenLayers/Format/GML/Base.js
26497 ====================================================================== */
26499 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
26500 * full list of contributors). Published under the 2-clause BSD license.
26501 * See license.txt in the OpenLayers distribution or repository for the
26502 * full text of the license. */
26505 * @requires OpenLayers/Format/XML.js
26506 * @requires OpenLayers/Format/GML.js
26510 * Though required in the full build, if the GML format is excluded, we set
26511 * the namespace here.
26513 if(!OpenLayers.Format.GML) {
26514 OpenLayers.Format.GML = {};
26518 * Class: OpenLayers.Format.GML.Base
26519 * Superclass for GML parsers.
26522 * - <OpenLayers.Format.XML>
26524 OpenLayers.Format.GML.Base = OpenLayers.Class(OpenLayers.Format.XML, {
26527 * Property: namespaces
26528 * {Object} Mapping of namespace aliases to namespace URIs.
26531 gml: "http://www.opengis.net/gml",
26532 xlink: "http://www.w3.org/1999/xlink",
26533 xsi: "http://www.w3.org/2001/XMLSchema-instance",
26534 wfs: "http://www.opengis.net/wfs" // this is a convenience for reading wfs:FeatureCollection
26538 * Property: defaultPrefix
26540 defaultPrefix: "gml",
26543 * Property: schemaLocation
26544 * {String} Schema location for a particular minor version.
26546 schemaLocation: null,
26549 * APIProperty: featureType
26550 * {Array(String) or String} The local (without prefix) feature typeName(s).
26555 * APIProperty: featureNS
26556 * {String} The feature namespace. Must be set in the options at
26562 * APIProperty: geometry
26563 * {String} Name of geometry element. Defaults to "geometry". If null, it
26564 * will be set on <read> when the first geometry is parsed.
26566 geometryName: "geometry",
26569 * APIProperty: extractAttributes
26570 * {Boolean} Extract attributes from GML. Default is true.
26572 extractAttributes: true,
26575 * APIProperty: srsName
26576 * {String} URI for spatial reference system. This is optional for
26577 * single part geometries and mandatory for collections and multis.
26578 * If set, the srsName attribute will be written for all geometries.
26585 * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
26586 * Changing is not recommended, a new Format should be instantiated.
26591 * Property: geometryTypes
26592 * {Object} Maps OpenLayers geometry class names to GML element names.
26593 * Use <setGeometryTypes> before accessing this property.
26595 geometryTypes: null,
26598 * Property: singleFeatureType
26599 * {Boolean} True if there is only 1 featureType, and not an array
26602 singleFeatureType: null,
26605 * Property: autoConfig
26606 * {Boolean} Indicates if the format was configured without a <featureNS>,
26607 * but auto-configured <featureNS> and <featureType> during read.
26608 * Subclasses making use of <featureType> auto-configuration should make
26609 * the first call to the <readNode> method (usually in the read method)
26610 * with true as 3rd argument, so the auto-configured featureType can be
26611 * reset and the format can be reused for subsequent reads with data from
26612 * different featureTypes. Set to false after read if you want to keep the
26613 * auto-configured values.
26617 * Property: regExes
26618 * Compiled regular expressions for manipulating strings.
26621 trimSpace: (/^\s*|\s*$/g),
26622 removeSpace: (/\s*/g),
26623 splitSpace: (/\s+/),
26624 trimComma: (/\s*,\s*/g),
26625 featureMember: (/^(.*:)?featureMembers?$/)
26629 * Constructor: OpenLayers.Format.GML.Base
26630 * Instances of this class are not created directly. Use the
26631 * <OpenLayers.Format.GML.v2> or <OpenLayers.Format.GML.v3> constructor
26635 * options - {Object} An optional object whose properties will be set on
26638 * Valid options properties:
26639 * featureType - {Array(String) or String} Local (without prefix) feature
26640 * typeName(s) (required for write).
26641 * featureNS - {String} Feature namespace (required for write).
26642 * geometryName - {String} Geometry element name (required for write).
26644 initialize: function(options) {
26645 OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
26646 this.setGeometryTypes();
26647 if(options && options.featureNS) {
26648 this.setNamespace("feature", options.featureNS);
26650 this.singleFeatureType = !options || (typeof options.featureType === "string");
26657 * data - {DOMElement} A gml:featureMember element, a gml:featureMembers
26658 * element, or an element containing either of the above at any level.
26661 * {Array(<OpenLayers.Feature.Vector>)} An array of features.
26663 read: function(data) {
26664 if(typeof data == "string") {
26665 data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
26667 if(data && data.nodeType == 9) {
26668 data = data.documentElement;
26671 this.readNode(data, {features: features}, true);
26672 if(features.length == 0) {
26673 // look for gml:featureMember elements
26674 var elements = this.getElementsByTagNameNS(
26675 data, this.namespaces.gml, "featureMember"
26677 if(elements.length) {
26678 for(var i=0, len=elements.length; i<len; ++i) {
26679 this.readNode(elements[i], {features: features}, true);
26682 // look for gml:featureMembers elements (this is v3, but does no harm here)
26683 var elements = this.getElementsByTagNameNS(
26684 data, this.namespaces.gml, "featureMembers"
26686 if(elements.length) {
26687 // there can be only one
26688 this.readNode(elements[0], {features: features}, true);
26697 * Shorthand for applying one of the named readers given the node
26698 * namespace and local name. Readers take two args (node, obj) and
26699 * generally extend or modify the second.
26702 * node - {DOMElement} The node to be read (required).
26703 * obj - {Object} The object to be modified (optional).
26704 * first - {Boolean} Should be set to true for the first node read. This
26705 * is usually the readNode call in the read method. Without this being
26706 * set, auto-configured properties will stick on subsequent reads.
26709 * {Object} The input object, modified (or a new one if none was provided).
26711 readNode: function(node, obj, first) {
26712 // on subsequent calls of format.read(), we want to reset auto-
26713 // configured properties and auto-configure again.
26714 if (first === true && this.autoConfig === true) {
26715 this.featureType = null;
26716 delete this.namespaceAlias[this.featureNS];
26717 delete this.namespaces["feature"];
26718 this.featureNS = null;
26720 // featureType auto-configuration
26721 if (!this.featureNS && (!(node.prefix in this.namespaces) &&
26722 node.parentNode.namespaceURI == this.namespaces["gml"] &&
26723 this.regExes.featureMember.test(node.parentNode.nodeName))) {
26724 this.featureType = node.nodeName.split(":").pop();
26725 this.setNamespace("feature", node.namespaceURI);
26726 this.featureNS = node.namespaceURI;
26727 this.autoConfig = true;
26729 return OpenLayers.Format.XML.prototype.readNode.apply(this, [node, obj]);
26733 * Property: readers
26734 * Contains public functions, grouped by namespace prefix, that will
26735 * be applied when a namespaced node is found matching the function
26736 * name. The function will be applied in the scope of this parser
26737 * with two arguments: the node being read and a context object passed
26742 "_inherit": function(node, obj, container) {
26743 // To be implemented by version specific parsers
26745 "featureMember": function(node, obj) {
26746 this.readChildNodes(node, obj);
26748 "featureMembers": function(node, obj) {
26749 this.readChildNodes(node, obj);
26751 "name": function(node, obj) {
26752 obj.name = this.getChildValue(node);
26754 "boundedBy": function(node, obj) {
26755 var container = {};
26756 this.readChildNodes(node, container);
26757 if(container.components && container.components.length > 0) {
26758 obj.bounds = container.components[0];
26761 "Point": function(node, container) {
26762 var obj = {points: []};
26763 this.readChildNodes(node, obj);
26764 if(!container.components) {
26765 container.components = [];
26767 container.components.push(obj.points[0]);
26769 "coordinates": function(node, obj) {
26770 var str = this.getChildValue(node).replace(
26771 this.regExes.trimSpace, ""
26773 str = str.replace(this.regExes.trimComma, ",");
26774 var pointList = str.split(this.regExes.splitSpace);
26776 var numPoints = pointList.length;
26777 var points = new Array(numPoints);
26778 for(var i=0; i<numPoints; ++i) {
26779 coords = pointList[i].split(",");
26781 points[i] = new OpenLayers.Geometry.Point(
26782 coords[0], coords[1], coords[2]
26785 points[i] = new OpenLayers.Geometry.Point(
26786 coords[1], coords[0], coords[2]
26790 obj.points = points;
26792 "coord": function(node, obj) {
26794 this.readChildNodes(node, coord);
26798 obj.points.push(new OpenLayers.Geometry.Point(
26799 coord.x, coord.y, coord.z
26802 "X": function(node, coord) {
26803 coord.x = this.getChildValue(node);
26805 "Y": function(node, coord) {
26806 coord.y = this.getChildValue(node);
26808 "Z": function(node, coord) {
26809 coord.z = this.getChildValue(node);
26811 "MultiPoint": function(node, container) {
26812 var obj = {components: []};
26813 this.readers.gml._inherit.apply(this, [node, obj, container]);
26814 this.readChildNodes(node, obj);
26815 container.components = [
26816 new OpenLayers.Geometry.MultiPoint(obj.components)
26819 "pointMember": function(node, obj) {
26820 this.readChildNodes(node, obj);
26822 "LineString": function(node, container) {
26824 this.readers.gml._inherit.apply(this, [node, obj, container]);
26825 this.readChildNodes(node, obj);
26826 if(!container.components) {
26827 container.components = [];
26829 container.components.push(
26830 new OpenLayers.Geometry.LineString(obj.points)
26833 "MultiLineString": function(node, container) {
26834 var obj = {components: []};
26835 this.readers.gml._inherit.apply(this, [node, obj, container]);
26836 this.readChildNodes(node, obj);
26837 container.components = [
26838 new OpenLayers.Geometry.MultiLineString(obj.components)
26841 "lineStringMember": function(node, obj) {
26842 this.readChildNodes(node, obj);
26844 "Polygon": function(node, container) {
26845 var obj = {outer: null, inner: []};
26846 this.readers.gml._inherit.apply(this, [node, obj, container]);
26847 this.readChildNodes(node, obj);
26848 obj.inner.unshift(obj.outer);
26849 if(!container.components) {
26850 container.components = [];
26852 container.components.push(
26853 new OpenLayers.Geometry.Polygon(obj.inner)
26856 "LinearRing": function(node, obj) {
26857 var container = {};
26858 this.readers.gml._inherit.apply(this, [node, container]);
26859 this.readChildNodes(node, container);
26860 obj.components = [new OpenLayers.Geometry.LinearRing(
26864 "MultiPolygon": function(node, container) {
26865 var obj = {components: []};
26866 this.readers.gml._inherit.apply(this, [node, obj, container]);
26867 this.readChildNodes(node, obj);
26868 container.components = [
26869 new OpenLayers.Geometry.MultiPolygon(obj.components)
26872 "polygonMember": function(node, obj) {
26873 this.readChildNodes(node, obj);
26875 "GeometryCollection": function(node, container) {
26876 var obj = {components: []};
26877 this.readers.gml._inherit.apply(this, [node, obj, container]);
26878 this.readChildNodes(node, obj);
26879 container.components = [
26880 new OpenLayers.Geometry.Collection(obj.components)
26883 "geometryMember": function(node, obj) {
26884 this.readChildNodes(node, obj);
26888 "*": function(node, obj) {
26889 // The node can either be named like the featureType, or it
26890 // can be a child of the feature:featureType. Children can be
26891 // geometry or attributes.
26893 var local = node.localName || node.nodeName.split(":").pop();
26894 // Since an attribute can have the same name as the feature type
26895 // we only want to read the node as a feature if the parent
26896 // node can have feature nodes as children. In this case, the
26897 // obj.features property is set.
26898 if (obj.features) {
26899 if (!this.singleFeatureType &&
26900 (OpenLayers.Util.indexOf(this.featureType, local) !== -1)) {
26901 name = "_typeName";
26902 } else if(local === this.featureType) {
26903 name = "_typeName";
26906 // Assume attribute elements have one child node and that the child
26907 // is a text node. Otherwise assume it is a geometry node.
26908 if(node.childNodes.length == 0 ||
26909 (node.childNodes.length == 1 && node.firstChild.nodeType == 3)) {
26910 if(this.extractAttributes) {
26911 name = "_attribute";
26914 name = "_geometry";
26918 this.readers.feature[name].apply(this, [node, obj]);
26921 "_typeName": function(node, obj) {
26922 var container = {components: [], attributes: {}};
26923 this.readChildNodes(node, container);
26924 // look for common gml namespaced elements
26925 if(container.name) {
26926 container.attributes.name = container.name;
26928 var feature = new OpenLayers.Feature.Vector(
26929 container.components[0], container.attributes
26931 if (!this.singleFeatureType) {
26932 feature.type = node.nodeName.split(":").pop();
26933 feature.namespace = node.namespaceURI;
26935 var fid = node.getAttribute("fid") ||
26936 this.getAttributeNS(node, this.namespaces["gml"], "id");
26940 if(this.internalProjection && this.externalProjection &&
26941 feature.geometry) {
26942 feature.geometry.transform(
26943 this.externalProjection, this.internalProjection
26946 if(container.bounds) {
26947 feature.bounds = container.bounds;
26949 obj.features.push(feature);
26951 "_geometry": function(node, obj) {
26952 if (!this.geometryName) {
26953 this.geometryName = node.nodeName.split(":").pop();
26955 this.readChildNodes(node, obj);
26957 "_attribute": function(node, obj) {
26958 var local = node.localName || node.nodeName.split(":").pop();
26959 var value = this.getChildValue(node);
26960 obj.attributes[local] = value;
26964 "FeatureCollection": function(node, obj) {
26965 this.readChildNodes(node, obj);
26974 * features - {Array(<OpenLayers.Feature.Vector>) | OpenLayers.Feature.Vector}
26975 * An array of features or a single feature.
26978 * {String} Given an array of features, a doc with a gml:featureMembers
26979 * element will be returned. Given a single feature, a doc with a
26980 * gml:featureMember element will be returned.
26982 write: function(features) {
26984 if(OpenLayers.Util.isArray(features)) {
26985 name = "featureMembers";
26987 name = "featureMember";
26989 var root = this.writeNode("gml:" + name, features);
26990 this.setAttributeNS(
26991 root, this.namespaces["xsi"],
26992 "xsi:schemaLocation", this.schemaLocation
26995 return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
26999 * Property: writers
27000 * As a compliment to the readers property, this structure contains public
27001 * writing functions grouped by namespace alias and named like the
27002 * node names they produce.
27006 "featureMember": function(feature) {
27007 var node = this.createElementNSPlus("gml:featureMember");
27008 this.writeNode("feature:_typeName", feature, node);
27011 "MultiPoint": function(geometry) {
27012 var node = this.createElementNSPlus("gml:MultiPoint");
27013 var components = geometry.components || [geometry];
27014 for(var i=0, ii=components.length; i<ii; ++i) {
27015 this.writeNode("pointMember", components[i], node);
27019 "pointMember": function(geometry) {
27020 var node = this.createElementNSPlus("gml:pointMember");
27021 this.writeNode("Point", geometry, node);
27024 "MultiLineString": function(geometry) {
27025 var node = this.createElementNSPlus("gml:MultiLineString");
27026 var components = geometry.components || [geometry];
27027 for(var i=0, ii=components.length; i<ii; ++i) {
27028 this.writeNode("lineStringMember", components[i], node);
27032 "lineStringMember": function(geometry) {
27033 var node = this.createElementNSPlus("gml:lineStringMember");
27034 this.writeNode("LineString", geometry, node);
27037 "MultiPolygon": function(geometry) {
27038 var node = this.createElementNSPlus("gml:MultiPolygon");
27039 var components = geometry.components || [geometry];
27040 for(var i=0, ii=components.length; i<ii; ++i) {
27042 "polygonMember", components[i], node
27047 "polygonMember": function(geometry) {
27048 var node = this.createElementNSPlus("gml:polygonMember");
27049 this.writeNode("Polygon", geometry, node);
27052 "GeometryCollection": function(geometry) {
27053 var node = this.createElementNSPlus("gml:GeometryCollection");
27054 for(var i=0, len=geometry.components.length; i<len; ++i) {
27055 this.writeNode("geometryMember", geometry.components[i], node);
27059 "geometryMember": function(geometry) {
27060 var node = this.createElementNSPlus("gml:geometryMember");
27061 var child = this.writeNode("feature:_geometry", geometry);
27062 node.appendChild(child.firstChild);
27067 "_typeName": function(feature) {
27068 var node = this.createElementNSPlus("feature:" + this.featureType, {
27069 attributes: {fid: feature.fid}
27071 if(feature.geometry) {
27072 this.writeNode("feature:_geometry", feature.geometry, node);
27074 for(var name in feature.attributes) {
27075 var value = feature.attributes[name];
27076 if(value != null) {
27078 "feature:_attribute",
27079 {name: name, value: value}, node
27085 "_geometry": function(geometry) {
27086 if(this.externalProjection && this.internalProjection) {
27087 geometry = geometry.clone().transform(
27088 this.internalProjection, this.externalProjection
27091 var node = this.createElementNSPlus(
27092 "feature:" + this.geometryName
27094 var type = this.geometryTypes[geometry.CLASS_NAME];
27095 var child = this.writeNode("gml:" + type, geometry, node);
27097 child.setAttribute("srsName", this.srsName);
27101 "_attribute": function(obj) {
27102 return this.createElementNSPlus("feature:" + obj.name, {
27108 "FeatureCollection": function(features) {
27110 * This is only here because GML2 only describes abstract
27111 * feature collections. Typically, you would not be using
27112 * the GML format to write wfs elements. This just provides
27113 * some way to write out lists of features. GML3 defines the
27114 * featureMembers element, so that is used by default instead.
27116 var node = this.createElementNSPlus("wfs:FeatureCollection");
27117 for(var i=0, len=features.length; i<len; ++i) {
27118 this.writeNode("gml:featureMember", features[i], node);
27126 * Method: setGeometryTypes
27127 * Sets the <geometryTypes> mapping.
27129 setGeometryTypes: function() {
27130 this.geometryTypes = {
27131 "OpenLayers.Geometry.Point": "Point",
27132 "OpenLayers.Geometry.MultiPoint": "MultiPoint",
27133 "OpenLayers.Geometry.LineString": "LineString",
27134 "OpenLayers.Geometry.MultiLineString": "MultiLineString",
27135 "OpenLayers.Geometry.Polygon": "Polygon",
27136 "OpenLayers.Geometry.MultiPolygon": "MultiPolygon",
27137 "OpenLayers.Geometry.Collection": "GeometryCollection"
27141 CLASS_NAME: "OpenLayers.Format.GML.Base"
27144 /* ======================================================================
27145 OpenLayers/Format/GML/v2.js
27146 ====================================================================== */
27148 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
27149 * full list of contributors). Published under the 2-clause BSD license.
27150 * See license.txt in the OpenLayers distribution or repository for the
27151 * full text of the license. */
27154 * @requires OpenLayers/Format/GML/Base.js
27158 * Class: OpenLayers.Format.GML.v2
27159 * Parses GML version 2.
27162 * - <OpenLayers.Format.GML.Base>
27164 OpenLayers.Format.GML.v2 = OpenLayers.Class(OpenLayers.Format.GML.Base, {
27167 * Property: schemaLocation
27168 * {String} Schema location for a particular minor version.
27170 schemaLocation: "http://www.opengis.net/gml http://schemas.opengis.net/gml/2.1.2/feature.xsd",
27173 * Constructor: OpenLayers.Format.GML.v2
27174 * Create a parser for GML v2.
27177 * options - {Object} An optional object whose properties will be set on
27180 * Valid options properties:
27181 * featureType - {String} Local (without prefix) feature typeName (required).
27182 * featureNS - {String} Feature namespace (required).
27183 * geometryName - {String} Geometry element name.
27185 initialize: function(options) {
27186 OpenLayers.Format.GML.Base.prototype.initialize.apply(this, [options]);
27190 * Property: readers
27191 * Contains public functions, grouped by namespace prefix, that will
27192 * be applied when a namespaced node is found matching the function
27193 * name. The function will be applied in the scope of this parser
27194 * with two arguments: the node being read and a context object passed
27198 "gml": OpenLayers.Util.applyDefaults({
27199 "outerBoundaryIs": function(node, container) {
27201 this.readChildNodes(node, obj);
27202 container.outer = obj.components[0];
27204 "innerBoundaryIs": function(node, container) {
27206 this.readChildNodes(node, obj);
27207 container.inner.push(obj.components[0]);
27209 "Box": function(node, container) {
27211 this.readChildNodes(node, obj);
27212 if(!container.components) {
27213 container.components = [];
27215 var min = obj.points[0];
27216 var max = obj.points[1];
27217 container.components.push(
27218 new OpenLayers.Bounds(min.x, min.y, max.x, max.y)
27221 }, OpenLayers.Format.GML.Base.prototype.readers["gml"]),
27222 "feature": OpenLayers.Format.GML.Base.prototype.readers["feature"],
27223 "wfs": OpenLayers.Format.GML.Base.prototype.readers["wfs"]
27230 * features - {Array(<OpenLayers.Feature.Vector>) | OpenLayers.Feature.Vector}
27231 * An array of features or a single feature.
27234 * {String} Given an array of features, a doc with a gml:featureMembers
27235 * element will be returned. Given a single feature, a doc with a
27236 * gml:featureMember element will be returned.
27238 write: function(features) {
27240 if(OpenLayers.Util.isArray(features)) {
27241 // GML2 only has abstract feature collections
27242 // wfs provides a feature collection from a well-known schema
27243 name = "wfs:FeatureCollection";
27245 name = "gml:featureMember";
27247 var root = this.writeNode(name, features);
27248 this.setAttributeNS(
27249 root, this.namespaces["xsi"],
27250 "xsi:schemaLocation", this.schemaLocation
27253 return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
27257 * Property: writers
27258 * As a compliment to the readers property, this structure contains public
27259 * writing functions grouped by namespace alias and named like the
27260 * node names they produce.
27263 "gml": OpenLayers.Util.applyDefaults({
27264 "Point": function(geometry) {
27265 var node = this.createElementNSPlus("gml:Point");
27266 this.writeNode("coordinates", [geometry], node);
27269 "coordinates": function(points) {
27270 var numPoints = points.length;
27271 var parts = new Array(numPoints);
27273 for(var i=0; i<numPoints; ++i) {
27276 parts[i] = point.x + "," + point.y;
27278 parts[i] = point.y + "," + point.x;
27280 if(point.z != undefined) { // allow null or undefined
27281 parts[i] += "," + point.z;
27284 return this.createElementNSPlus("gml:coordinates", {
27286 decimal: ".", cs: ",", ts: " "
27288 value: (numPoints == 1) ? parts[0] : parts.join(" ")
27291 "LineString": function(geometry) {
27292 var node = this.createElementNSPlus("gml:LineString");
27293 this.writeNode("coordinates", geometry.components, node);
27296 "Polygon": function(geometry) {
27297 var node = this.createElementNSPlus("gml:Polygon");
27298 this.writeNode("outerBoundaryIs", geometry.components[0], node);
27299 for(var i=1; i<geometry.components.length; ++i) {
27301 "innerBoundaryIs", geometry.components[i], node
27306 "outerBoundaryIs": function(ring) {
27307 var node = this.createElementNSPlus("gml:outerBoundaryIs");
27308 this.writeNode("LinearRing", ring, node);
27311 "innerBoundaryIs": function(ring) {
27312 var node = this.createElementNSPlus("gml:innerBoundaryIs");
27313 this.writeNode("LinearRing", ring, node);
27316 "LinearRing": function(ring) {
27317 var node = this.createElementNSPlus("gml:LinearRing");
27318 this.writeNode("coordinates", ring.components, node);
27321 "Box": function(bounds) {
27322 var node = this.createElementNSPlus("gml:Box");
27323 this.writeNode("coordinates", [
27324 {x: bounds.left, y: bounds.bottom},
27325 {x: bounds.right, y: bounds.top}
27327 // srsName attribute is optional for gml:Box
27329 node.setAttribute("srsName", this.srsName);
27333 }, OpenLayers.Format.GML.Base.prototype.writers["gml"]),
27334 "feature": OpenLayers.Format.GML.Base.prototype.writers["feature"],
27335 "wfs": OpenLayers.Format.GML.Base.prototype.writers["wfs"]
27338 CLASS_NAME: "OpenLayers.Format.GML.v2"
27341 /* ======================================================================
27342 OpenLayers/Filter/Function.js
27343 ====================================================================== */
27345 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
27346 * full list of contributors). Published under the 2-clause BSD license.
27347 * See license.txt in the OpenLayers distribution or repository for the
27348 * full text of the license. */
27351 * @requires OpenLayers/Filter.js
27355 * Class: OpenLayers.Filter.Function
27356 * This class represents a filter function.
27357 * We are using this class for creation of complex
27358 * filters that can contain filter functions as values.
27359 * Nesting function as other functions parameter is supported.
27362 * - <OpenLayers.Filter>
27364 OpenLayers.Filter.Function = OpenLayers.Class(OpenLayers.Filter, {
27367 * APIProperty: name
27368 * {String} Name of the function.
27373 * APIProperty: params
27374 * {Array(<OpenLayers.Filter.Function> || String || Number)} Function parameters
27375 * For now support only other Functions, String or Number
27380 * Constructor: OpenLayers.Filter.Function
27381 * Creates a filter function.
27384 * options - {Object} An optional object with properties to set on the
27388 * {<OpenLayers.Filter.Function>}
27391 CLASS_NAME: "OpenLayers.Filter.Function"
27394 /* ======================================================================
27395 OpenLayers/BaseTypes/Date.js
27396 ====================================================================== */
27398 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
27399 * full list of contributors). Published under the 2-clause BSD license.
27400 * See license.txt in the OpenLayers distribution or repository for the
27401 * full text of the license. */
27404 * @requires OpenLayers/SingleFile.js
27408 * Namespace: OpenLayers.Date
27409 * Contains implementations of Date.parse and date.toISOString that match the
27410 * ECMAScript 5 specification for parsing RFC 3339 dates.
27411 * http://tools.ietf.org/html/rfc3339
27413 OpenLayers.Date = {
27416 * APIProperty: dateRegEx
27417 * The regex to be used for validating dates. You can provide your own
27418 * regex for instance for adding support for years before BC. Default
27419 * value is: /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:(?:T(\d{1,2}):(\d{2}):(\d{2}(?:\.\d+)?)(Z|(?:[+-]\d{1,2}(?::(\d{2}))?)))|Z)?$/
27421 dateRegEx: /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:(?:T(\d{1,2}):(\d{2}):(\d{2}(?:\.\d+)?)(Z|(?:[+-]\d{1,2}(?::(\d{2}))?)))|Z)?$/,
27424 * APIMethod: toISOString
27425 * Generates a string representing a date. The format of the string follows
27426 * the profile of ISO 8601 for date and time on the Internet (see
27427 * http://tools.ietf.org/html/rfc3339). If the toISOString method is
27428 * available on the Date prototype, that is used. The toISOString
27429 * method for Date instances is defined in ECMA-262.
27432 * date - {Date} A date object.
27435 * {String} A string representing the date (e.g.
27436 * "2010-08-07T16:58:23.123Z"). If the date does not have a valid time
27437 * (i.e. isNaN(date.getTime())) this method returns the string "Invalid
27438 * Date". The ECMA standard says the toISOString method should throw
27439 * RangeError in this case, but Firefox returns a string instead. For
27440 * best results, use isNaN(date.getTime()) to determine date validity
27441 * before generating date strings.
27443 toISOString: (function() {
27444 if ("toISOString" in Date.prototype) {
27445 return function(date) {
27446 return date.toISOString();
27449 return function(date) {
27451 if (isNaN(date.getTime())) {
27452 // ECMA-262 says throw RangeError, Firefox returns
27454 str = "Invalid Date";
27457 date.getUTCFullYear() + "-" +
27458 OpenLayers.Number.zeroPad(date.getUTCMonth() + 1, 2) + "-" +
27459 OpenLayers.Number.zeroPad(date.getUTCDate(), 2) + "T" +
27460 OpenLayers.Number.zeroPad(date.getUTCHours(), 2) + ":" +
27461 OpenLayers.Number.zeroPad(date.getUTCMinutes(), 2) + ":" +
27462 OpenLayers.Number.zeroPad(date.getUTCSeconds(), 2) + "." +
27463 OpenLayers.Number.zeroPad(date.getUTCMilliseconds(), 3) + "Z";
27473 * Generate a date object from a string. The format for the string follows
27474 * the profile of ISO 8601 for date and time on the Internet (see
27475 * http://tools.ietf.org/html/rfc3339). We don't call the native
27476 * Date.parse because of inconsistency between implmentations. In
27477 * Chrome, calling Date.parse with a string that doesn't contain any
27478 * indication of the timezone (e.g. "2011"), the date is interpreted
27479 * in local time. On Firefox, the assumption is UTC.
27482 * str - {String} A string representing the date (e.g.
27483 * "2010", "2010-08", "2010-08-07", "2010-08-07T16:58:23.123Z",
27484 * "2010-08-07T11:58:23.123-06").
27487 * {Date} A date object. If the string could not be parsed, an invalid
27488 * date is returned (i.e. isNaN(date.getTime())).
27490 parse: function(str) {
27492 var match = str.match(this.dateRegEx);
27493 if (match && (match[1] || match[7])) { // must have at least year or time
27494 var year = parseInt(match[1], 10) || 0;
27495 var month = (parseInt(match[2], 10) - 1) || 0;
27496 var day = parseInt(match[3], 10) || 1;
27497 date = new Date(Date.UTC(year, month, day));
27499 var type = match[7];
27501 var hours = parseInt(match[4], 10);
27502 var minutes = parseInt(match[5], 10);
27503 var secFrac = parseFloat(match[6]);
27504 var seconds = secFrac | 0;
27505 var milliseconds = Math.round(1000 * (secFrac - seconds));
27506 date.setUTCHours(hours, minutes, seconds, milliseconds);
27508 if (type !== "Z") {
27509 var hoursOffset = parseInt(type, 10);
27510 var minutesOffset = parseInt(match[8], 10) || 0;
27511 var offset = -1000 * (60 * (hoursOffset * 60) + minutesOffset * 60);
27512 date = new Date(date.getTime() + offset);
27516 date = new Date("invalid");
27521 /* ======================================================================
27522 OpenLayers/Format/Filter/v1.js
27523 ====================================================================== */
27525 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
27526 * full list of contributors). Published under the 2-clause BSD license.
27527 * See license.txt in the OpenLayers distribution or repository for the
27528 * full text of the license. */
27530 * @requires OpenLayers/Format/Filter.js
27531 * @requires OpenLayers/Format/XML.js
27532 * @requires OpenLayers/Filter/Function.js
27533 * @requires OpenLayers/BaseTypes/Date.js
27537 * Class: OpenLayers.Format.Filter.v1
27538 * Superclass for Filter version 1 parsers.
27541 * - <OpenLayers.Format.XML>
27543 OpenLayers.Format.Filter.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
27546 * Property: namespaces
27547 * {Object} Mapping of namespace aliases to namespace URIs.
27550 ogc: "http://www.opengis.net/ogc",
27551 gml: "http://www.opengis.net/gml",
27552 xlink: "http://www.w3.org/1999/xlink",
27553 xsi: "http://www.w3.org/2001/XMLSchema-instance"
27557 * Property: defaultPrefix
27559 defaultPrefix: "ogc",
27562 * Property: schemaLocation
27563 * {String} Schema location for a particular minor version.
27565 schemaLocation: null,
27568 * Constructor: OpenLayers.Format.Filter.v1
27569 * Instances of this class are not created directly. Use the
27570 * <OpenLayers.Format.Filter> constructor instead.
27573 * options - {Object} An optional object whose properties will be set on
27576 initialize: function(options) {
27577 OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
27584 * data - {DOMElement} A Filter document element.
27587 * {<OpenLayers.Filter>} A filter object.
27589 read: function(data) {
27591 this.readers.ogc["Filter"].apply(this, [data, obj]);
27596 * Property: readers
27597 * Contains public functions, grouped by namespace prefix, that will
27598 * be applied when a namespaced node is found matching the function
27599 * name. The function will be applied in the scope of this parser
27600 * with two arguments: the node being read and a context object passed
27605 "_expression": function(node) {
27606 // only the simplest of ogc:expression handled
27607 // "some text and an <PropertyName>attribute</PropertyName>"}
27608 var obj, value = "";
27609 for(var child=node.firstChild; child; child=child.nextSibling) {
27610 switch(child.nodeType) {
27612 obj = this.readNode(child);
27613 if (obj.property) {
27614 value += "${" + obj.property + "}";
27615 } else if (obj.value !== undefined) {
27616 value += obj.value;
27619 case 3: // text node
27620 case 4: // cdata section
27621 value += child.nodeValue;
27626 "Filter": function(node, parent) {
27627 // Filters correspond to subclasses of OpenLayers.Filter.
27628 // Since they contain information we don't persist, we
27629 // create a temporary object and then pass on the filter
27630 // (ogc:Filter) to the parent obj.
27635 this.readChildNodes(node, obj);
27636 if(obj.fids.length > 0) {
27637 parent.filter = new OpenLayers.Filter.FeatureId({
27640 } else if(obj.filters.length > 0) {
27641 parent.filter = obj.filters[0];
27644 "FeatureId": function(node, obj) {
27645 var fid = node.getAttribute("fid");
27647 obj.fids.push(fid);
27650 "And": function(node, obj) {
27651 var filter = new OpenLayers.Filter.Logical({
27652 type: OpenLayers.Filter.Logical.AND
27654 this.readChildNodes(node, filter);
27655 obj.filters.push(filter);
27657 "Or": function(node, obj) {
27658 var filter = new OpenLayers.Filter.Logical({
27659 type: OpenLayers.Filter.Logical.OR
27661 this.readChildNodes(node, filter);
27662 obj.filters.push(filter);
27664 "Not": function(node, obj) {
27665 var filter = new OpenLayers.Filter.Logical({
27666 type: OpenLayers.Filter.Logical.NOT
27668 this.readChildNodes(node, filter);
27669 obj.filters.push(filter);
27671 "PropertyIsLessThan": function(node, obj) {
27672 var filter = new OpenLayers.Filter.Comparison({
27673 type: OpenLayers.Filter.Comparison.LESS_THAN
27675 this.readChildNodes(node, filter);
27676 obj.filters.push(filter);
27678 "PropertyIsGreaterThan": function(node, obj) {
27679 var filter = new OpenLayers.Filter.Comparison({
27680 type: OpenLayers.Filter.Comparison.GREATER_THAN
27682 this.readChildNodes(node, filter);
27683 obj.filters.push(filter);
27685 "PropertyIsLessThanOrEqualTo": function(node, obj) {
27686 var filter = new OpenLayers.Filter.Comparison({
27687 type: OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO
27689 this.readChildNodes(node, filter);
27690 obj.filters.push(filter);
27692 "PropertyIsGreaterThanOrEqualTo": function(node, obj) {
27693 var filter = new OpenLayers.Filter.Comparison({
27694 type: OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO
27696 this.readChildNodes(node, filter);
27697 obj.filters.push(filter);
27699 "PropertyIsBetween": function(node, obj) {
27700 var filter = new OpenLayers.Filter.Comparison({
27701 type: OpenLayers.Filter.Comparison.BETWEEN
27703 this.readChildNodes(node, filter);
27704 obj.filters.push(filter);
27706 "Literal": function(node, obj) {
27707 obj.value = OpenLayers.String.numericIf(
27708 this.getChildValue(node), true);
27710 "PropertyName": function(node, filter) {
27711 filter.property = this.getChildValue(node);
27713 "LowerBoundary": function(node, filter) {
27714 filter.lowerBoundary = OpenLayers.String.numericIf(
27715 this.readers.ogc._expression.call(this, node), true);
27717 "UpperBoundary": function(node, filter) {
27718 filter.upperBoundary = OpenLayers.String.numericIf(
27719 this.readers.ogc._expression.call(this, node), true);
27721 "Intersects": function(node, obj) {
27722 this.readSpatial(node, obj, OpenLayers.Filter.Spatial.INTERSECTS);
27724 "Within": function(node, obj) {
27725 this.readSpatial(node, obj, OpenLayers.Filter.Spatial.WITHIN);
27727 "Contains": function(node, obj) {
27728 this.readSpatial(node, obj, OpenLayers.Filter.Spatial.CONTAINS);
27730 "DWithin": function(node, obj) {
27731 this.readSpatial(node, obj, OpenLayers.Filter.Spatial.DWITHIN);
27733 "Distance": function(node, obj) {
27734 obj.distance = parseInt(this.getChildValue(node));
27735 obj.distanceUnits = node.getAttribute("units");
27737 "Function": function(node, obj) {
27738 //TODO write decoder for it
27741 "PropertyIsNull": function(node, obj) {
27742 var filter = new OpenLayers.Filter.Comparison({
27743 type: OpenLayers.Filter.Comparison.IS_NULL
27745 this.readChildNodes(node, filter);
27746 obj.filters.push(filter);
27752 * Method: readSpatial
27754 * Read a {<OpenLayers.Filter.Spatial>} filter.
27757 * node - {DOMElement} A DOM element that contains an ogc:expression.
27758 * obj - {Object} The target object.
27759 * type - {String} One of the OpenLayers.Filter.Spatial.* constants.
27762 * {<OpenLayers.Filter.Spatial>} The created filter.
27764 readSpatial: function(node, obj, type) {
27765 var filter = new OpenLayers.Filter.Spatial({
27768 this.readChildNodes(node, filter);
27769 filter.value = filter.components[0];
27770 delete filter.components;
27771 obj.filters.push(filter);
27775 * APIMethod: encodeLiteral
27776 * Generates the string representation of a value for use in <Literal>
27777 * elements. The default encoder writes Date values as ISO 8601
27781 * value - {Object} Literal value to encode
27784 * {String} String representation of the provided value.
27786 encodeLiteral: function(value) {
27787 if (value instanceof Date) {
27788 value = OpenLayers.Date.toISOString(value);
27794 * Method: writeOgcExpression
27795 * Limited support for writing OGC expressions. Currently it supports
27796 * (<OpenLayers.Filter.Function> || String || Number)
27799 * value - (<OpenLayers.Filter.Function> || String || Number)
27800 * node - {DOMElement} A parent DOM element
27803 * {DOMElement} Updated node element.
27805 writeOgcExpression: function(value, node) {
27806 if (value instanceof OpenLayers.Filter.Function){
27807 this.writeNode("Function", value, node);
27809 this.writeNode("Literal", value, node);
27818 * filter - {<OpenLayers.Filter>} A filter object.
27821 * {DOMElement} An ogc:Filter element.
27823 write: function(filter) {
27824 return this.writers.ogc["Filter"].apply(this, [filter]);
27828 * Property: writers
27829 * As a compliment to the readers property, this structure contains public
27830 * writing functions grouped by namespace alias and named like the
27831 * node names they produce.
27835 "Filter": function(filter) {
27836 var node = this.createElementNSPlus("ogc:Filter");
27837 this.writeNode(this.getFilterType(filter), filter, node);
27840 "_featureIds": function(filter) {
27841 var node = this.createDocumentFragment();
27842 for (var i=0, ii=filter.fids.length; i<ii; ++i) {
27843 this.writeNode("ogc:FeatureId", filter.fids[i], node);
27847 "FeatureId": function(fid) {
27848 return this.createElementNSPlus("ogc:FeatureId", {
27849 attributes: {fid: fid}
27852 "And": function(filter) {
27853 var node = this.createElementNSPlus("ogc:And");
27855 for (var i=0, ii=filter.filters.length; i<ii; ++i) {
27856 childFilter = filter.filters[i];
27858 this.getFilterType(childFilter), childFilter, node
27863 "Or": function(filter) {
27864 var node = this.createElementNSPlus("ogc:Or");
27866 for (var i=0, ii=filter.filters.length; i<ii; ++i) {
27867 childFilter = filter.filters[i];
27869 this.getFilterType(childFilter), childFilter, node
27874 "Not": function(filter) {
27875 var node = this.createElementNSPlus("ogc:Not");
27876 var childFilter = filter.filters[0];
27878 this.getFilterType(childFilter), childFilter, node
27882 "PropertyIsLessThan": function(filter) {
27883 var node = this.createElementNSPlus("ogc:PropertyIsLessThan");
27884 // no ogc:expression handling for PropertyName for now
27885 this.writeNode("PropertyName", filter, node);
27886 // handle Literals or Functions for now
27887 this.writeOgcExpression(filter.value, node);
27890 "PropertyIsGreaterThan": function(filter) {
27891 var node = this.createElementNSPlus("ogc:PropertyIsGreaterThan");
27892 // no ogc:expression handling for PropertyName for now
27893 this.writeNode("PropertyName", filter, node);
27894 // handle Literals or Functions for now
27895 this.writeOgcExpression(filter.value, node);
27898 "PropertyIsLessThanOrEqualTo": function(filter) {
27899 var node = this.createElementNSPlus("ogc:PropertyIsLessThanOrEqualTo");
27900 // no ogc:expression handling for PropertyName for now
27901 this.writeNode("PropertyName", filter, node);
27902 // handle Literals or Functions for now
27903 this.writeOgcExpression(filter.value, node);
27906 "PropertyIsGreaterThanOrEqualTo": function(filter) {
27907 var node = this.createElementNSPlus("ogc:PropertyIsGreaterThanOrEqualTo");
27908 // no ogc:expression handling for PropertyName for now
27909 this.writeNode("PropertyName", filter, node);
27910 // handle Literals or Functions for now
27911 this.writeOgcExpression(filter.value, node);
27914 "PropertyIsBetween": function(filter) {
27915 var node = this.createElementNSPlus("ogc:PropertyIsBetween");
27916 // no ogc:expression handling for PropertyName for now
27917 this.writeNode("PropertyName", filter, node);
27918 this.writeNode("LowerBoundary", filter, node);
27919 this.writeNode("UpperBoundary", filter, node);
27922 "PropertyName": function(filter) {
27923 // no ogc:expression handling for now
27924 return this.createElementNSPlus("ogc:PropertyName", {
27925 value: filter.property
27928 "Literal": function(value) {
27929 var encode = this.encodeLiteral ||
27930 OpenLayers.Format.Filter.v1.prototype.encodeLiteral;
27931 return this.createElementNSPlus("ogc:Literal", {
27932 value: encode(value)
27935 "LowerBoundary": function(filter) {
27936 // handle Literals or Functions for now
27937 var node = this.createElementNSPlus("ogc:LowerBoundary");
27938 this.writeOgcExpression(filter.lowerBoundary, node);
27941 "UpperBoundary": function(filter) {
27942 // handle Literals or Functions for now
27943 var node = this.createElementNSPlus("ogc:UpperBoundary");
27944 this.writeNode("Literal", filter.upperBoundary, node);
27947 "INTERSECTS": function(filter) {
27948 return this.writeSpatial(filter, "Intersects");
27950 "WITHIN": function(filter) {
27951 return this.writeSpatial(filter, "Within");
27953 "CONTAINS": function(filter) {
27954 return this.writeSpatial(filter, "Contains");
27956 "DWITHIN": function(filter) {
27957 var node = this.writeSpatial(filter, "DWithin");
27958 this.writeNode("Distance", filter, node);
27961 "Distance": function(filter) {
27962 return this.createElementNSPlus("ogc:Distance", {
27964 units: filter.distanceUnits
27966 value: filter.distance
27969 "Function": function(filter) {
27970 var node = this.createElementNSPlus("ogc:Function", {
27975 var params = filter.params;
27976 for(var i=0, len=params.length; i<len; i++){
27977 this.writeOgcExpression(params[i], node);
27981 "PropertyIsNull": function(filter) {
27982 var node = this.createElementNSPlus("ogc:PropertyIsNull");
27983 this.writeNode("PropertyName", filter, node);
27990 * Method: getFilterType
27992 getFilterType: function(filter) {
27993 var filterType = this.filterMap[filter.type];
27995 throw "Filter writing not supported for rule type: " + filter.type;
28001 * Property: filterMap
28002 * {Object} Contains a member for each filter type. Values are node names
28003 * for corresponding OGC Filter child elements.
28009 "==": "PropertyIsEqualTo",
28010 "!=": "PropertyIsNotEqualTo",
28011 "<": "PropertyIsLessThan",
28012 ">": "PropertyIsGreaterThan",
28013 "<=": "PropertyIsLessThanOrEqualTo",
28014 ">=": "PropertyIsGreaterThanOrEqualTo",
28015 "..": "PropertyIsBetween",
28016 "~": "PropertyIsLike",
28017 "NULL": "PropertyIsNull",
28019 "DWITHIN": "DWITHIN",
28020 "WITHIN": "WITHIN",
28021 "CONTAINS": "CONTAINS",
28022 "INTERSECTS": "INTERSECTS",
28023 "FID": "_featureIds"
28026 CLASS_NAME: "OpenLayers.Format.Filter.v1"
28029 /* ======================================================================
28030 OpenLayers/Format/Filter/v1_0_0.js
28031 ====================================================================== */
28033 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
28034 * full list of contributors). Published under the 2-clause BSD license.
28035 * See license.txt in the OpenLayers distribution or repository for the
28036 * full text of the license. */
28039 * @requires OpenLayers/Format/GML/v2.js
28040 * @requires OpenLayers/Format/Filter/v1.js
28044 * Class: OpenLayers.Format.Filter.v1_0_0
28045 * Write ogc:Filter version 1.0.0.
28048 * - <OpenLayers.Format.GML.v2>
28049 * - <OpenLayers.Format.Filter.v1>
28051 OpenLayers.Format.Filter.v1_0_0 = OpenLayers.Class(
28052 OpenLayers.Format.GML.v2, OpenLayers.Format.Filter.v1, {
28055 * Constant: VERSION
28061 * Property: schemaLocation
28062 * {String} http://www.opengis.net/ogc/filter/1.0.0/filter.xsd
28064 schemaLocation: "http://www.opengis.net/ogc/filter/1.0.0/filter.xsd",
28067 * Constructor: OpenLayers.Format.Filter.v1_0_0
28068 * Instances of this class are not created directly. Use the
28069 * <OpenLayers.Format.Filter> constructor instead.
28072 * options - {Object} An optional object whose properties will be set on
28075 initialize: function(options) {
28076 OpenLayers.Format.GML.v2.prototype.initialize.apply(
28082 * Property: readers
28083 * Contains public functions, grouped by namespace prefix, that will
28084 * be applied when a namespaced node is found matching the function
28085 * name. The function will be applied in the scope of this parser
28086 * with two arguments: the node being read and a context object passed
28090 "ogc": OpenLayers.Util.applyDefaults({
28091 "PropertyIsEqualTo": function(node, obj) {
28092 var filter = new OpenLayers.Filter.Comparison({
28093 type: OpenLayers.Filter.Comparison.EQUAL_TO
28095 this.readChildNodes(node, filter);
28096 obj.filters.push(filter);
28098 "PropertyIsNotEqualTo": function(node, obj) {
28099 var filter = new OpenLayers.Filter.Comparison({
28100 type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO
28102 this.readChildNodes(node, filter);
28103 obj.filters.push(filter);
28105 "PropertyIsLike": function(node, obj) {
28106 var filter = new OpenLayers.Filter.Comparison({
28107 type: OpenLayers.Filter.Comparison.LIKE
28109 this.readChildNodes(node, filter);
28110 var wildCard = node.getAttribute("wildCard");
28111 var singleChar = node.getAttribute("singleChar");
28112 var esc = node.getAttribute("escape");
28113 filter.value2regex(wildCard, singleChar, esc);
28114 obj.filters.push(filter);
28116 }, OpenLayers.Format.Filter.v1.prototype.readers["ogc"]),
28117 "gml": OpenLayers.Format.GML.v2.prototype.readers["gml"],
28118 "feature": OpenLayers.Format.GML.v2.prototype.readers["feature"]
28122 * Property: writers
28123 * As a compliment to the readers property, this structure contains public
28124 * writing functions grouped by namespace alias and named like the
28125 * node names they produce.
28128 "ogc": OpenLayers.Util.applyDefaults({
28129 "PropertyIsEqualTo": function(filter) {
28130 var node = this.createElementNSPlus("ogc:PropertyIsEqualTo");
28131 // no ogc:expression handling for PropertyName for now
28132 this.writeNode("PropertyName", filter, node);
28133 // handle Literals or Functions for now
28134 this.writeOgcExpression(filter.value, node);
28137 "PropertyIsNotEqualTo": function(filter) {
28138 var node = this.createElementNSPlus("ogc:PropertyIsNotEqualTo");
28139 // no ogc:expression handling for PropertyName for now
28140 this.writeNode("PropertyName", filter, node);
28141 // handle Literals or Functions for now
28142 this.writeOgcExpression(filter.value, node);
28145 "PropertyIsLike": function(filter) {
28146 var node = this.createElementNSPlus("ogc:PropertyIsLike", {
28148 wildCard: "*", singleChar: ".", escape: "!"
28151 // no ogc:expression handling for now
28152 this.writeNode("PropertyName", filter, node);
28153 // convert regex string to ogc string
28154 this.writeNode("Literal", filter.regex2value(), node);
28157 "BBOX": function(filter) {
28158 var node = this.createElementNSPlus("ogc:BBOX");
28159 // PropertyName is mandatory in 1.0.0, but e.g. GeoServer also
28160 // accepts filters without it. When this is used with
28161 // OpenLayers.Protocol.WFS, OpenLayers.Format.WFST will set a
28162 // missing filter.property to the geometryName that is
28163 // configured with the protocol, which defaults to "the_geom".
28164 // So the only way to omit this mandatory property is to not
28165 // set the property on the filter and to set the geometryName
28166 // on the WFS protocol to null. The latter also happens when
28167 // the protocol is configured without a geometryName and a
28169 filter.property && this.writeNode("PropertyName", filter, node);
28170 var box = this.writeNode("gml:Box", filter.value, node);
28171 if(filter.projection) {
28172 box.setAttribute("srsName", filter.projection);
28176 }, OpenLayers.Format.Filter.v1.prototype.writers["ogc"]),
28177 "gml": OpenLayers.Format.GML.v2.prototype.writers["gml"],
28178 "feature": OpenLayers.Format.GML.v2.prototype.writers["feature"]
28182 * Method: writeSpatial
28184 * Read a {<OpenLayers.Filter.Spatial>} filter and converts it into XML.
28187 * filter - {<OpenLayers.Filter.Spatial>} The filter.
28188 * name - {String} Name of the generated XML element.
28191 * {DOMElement} The created XML element.
28193 writeSpatial: function(filter, name) {
28194 var node = this.createElementNSPlus("ogc:"+name);
28195 this.writeNode("PropertyName", filter, node);
28196 if(filter.value instanceof OpenLayers.Filter.Function) {
28197 this.writeNode("Function", filter.value, node);
28200 if(filter.value instanceof OpenLayers.Geometry) {
28201 child = this.writeNode("feature:_geometry", filter.value).firstChild;
28203 child = this.writeNode("gml:Box", filter.value);
28205 if(filter.projection) {
28206 child.setAttribute("srsName", filter.projection);
28208 node.appendChild(child);
28214 CLASS_NAME: "OpenLayers.Format.Filter.v1_0_0"
28217 /* ======================================================================
28218 OpenLayers/Format/WFST/v1_0_0.js
28219 ====================================================================== */
28221 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
28222 * full list of contributors). Published under the 2-clause BSD license.
28223 * See license.txt in the OpenLayers distribution or repository for the
28224 * full text of the license. */
28227 * @requires OpenLayers/Format/WFST/v1.js
28228 * @requires OpenLayers/Format/Filter/v1_0_0.js
28232 * Class: OpenLayers.Format.WFST.v1_0_0
28233 * A format for creating WFS v1.0.0 transactions. Create a new instance with the
28234 * <OpenLayers.Format.WFST.v1_0_0> constructor.
28237 * - <OpenLayers.Format.Filter.v1_0_0>
28238 * - <OpenLayers.Format.WFST.v1>
28240 OpenLayers.Format.WFST.v1_0_0 = OpenLayers.Class(
28241 OpenLayers.Format.Filter.v1_0_0, OpenLayers.Format.WFST.v1, {
28244 * Property: version
28245 * {String} WFS version number.
28250 * APIProperty: srsNameInQuery
28251 * {Boolean} If true the reference system is passed in Query requests
28252 * via the "srsName" attribute to the "wfs:Query" element, this
28253 * property defaults to false as it isn't WFS 1.0.0 compliant.
28255 srsNameInQuery: false,
28258 * Property: schemaLocations
28259 * {Object} Properties are namespace aliases, values are schema locations.
28262 "wfs": "http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd"
28266 * Constructor: OpenLayers.Format.WFST.v1_0_0
28267 * A class for parsing and generating WFS v1.0.0 transactions.
28270 * options - {Object} Optional object whose properties will be set on the
28273 * Valid options properties:
28274 * featureType - {String} Local (without prefix) feature typeName (required).
28275 * featureNS - {String} Feature namespace (optional).
28276 * featurePrefix - {String} Feature namespace alias (optional - only used
28277 * if featureNS is provided). Default is 'feature'.
28278 * geometryName - {String} Name of geometry attribute. Default is 'the_geom'.
28280 initialize: function(options) {
28281 OpenLayers.Format.Filter.v1_0_0.prototype.initialize.apply(this, [options]);
28282 OpenLayers.Format.WFST.v1.prototype.initialize.apply(this, [options]);
28287 * Shorthand for applying one of the named readers given the node
28288 * namespace and local name. Readers take two args (node, obj) and
28289 * generally extend or modify the second.
28292 * node - {DOMElement} The node to be read (required).
28293 * obj - {Object} The object to be modified (optional).
28294 * first - {Boolean} Should be set to true for the first node read. This
28295 * is usually the readNode call in the read method. Without this being
28296 * set, auto-configured properties will stick on subsequent reads.
28299 * {Object} The input object, modified (or a new one if none was provided).
28301 readNode: function(node, obj, first) {
28302 // Not the superclass, only the mixin classes inherit from
28303 // Format.GML.v2. We need this because we don't want to get readNode
28304 // from the superclass's superclass, which is OpenLayers.Format.XML.
28305 return OpenLayers.Format.GML.v2.prototype.readNode.apply(this, arguments);
28309 * Property: readers
28310 * Contains public functions, grouped by namespace prefix, that will
28311 * be applied when a namespaced node is found matching the function
28312 * name. The function will be applied in the scope of this parser
28313 * with two arguments: the node being read and a context object passed
28317 "wfs": OpenLayers.Util.applyDefaults({
28318 "WFS_TransactionResponse": function(node, obj) {
28319 obj.insertIds = [];
28320 obj.success = false;
28321 this.readChildNodes(node, obj);
28323 "InsertResult": function(node, container) {
28324 var obj = {fids: []};
28325 this.readChildNodes(node, obj);
28326 container.insertIds = container.insertIds.concat(obj.fids);
28328 "TransactionResult": function(node, obj) {
28329 this.readChildNodes(node, obj);
28331 "Status": function(node, obj) {
28332 this.readChildNodes(node, obj);
28334 "SUCCESS": function(node, obj) {
28335 obj.success = true;
28337 }, OpenLayers.Format.WFST.v1.prototype.readers["wfs"]),
28338 "gml": OpenLayers.Format.GML.v2.prototype.readers["gml"],
28339 "feature": OpenLayers.Format.GML.v2.prototype.readers["feature"],
28340 "ogc": OpenLayers.Format.Filter.v1_0_0.prototype.readers["ogc"]
28344 * Property: writers
28345 * As a compliment to the readers property, this structure contains public
28346 * writing functions grouped by namespace alias and named like the
28347 * node names they produce.
28350 "wfs": OpenLayers.Util.applyDefaults({
28351 "Query": function(options) {
28352 options = OpenLayers.Util.extend({
28353 featureNS: this.featureNS,
28354 featurePrefix: this.featurePrefix,
28355 featureType: this.featureType,
28356 srsName: this.srsName,
28357 srsNameInQuery: this.srsNameInQuery
28359 var prefix = options.featurePrefix;
28360 var node = this.createElementNSPlus("wfs:Query", {
28362 typeName: (prefix ? prefix + ":" : "") +
28363 options.featureType
28366 if(options.srsNameInQuery && options.srsName) {
28367 node.setAttribute("srsName", options.srsName);
28369 if(options.featureNS) {
28370 node.setAttribute("xmlns:" + prefix, options.featureNS);
28372 if(options.propertyNames) {
28373 for(var i=0,len = options.propertyNames.length; i<len; i++) {
28375 "ogc:PropertyName",
28376 {property: options.propertyNames[i]},
28381 if(options.filter) {
28382 this.setFilterProperty(options.filter);
28383 this.writeNode("ogc:Filter", options.filter, node);
28387 }, OpenLayers.Format.WFST.v1.prototype.writers["wfs"]),
28388 "gml": OpenLayers.Format.GML.v2.prototype.writers["gml"],
28389 "feature": OpenLayers.Format.GML.v2.prototype.writers["feature"],
28390 "ogc": OpenLayers.Format.Filter.v1_0_0.prototype.writers["ogc"]
28393 CLASS_NAME: "OpenLayers.Format.WFST.v1_0_0"
28395 /* ======================================================================
28396 OpenLayers/Renderer/Elements.js
28397 ====================================================================== */
28399 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
28400 * full list of contributors). Published under the 2-clause BSD license.
28401 * See license.txt in the OpenLayers distribution or repository for the
28402 * full text of the license. */
28405 * @requires OpenLayers/Renderer.js
28409 * Class: OpenLayers.ElementsIndexer
28410 * This class takes care of figuring out which order elements should be
28411 * placed in the DOM based on given indexing methods.
28413 OpenLayers.ElementsIndexer = OpenLayers.Class({
28416 * Property: maxZIndex
28417 * {Integer} This is the largest-most z-index value for a node
28418 * contained within the indexer.
28424 * {Array<String>} This is an array of node id's stored in the
28425 * order that they should show up on screen. Id's higher up in the
28426 * array (higher array index) represent nodes with higher z-indeces.
28431 * Property: indices
28432 * {Object} This is a hash that maps node ids to their z-index value
28433 * stored in the indexer. This is done to make finding a nodes z-index
28439 * Property: compare
28440 * {Function} This is the function used to determine placement of
28441 * of a new node within the indexer. If null, this defaults to to
28442 * the Z_ORDER_DRAWING_ORDER comparison method.
28447 * APIMethod: initialize
28448 * Create a new indexer with
28451 * yOrdering - {Boolean} Whether to use y-ordering.
28453 initialize: function(yOrdering) {
28455 this.compare = yOrdering ?
28456 OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER :
28457 OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER;
28463 * APIMethod: insert
28464 * Insert a new node into the indexer. In order to find the correct
28465 * positioning for the node to be inserted, this method uses a binary
28466 * search. This makes inserting O(log(n)).
28469 * newNode - {DOMElement} The new node to be inserted.
28472 * {DOMElement} the node before which we should insert our newNode, or
28473 * null if newNode can just be appended.
28475 insert: function(newNode) {
28476 // If the node is known to the indexer, remove it so we can
28477 // recalculate where it should go.
28478 if (this.exists(newNode)) {
28479 this.remove(newNode);
28482 var nodeId = newNode.id;
28484 this.determineZIndex(newNode);
28486 var leftIndex = -1;
28487 var rightIndex = this.order.length;
28490 while (rightIndex - leftIndex > 1) {
28491 middle = parseInt((leftIndex + rightIndex) / 2);
28493 var placement = this.compare(this, newNode,
28494 OpenLayers.Util.getElement(this.order[middle]));
28496 if (placement > 0) {
28497 leftIndex = middle;
28499 rightIndex = middle;
28503 this.order.splice(rightIndex, 0, nodeId);
28504 this.indices[nodeId] = this.getZIndex(newNode);
28506 // If the new node should be before another in the index
28507 // order, return the node before which we have to insert the new one;
28508 // else, return null to indicate that the new node can be appended.
28509 return this.getNextElement(rightIndex);
28513 * APIMethod: remove
28516 * node - {DOMElement} The node to be removed.
28518 remove: function(node) {
28519 var nodeId = node.id;
28520 var arrayIndex = OpenLayers.Util.indexOf(this.order, nodeId);
28521 if (arrayIndex >= 0) {
28522 // Remove it from the order array, as well as deleting the node
28523 // from the indeces hash.
28524 this.order.splice(arrayIndex, 1);
28525 delete this.indices[nodeId];
28527 // Reset the maxium z-index based on the last item in the
28529 if (this.order.length > 0) {
28530 var lastId = this.order[this.order.length - 1];
28531 this.maxZIndex = this.indices[lastId];
28533 this.maxZIndex = 0;
28541 clear: function() {
28544 this.maxZIndex = 0;
28548 * APIMethod: exists
28551 * node - {DOMElement} The node to test for existence.
28554 * {Boolean} Whether or not the node exists in the indexer?
28556 exists: function(node) {
28557 return (this.indices[node.id] != null);
28561 * APIMethod: getZIndex
28562 * Get the z-index value for the current node from the node data itself.
28565 * node - {DOMElement} The node whose z-index to get.
28568 * {Integer} The z-index value for the specified node (from the node
28571 getZIndex: function(node) {
28572 return node._style.graphicZIndex;
28576 * Method: determineZIndex
28577 * Determine the z-index for the current node if there isn't one,
28578 * and set the maximum value if we've found a new maximum.
28581 * node - {DOMElement}
28583 determineZIndex: function(node) {
28584 var zIndex = node._style.graphicZIndex;
28586 // Everything must have a zIndex. If none is specified,
28587 // this means the user *must* (hint: assumption) want this
28588 // node to succomb to drawing order. To enforce drawing order
28589 // over all indexing methods, we'll create a new z-index that's
28590 // greater than any currently in the indexer.
28591 if (zIndex == null) {
28592 zIndex = this.maxZIndex;
28593 node._style.graphicZIndex = zIndex;
28594 } else if (zIndex > this.maxZIndex) {
28595 this.maxZIndex = zIndex;
28600 * APIMethod: getNextElement
28601 * Get the next element in the order stack.
28604 * index - {Integer} The index of the current node in this.order.
28607 * {DOMElement} the node following the index passed in, or
28610 getNextElement: function(index) {
28611 var nextIndex = index + 1;
28612 if (nextIndex < this.order.length) {
28613 var nextElement = OpenLayers.Util.getElement(this.order[nextIndex]);
28614 if (nextElement == undefined) {
28615 nextElement = this.getNextElement(nextIndex);
28617 return nextElement;
28623 CLASS_NAME: "OpenLayers.ElementsIndexer"
28627 * Namespace: OpenLayers.ElementsIndexer.IndexingMethods
28628 * These are the compare methods for figuring out where a new node should be
28629 * placed within the indexer. These methods are very similar to general
28630 * sorting methods in that they return -1, 0, and 1 to specify the
28631 * direction in which new nodes fall in the ordering.
28633 OpenLayers.ElementsIndexer.IndexingMethods = {
28637 * This compare method is used by other comparison methods.
28638 * It can be used individually for ordering, but is not recommended,
28639 * because it doesn't subscribe to drawing order.
28642 * indexer - {<OpenLayers.ElementsIndexer>}
28643 * newNode - {DOMElement}
28644 * nextNode - {DOMElement}
28649 Z_ORDER: function(indexer, newNode, nextNode) {
28650 var newZIndex = indexer.getZIndex(newNode);
28654 var nextZIndex = indexer.getZIndex(nextNode);
28655 returnVal = newZIndex - nextZIndex;
28662 * APIMethod: Z_ORDER_DRAWING_ORDER
28663 * This method orders nodes by their z-index, but does so in a way
28664 * that, if there are other nodes with the same z-index, the newest
28665 * drawn will be the front most within that z-index. This is the
28666 * default indexing method.
28669 * indexer - {<OpenLayers.ElementsIndexer>}
28670 * newNode - {DOMElement}
28671 * nextNode - {DOMElement}
28676 Z_ORDER_DRAWING_ORDER: function(indexer, newNode, nextNode) {
28677 var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(
28683 // Make Z_ORDER subscribe to drawing order by pushing it above
28684 // all of the other nodes with the same z-index.
28685 if (nextNode && returnVal == 0) {
28693 * APIMethod: Z_ORDER_Y_ORDER
28694 * This one should really be called Z_ORDER_Y_ORDER_DRAWING_ORDER, as it
28695 * best describes which ordering methods have precedence (though, the
28696 * name would be too long). This method orders nodes by their z-index,
28697 * but does so in a way that, if there are other nodes with the same
28698 * z-index, the nodes with the lower y position will be "closer" than
28699 * those with a higher y position. If two nodes have the exact same y
28700 * position, however, then this method will revert to using drawing
28701 * order to decide placement.
28704 * indexer - {<OpenLayers.ElementsIndexer>}
28705 * newNode - {DOMElement}
28706 * nextNode - {DOMElement}
28711 Z_ORDER_Y_ORDER: function(indexer, newNode, nextNode) {
28712 var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(
28718 if (nextNode && returnVal === 0) {
28719 var result = nextNode._boundsBottom - newNode._boundsBottom;
28720 returnVal = (result === 0) ? 1 : result;
28728 * Class: OpenLayers.Renderer.Elements
28729 * This is another virtual class in that it should never be instantiated by
28730 * itself as a Renderer. It exists because there is *tons* of shared
28731 * functionality between different vector libraries which use nodes/elements
28732 * as a base for rendering vectors.
28734 * The highlevel bits of code that are implemented here are the adding and
28735 * removing of geometries, which is essentially the same for any
28736 * element-based renderer. The details of creating each node and drawing the
28737 * paths are of course different, but the machinery is the same.
28740 * - <OpenLayers.Renderer>
28742 OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
28745 * Property: rendererRoot
28748 rendererRoot: null,
28757 * Property: vectorRoot
28763 * Property: textRoot
28775 * Property: xOffset
28776 * {Number} Offset to apply to the renderer viewport translation in x
28777 * direction. If the renderer extent's center is on the right of the
28778 * dateline (i.e. exceeds the world bounds), we shift the viewport to the
28779 * left by one world width. This avoids that features disappear from the
28780 * map viewport. Because our dateline handling logic in other places
28781 * ensures that extents crossing the dateline always have a center
28782 * exceeding the world bounds on the left, we need this offset to make sure
28783 * that the same is true for the renderer extent in pixel space as well.
28788 * Property: rightOfDateLine
28789 * {Boolean} Keeps track of the location of the map extent relative to the
28790 * date line. The <setExtent> method compares this value (which is the one
28791 * from the previous <setExtent> call) with the current position of the map
28792 * extent relative to the date line and updates the xOffset when the extent
28793 * has moved from one side of the date line to the other.
28797 * Property: Indexer
28798 * {<OpenLayers.ElementIndexer>} An instance of OpenLayers.ElementsIndexer
28799 * created upon initialization if the zIndexing or yOrdering options
28800 * passed to this renderer's constructor are set to true.
28805 * Constant: BACKGROUND_ID_SUFFIX
28808 BACKGROUND_ID_SUFFIX: "_background",
28811 * Constant: LABEL_ID_SUFFIX
28814 LABEL_ID_SUFFIX: "_label",
28817 * Constant: LABEL_OUTLINE_SUFFIX
28820 LABEL_OUTLINE_SUFFIX: "_outline",
28823 * Constructor: OpenLayers.Renderer.Elements
28826 * containerID - {String}
28827 * options - {Object} options for this renderer.
28829 * Supported options are:
28830 * yOrdering - {Boolean} Whether to use y-ordering
28831 * zIndexing - {Boolean} Whether to use z-indexing. Will be ignored
28832 * if yOrdering is set to true.
28834 initialize: function(containerID, options) {
28835 OpenLayers.Renderer.prototype.initialize.apply(this, arguments);
28837 this.rendererRoot = this.createRenderRoot();
28838 this.root = this.createRoot("_root");
28839 this.vectorRoot = this.createRoot("_vroot");
28840 this.textRoot = this.createRoot("_troot");
28842 this.root.appendChild(this.vectorRoot);
28843 this.root.appendChild(this.textRoot);
28845 this.rendererRoot.appendChild(this.root);
28846 this.container.appendChild(this.rendererRoot);
28848 if(options && (options.zIndexing || options.yOrdering)) {
28849 this.indexer = new OpenLayers.ElementsIndexer(options.yOrdering);
28856 destroy: function() {
28860 this.rendererRoot = null;
28864 OpenLayers.Renderer.prototype.destroy.apply(this, arguments);
28869 * Remove all the elements from the root
28871 clear: function() {
28873 var root = this.vectorRoot;
28875 while (child = root.firstChild) {
28876 root.removeChild(child);
28879 root = this.textRoot;
28881 while (child = root.firstChild) {
28882 root.removeChild(child);
28885 if (this.indexer) {
28886 this.indexer.clear();
28891 * Method: setExtent
28892 * Set the visible part of the layer.
28895 * extent - {<OpenLayers.Bounds>}
28896 * resolutionChanged - {Boolean}
28899 * {Boolean} true to notify the layer that the new extent does not exceed
28900 * the coordinate range, and the features will not need to be redrawn.
28903 setExtent: function(extent, resolutionChanged) {
28904 var coordSysUnchanged = OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);
28905 var resolution = this.getResolution();
28906 if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
28907 var rightOfDateLine,
28908 ratio = extent.getWidth() / this.map.getExtent().getWidth(),
28909 extent = extent.scale(1 / ratio),
28910 world = this.map.getMaxExtent();
28911 if (world.right > extent.left && world.right < extent.right) {
28912 rightOfDateLine = true;
28913 } else if (world.left > extent.left && world.left < extent.right) {
28914 rightOfDateLine = false;
28916 if (rightOfDateLine !== this.rightOfDateLine || resolutionChanged) {
28917 coordSysUnchanged = false;
28918 this.xOffset = rightOfDateLine === true ?
28919 world.getWidth() / resolution : 0;
28921 this.rightOfDateLine = rightOfDateLine;
28923 return coordSysUnchanged;
28927 * Method: getNodeType
28928 * This function is in charge of asking the specific renderer which type
28929 * of node to create for the given geometry and style. All geometries
28930 * in an Elements-based renderer consist of one node and some
28931 * attributes. We have the nodeFactory() function which creates a node
28932 * for us, but it takes a 'type' as input, and that is precisely what
28933 * this function tells us.
28936 * geometry - {<OpenLayers.Geometry>}
28940 * {String} The corresponding node type for the specified geometry
28942 getNodeType: function(geometry, style) { },
28945 * Method: drawGeometry
28946 * Draw the geometry, creating new nodes, setting paths, setting style,
28947 * setting featureId on the node. This method should only be called
28948 * by the renderer itself.
28951 * geometry - {<OpenLayers.Geometry>}
28953 * featureId - {String}
28956 * {Boolean} true if the geometry has been drawn completely; null if
28957 * incomplete; false otherwise
28959 drawGeometry: function(geometry, style, featureId) {
28960 var className = geometry.CLASS_NAME;
28961 var rendered = true;
28962 if ((className == "OpenLayers.Geometry.Collection") ||
28963 (className == "OpenLayers.Geometry.MultiPoint") ||
28964 (className == "OpenLayers.Geometry.MultiLineString") ||
28965 (className == "OpenLayers.Geometry.MultiPolygon")) {
28966 for (var i = 0, len=geometry.components.length; i<len; i++) {
28967 rendered = this.drawGeometry(
28968 geometry.components[i], style, featureId) && rendered;
28974 var removeBackground = false;
28975 if (style.display != "none") {
28976 if (style.backgroundGraphic) {
28977 this.redrawBackgroundNode(geometry.id, geometry, style,
28980 removeBackground = true;
28982 rendered = this.redrawNode(geometry.id, geometry, style,
28985 if (rendered == false) {
28986 var node = document.getElementById(geometry.id);
28988 if (node._style.backgroundGraphic) {
28989 removeBackground = true;
28991 node.parentNode.removeChild(node);
28994 if (removeBackground) {
28995 var node = document.getElementById(
28996 geometry.id + this.BACKGROUND_ID_SUFFIX);
28998 node.parentNode.removeChild(node);
29005 * Method: redrawNode
29009 * geometry - {<OpenLayers.Geometry>}
29011 * featureId - {String}
29014 * {Boolean} true if the complete geometry could be drawn, null if parts of
29015 * the geometry could not be drawn, false otherwise
29017 redrawNode: function(id, geometry, style, featureId) {
29018 style = this.applyDefaultSymbolizer(style);
29019 // Get the node if it's already on the map.
29020 var node = this.nodeFactory(id, this.getNodeType(geometry, style));
29022 // Set the data for the node, then draw it.
29023 node._featureId = featureId;
29024 node._boundsBottom = geometry.getBounds().bottom;
29025 node._geometryClass = geometry.CLASS_NAME;
29026 node._style = style;
29028 var drawResult = this.drawGeometryNode(node, geometry, style);
29029 if(drawResult === false) {
29033 node = drawResult.node;
29035 // Insert the node into the indexer so it can show us where to
29036 // place it. Note that this operation is O(log(n)). If there's a
29037 // performance problem (when dragging, for instance) this is
29038 // likely where it would be.
29039 if (this.indexer) {
29040 var insert = this.indexer.insert(node);
29042 this.vectorRoot.insertBefore(node, insert);
29044 this.vectorRoot.appendChild(node);
29047 // if there's no indexer, simply append the node to root,
29048 // but only if the node is a new one
29049 if (node.parentNode !== this.vectorRoot){
29050 this.vectorRoot.appendChild(node);
29054 this.postDraw(node);
29056 return drawResult.complete;
29060 * Method: redrawBackgroundNode
29061 * Redraws the node using special 'background' style properties. Basically
29062 * just calls redrawNode(), but instead of directly using the
29063 * 'externalGraphic', 'graphicXOffset', 'graphicYOffset', and
29064 * 'graphicZIndex' properties directly from the specified 'style'
29065 * parameter, we create a new style object and set those properties
29066 * from the corresponding 'background'-prefixed properties from
29067 * specified 'style' parameter.
29071 * geometry - {<OpenLayers.Geometry>}
29073 * featureId - {String}
29076 * {Boolean} true if the complete geometry could be drawn, null if parts of
29077 * the geometry could not be drawn, false otherwise
29079 redrawBackgroundNode: function(id, geometry, style, featureId) {
29080 var backgroundStyle = OpenLayers.Util.extend({}, style);
29082 // Set regular style attributes to apply to the background styles.
29083 backgroundStyle.externalGraphic = backgroundStyle.backgroundGraphic;
29084 backgroundStyle.graphicXOffset = backgroundStyle.backgroundXOffset;
29085 backgroundStyle.graphicYOffset = backgroundStyle.backgroundYOffset;
29086 backgroundStyle.graphicZIndex = backgroundStyle.backgroundGraphicZIndex;
29087 backgroundStyle.graphicWidth = backgroundStyle.backgroundWidth || backgroundStyle.graphicWidth;
29088 backgroundStyle.graphicHeight = backgroundStyle.backgroundHeight || backgroundStyle.graphicHeight;
29090 // Erase background styles.
29091 backgroundStyle.backgroundGraphic = null;
29092 backgroundStyle.backgroundXOffset = null;
29093 backgroundStyle.backgroundYOffset = null;
29094 backgroundStyle.backgroundGraphicZIndex = null;
29096 return this.redrawNode(
29097 id + this.BACKGROUND_ID_SUFFIX,
29105 * Method: drawGeometryNode
29106 * Given a node, draw a geometry on the specified layer.
29107 * node and geometry are required arguments, style is optional.
29108 * This method is only called by the render itself.
29111 * node - {DOMElement}
29112 * geometry - {<OpenLayers.Geometry>}
29116 * {Object} a hash with properties "node" (the drawn node) and "complete"
29117 * (null if parts of the geometry could not be drawn, false if nothing
29120 drawGeometryNode: function(node, geometry, style) {
29121 style = style || node._style;
29124 'isFilled': style.fill === undefined ?
29127 'isStroked': style.stroke === undefined ?
29128 !!style.strokeWidth :
29132 switch (geometry.CLASS_NAME) {
29133 case "OpenLayers.Geometry.Point":
29134 if(style.graphic === false) {
29135 options.isFilled = false;
29136 options.isStroked = false;
29138 drawn = this.drawPoint(node, geometry);
29140 case "OpenLayers.Geometry.LineString":
29141 options.isFilled = false;
29142 drawn = this.drawLineString(node, geometry);
29144 case "OpenLayers.Geometry.LinearRing":
29145 drawn = this.drawLinearRing(node, geometry);
29147 case "OpenLayers.Geometry.Polygon":
29148 drawn = this.drawPolygon(node, geometry);
29150 case "OpenLayers.Geometry.Rectangle":
29151 drawn = this.drawRectangle(node, geometry);
29157 node._options = options;
29160 //TBD simplify this
29161 if (drawn != false) {
29163 node: this.setStyle(node, style, options, geometry),
29173 * Things that have do be done after the geometry node is appended
29174 * to its parent node. To be overridden by subclasses.
29177 * node - {DOMElement}
29179 postDraw: function(node) {},
29182 * Method: drawPoint
29183 * Virtual function for drawing Point Geometry.
29184 * Should be implemented by subclasses.
29185 * This method is only called by the renderer itself.
29188 * node - {DOMElement}
29189 * geometry - {<OpenLayers.Geometry>}
29192 * {DOMElement} or false if the renderer could not draw the point
29194 drawPoint: function(node, geometry) {},
29197 * Method: drawLineString
29198 * Virtual function for drawing LineString Geometry.
29199 * Should be implemented by subclasses.
29200 * This method is only called by the renderer itself.
29203 * node - {DOMElement}
29204 * geometry - {<OpenLayers.Geometry>}
29207 * {DOMElement} or null if the renderer could not draw all components of
29208 * the linestring, or false if nothing could be drawn
29210 drawLineString: function(node, geometry) {},
29213 * Method: drawLinearRing
29214 * Virtual function for drawing LinearRing Geometry.
29215 * Should be implemented by subclasses.
29216 * This method is only called by the renderer itself.
29219 * node - {DOMElement}
29220 * geometry - {<OpenLayers.Geometry>}
29223 * {DOMElement} or null if the renderer could not draw all components
29224 * of the linear ring, or false if nothing could be drawn
29226 drawLinearRing: function(node, geometry) {},
29229 * Method: drawPolygon
29230 * Virtual function for drawing Polygon Geometry.
29231 * Should be implemented by subclasses.
29232 * This method is only called by the renderer itself.
29235 * node - {DOMElement}
29236 * geometry - {<OpenLayers.Geometry>}
29239 * {DOMElement} or null if the renderer could not draw all components
29240 * of the polygon, or false if nothing could be drawn
29242 drawPolygon: function(node, geometry) {},
29245 * Method: drawRectangle
29246 * Virtual function for drawing Rectangle Geometry.
29247 * Should be implemented by subclasses.
29248 * This method is only called by the renderer itself.
29251 * node - {DOMElement}
29252 * geometry - {<OpenLayers.Geometry>}
29255 * {DOMElement} or false if the renderer could not draw the rectangle
29257 drawRectangle: function(node, geometry) {},
29260 * Method: drawCircle
29261 * Virtual function for drawing Circle Geometry.
29262 * Should be implemented by subclasses.
29263 * This method is only called by the renderer itself.
29266 * node - {DOMElement}
29267 * geometry - {<OpenLayers.Geometry>}
29270 * {DOMElement} or false if the renderer could not draw the circle
29272 drawCircle: function(node, geometry) {},
29275 * Method: removeText
29279 * featureId - {String}
29281 removeText: function(featureId) {
29282 var label = document.getElementById(featureId + this.LABEL_ID_SUFFIX);
29284 this.textRoot.removeChild(label);
29286 var outline = document.getElementById(featureId + this.LABEL_OUTLINE_SUFFIX);
29288 this.textRoot.removeChild(outline);
29293 * Method: getFeatureIdFromEvent
29296 * evt - {Object} An <OpenLayers.Event> object
29299 * {String} A feature id or undefined.
29301 getFeatureIdFromEvent: function(evt) {
29302 var target = evt.target;
29303 var useElement = target && target.correspondingUseElement;
29304 var node = useElement ? useElement : (target || evt.srcElement);
29305 return node._featureId;
29309 * Method: eraseGeometry
29310 * Erase a geometry from the renderer. In the case of a multi-geometry,
29311 * we cycle through and recurse on ourselves. Otherwise, we look for a
29312 * node with the geometry.id, destroy its geometry, and remove it from
29316 * geometry - {<OpenLayers.Geometry>}
29317 * featureId - {String}
29319 eraseGeometry: function(geometry, featureId) {
29320 if ((geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPoint") ||
29321 (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiLineString") ||
29322 (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPolygon") ||
29323 (geometry.CLASS_NAME == "OpenLayers.Geometry.Collection")) {
29324 for (var i=0, len=geometry.components.length; i<len; i++) {
29325 this.eraseGeometry(geometry.components[i], featureId);
29328 var element = OpenLayers.Util.getElement(geometry.id);
29329 if (element && element.parentNode) {
29330 if (element.geometry) {
29331 element.geometry.destroy();
29332 element.geometry = null;
29334 element.parentNode.removeChild(element);
29336 if (this.indexer) {
29337 this.indexer.remove(element);
29340 if (element._style.backgroundGraphic) {
29341 var backgroundId = geometry.id + this.BACKGROUND_ID_SUFFIX;
29342 var bElem = OpenLayers.Util.getElement(backgroundId);
29343 if (bElem && bElem.parentNode) {
29344 // No need to destroy the geometry since the element and the background
29345 // node share the same geometry.
29346 bElem.parentNode.removeChild(bElem);
29354 * Method: nodeFactory
29355 * Create new node of the specified type, with the (optional) specified id.
29357 * If node already exists with same ID and a different type, we remove it
29358 * and then call ourselves again to recreate it.
29362 * type - {String} type Kind of node to draw.
29365 * {DOMElement} A new node of the given type and id.
29367 nodeFactory: function(id, type) {
29368 var node = OpenLayers.Util.getElement(id);
29370 if (!this.nodeTypeCompare(node, type)) {
29371 node.parentNode.removeChild(node);
29372 node = this.nodeFactory(id, type);
29375 node = this.createNode(type, id);
29381 * Method: nodeTypeCompare
29384 * node - {DOMElement}
29385 * type - {String} Kind of node
29388 * {Boolean} Whether or not the specified node is of the specified type
29389 * This function must be overridden by subclasses.
29391 nodeTypeCompare: function(node, type) {},
29394 * Method: createNode
29397 * type - {String} Kind of node to draw.
29398 * id - {String} Id for node.
29401 * {DOMElement} A new node of the given type and id.
29402 * This function must be overridden by subclasses.
29404 createNode: function(type, id) {},
29408 * moves this renderer's root to a different renderer.
29411 * renderer - {<OpenLayers.Renderer>} target renderer for the moved root
29413 moveRoot: function(renderer) {
29414 var root = this.root;
29415 if(renderer.root.parentNode == this.rendererRoot) {
29416 root = renderer.root;
29418 root.parentNode.removeChild(root);
29419 renderer.rendererRoot.appendChild(root);
29423 * Method: getRenderLayerId
29424 * Gets the layer that this renderer's output appears on. If moveRoot was
29425 * used, this will be different from the id of the layer containing the
29426 * features rendered by this renderer.
29429 * {String} the id of the output layer.
29431 getRenderLayerId: function() {
29432 return this.root.parentNode.parentNode.id;
29436 * Method: isComplexSymbol
29437 * Determines if a symbol cannot be rendered using drawCircle
29440 * graphicName - {String}
29443 * {Boolean} true if the symbol is complex, false if not
29445 isComplexSymbol: function(graphicName) {
29446 return (graphicName != "circle") && !!graphicName;
29449 CLASS_NAME: "OpenLayers.Renderer.Elements"
29452 /* ======================================================================
29453 OpenLayers/Control/Zoom.js
29454 ====================================================================== */
29456 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
29457 * full list of contributors). Published under the 2-clause BSD license.
29458 * See license.txt in the OpenLayers distribution or repository for the
29459 * full text of the license. */
29462 * @requires OpenLayers/Control.js
29463 * @requires OpenLayers/Events/buttonclick.js
29467 * Class: OpenLayers.Control.Zoom
29468 * The Zoom control is a pair of +/- links for zooming in and out.
29471 * - <OpenLayers.Control>
29473 OpenLayers.Control.Zoom = OpenLayers.Class(OpenLayers.Control, {
29476 * APIProperty: zoomInText
29478 * Text for zoom-in link. Default is "+".
29483 * APIProperty: zoomInId
29485 * Instead of having the control create a zoom in link, you can provide
29486 * the identifier for an anchor element already added to the document.
29487 * By default, an element with id "olZoomInLink" will be searched for
29488 * and used if it exists.
29490 zoomInId: "olZoomInLink",
29493 * APIProperty: zoomOutText
29495 * Text for zoom-out link. Default is "\u2212".
29497 zoomOutText: "\u2212",
29500 * APIProperty: zoomOutId
29502 * Instead of having the control create a zoom out link, you can provide
29503 * the identifier for an anchor element already added to the document.
29504 * By default, an element with id "olZoomOutLink" will be searched for
29505 * and used if it exists.
29507 zoomOutId: "olZoomOutLink",
29513 * {DOMElement} A reference to the DOMElement containing the zoom links.
29516 var div = OpenLayers.Control.prototype.draw.apply(this),
29517 links = this.getOrCreateLinks(div),
29518 zoomIn = links.zoomIn,
29519 zoomOut = links.zoomOut,
29520 eventsInstance = this.map.events;
29522 if (zoomOut.parentNode !== div) {
29523 eventsInstance = this.events;
29524 eventsInstance.attachToElement(zoomOut.parentNode);
29526 eventsInstance.register("buttonclick", this, this.onZoomClick);
29528 this.zoomInLink = zoomIn;
29529 this.zoomOutLink = zoomOut;
29534 * Method: getOrCreateLinks
29537 * el - {DOMElement}
29540 * {Object} Object with zoomIn and zoomOut properties referencing links.
29542 getOrCreateLinks: function(el) {
29543 var zoomIn = document.getElementById(this.zoomInId),
29544 zoomOut = document.getElementById(this.zoomOutId);
29546 zoomIn = document.createElement("a");
29547 zoomIn.href = "#zoomIn";
29548 zoomIn.appendChild(document.createTextNode(this.zoomInText));
29549 zoomIn.className = "olControlZoomIn";
29550 el.appendChild(zoomIn);
29552 OpenLayers.Element.addClass(zoomIn, "olButton");
29554 zoomOut = document.createElement("a");
29555 zoomOut.href = "#zoomOut";
29556 zoomOut.appendChild(document.createTextNode(this.zoomOutText));
29557 zoomOut.className = "olControlZoomOut";
29558 el.appendChild(zoomOut);
29560 OpenLayers.Element.addClass(zoomOut, "olButton");
29562 zoomIn: zoomIn, zoomOut: zoomOut
29567 * Method: onZoomClick
29568 * Called when zoomin/out link is clicked.
29570 onZoomClick: function(evt) {
29571 var button = evt.buttonElement;
29572 if (button === this.zoomInLink) {
29574 } else if (button === this.zoomOutLink) {
29575 this.map.zoomOut();
29583 destroy: function() {
29585 this.map.events.unregister("buttonclick", this, this.onZoomClick);
29587 delete this.zoomInLink;
29588 delete this.zoomOutLink;
29589 OpenLayers.Control.prototype.destroy.apply(this);
29592 CLASS_NAME: "OpenLayers.Control.Zoom"
29594 /* ======================================================================
29595 OpenLayers/Protocol.js
29596 ====================================================================== */
29598 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
29599 * full list of contributors). Published under the 2-clause BSD license.
29600 * See license.txt in the OpenLayers distribution or repository for the
29601 * full text of the license. */
29604 * @requires OpenLayers/BaseTypes/Class.js
29608 * Class: OpenLayers.Protocol
29609 * Abstract vector layer protocol class. Not to be instantiated directly. Use
29610 * one of the protocol subclasses instead.
29612 OpenLayers.Protocol = OpenLayers.Class({
29616 * {<OpenLayers.Format>} The format used by this protocol.
29621 * Property: options
29622 * {Object} Any options sent to the constructor.
29627 * Property: autoDestroy
29628 * {Boolean} The creator of the protocol can set autoDestroy to false
29629 * to fully control when the protocol is destroyed. Defaults to
29635 * Property: defaultFilter
29636 * {<OpenLayers.Filter>} Optional default filter to read requests
29638 defaultFilter: null,
29641 * Constructor: OpenLayers.Protocol
29642 * Abstract class for vector protocols. Create instances of a subclass.
29645 * options - {Object} Optional object whose properties will be set on the
29648 initialize: function(options) {
29649 options = options || {};
29650 OpenLayers.Util.extend(this, options);
29651 this.options = options;
29655 * Method: mergeWithDefaultFilter
29656 * Merge filter passed to the read method with the default one
29659 * filter - {<OpenLayers.Filter>}
29661 mergeWithDefaultFilter: function(filter) {
29663 if (filter && this.defaultFilter) {
29664 merged = new OpenLayers.Filter.Logical({
29665 type: OpenLayers.Filter.Logical.AND,
29666 filters: [this.defaultFilter, filter]
29669 merged = filter || this.defaultFilter || undefined;
29675 * APIMethod: destroy
29676 * Clean up the protocol.
29678 destroy: function() {
29679 this.options = null;
29680 this.format = null;
29685 * Construct a request for reading new features.
29688 * options - {Object} Optional object for configuring the request.
29691 * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
29692 * object, the same object will be passed to the callback function passed
29693 * if one exists in the options object.
29695 read: function(options) {
29696 options = options || {};
29697 options.filter = this.mergeWithDefaultFilter(options.filter);
29702 * APIMethod: create
29703 * Construct a request for writing newly created features.
29706 * features - {Array({<OpenLayers.Feature.Vector>})} or
29707 * {<OpenLayers.Feature.Vector>}
29708 * options - {Object} Optional object for configuring the request.
29711 * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
29712 * object, the same object will be passed to the callback function passed
29713 * if one exists in the options object.
29715 create: function() {
29719 * APIMethod: update
29720 * Construct a request updating modified features.
29723 * features - {Array({<OpenLayers.Feature.Vector>})} or
29724 * {<OpenLayers.Feature.Vector>}
29725 * options - {Object} Optional object for configuring the request.
29728 * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
29729 * object, the same object will be passed to the callback function passed
29730 * if one exists in the options object.
29732 update: function() {
29736 * APIMethod: delete
29737 * Construct a request deleting a removed feature.
29740 * feature - {<OpenLayers.Feature.Vector>}
29741 * options - {Object} Optional object for configuring the request.
29744 * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
29745 * object, the same object will be passed to the callback function passed
29746 * if one exists in the options object.
29748 "delete": function() {
29752 * APIMethod: commit
29753 * Go over the features and for each take action
29754 * based on the feature state. Possible actions are create,
29755 * update and delete.
29758 * features - {Array({<OpenLayers.Feature.Vector>})}
29759 * options - {Object} Object whose possible keys are "create", "update",
29760 * "delete", "callback" and "scope", the values referenced by the
29761 * first three are objects as passed to the "create", "update", and
29762 * "delete" methods, the value referenced by the "callback" key is
29763 * a function which is called when the commit operation is complete
29764 * using the scope referenced by the "scope" key.
29767 * {Array({<OpenLayers.Protocol.Response>})} An array of
29768 * <OpenLayers.Protocol.Response> objects.
29770 commit: function() {
29775 * Abort an ongoing request.
29778 * response - {<OpenLayers.Protocol.Response>}
29780 abort: function(response) {
29784 * Method: createCallback
29785 * Returns a function that applies the given public method with resp and
29786 * options arguments.
29789 * method - {Function} The method to be applied by the callback.
29790 * response - {<OpenLayers.Protocol.Response>} The protocol response object.
29791 * options - {Object} Options sent to the protocol method
29793 createCallback: function(method, response, options) {
29794 return OpenLayers.Function.bind(function() {
29795 method.apply(this, [response, options]);
29799 CLASS_NAME: "OpenLayers.Protocol"
29803 * Class: OpenLayers.Protocol.Response
29804 * Protocols return Response objects to their users.
29806 OpenLayers.Protocol.Response = OpenLayers.Class({
29809 * {Number} - OpenLayers.Protocol.Response.SUCCESS or
29810 * OpenLayers.Protocol.Response.FAILURE
29815 * Property: requestType
29816 * {String} The type of request this response corresponds to. Either
29817 * "create", "read", "update" or "delete".
29823 * {Boolean} - true if this is the last response expected in a commit,
29824 * false otherwise, defaults to true.
29829 * Property: features
29830 * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}
29831 * The features returned in the response by the server. Depending on the
29832 * protocol's read payload, either features or data will be populated.
29839 * The data returned in the response by the server. Depending on the
29840 * protocol's read payload, either features or data will be populated.
29845 * Property: reqFeatures
29846 * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}
29847 * The features provided by the user and placed in the request by the
29859 * {Object} The error object in case a service exception was encountered.
29864 * Constructor: OpenLayers.Protocol.Response
29867 * options - {Object} Optional object whose properties will be set on the
29870 initialize: function(options) {
29871 OpenLayers.Util.extend(this, options);
29878 * {Boolean} - true on success, false otherwise
29880 success: function() {
29881 return this.code > 0;
29884 CLASS_NAME: "OpenLayers.Protocol.Response"
29887 OpenLayers.Protocol.Response.SUCCESS = 1;
29888 OpenLayers.Protocol.Response.FAILURE = 0;
29889 /* ======================================================================
29890 OpenLayers/Protocol/WFS.js
29891 ====================================================================== */
29893 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
29894 * full list of contributors). Published under the 2-clause BSD license.
29895 * See license.txt in the OpenLayers distribution or repository for the
29896 * full text of the license. */
29899 * @requires OpenLayers/Protocol.js
29903 * Class: OpenLayers.Protocol.WFS
29904 * Used to create a versioned WFS protocol. Default version is 1.0.0.
29907 * {<OpenLayers.Protocol>} A WFS protocol of the given version.
29911 * var protocol = new OpenLayers.Protocol.WFS({
29912 * version: "1.1.0",
29913 * url: "http://demo.opengeo.org/geoserver/wfs",
29914 * featureType: "tasmania_roads",
29915 * featureNS: "http://www.openplans.org/topp",
29916 * geometryName: "the_geom"
29920 * See the protocols for specific WFS versions for more detail.
29922 OpenLayers.Protocol.WFS = function(options) {
29923 options = OpenLayers.Util.applyDefaults(
29924 options, OpenLayers.Protocol.WFS.DEFAULTS
29926 var cls = OpenLayers.Protocol.WFS["v"+options.version.replace(/\./g, "_")];
29928 throw "Unsupported WFS version: " + options.version;
29930 return new cls(options);
29934 * Function: fromWMSLayer
29935 * Convenience function to create a WFS protocol from a WMS layer. This makes
29936 * the assumption that a WFS requests can be issued at the same URL as
29937 * WMS requests and that a WFS featureType exists with the same name as the
29940 * This function is designed to auto-configure <url>, <featureType>,
29941 * <featurePrefix> and <srsName> for WFS <version> 1.1.0. Note that
29942 * srsName matching with the WMS layer will not work with WFS 1.0.0.
29945 * layer - {<OpenLayers.Layer.WMS>} WMS layer that has a matching WFS
29946 * FeatureType at the same server url with the same typename.
29947 * options - {Object} Default properties to be set on the protocol.
29950 * {<OpenLayers.Protocol.WFS>}
29952 OpenLayers.Protocol.WFS.fromWMSLayer = function(layer, options) {
29953 var typeName, featurePrefix;
29954 var param = layer.params["LAYERS"];
29955 var parts = (OpenLayers.Util.isArray(param) ? param[0] : param).split(":");
29956 if(parts.length > 1) {
29957 featurePrefix = parts[0];
29959 typeName = parts.pop();
29960 var protocolOptions = {
29962 featureType: typeName,
29963 featurePrefix: featurePrefix,
29964 srsName: layer.projection && layer.projection.getCode() ||
29965 layer.map && layer.map.getProjectionObject().getCode(),
29968 return new OpenLayers.Protocol.WFS(OpenLayers.Util.applyDefaults(
29969 options, protocolOptions
29974 * Constant: OpenLayers.Protocol.WFS.DEFAULTS
29976 OpenLayers.Protocol.WFS.DEFAULTS = {
29979 /* ======================================================================
29980 OpenLayers/Layer/Markers.js
29981 ====================================================================== */
29983 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
29984 * full list of contributors). Published under the 2-clause BSD license.
29985 * See license.txt in the OpenLayers distribution or repository for the
29986 * full text of the license. */
29990 * @requires OpenLayers/Layer.js
29994 * Class: OpenLayers.Layer.Markers
29997 * - <OpenLayers.Layer>
29999 OpenLayers.Layer.Markers = OpenLayers.Class(OpenLayers.Layer, {
30002 * APIProperty: isBaseLayer
30003 * {Boolean} Markers layer is never a base layer.
30005 isBaseLayer: false,
30008 * APIProperty: markers
30009 * {Array(<OpenLayers.Marker>)} internal marker list
30016 * {Boolean} internal state of drawing. This is a workaround for the fact
30017 * that the map does not call moveTo with a zoomChanged when the map is
30018 * first starting up. This lets us catch the case where we have *never*
30019 * drawn the layer, and draw it even if the zoom hasn't changed.
30024 * Constructor: OpenLayers.Layer.Markers
30025 * Create a Markers layer.
30029 * options - {Object} Hashtable of extra options to tag onto the layer
30031 initialize: function(name, options) {
30032 OpenLayers.Layer.prototype.initialize.apply(this, arguments);
30037 * APIMethod: destroy
30039 destroy: function() {
30040 this.clearMarkers();
30041 this.markers = null;
30042 OpenLayers.Layer.prototype.destroy.apply(this, arguments);
30046 * APIMethod: setOpacity
30047 * Sets the opacity for all the markers.
30050 * opacity - {Float}
30052 setOpacity: function(opacity) {
30053 if (opacity != this.opacity) {
30054 this.opacity = opacity;
30055 for (var i=0, len=this.markers.length; i<len; i++) {
30056 this.markers[i].setOpacity(this.opacity);
30065 * bounds - {<OpenLayers.Bounds>}
30066 * zoomChanged - {Boolean}
30067 * dragging - {Boolean}
30069 moveTo:function(bounds, zoomChanged, dragging) {
30070 OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
30072 if (zoomChanged || !this.drawn) {
30073 for(var i=0, len=this.markers.length; i<len; i++) {
30074 this.drawMarker(this.markers[i]);
30081 * APIMethod: addMarker
30084 * marker - {<OpenLayers.Marker>}
30086 addMarker: function(marker) {
30087 this.markers.push(marker);
30089 if (this.opacity < 1) {
30090 marker.setOpacity(this.opacity);
30093 if (this.map && this.map.getExtent()) {
30094 marker.map = this.map;
30095 this.drawMarker(marker);
30100 * APIMethod: removeMarker
30103 * marker - {<OpenLayers.Marker>}
30105 removeMarker: function(marker) {
30106 if (this.markers && this.markers.length) {
30107 OpenLayers.Util.removeItem(this.markers, marker);
30113 * Method: clearMarkers
30114 * This method removes all markers from a layer. The markers are not
30115 * destroyed by this function, but are removed from the list of markers.
30117 clearMarkers: function() {
30118 if (this.markers != null) {
30119 while(this.markers.length > 0) {
30120 this.removeMarker(this.markers[0]);
30126 * Method: drawMarker
30127 * Calculate the pixel location for the marker, create it, and
30128 * add it to the layer's div
30131 * marker - {<OpenLayers.Marker>}
30133 drawMarker: function(marker) {
30134 var px = this.map.getLayerPxFromLonLat(marker.lonlat);
30136 marker.display(false);
30138 if (!marker.isDrawn()) {
30139 var markerImg = marker.draw(px);
30140 this.div.appendChild(markerImg);
30141 } else if(marker.icon) {
30142 marker.icon.moveTo(px);
30148 * APIMethod: getDataExtent
30149 * Calculates the max extent which includes all of the markers.
30152 * {<OpenLayers.Bounds>}
30154 getDataExtent: function () {
30155 var maxExtent = null;
30157 if ( this.markers && (this.markers.length > 0)) {
30158 var maxExtent = new OpenLayers.Bounds();
30159 for(var i=0, len=this.markers.length; i<len; i++) {
30160 var marker = this.markers[i];
30161 maxExtent.extend(marker.lonlat);
30168 CLASS_NAME: "OpenLayers.Layer.Markers"
30170 /* ======================================================================
30171 OpenLayers/Strategy/BBOX.js
30172 ====================================================================== */
30174 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
30175 * full list of contributors). Published under the 2-clause BSD license.
30176 * See license.txt in the OpenLayers distribution or repository for the
30177 * full text of the license. */
30180 * @requires OpenLayers/Strategy.js
30181 * @requires OpenLayers/Filter/Spatial.js
30185 * Class: OpenLayers.Strategy.BBOX
30186 * A simple strategy that reads new features when the viewport invalidates
30190 * - <OpenLayers.Strategy>
30192 OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, {
30196 * {<OpenLayers.Bounds>} The current data bounds (in the same projection
30197 * as the layer - not always the same projection as the map).
30202 * Property: resolution
30203 * {Float} The current data resolution.
30208 * APIProperty: ratio
30209 * {Float} The ratio of the data bounds to the viewport bounds (in each
30210 * dimension). Default is 2.
30215 * Property: resFactor
30216 * {Float} Optional factor used to determine when previously requested
30217 * features are invalid. If set, the resFactor will be compared to the
30218 * resolution of the previous request to the current map resolution.
30219 * If resFactor > (old / new) and 1/resFactor < (old / new). If you
30220 * set a resFactor of 1, data will be requested every time the
30221 * resolution changes. If you set a resFactor of 3, data will be
30222 * requested if the old resolution is 3 times the new, or if the new is
30223 * 3 times the old. If the old bounds do not contain the new bounds
30224 * new data will always be requested (with or without considering
30230 * Property: response
30231 * {<OpenLayers.Protocol.Response>} The protocol response object returned
30232 * by the layer protocol.
30237 * Constructor: OpenLayers.Strategy.BBOX
30238 * Create a new BBOX strategy.
30241 * options - {Object} Optional object whose properties will be set on the
30247 * Set up strategy with regard to reading new batches of remote data.
30250 * {Boolean} The strategy was successfully activated.
30252 activate: function() {
30253 var activated = OpenLayers.Strategy.prototype.activate.call(this);
30255 this.layer.events.on({
30256 "moveend": this.update,
30257 "refresh": this.update,
30258 "visibilitychanged": this.update,
30267 * Method: deactivate
30268 * Tear down strategy with regard to reading new batches of remote data.
30271 * {Boolean} The strategy was successfully deactivated.
30273 deactivate: function() {
30274 var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
30276 this.layer.events.un({
30277 "moveend": this.update,
30278 "refresh": this.update,
30279 "visibilitychanged": this.update,
30283 return deactivated;
30288 * Callback function called on "moveend" or "refresh" layer events.
30291 * options - {Object} Optional object whose properties will determine
30292 * the behaviour of this Strategy
30294 * Valid options include:
30295 * force - {Boolean} if true, new data must be unconditionally read.
30296 * noAbort - {Boolean} if true, do not abort previous requests.
30298 update: function(options) {
30299 var mapBounds = this.getMapBounds();
30300 if (mapBounds !== null && ((options && options.force) ||
30301 (this.layer.visibility && this.layer.calculateInRange() && this.invalidBounds(mapBounds)))) {
30302 this.calculateBounds(mapBounds);
30303 this.resolution = this.layer.map.getResolution();
30304 this.triggerRead(options);
30309 * Method: getMapBounds
30310 * Get the map bounds expressed in the same projection as this layer.
30313 * {<OpenLayers.Bounds>} Map bounds in the projection of the layer.
30315 getMapBounds: function() {
30316 if (this.layer.map === null) {
30319 var bounds = this.layer.map.getExtent();
30320 if(bounds && !this.layer.projection.equals(
30321 this.layer.map.getProjectionObject())) {
30322 bounds = bounds.clone().transform(
30323 this.layer.map.getProjectionObject(), this.layer.projection
30330 * Method: invalidBounds
30331 * Determine whether the previously requested set of features is invalid.
30332 * This occurs when the new map bounds do not contain the previously
30333 * requested bounds. In addition, if <resFactor> is set, it will be
30337 * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be
30338 * retrieved from the map object if not provided
30343 invalidBounds: function(mapBounds) {
30345 mapBounds = this.getMapBounds();
30347 var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds);
30348 if(!invalid && this.resFactor) {
30349 var ratio = this.resolution / this.layer.map.getResolution();
30350 invalid = (ratio >= this.resFactor || ratio <= (1 / this.resFactor));
30356 * Method: calculateBounds
30359 * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be
30360 * retrieved from the map object if not provided
30362 calculateBounds: function(mapBounds) {
30364 mapBounds = this.getMapBounds();
30366 var center = mapBounds.getCenterLonLat();
30367 var dataWidth = mapBounds.getWidth() * this.ratio;
30368 var dataHeight = mapBounds.getHeight() * this.ratio;
30369 this.bounds = new OpenLayers.Bounds(
30370 center.lon - (dataWidth / 2),
30371 center.lat - (dataHeight / 2),
30372 center.lon + (dataWidth / 2),
30373 center.lat + (dataHeight / 2)
30378 * Method: triggerRead
30381 * options - {Object} Additional options for the protocol's read method
30385 * {<OpenLayers.Protocol.Response>} The protocol response object
30386 * returned by the layer protocol.
30388 triggerRead: function(options) {
30389 if (this.response && !(options && options.noAbort === true)) {
30390 this.layer.protocol.abort(this.response);
30391 this.layer.events.triggerEvent("loadend");
30393 var evt = {filter: this.createFilter()};
30394 this.layer.events.triggerEvent("loadstart", evt);
30395 this.response = this.layer.protocol.read(
30396 OpenLayers.Util.applyDefaults({
30397 filter: evt.filter,
30398 callback: this.merge,
30404 * Method: createFilter
30405 * Creates a spatial BBOX filter. If the layer that this strategy belongs
30406 * to has a filter property, this filter will be combined with the BBOX
30410 * {<OpenLayers.Filter>} The filter object.
30412 createFilter: function() {
30413 var filter = new OpenLayers.Filter.Spatial({
30414 type: OpenLayers.Filter.Spatial.BBOX,
30415 value: this.bounds,
30416 projection: this.layer.projection
30418 if (this.layer.filter) {
30419 filter = new OpenLayers.Filter.Logical({
30420 type: OpenLayers.Filter.Logical.AND,
30421 filters: [this.layer.filter, filter]
30429 * Given a list of features, determine which ones to add to the layer.
30430 * If the layer projection differs from the map projection, features
30431 * will be transformed from the layer projection to the map projection.
30434 * resp - {<OpenLayers.Protocol.Response>} The response object passed
30437 merge: function(resp) {
30438 this.layer.destroyFeatures();
30439 if (resp.success()) {
30440 var features = resp.features;
30441 if(features && features.length > 0) {
30442 var remote = this.layer.projection;
30443 var local = this.layer.map.getProjectionObject();
30444 if(!local.equals(remote)) {
30446 for(var i=0, len=features.length; i<len; ++i) {
30447 geom = features[i].geometry;
30449 geom.transform(remote, local);
30453 this.layer.addFeatures(features);
30456 this.bounds = null;
30458 this.response = null;
30459 this.layer.events.triggerEvent("loadend", {response: resp});
30462 CLASS_NAME: "OpenLayers.Strategy.BBOX"
30464 /* ======================================================================
30465 OpenLayers/Handler/Feature.js
30466 ====================================================================== */
30468 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
30469 * full list of contributors). Published under the 2-clause BSD license.
30470 * See license.txt in the OpenLayers distribution or repository for the
30471 * full text of the license. */
30475 * @requires OpenLayers/Handler.js
30479 * Class: OpenLayers.Handler.Feature
30480 * Handler to respond to mouse events related to a drawn feature. Callbacks
30481 * with the following keys will be notified of the following events
30482 * associated with features: click, clickout, over, out, and dblclick.
30484 * This handler stops event propagation for mousedown and mouseup if those
30485 * browser events target features that can be selected.
30488 * - <OpenLayers.Handler>
30490 OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, {
30493 * Property: EVENTMAP
30494 * {Object} A object mapping the browser events to objects with callback
30495 * keys for in and out.
30498 'click': {'in': 'click', 'out': 'clickout'},
30499 'mousemove': {'in': 'over', 'out': 'out'},
30500 'dblclick': {'in': 'dblclick', 'out': null},
30501 'mousedown': {'in': null, 'out': null},
30502 'mouseup': {'in': null, 'out': null},
30503 'touchstart': {'in': 'click', 'out': 'clickout'}
30507 * Property: feature
30508 * {<OpenLayers.Feature.Vector>} The last feature that was hovered.
30513 * Property: lastFeature
30514 * {<OpenLayers.Feature.Vector>} The last feature that was handled.
30520 * {<OpenLayers.Pixel>} The location of the last mousedown.
30526 * {<OpenLayers.Pixel>} The location of the last mouseup.
30531 * Property: clickTolerance
30532 * {Number} The number of pixels the mouse can move between mousedown
30533 * and mouseup for the event to still be considered a click.
30534 * Dragging the map should not trigger the click and clickout callbacks
30535 * unless the map is moved by less than this tolerance. Defaults to 4.
30540 * Property: geometryTypes
30541 * To restrict dragging to a limited set of geometry types, send a list
30542 * of strings corresponding to the geometry class names.
30544 * @type Array(String)
30546 geometryTypes: null,
30549 * Property: stopClick
30550 * {Boolean} If stopClick is set to true, handled clicks do not
30551 * propagate to other click listeners. Otherwise, handled clicks
30552 * do propagate. Unhandled clicks always propagate, whatever the
30553 * value of stopClick. Defaults to true.
30558 * Property: stopDown
30559 * {Boolean} If stopDown is set to true, handled mousedowns do not
30560 * propagate to other mousedown listeners. Otherwise, handled
30561 * mousedowns do propagate. Unhandled mousedowns always propagate,
30562 * whatever the value of stopDown. Defaults to true.
30568 * {Boolean} If stopUp is set to true, handled mouseups do not
30569 * propagate to other mouseup listeners. Otherwise, handled mouseups
30570 * do propagate. Unhandled mouseups always propagate, whatever the
30571 * value of stopUp. Defaults to false.
30576 * Constructor: OpenLayers.Handler.Feature
30579 * control - {<OpenLayers.Control>}
30580 * layer - {<OpenLayers.Layer.Vector>}
30581 * callbacks - {Object} An object with a 'over' property whos value is
30582 * a function to be called when the mouse is over a feature. The
30583 * callback should expect to recieve a single argument, the feature.
30584 * options - {Object}
30586 initialize: function(control, layer, callbacks, options) {
30587 OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]);
30588 this.layer = layer;
30592 * Method: touchstart
30593 * Handle touchstart events
30599 * {Boolean} Let the event propagate.
30601 touchstart: function(evt) {
30603 return OpenLayers.Event.isMultiTouch(evt) ?
30604 true : this.mousedown(evt);
30608 * Method: touchmove
30609 * Handle touchmove events. We just prevent the browser default behavior,
30610 * for Android Webkit not to select text when moving the finger after
30611 * selecting a feature.
30616 touchmove: function(evt) {
30617 OpenLayers.Event.preventDefault(evt);
30621 * Method: mousedown
30622 * Handle mouse down. Stop propagation if a feature is targeted by this
30623 * event (stops map dragging during feature selection).
30628 mousedown: function(evt) {
30629 // Feature selection is only done with a left click. Other handlers may stop the
30630 // propagation of left-click mousedown events but not right-click mousedown events.
30631 // This mismatch causes problems when comparing the location of the down and up
30632 // events in the click function so it is important ignore right-clicks.
30633 if (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt)) {
30634 this.down = evt.xy;
30636 return this.handle(evt) ? !this.stopDown : true;
30641 * Handle mouse up. Stop propagation if a feature is targeted by this
30647 mouseup: function(evt) {
30649 return this.handle(evt) ? !this.stopUp : true;
30654 * Handle click. Call the "click" callback if click on a feature,
30655 * or the "clickout" callback if click outside any feature.
30663 click: function(evt) {
30664 return this.handle(evt) ? !this.stopClick : true;
30668 * Method: mousemove
30669 * Handle mouse moves. Call the "over" callback if moving in to a feature,
30670 * or the "out" callback if moving out of a feature.
30678 mousemove: function(evt) {
30679 if (!this.callbacks['over'] && !this.callbacks['out']) {
30688 * Handle dblclick. Call the "dblclick" callback if dblclick on a feature.
30696 dblclick: function(evt) {
30697 return !this.handle(evt);
30701 * Method: geometryTypeMatches
30702 * Return true if the geometry type of the passed feature matches
30703 * one of the geometry types in the geometryTypes array.
30706 * feature - {<OpenLayers.Vector.Feature>}
30711 geometryTypeMatches: function(feature) {
30712 return this.geometryTypes == null ||
30713 OpenLayers.Util.indexOf(this.geometryTypes,
30714 feature.geometry.CLASS_NAME) > -1;
30724 * {Boolean} The event occurred over a relevant feature.
30726 handle: function(evt) {
30727 if(this.feature && !this.feature.layer) {
30728 // feature has been destroyed
30729 this.feature = null;
30731 var type = evt.type;
30732 var handled = false;
30733 var previouslyIn = !!(this.feature); // previously in a feature
30734 var click = (type == "click" || type == "dblclick" || type == "touchstart");
30735 this.feature = this.layer.getFeatureFromEvent(evt);
30736 if(this.feature && !this.feature.layer) {
30737 // feature has been destroyed
30738 this.feature = null;
30740 if(this.lastFeature && !this.lastFeature.layer) {
30741 // last feature has been destroyed
30742 this.lastFeature = null;
30745 if(type === "touchstart") {
30746 // stop the event to prevent Android Webkit from
30747 // "flashing" the map div
30748 OpenLayers.Event.preventDefault(evt);
30750 var inNew = (this.feature != this.lastFeature);
30751 if(this.geometryTypeMatches(this.feature)) {
30753 if(previouslyIn && inNew) {
30754 // out of last feature and in to another
30755 if(this.lastFeature) {
30756 this.triggerCallback(type, 'out', [this.lastFeature]);
30758 this.triggerCallback(type, 'in', [this.feature]);
30759 } else if(!previouslyIn || click) {
30760 // in feature for the first time
30761 this.triggerCallback(type, 'in', [this.feature]);
30763 this.lastFeature = this.feature;
30766 // not in to a feature
30767 if(this.lastFeature && (previouslyIn && inNew || click)) {
30768 // out of last feature for the first time
30769 this.triggerCallback(type, 'out', [this.lastFeature]);
30771 // next time the mouse goes in a feature whose geometry type
30772 // doesn't match we don't want to call the 'out' callback
30773 // again, so let's set this.feature to null so that
30774 // previouslyIn will evaluate to false the next time
30775 // we enter handle. Yes, a bit hackish...
30776 this.feature = null;
30778 } else if(this.lastFeature && (previouslyIn || click)) {
30779 this.triggerCallback(type, 'out', [this.lastFeature]);
30785 * Method: triggerCallback
30786 * Call the callback keyed in the event map with the supplied arguments.
30787 * For click and clickout, the <clickTolerance> is checked first.
30792 triggerCallback: function(type, mode, args) {
30793 var key = this.EVENTMAP[type][mode];
30795 if(type == 'click' && this.up && this.down) {
30796 // for click/clickout, only trigger callback if tolerance is met
30797 var dpx = Math.sqrt(
30798 Math.pow(this.up.x - this.down.x, 2) +
30799 Math.pow(this.up.y - this.down.y, 2)
30801 if(dpx <= this.clickTolerance) {
30802 this.callback(key, args);
30804 // we're done with this set of events now: clear the cached
30805 // positions so we can't trip over them later (this can occur
30806 // if one of the up/down events gets eaten before it gets to us
30807 // but we still get the click)
30808 this.up = this.down = null;
30810 this.callback(key, args);
30817 * Turn on the handler. Returns false if the handler was already active.
30822 activate: function() {
30823 var activated = false;
30824 if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
30825 this.moveLayerToTop();
30826 this.map.events.on({
30827 "removelayer": this.handleMapEvents,
30828 "changelayer": this.handleMapEvents,
30837 * Method: deactivate
30838 * Turn off the handler. Returns false if the handler was already active.
30843 deactivate: function() {
30844 var deactivated = false;
30845 if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
30846 this.moveLayerBack();
30847 this.feature = null;
30848 this.lastFeature = null;
30851 this.map.events.un({
30852 "removelayer": this.handleMapEvents,
30853 "changelayer": this.handleMapEvents,
30856 deactivated = true;
30858 return deactivated;
30862 * Method: handleMapEvents
30867 handleMapEvents: function(evt) {
30868 if (evt.type == "removelayer" || evt.property == "order") {
30869 this.moveLayerToTop();
30874 * Method: moveLayerToTop
30875 * Moves the layer for this handler to the top, so mouse events can reach
30878 moveLayerToTop: function() {
30879 var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1,
30880 this.layer.getZIndex()) + 1;
30881 this.layer.setZIndex(index);
30886 * Method: moveLayerBack
30887 * Moves the layer back to the position determined by the map's layers
30890 moveLayerBack: function() {
30891 var index = this.layer.getZIndex() - 1;
30892 if (index >= this.map.Z_INDEX_BASE['Feature']) {
30893 this.layer.setZIndex(index);
30895 this.map.setLayerZIndex(this.layer,
30896 this.map.getLayerIndex(this.layer));
30900 CLASS_NAME: "OpenLayers.Handler.Feature"
30902 /* ======================================================================
30903 OpenLayers/StyleMap.js
30904 ====================================================================== */
30906 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
30907 * full list of contributors). Published under the 2-clause BSD license.
30908 * See license.txt in the OpenLayers distribution or repository for the
30909 * full text of the license. */
30912 * @requires OpenLayers/BaseTypes/Class.js
30913 * @requires OpenLayers/Style.js
30914 * @requires OpenLayers/Feature/Vector.js
30918 * Class: OpenLayers.StyleMap
30920 OpenLayers.StyleMap = OpenLayers.Class({
30924 * {Object} Hash of {<OpenLayers.Style>}, keyed by names of well known
30925 * rendering intents (e.g. "default", "temporary", "select", "delete").
30930 * Property: extendDefault
30931 * {Boolean} if true, every render intent will extend the symbolizers
30932 * specified for the "default" intent at rendering time. Otherwise, every
30933 * rendering intent will be treated as a completely independent style.
30935 extendDefault: true,
30938 * Constructor: OpenLayers.StyleMap
30941 * style - {Object} Optional. Either a style hash, or a style object, or
30942 * a hash of style objects (style hashes) keyed by rendering
30943 * intent. If just one style hash or style object is passed,
30944 * this will be used for all known render intents (default,
30945 * select, temporary)
30946 * options - {Object} optional hash of additional options for this
30949 initialize: function (style, options) {
30951 "default": new OpenLayers.Style(
30952 OpenLayers.Feature.Vector.style["default"]),
30953 "select": new OpenLayers.Style(
30954 OpenLayers.Feature.Vector.style["select"]),
30955 "temporary": new OpenLayers.Style(
30956 OpenLayers.Feature.Vector.style["temporary"]),
30957 "delete": new OpenLayers.Style(
30958 OpenLayers.Feature.Vector.style["delete"])
30961 // take whatever the user passed as style parameter and convert it
30962 // into parts of stylemap.
30963 if(style instanceof OpenLayers.Style) {
30964 // user passed a style object
30965 this.styles["default"] = style;
30966 this.styles["select"] = style;
30967 this.styles["temporary"] = style;
30968 this.styles["delete"] = style;
30969 } else if(typeof style == "object") {
30970 for(var key in style) {
30971 if(style[key] instanceof OpenLayers.Style) {
30972 // user passed a hash of style objects
30973 this.styles[key] = style[key];
30974 } else if(typeof style[key] == "object") {
30975 // user passsed a hash of style hashes
30976 this.styles[key] = new OpenLayers.Style(style[key]);
30978 // user passed a style hash (i.e. symbolizer)
30979 this.styles["default"] = new OpenLayers.Style(style);
30980 this.styles["select"] = new OpenLayers.Style(style);
30981 this.styles["temporary"] = new OpenLayers.Style(style);
30982 this.styles["delete"] = new OpenLayers.Style(style);
30987 OpenLayers.Util.extend(this, options);
30993 destroy: function() {
30994 for(var key in this.styles) {
30995 this.styles[key].destroy();
30997 this.styles = null;
31001 * Method: createSymbolizer
31002 * Creates the symbolizer for a feature for a render intent.
31005 * feature - {<OpenLayers.Feature>} The feature to evaluate the rules
31006 * of the intended style against.
31007 * intent - {String} The intent determines the symbolizer that will be
31008 * used to draw the feature. Well known intents are "default"
31009 * (for just drawing the features), "select" (for selected
31010 * features) and "temporary" (for drawing features).
31013 * {Object} symbolizer hash
31015 createSymbolizer: function(feature, intent) {
31017 feature = new OpenLayers.Feature.Vector();
31019 if(!this.styles[intent]) {
31020 intent = "default";
31022 feature.renderIntent = intent;
31023 var defaultSymbolizer = {};
31024 if(this.extendDefault && intent != "default") {
31025 defaultSymbolizer = this.styles["default"].createSymbolizer(feature);
31027 return OpenLayers.Util.extend(defaultSymbolizer,
31028 this.styles[intent].createSymbolizer(feature));
31032 * Method: addUniqueValueRules
31033 * Convenience method to create comparison rules for unique values of a
31034 * property. The rules will be added to the style object for a specified
31035 * rendering intent. This method is a shortcut for creating something like
31036 * the "unique value legends" familiar from well known desktop GIS systems
31039 * renderIntent - {String} rendering intent to add the rules to
31040 * property - {String} values of feature attributes to create the
31042 * symbolizers - {Object} Hash of symbolizers, keyed by the desired
31044 * context - {Object} An optional object with properties that
31045 * symbolizers' property values should be evaluated
31046 * against. If no context is specified, feature.attributes
31049 addUniqueValueRules: function(renderIntent, property, symbolizers, context) {
31051 for (var value in symbolizers) {
31052 rules.push(new OpenLayers.Rule({
31053 symbolizer: symbolizers[value],
31055 filter: new OpenLayers.Filter.Comparison({
31056 type: OpenLayers.Filter.Comparison.EQUAL_TO,
31057 property: property,
31062 this.styles[renderIntent].addRules(rules);
31065 CLASS_NAME: "OpenLayers.StyleMap"
31067 /* ======================================================================
31068 OpenLayers/Layer/Vector.js
31069 ====================================================================== */
31071 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
31072 * full list of contributors). Published under the 2-clause BSD license.
31073 * See license.txt in the OpenLayers distribution or repository for the
31074 * full text of the license. */
31077 * @requires OpenLayers/Layer.js
31078 * @requires OpenLayers/Renderer.js
31079 * @requires OpenLayers/StyleMap.js
31080 * @requires OpenLayers/Feature/Vector.js
31081 * @requires OpenLayers/Console.js
31082 * @requires OpenLayers/Lang.js
31086 * Class: OpenLayers.Layer.Vector
31087 * Instances of OpenLayers.Layer.Vector are used to render vector data from
31088 * a variety of sources. Create a new vector layer with the
31089 * <OpenLayers.Layer.Vector> constructor.
31092 * - <OpenLayers.Layer>
31094 OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
31097 * APIProperty: events
31098 * {<OpenLayers.Events>}
31100 * Register a listener for a particular event with the following syntax:
31102 * layer.events.register(type, obj, listener);
31105 * Listeners will be called with a reference to an event object. The
31106 * properties of this event depends on exactly what happened.
31108 * All event objects have at least the following properties:
31109 * object - {Object} A reference to layer.events.object.
31110 * element - {DOMElement} A reference to layer.events.element.
31112 * Supported map event types (in addition to those from <OpenLayers.Layer.events>):
31113 * beforefeatureadded - Triggered before a feature is added. Listeners
31114 * will receive an object with a *feature* property referencing the
31115 * feature to be added. To stop the feature from being added, a
31116 * listener should return false.
31117 * beforefeaturesadded - Triggered before an array of features is added.
31118 * Listeners will receive an object with a *features* property
31119 * referencing the feature to be added. To stop the features from
31120 * being added, a listener should return false.
31121 * featureadded - Triggered after a feature is added. The event
31122 * object passed to listeners will have a *feature* property with a
31123 * reference to the added feature.
31124 * featuresadded - Triggered after features are added. The event
31125 * object passed to listeners will have a *features* property with a
31126 * reference to an array of added features.
31127 * beforefeatureremoved - Triggered before a feature is removed. Listeners
31128 * will receive an object with a *feature* property referencing the
31129 * feature to be removed.
31130 * beforefeaturesremoved - Triggered before multiple features are removed.
31131 * Listeners will receive an object with a *features* property
31132 * referencing the features to be removed.
31133 * featureremoved - Triggerd after a feature is removed. The event
31134 * object passed to listeners will have a *feature* property with a
31135 * reference to the removed feature.
31136 * featuresremoved - Triggered after features are removed. The event
31137 * object passed to listeners will have a *features* property with a
31138 * reference to an array of removed features.
31139 * beforefeatureselected - Triggered before a feature is selected. Listeners
31140 * will receive an object with a *feature* property referencing the
31141 * feature to be selected. To stop the feature from being selectd, a
31142 * listener should return false.
31143 * featureselected - Triggered after a feature is selected. Listeners
31144 * will receive an object with a *feature* property referencing the
31145 * selected feature.
31146 * featureunselected - Triggered after a feature is unselected.
31147 * Listeners will receive an object with a *feature* property
31148 * referencing the unselected feature.
31149 * beforefeaturemodified - Triggered when a feature is selected to
31150 * be modified. Listeners will receive an object with a *feature*
31151 * property referencing the selected feature.
31152 * featuremodified - Triggered when a feature has been modified.
31153 * Listeners will receive an object with a *feature* property referencing
31154 * the modified feature.
31155 * afterfeaturemodified - Triggered when a feature is finished being modified.
31156 * Listeners will receive an object with a *feature* property referencing
31157 * the modified feature.
31158 * vertexmodified - Triggered when a vertex within any feature geometry
31159 * has been modified. Listeners will receive an object with a
31160 * *feature* property referencing the modified feature, a *vertex*
31161 * property referencing the vertex modified (always a point geometry),
31162 * and a *pixel* property referencing the pixel location of the
31164 * vertexremoved - Triggered when a vertex within any feature geometry
31165 * has been deleted. Listeners will receive an object with a
31166 * *feature* property referencing the modified feature, a *vertex*
31167 * property referencing the vertex modified (always a point geometry),
31168 * and a *pixel* property referencing the pixel location of the
31170 * sketchstarted - Triggered when a feature sketch bound for this layer
31171 * is started. Listeners will receive an object with a *feature*
31172 * property referencing the new sketch feature and a *vertex* property
31173 * referencing the creation point.
31174 * sketchmodified - Triggered when a feature sketch bound for this layer
31175 * is modified. Listeners will receive an object with a *vertex*
31176 * property referencing the modified vertex and a *feature* property
31177 * referencing the sketch feature.
31178 * sketchcomplete - Triggered when a feature sketch bound for this layer
31179 * is complete. Listeners will receive an object with a *feature*
31180 * property referencing the sketch feature. By returning false, a
31181 * listener can stop the sketch feature from being added to the layer.
31182 * refresh - Triggered when something wants a strategy to ask the protocol
31183 * for a new set of features.
31187 * APIProperty: isBaseLayer
31188 * {Boolean} The layer is a base layer. Default is false. Set this property
31189 * in the layer options.
31191 isBaseLayer: false,
31194 * APIProperty: isFixed
31195 * {Boolean} Whether the layer remains in one place while dragging the
31196 * map. Note that setting this to true will move the layer to the bottom
31197 * of the layer stack.
31202 * APIProperty: features
31203 * {Array(<OpenLayers.Feature.Vector>)}
31209 * {<OpenLayers.Filter>} The filter set in this layer,
31210 * a strategy launching read requests can combined
31211 * this filter with its own filter.
31216 * Property: selectedFeatures
31217 * {Array(<OpenLayers.Feature.Vector>)}
31219 selectedFeatures: null,
31222 * Property: unrenderedFeatures
31223 * {Object} hash of features, keyed by feature.id, that the renderer
31226 unrenderedFeatures: null,
31229 * APIProperty: reportError
31230 * {Boolean} report friendly error message when loading of renderer
31236 * APIProperty: style
31237 * {Object} Default style for the layer
31242 * Property: styleMap
31243 * {<OpenLayers.StyleMap>}
31248 * Property: strategies
31249 * {Array(<OpenLayers.Strategy>})} Optional list of strategies for the layer.
31254 * Property: protocol
31255 * {<OpenLayers.Protocol>} Optional protocol for the layer.
31260 * Property: renderers
31261 * {Array(String)} List of supported Renderer classes. Add to this list to
31262 * add support for additional renderers. This list is ordered:
31263 * the first renderer which returns true for the 'supported()'
31264 * method will be used, if not defined in the 'renderer' option.
31266 renderers: ['SVG', 'VML', 'Canvas'],
31269 * Property: renderer
31270 * {<OpenLayers.Renderer>}
31275 * APIProperty: rendererOptions
31276 * {Object} Options for the renderer. See {<OpenLayers.Renderer>} for
31277 * supported options.
31279 rendererOptions: null,
31282 * APIProperty: geometryType
31283 * {String} geometryType allows you to limit the types of geometries this
31284 * layer supports. This should be set to something like
31285 * "OpenLayers.Geometry.Point" to limit types.
31287 geometryType: null,
31291 * {Boolean} Whether the Vector Layer features have been drawn yet.
31296 * APIProperty: ratio
31297 * {Float} This specifies the ratio of the size of the visiblity of the Vector Layer features to the size of the map.
31302 * Constructor: OpenLayers.Layer.Vector
31303 * Create a new vector layer
31306 * name - {String} A name for the layer
31307 * options - {Object} Optional object with non-default properties to set on
31311 * {<OpenLayers.Layer.Vector>} A new vector layer
31313 initialize: function(name, options) {
31314 OpenLayers.Layer.prototype.initialize.apply(this, arguments);
31316 // allow user-set renderer, otherwise assign one
31317 if (!this.renderer || !this.renderer.supported()) {
31318 this.assignRenderer();
31321 // if no valid renderer found, display error
31322 if (!this.renderer || !this.renderer.supported()) {
31323 this.renderer = null;
31324 this.displayError();
31327 if (!this.styleMap) {
31328 this.styleMap = new OpenLayers.StyleMap();
31331 this.features = [];
31332 this.selectedFeatures = [];
31333 this.unrenderedFeatures = {};
31335 // Allow for custom layer behavior
31336 if(this.strategies){
31337 for(var i=0, len=this.strategies.length; i<len; i++) {
31338 this.strategies[i].setLayer(this);
31345 * APIMethod: destroy
31346 * Destroy this layer
31348 destroy: function() {
31349 if (this.strategies) {
31350 var strategy, i, len;
31351 for(i=0, len=this.strategies.length; i<len; i++) {
31352 strategy = this.strategies[i];
31353 if(strategy.autoDestroy) {
31354 strategy.destroy();
31357 this.strategies = null;
31359 if (this.protocol) {
31360 if(this.protocol.autoDestroy) {
31361 this.protocol.destroy();
31363 this.protocol = null;
31365 this.destroyFeatures();
31366 this.features = null;
31367 this.selectedFeatures = null;
31368 this.unrenderedFeatures = null;
31369 if (this.renderer) {
31370 this.renderer.destroy();
31372 this.renderer = null;
31373 this.geometryType = null;
31375 OpenLayers.Layer.prototype.destroy.apply(this, arguments);
31380 * Create a clone of this layer.
31382 * Note: Features of the layer are also cloned.
31385 * {<OpenLayers.Layer.Vector>} An exact clone of this layer
31387 clone: function (obj) {
31390 obj = new OpenLayers.Layer.Vector(this.name, this.getOptions());
31393 //get all additions from superclasses
31394 obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
31396 // copy/set any non-init, non-simple values here
31397 var features = this.features;
31398 var len = features.length;
31399 var clonedFeatures = new Array(len);
31400 for(var i=0; i<len; ++i) {
31401 clonedFeatures[i] = features[i].clone();
31403 obj.features = clonedFeatures;
31410 * Ask the layer to request features again and redraw them. Triggers
31411 * the refresh event if the layer is in range and visible.
31414 * obj - {Object} Optional object with properties for any listener of
31415 * the refresh event.
31417 refresh: function(obj) {
31418 if(this.calculateInRange() && this.visibility) {
31419 this.events.triggerEvent("refresh", obj);
31424 * Method: assignRenderer
31425 * Iterates through the available renderer implementations and selects
31426 * and assigns the first one whose "supported()" function returns true.
31428 assignRenderer: function() {
31429 for (var i=0, len=this.renderers.length; i<len; i++) {
31430 var rendererClass = this.renderers[i];
31431 var renderer = (typeof rendererClass == "function") ?
31433 OpenLayers.Renderer[rendererClass];
31434 if (renderer && renderer.prototype.supported()) {
31435 this.renderer = new renderer(this.div, this.rendererOptions);
31442 * Method: displayError
31443 * Let the user know their browser isn't supported.
31445 displayError: function() {
31446 if (this.reportError) {
31447 OpenLayers.Console.userError(OpenLayers.i18n("browserNotSupported",
31448 {renderers: this. renderers.join('\n')}));
31454 * The layer has been added to the map.
31456 * If there is no renderer set, the layer can't be used. Remove it.
31457 * Otherwise, give the renderer a reference to the map and set its size.
31460 * map - {<OpenLayers.Map>}
31462 setMap: function(map) {
31463 OpenLayers.Layer.prototype.setMap.apply(this, arguments);
31465 if (!this.renderer) {
31466 this.map.removeLayer(this);
31468 this.renderer.map = this.map;
31470 var newSize = this.map.getSize();
31471 newSize.w = newSize.w * this.ratio;
31472 newSize.h = newSize.h * this.ratio;
31473 this.renderer.setSize(newSize);
31479 * Called at the end of the map.addLayer sequence. At this point, the map
31480 * will have a base layer. Any autoActivate strategies will be
31483 afterAdd: function() {
31484 if(this.strategies) {
31485 var strategy, i, len;
31486 for(i=0, len=this.strategies.length; i<len; i++) {
31487 strategy = this.strategies[i];
31488 if(strategy.autoActivate) {
31489 strategy.activate();
31496 * Method: removeMap
31497 * The layer has been removed from the map.
31500 * map - {<OpenLayers.Map>}
31502 removeMap: function(map) {
31503 this.drawn = false;
31504 if(this.strategies) {
31505 var strategy, i, len;
31506 for(i=0, len=this.strategies.length; i<len; i++) {
31507 strategy = this.strategies[i];
31508 if(strategy.autoActivate) {
31509 strategy.deactivate();
31516 * Method: onMapResize
31517 * Notify the renderer of the change in size.
31520 onMapResize: function() {
31521 OpenLayers.Layer.prototype.onMapResize.apply(this, arguments);
31523 var newSize = this.map.getSize();
31524 newSize.w = newSize.w * this.ratio;
31525 newSize.h = newSize.h * this.ratio;
31526 this.renderer.setSize(newSize);
31531 * Reset the vector layer's div so that it once again is lined up with
31532 * the map. Notify the renderer of the change of extent, and in the
31533 * case of a change of zoom level (resolution), have the
31534 * renderer redraw features.
31536 * If the layer has not yet been drawn, cycle through the layer's
31537 * features and draw each one.
31540 * bounds - {<OpenLayers.Bounds>}
31541 * zoomChanged - {Boolean}
31542 * dragging - {Boolean}
31544 moveTo: function(bounds, zoomChanged, dragging) {
31545 OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
31547 var coordSysUnchanged = true;
31549 this.renderer.root.style.visibility = 'hidden';
31551 var viewSize = this.map.getSize(),
31552 viewWidth = viewSize.w,
31553 viewHeight = viewSize.h,
31554 offsetLeft = (viewWidth / 2 * this.ratio) - viewWidth / 2,
31555 offsetTop = (viewHeight / 2 * this.ratio) - viewHeight / 2;
31556 offsetLeft += this.map.layerContainerOriginPx.x;
31557 offsetLeft = -Math.round(offsetLeft);
31558 offsetTop += this.map.layerContainerOriginPx.y;
31559 offsetTop = -Math.round(offsetTop);
31561 this.div.style.left = offsetLeft + 'px';
31562 this.div.style.top = offsetTop + 'px';
31564 var extent = this.map.getExtent().scale(this.ratio);
31565 coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged);
31567 this.renderer.root.style.visibility = 'visible';
31569 // Force a reflow on gecko based browsers to prevent jump/flicker.
31570 // This seems to happen on only certain configurations; it was originally
31571 // noticed in FF 2.0 and Linux.
31572 if (OpenLayers.IS_GECKO === true) {
31573 this.div.scrollLeft = this.div.scrollLeft;
31576 if (!zoomChanged && coordSysUnchanged) {
31577 for (var i in this.unrenderedFeatures) {
31578 var feature = this.unrenderedFeatures[i];
31579 this.drawFeature(feature);
31583 if (!this.drawn || zoomChanged || !coordSysUnchanged) {
31586 for(var i=0, len=this.features.length; i<len; i++) {
31587 this.renderer.locked = (i !== (len - 1));
31588 feature = this.features[i];
31589 this.drawFeature(feature);
31595 * APIMethod: display
31596 * Hide or show the Layer
31599 * display - {Boolean}
31601 display: function(display) {
31602 OpenLayers.Layer.prototype.display.apply(this, arguments);
31603 // we need to set the display style of the root in case it is attached
31604 // to a foreign layer
31605 var currentDisplay = this.div.style.display;
31606 if(currentDisplay != this.renderer.root.style.display) {
31607 this.renderer.root.style.display = currentDisplay;
31612 * APIMethod: addFeatures
31613 * Add Features to the layer.
31616 * features - {Array(<OpenLayers.Feature.Vector>)}
31617 * options - {Object}
31619 addFeatures: function(features, options) {
31620 if (!(OpenLayers.Util.isArray(features))) {
31621 features = [features];
31624 var notify = !options || !options.silent;
31626 var event = {features: features};
31627 var ret = this.events.triggerEvent("beforefeaturesadded", event);
31628 if(ret === false) {
31631 features = event.features;
31634 // Track successfully added features for featuresadded event, since
31635 // beforefeatureadded can veto single features.
31636 var featuresAdded = [];
31637 for (var i=0, len=features.length; i<len; i++) {
31638 if (i != (features.length - 1)) {
31639 this.renderer.locked = true;
31641 this.renderer.locked = false;
31643 var feature = features[i];
31645 if (this.geometryType &&
31646 !(feature.geometry instanceof this.geometryType)) {
31647 throw new TypeError('addFeatures: component should be an ' +
31648 this.geometryType.prototype.CLASS_NAME);
31651 //give feature reference to its layer
31652 feature.layer = this;
31654 if (!feature.style && this.style) {
31655 feature.style = OpenLayers.Util.extend({}, this.style);
31659 if(this.events.triggerEvent("beforefeatureadded",
31660 {feature: feature}) === false) {
31663 this.preFeatureInsert(feature);
31666 featuresAdded.push(feature);
31667 this.features.push(feature);
31668 this.drawFeature(feature);
31671 this.events.triggerEvent("featureadded", {
31674 this.onFeatureInsert(feature);
31679 this.events.triggerEvent("featuresadded", {features: featuresAdded});
31685 * APIMethod: removeFeatures
31686 * Remove features from the layer. This erases any drawn features and
31687 * removes them from the layer's control. The beforefeatureremoved
31688 * and featureremoved events will be triggered for each feature. The
31689 * featuresremoved event will be triggered after all features have
31690 * been removed. To supress event triggering, use the silent option.
31693 * features - {Array(<OpenLayers.Feature.Vector>)} List of features to be
31695 * options - {Object} Optional properties for changing behavior of the
31699 * silent - {Boolean} Supress event triggering. Default is false.
31701 removeFeatures: function(features, options) {
31702 if(!features || features.length === 0) {
31705 if (features === this.features) {
31706 return this.removeAllFeatures(options);
31708 if (!(OpenLayers.Util.isArray(features))) {
31709 features = [features];
31711 if (features === this.selectedFeatures) {
31712 features = features.slice();
31715 var notify = !options || !options.silent;
31718 this.events.triggerEvent(
31719 "beforefeaturesremoved", {features: features}
31723 for (var i = features.length - 1; i >= 0; i--) {
31724 // We remain locked so long as we're not at 0
31725 // and the 'next' feature has a geometry. We do the geometry check
31726 // because if all the features after the current one are 'null', we
31727 // won't call eraseGeometry, so we break the 'renderer functions
31728 // will always be called with locked=false *last*' rule. The end result
31729 // is a possible gratiutious unlocking to save a loop through the rest
31730 // of the list checking the remaining features every time. So long as
31731 // null geoms are rare, this is probably okay.
31732 if (i != 0 && features[i-1].geometry) {
31733 this.renderer.locked = true;
31735 this.renderer.locked = false;
31738 var feature = features[i];
31739 delete this.unrenderedFeatures[feature.id];
31742 this.events.triggerEvent("beforefeatureremoved", {
31747 this.features = OpenLayers.Util.removeItem(this.features, feature);
31748 // feature has no layer at this point
31749 feature.layer = null;
31751 if (feature.geometry) {
31752 this.renderer.eraseFeatures(feature);
31755 //in the case that this feature is one of the selected features,
31756 // remove it from that array as well.
31757 if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1){
31758 OpenLayers.Util.removeItem(this.selectedFeatures, feature);
31762 this.events.triggerEvent("featureremoved", {
31769 this.events.triggerEvent("featuresremoved", {features: features});
31774 * APIMethod: removeAllFeatures
31775 * Remove all features from the layer.
31778 * options - {Object} Optional properties for changing behavior of the
31782 * silent - {Boolean} Supress event triggering. Default is false.
31784 removeAllFeatures: function(options) {
31785 var notify = !options || !options.silent;
31786 var features = this.features;
31788 this.events.triggerEvent(
31789 "beforefeaturesremoved", {features: features}
31793 for (var i = features.length-1; i >= 0; i--) {
31794 feature = features[i];
31796 this.events.triggerEvent("beforefeatureremoved", {
31800 feature.layer = null;
31802 this.events.triggerEvent("featureremoved", {
31807 this.renderer.clear();
31808 this.features = [];
31809 this.unrenderedFeatures = {};
31810 this.selectedFeatures = [];
31812 this.events.triggerEvent("featuresremoved", {features: features});
31817 * APIMethod: destroyFeatures
31818 * Erase and destroy features on the layer.
31821 * features - {Array(<OpenLayers.Feature.Vector>)} An optional array of
31822 * features to destroy. If not supplied, all features on the layer
31823 * will be destroyed.
31824 * options - {Object}
31826 destroyFeatures: function(features, options) {
31827 var all = (features == undefined); // evaluates to true if
31828 // features is null
31830 features = this.features;
31833 this.removeFeatures(features, options);
31834 for(var i=features.length-1; i>=0; i--) {
31835 features[i].destroy();
31841 * APIMethod: drawFeature
31842 * Draw (or redraw) a feature on the layer. If the optional style argument
31843 * is included, this style will be used. If no style is included, the
31844 * feature's style will be used. If the feature doesn't have a style,
31845 * the layer's style will be used.
31847 * This function is not designed to be used when adding features to
31848 * the layer (use addFeatures instead). It is meant to be used when
31849 * the style of a feature has changed, or in some other way needs to
31850 * visually updated *after* it has already been added to a layer. You
31851 * must add the feature to the layer for most layer-related events to
31855 * feature - {<OpenLayers.Feature.Vector>}
31856 * style - {String | Object} Named render intent or full symbolizer object.
31858 drawFeature: function(feature, style) {
31859 // don't try to draw the feature with the renderer if the layer is not
31864 if (typeof style != "object") {
31865 if(!style && feature.state === OpenLayers.State.DELETE) {
31868 var renderIntent = style || feature.renderIntent;
31869 style = feature.style || this.style;
31871 style = this.styleMap.createSymbolizer(feature, renderIntent);
31875 var drawn = this.renderer.drawFeature(feature, style);
31876 //TODO remove the check for null when we get rid of Renderer.SVG
31877 if (drawn === false || drawn === null) {
31878 this.unrenderedFeatures[feature.id] = feature;
31880 delete this.unrenderedFeatures[feature.id];
31885 * Method: eraseFeatures
31886 * Erase features from the layer.
31889 * features - {Array(<OpenLayers.Feature.Vector>)}
31891 eraseFeatures: function(features) {
31892 this.renderer.eraseFeatures(features);
31896 * Method: getFeatureFromEvent
31897 * Given an event, return a feature if the event occurred over one.
31898 * Otherwise, return null.
31904 * {<OpenLayers.Feature.Vector>} A feature if one was under the event.
31906 getFeatureFromEvent: function(evt) {
31907 if (!this.renderer) {
31908 throw new Error('getFeatureFromEvent called on layer with no ' +
31909 'renderer. This usually means you destroyed a ' +
31910 'layer, but not some handler which is associated ' +
31913 var feature = null;
31914 var featureId = this.renderer.getFeatureIdFromEvent(evt);
31916 if (typeof featureId === "string") {
31917 feature = this.getFeatureById(featureId);
31919 feature = featureId;
31926 * APIMethod: getFeatureBy
31927 * Given a property value, return the feature if it exists in the features array
31930 * property - {String}
31934 * {<OpenLayers.Feature.Vector>} A feature corresponding to the given
31935 * property value or null if there is no such feature.
31937 getFeatureBy: function(property, value) {
31938 //TBD - would it be more efficient to use a hash for this.features?
31939 var feature = null;
31940 for(var i=0, len=this.features.length; i<len; ++i) {
31941 if(this.features[i][property] == value) {
31942 feature = this.features[i];
31950 * APIMethod: getFeatureById
31951 * Given a feature id, return the feature if it exists in the features array
31954 * featureId - {String}
31957 * {<OpenLayers.Feature.Vector>} A feature corresponding to the given
31958 * featureId or null if there is no such feature.
31960 getFeatureById: function(featureId) {
31961 return this.getFeatureBy('id', featureId);
31965 * APIMethod: getFeatureByFid
31966 * Given a feature fid, return the feature if it exists in the features array
31969 * featureFid - {String}
31972 * {<OpenLayers.Feature.Vector>} A feature corresponding to the given
31973 * featureFid or null if there is no such feature.
31975 getFeatureByFid: function(featureFid) {
31976 return this.getFeatureBy('fid', featureFid);
31980 * APIMethod: getFeaturesByAttribute
31981 * Returns an array of features that have the given attribute key set to the
31982 * given value. Comparison of attribute values takes care of datatypes, e.g.
31983 * the string '1234' is not equal to the number 1234.
31986 * attrName - {String}
31987 * attrValue - {Mixed}
31990 * Array({<OpenLayers.Feature.Vector>}) An array of features that have the
31991 * passed named attribute set to the given value.
31993 getFeaturesByAttribute: function(attrName, attrValue) {
31996 len = this.features.length,
31997 foundFeatures = [];
31998 for(i = 0; i < len; i++) {
31999 feature = this.features[i];
32000 if(feature && feature.attributes) {
32001 if (feature.attributes[attrName] === attrValue) {
32002 foundFeatures.push(feature);
32006 return foundFeatures;
32010 * Unselect the selected features
32011 * i.e. clears the featureSelection array
32012 * change the style back
32013 clearSelection: function() {
32015 var vectorLayer = this.map.vectorLayer;
32016 for (var i = 0; i < this.map.featureSelection.length; i++) {
32017 var featureSelection = this.map.featureSelection[i];
32018 vectorLayer.drawFeature(featureSelection, vectorLayer.style);
32020 this.map.featureSelection = [];
32026 * APIMethod: onFeatureInsert
32027 * method called after a feature is inserted.
32028 * Does nothing by default. Override this if you
32029 * need to do something on feature updates.
32032 * feature - {<OpenLayers.Feature.Vector>}
32034 onFeatureInsert: function(feature) {
32038 * APIMethod: preFeatureInsert
32039 * method called before a feature is inserted.
32040 * Does nothing by default. Override this if you
32041 * need to do something when features are first added to the
32042 * layer, but before they are drawn, such as adjust the style.
32045 * feature - {<OpenLayers.Feature.Vector>}
32047 preFeatureInsert: function(feature) {
32051 * APIMethod: getDataExtent
32052 * Calculates the max extent which includes all of the features.
32055 * {<OpenLayers.Bounds>} or null if the layer has no features with
32058 getDataExtent: function () {
32059 var maxExtent = null;
32060 var features = this.features;
32061 if(features && (features.length > 0)) {
32062 var geometry = null;
32063 for(var i=0, len=features.length; i<len; i++) {
32064 geometry = features[i].geometry;
32066 if (maxExtent === null) {
32067 maxExtent = new OpenLayers.Bounds();
32069 maxExtent.extend(geometry.getBounds());
32076 CLASS_NAME: "OpenLayers.Layer.Vector"
32078 /* ======================================================================
32079 OpenLayers/Layer/Vector/RootContainer.js
32080 ====================================================================== */
32082 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
32083 * full list of contributors). Published under the 2-clause BSD license.
32084 * See license.txt in the OpenLayers distribution or repository for the
32085 * full text of the license. */
32088 * @requires OpenLayers/Layer/Vector.js
32092 * Class: OpenLayers.Layer.Vector.RootContainer
32093 * A special layer type to combine multiple vector layers inside a single
32094 * renderer root container. This class is not supposed to be instantiated
32095 * from user space, it is a helper class for controls that require event
32096 * processing for multiple vector layers.
32099 * - <OpenLayers.Layer.Vector>
32101 OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, {
32104 * Property: displayInLayerSwitcher
32105 * Set to false for this layer type
32107 displayInLayerSwitcher: false,
32110 * APIProperty: layers
32111 * Layers that are attached to this container. Required config option.
32116 * Constructor: OpenLayers.Layer.Vector.RootContainer
32117 * Create a new root container for multiple vector layer. This constructor
32118 * is not supposed to be used from user space, it is only to be used by
32119 * controls that need feature selection across multiple vector layers.
32122 * name - {String} A name for the layer
32123 * options - {Object} Optional object with non-default properties to set on
32126 * Required options properties:
32127 * layers - {Array(<OpenLayers.Layer.Vector>)} The layers managed by this
32131 * {<OpenLayers.Layer.Vector.RootContainer>} A new vector layer root
32138 display: function() {},
32141 * Method: getFeatureFromEvent
32142 * walk through the layers to find the feature returned by the event
32145 * evt - {Object} event object with a feature property
32148 * {<OpenLayers.Feature.Vector>}
32150 getFeatureFromEvent: function(evt) {
32151 var layers = this.layers;
32153 for(var i=0; i<layers.length; i++) {
32154 feature = layers[i].getFeatureFromEvent(evt);
32165 * map - {<OpenLayers.Map>}
32167 setMap: function(map) {
32168 OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);
32169 this.collectRoots();
32170 map.events.register("changelayer", this, this.handleChangeLayer);
32174 * Method: removeMap
32177 * map - {<OpenLayers.Map>}
32179 removeMap: function(map) {
32180 map.events.unregister("changelayer", this, this.handleChangeLayer);
32182 OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments);
32186 * Method: collectRoots
32187 * Collects the root nodes of all layers this control is configured with
32188 * and moveswien the nodes to this control's layer
32190 collectRoots: function() {
32192 // walk through all map layers, because we want to keep the order
32193 for(var i=0; i<this.map.layers.length; ++i) {
32194 layer = this.map.layers[i];
32195 if(OpenLayers.Util.indexOf(this.layers, layer) != -1) {
32196 layer.renderer.moveRoot(this.renderer);
32202 * Method: resetRoots
32203 * Resets the root nodes back into the layers they belong to.
32205 resetRoots: function() {
32207 for(var i=0; i<this.layers.length; ++i) {
32208 layer = this.layers[i];
32209 if(this.renderer && layer.renderer.getRenderLayerId() == this.id) {
32210 this.renderer.moveRoot(layer.renderer);
32216 * Method: handleChangeLayer
32217 * Event handler for the map's changelayer event. We need to rebuild
32218 * this container's layer dom if order of one of its layers changes.
32219 * This handler is added with the setMap method, and removed with the
32220 * removeMap method.
32225 handleChangeLayer: function(evt) {
32226 var layer = evt.layer;
32227 if(evt.property == "order" &&
32228 OpenLayers.Util.indexOf(this.layers, layer) != -1) {
32230 this.collectRoots();
32234 CLASS_NAME: "OpenLayers.Layer.Vector.RootContainer"
32236 /* ======================================================================
32237 OpenLayers/Control/SelectFeature.js
32238 ====================================================================== */
32240 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
32241 * full list of contributors). Published under the 2-clause BSD license.
32242 * See license.txt in the OpenLayers distribution or repository for the
32243 * full text of the license. */
32247 * @requires OpenLayers/Control.js
32248 * @requires OpenLayers/Feature/Vector.js
32249 * @requires OpenLayers/Handler/Feature.js
32250 * @requires OpenLayers/Layer/Vector/RootContainer.js
32254 * Class: OpenLayers.Control.SelectFeature
32255 * The SelectFeature control selects vector features from a given layer on
32259 * - <OpenLayers.Control>
32261 OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {
32264 * APIProperty: events
32265 * {<OpenLayers.Events>} Events instance for listeners and triggering
32266 * control specific events.
32268 * Register a listener for a particular event with the following syntax:
32270 * control.events.register(type, obj, listener);
32273 * Supported event types (in addition to those from <OpenLayers.Control.events>):
32274 * beforefeaturehighlighted - Triggered before a feature is highlighted
32275 * featurehighlighted - Triggered when a feature is highlighted
32276 * featureunhighlighted - Triggered when a feature is unhighlighted
32277 * boxselectionstart - Triggered before box selection starts
32278 * boxselectionend - Triggered after box selection ends
32282 * Property: multipleKey
32283 * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets
32284 * the <multiple> property to true. Default is null.
32289 * Property: toggleKey
32290 * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets
32291 * the <toggle> property to true. Default is null.
32296 * APIProperty: multiple
32297 * {Boolean} Allow selection of multiple geometries. Default is false.
32302 * APIProperty: clickout
32303 * {Boolean} Unselect features when clicking outside any feature.
32309 * APIProperty: toggle
32310 * {Boolean} Unselect a selected feature on click. Default is false. Only
32311 * has meaning if hover is false.
32316 * APIProperty: hover
32317 * {Boolean} Select on mouse over and deselect on mouse out. If true, this
32318 * ignores clicks and only listens to mouse moves.
32323 * APIProperty: highlightOnly
32324 * {Boolean} If true do not actually select features (that is place them in
32325 * the layer's selected features array), just highlight them. This property
32326 * has no effect if hover is false. Defaults to false.
32328 highlightOnly: false,
32332 * {Boolean} Allow feature selection by drawing a box.
32337 * Property: onBeforeSelect
32338 * {Function} Optional function to be called before a feature is selected.
32339 * The function should expect to be called with a feature.
32341 onBeforeSelect: function() {},
32344 * APIProperty: onSelect
32345 * {Function} Optional function to be called when a feature is selected.
32346 * The function should expect to be called with a feature.
32348 onSelect: function() {},
32351 * APIProperty: onUnselect
32352 * {Function} Optional function to be called when a feature is unselected.
32353 * The function should expect to be called with a feature.
32355 onUnselect: function() {},
32359 * {Object} The scope to use with the onBeforeSelect, onSelect, onUnselect
32360 * callbacks. If null the scope will be this control.
32365 * APIProperty: geometryTypes
32366 * {Array(String)} To restrict selecting to a limited set of geometry types,
32367 * send a list of strings corresponding to the geometry class names.
32369 geometryTypes: null,
32373 * {<OpenLayers.Layer.Vector>} The vector layer with a common renderer
32374 * root for all layers this control is configured with (if an array of
32375 * layers was passed to the constructor), or the vector layer the control
32376 * was configured with (if a single layer was passed to the constructor).
32382 * {Array(<OpenLayers.Layer.Vector>)} The layers this control will work on,
32383 * or null if the control was configured with a single layer
32388 * APIProperty: callbacks
32389 * {Object} The functions that are sent to the handlers.feature for callback
32394 * APIProperty: selectStyle
32395 * {Object} Hash of styles
32400 * Property: renderIntent
32401 * {String} key used to retrieve the select style from the layer's
32404 renderIntent: "select",
32407 * Property: handlers
32408 * {Object} Object with references to multiple <OpenLayers.Handler>
32414 * Constructor: OpenLayers.Control.SelectFeature
32415 * Create a new control for selecting features.
32418 * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers. The
32419 * layer(s) this control will select features from.
32420 * options - {Object}
32422 initialize: function(layers, options) {
32423 OpenLayers.Control.prototype.initialize.apply(this, [options]);
32425 if(this.scope === null) {
32428 this.initLayer(layers);
32430 click: this.clickFeature,
32431 clickout: this.clickoutFeature
32434 callbacks.over = this.overFeature;
32435 callbacks.out = this.outFeature;
32438 this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);
32440 feature: new OpenLayers.Handler.Feature(
32441 this, this.layer, this.callbacks,
32442 {geometryTypes: this.geometryTypes}
32447 this.handlers.box = new OpenLayers.Handler.Box(
32448 this, {done: this.selectBox},
32449 {boxDivClassName: "olHandlerBoxSelectFeature"}
32455 * Method: initLayer
32456 * Assign the layer property. If layers is an array, we need to use
32460 * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers.
32462 initLayer: function(layers) {
32463 if(OpenLayers.Util.isArray(layers)) {
32464 this.layers = layers;
32465 this.layer = new OpenLayers.Layer.Vector.RootContainer(
32466 this.id + "_container", {
32471 this.layer = layers;
32478 destroy: function() {
32479 if(this.active && this.layers) {
32480 this.map.removeLayer(this.layer);
32482 OpenLayers.Control.prototype.destroy.apply(this, arguments);
32484 this.layer.destroy();
32490 * Activates the control.
32493 * {Boolean} The control was effectively activated.
32495 activate: function () {
32496 if (!this.active) {
32498 this.map.addLayer(this.layer);
32500 this.handlers.feature.activate();
32501 if(this.box && this.handlers.box) {
32502 this.handlers.box.activate();
32505 return OpenLayers.Control.prototype.activate.apply(
32511 * Method: deactivate
32512 * Deactivates the control.
32515 * {Boolean} The control was effectively deactivated.
32517 deactivate: function () {
32519 this.handlers.feature.deactivate();
32520 if(this.handlers.box) {
32521 this.handlers.box.deactivate();
32524 this.map.removeLayer(this.layer);
32527 return OpenLayers.Control.prototype.deactivate.apply(
32533 * Method: unselectAll
32534 * Unselect all selected features. To unselect all except for a single
32535 * feature, set the options.except property to the feature.
32538 * options - {Object} Optional configuration object.
32540 unselectAll: function(options) {
32541 // we'll want an option to supress notification here
32542 var layers = this.layers || [this.layer],
32543 layer, feature, l, numExcept;
32544 for(l=0; l<layers.length; ++l) {
32547 //layer.selectedFeatures is null when layer is destroyed and
32548 //one of it's preremovelayer listener calls setLayer
32549 //with another layer on this control
32550 if(layer.selectedFeatures != null) {
32551 while(layer.selectedFeatures.length > numExcept) {
32552 feature = layer.selectedFeatures[numExcept];
32553 if(!options || options.except != feature) {
32554 this.unselect(feature);
32564 * Method: clickFeature
32565 * Called on click in a feature
32566 * Only responds if this.hover is false.
32569 * feature - {<OpenLayers.Feature.Vector>}
32571 clickFeature: function(feature) {
32573 var selected = (OpenLayers.Util.indexOf(
32574 feature.layer.selectedFeatures, feature) > -1);
32576 if(this.toggleSelect()) {
32577 this.unselect(feature);
32578 } else if(!this.multipleSelect()) {
32579 this.unselectAll({except: feature});
32582 if(!this.multipleSelect()) {
32583 this.unselectAll({except: feature});
32585 this.select(feature);
32591 * Method: multipleSelect
32592 * Allow for multiple selected features based on <multiple> property and
32593 * <multipleKey> event modifier.
32596 * {Boolean} Allow for multiple selected features.
32598 multipleSelect: function() {
32599 return this.multiple || (this.handlers.feature.evt &&
32600 this.handlers.feature.evt[this.multipleKey]);
32604 * Method: toggleSelect
32605 * Event should toggle the selected state of a feature based on <toggle>
32606 * property and <toggleKey> event modifier.
32609 * {Boolean} Toggle the selected state of a feature.
32611 toggleSelect: function() {
32612 return this.toggle || (this.handlers.feature.evt &&
32613 this.handlers.feature.evt[this.toggleKey]);
32617 * Method: clickoutFeature
32618 * Called on click outside a previously clicked (selected) feature.
32619 * Only responds if this.hover is false.
32622 * feature - {<OpenLayers.Vector.Feature>}
32624 clickoutFeature: function(feature) {
32625 if(!this.hover && this.clickout) {
32626 this.unselectAll();
32631 * Method: overFeature
32632 * Called on over a feature.
32633 * Only responds if this.hover is true.
32636 * feature - {<OpenLayers.Feature.Vector>}
32638 overFeature: function(feature) {
32639 var layer = feature.layer;
32641 if(this.highlightOnly) {
32642 this.highlight(feature);
32643 } else if(OpenLayers.Util.indexOf(
32644 layer.selectedFeatures, feature) == -1) {
32645 this.select(feature);
32651 * Method: outFeature
32652 * Called on out of a selected feature.
32653 * Only responds if this.hover is true.
32656 * feature - {<OpenLayers.Feature.Vector>}
32658 outFeature: function(feature) {
32660 if(this.highlightOnly) {
32661 // we do nothing if we're not the last highlighter of the
32663 if(feature._lastHighlighter == this.id) {
32664 // if another select control had highlighted the feature before
32665 // we did it ourself then we use that control to highlight the
32666 // feature as it was before we highlighted it, else we just
32668 if(feature._prevHighlighter &&
32669 feature._prevHighlighter != this.id) {
32670 delete feature._lastHighlighter;
32671 var control = this.map.getControl(
32672 feature._prevHighlighter);
32674 control.highlight(feature);
32677 this.unhighlight(feature);
32681 this.unselect(feature);
32687 * Method: highlight
32688 * Redraw feature with the select style.
32691 * feature - {<OpenLayers.Feature.Vector>}
32693 highlight: function(feature) {
32694 var layer = feature.layer;
32695 var cont = this.events.triggerEvent("beforefeaturehighlighted", {
32698 if(cont !== false) {
32699 feature._prevHighlighter = feature._lastHighlighter;
32700 feature._lastHighlighter = this.id;
32701 var style = this.selectStyle || this.renderIntent;
32702 layer.drawFeature(feature, style);
32703 this.events.triggerEvent("featurehighlighted", {feature : feature});
32708 * Method: unhighlight
32709 * Redraw feature with the "default" style
32712 * feature - {<OpenLayers.Feature.Vector>}
32714 unhighlight: function(feature) {
32715 var layer = feature.layer;
32717 // 1. there's no other highlighter, in that case _prev is undefined,
32718 // and we just need to undef _last
32719 // 2. another control highlighted the feature after we did it, in
32720 // that case _last references this other control, and we just
32721 // need to undef _prev
32722 // 3. another control highlighted the feature before we did it, in
32723 // that case _prev references this other control, and we need to
32724 // set _last to _prev and undef _prev
32725 if(feature._prevHighlighter == undefined) {
32726 delete feature._lastHighlighter;
32727 } else if(feature._prevHighlighter == this.id) {
32728 delete feature._prevHighlighter;
32730 feature._lastHighlighter = feature._prevHighlighter;
32731 delete feature._prevHighlighter;
32733 layer.drawFeature(feature, feature.style || feature.layer.style ||
32735 this.events.triggerEvent("featureunhighlighted", {feature : feature});
32740 * Add feature to the layer's selectedFeature array, render the feature as
32741 * selected, and call the onSelect function.
32744 * feature - {<OpenLayers.Feature.Vector>}
32746 select: function(feature) {
32747 var cont = this.onBeforeSelect.call(this.scope, feature);
32748 var layer = feature.layer;
32749 if(cont !== false) {
32750 cont = layer.events.triggerEvent("beforefeatureselected", {
32753 if(cont !== false) {
32754 layer.selectedFeatures.push(feature);
32755 this.highlight(feature);
32756 // if the feature handler isn't involved in the feature
32757 // selection (because the box handler is used or the
32758 // feature is selected programatically) we fake the
32759 // feature handler to allow unselecting on click
32760 if(!this.handlers.feature.lastFeature) {
32761 this.handlers.feature.lastFeature = layer.selectedFeatures[0];
32763 layer.events.triggerEvent("featureselected", {feature: feature});
32764 this.onSelect.call(this.scope, feature);
32771 * Remove feature from the layer's selectedFeature array, render the feature as
32772 * normal, and call the onUnselect function.
32775 * feature - {<OpenLayers.Feature.Vector>}
32777 unselect: function(feature) {
32778 var layer = feature.layer;
32779 // Store feature style for restoration later
32780 this.unhighlight(feature);
32781 OpenLayers.Util.removeItem(layer.selectedFeatures, feature);
32782 layer.events.triggerEvent("featureunselected", {feature: feature});
32783 this.onUnselect.call(this.scope, feature);
32787 * Method: selectBox
32788 * Callback from the handlers.box set up when <box> selection is true
32792 * position - {<OpenLayers.Bounds> || <OpenLayers.Pixel> }
32794 selectBox: function(position) {
32795 if (position instanceof OpenLayers.Bounds) {
32796 var minXY = this.map.getLonLatFromPixel({
32800 var maxXY = this.map.getLonLatFromPixel({
32804 var bounds = new OpenLayers.Bounds(
32805 minXY.lon, minXY.lat, maxXY.lon, maxXY.lat
32808 // if multiple is false, first deselect currently selected features
32809 if (!this.multipleSelect()) {
32810 this.unselectAll();
32813 // because we're using a box, we consider we want multiple selection
32814 var prevMultiple = this.multiple;
32815 this.multiple = true;
32816 var layers = this.layers || [this.layer];
32817 this.events.triggerEvent("boxselectionstart", {layers: layers});
32819 for(var l=0; l<layers.length; ++l) {
32821 for(var i=0, len = layer.features.length; i<len; ++i) {
32822 var feature = layer.features[i];
32823 // check if the feature is displayed
32824 if (!feature.getVisibility()) {
32828 if (this.geometryTypes == null || OpenLayers.Util.indexOf(
32829 this.geometryTypes, feature.geometry.CLASS_NAME) > -1) {
32830 if (bounds.toGeometry().intersects(feature.geometry)) {
32831 if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) {
32832 this.select(feature);
32838 this.multiple = prevMultiple;
32839 this.events.triggerEvent("boxselectionend", {layers: layers});
32845 * Set the map property for the control.
32848 * map - {<OpenLayers.Map>}
32850 setMap: function(map) {
32851 this.handlers.feature.setMap(map);
32853 this.handlers.box.setMap(map);
32855 OpenLayers.Control.prototype.setMap.apply(this, arguments);
32859 * APIMethod: setLayer
32860 * Attach a new layer to the control, overriding any existing layers.
32863 * layers - Array of {<OpenLayers.Layer.Vector>} or a single
32864 * {<OpenLayers.Layer.Vector>}
32866 setLayer: function(layers) {
32867 var isActive = this.active;
32868 this.unselectAll();
32871 this.layer.destroy();
32872 this.layers = null;
32874 this.initLayer(layers);
32875 this.handlers.feature.layer = this.layer;
32881 CLASS_NAME: "OpenLayers.Control.SelectFeature"
32883 /* ======================================================================
32884 OpenLayers/Control/Attribution.js
32885 ====================================================================== */
32887 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
32888 * full list of contributors). Published under the 2-clause BSD license.
32889 * See license.txt in the OpenLayers distribution or repository for the
32890 * full text of the license. */
32893 * @requires OpenLayers/Control.js
32897 * Class: OpenLayers.Control.Attribution
32898 * The attribution control adds attribution from layers to the map display.
32899 * It uses 'attribution' property of each layer.
32902 * - <OpenLayers.Control>
32904 OpenLayers.Control.Attribution =
32905 OpenLayers.Class(OpenLayers.Control, {
32908 * APIProperty: separator
32909 * {String} String used to separate layers.
32914 * APIProperty: template
32915 * {String} Template for the attribution. This has to include the substring
32916 * "${layers}", which will be replaced by the layer specific
32917 * attributions, separated by <separator>. The default is "${layers}".
32919 template: "${layers}",
32922 * Constructor: OpenLayers.Control.Attribution
32925 * options - {Object} Options for control.
32932 destroy: function() {
32933 this.map.events.un({
32934 "removelayer": this.updateAttribution,
32935 "addlayer": this.updateAttribution,
32936 "changelayer": this.updateAttribution,
32937 "changebaselayer": this.updateAttribution,
32941 OpenLayers.Control.prototype.destroy.apply(this, arguments);
32946 * Initialize control.
32949 * {DOMElement} A reference to the DIV DOMElement containing the control
32952 OpenLayers.Control.prototype.draw.apply(this, arguments);
32954 this.map.events.on({
32955 'changebaselayer': this.updateAttribution,
32956 'changelayer': this.updateAttribution,
32957 'addlayer': this.updateAttribution,
32958 'removelayer': this.updateAttribution,
32961 this.updateAttribution();
32967 * Method: updateAttribution
32968 * Update attribution string.
32970 updateAttribution: function() {
32971 var attributions = [];
32972 if (this.map && this.map.layers) {
32973 for(var i=0, len=this.map.layers.length; i<len; i++) {
32974 var layer = this.map.layers[i];
32975 if (layer.attribution && layer.getVisibility()) {
32976 // add attribution only if attribution text is unique
32977 if (OpenLayers.Util.indexOf(
32978 attributions, layer.attribution) === -1) {
32979 attributions.push( layer.attribution );
32983 this.div.innerHTML = OpenLayers.String.format(this.template, {
32984 layers: attributions.join(this.separator)
32989 CLASS_NAME: "OpenLayers.Control.Attribution"
32991 /* ======================================================================
32992 OpenLayers/Handler/Drag.js
32993 ====================================================================== */
32995 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
32996 * full list of contributors). Published under the 2-clause BSD license.
32997 * See license.txt in the OpenLayers distribution or repository for the
32998 * full text of the license. */
33001 * @requires OpenLayers/Handler.js
33005 * Class: OpenLayers.Handler.Drag
33006 * The drag handler is used to deal with sequences of browser events related
33007 * to dragging. The handler is used by controls that want to know when
33008 * a drag sequence begins, when a drag is happening, and when it has
33011 * Controls that use the drag handler typically construct it with callbacks
33012 * for 'down', 'move', and 'done'. Callbacks for these keys are called
33013 * when the drag begins, with each move, and when the drag is done. In
33014 * addition, controls can have callbacks keyed to 'up' and 'out' if they
33015 * care to differentiate between the types of events that correspond with
33016 * the end of a drag sequence. If no drag actually occurs (no mouse move)
33017 * the 'down' and 'up' callbacks will be called, but not the 'done'
33020 * Create a new drag handler with the <OpenLayers.Handler.Drag> constructor.
33023 * - <OpenLayers.Handler>
33025 OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, {
33028 * Property: started
33029 * {Boolean} When a mousedown or touchstart event is received, we want to
33030 * record it, but not set 'dragging' until the mouse moves after starting.
33035 * Property: stopDown
33036 * {Boolean} Stop propagation of mousedown events from getting to listeners
33037 * on the same element. Default is true.
33042 * Property: dragging
33049 * {<OpenLayers.Pixel>} The last pixel location of the drag.
33055 * {<OpenLayers.Pixel>} The first pixel location of the drag.
33060 * Property: lastMoveEvt
33061 * {Object} The last mousemove event that occurred. Used to
33062 * position the map correctly when our "delay drag"
33068 * Property: oldOnselectstart
33071 oldOnselectstart: null,
33074 * Property: interval
33075 * {Integer} In order to increase performance, an interval (in
33076 * milliseconds) can be set to reduce the number of drag events
33077 * called. If set, a new drag event will not be set until the
33078 * interval has passed.
33079 * Defaults to 0, meaning no interval.
33084 * Property: timeoutId
33085 * {String} The id of the timeout used for the mousedown interval.
33086 * This is "private", and should be left alone.
33091 * APIProperty: documentDrag
33092 * {Boolean} If set to true, the handler will also handle mouse moves when
33093 * the cursor has moved out of the map viewport. Default is false.
33095 documentDrag: false,
33098 * Property: documentEvents
33099 * {Boolean} Are we currently observing document events?
33101 documentEvents: null,
33104 * Constructor: OpenLayers.Handler.Drag
33105 * Returns OpenLayers.Handler.Drag
33108 * control - {<OpenLayers.Control>} The control that is making use of
33109 * this handler. If a handler is being used without a control, the
33110 * handlers setMap method must be overridden to deal properly with
33112 * callbacks - {Object} An object containing a single function to be
33113 * called when the drag operation is finished. The callback should
33114 * expect to recieve a single argument, the pixel location of the event.
33115 * Callbacks for 'move' and 'done' are supported. You can also speficy
33116 * callbacks for 'down', 'up', and 'out' to respond to those events.
33117 * options - {Object}
33119 initialize: function(control, callbacks, options) {
33120 OpenLayers.Handler.prototype.initialize.apply(this, arguments);
33122 if (this.documentDrag === true) {
33124 this._docMove = function(evt) {
33126 xy: {x: evt.clientX, y: evt.clientY},
33130 this._docUp = function(evt) {
33131 me.mouseup({xy: {x: evt.clientX, y: evt.clientY}});
33138 * Method: dragstart
33139 * This private method is factorized from mousedown and touchstart methods
33142 * evt - {Event} The event
33145 * {Boolean} Let the event propagate.
33147 dragstart: function (evt) {
33148 var propagate = true;
33149 this.dragging = false;
33150 if (this.checkModifiers(evt) &&
33151 (OpenLayers.Event.isLeftClick(evt) ||
33152 OpenLayers.Event.isSingleTouch(evt))) {
33153 this.started = true;
33154 this.start = evt.xy;
33155 this.last = evt.xy;
33156 OpenLayers.Element.addClass(
33157 this.map.viewPortDiv, "olDragDown"
33160 this.callback("down", [evt.xy]);
33162 // prevent document dragging
33163 OpenLayers.Event.preventDefault(evt);
33165 if(!this.oldOnselectstart) {
33166 this.oldOnselectstart = document.onselectstart ?
33167 document.onselectstart : OpenLayers.Function.True;
33169 document.onselectstart = OpenLayers.Function.False;
33171 propagate = !this.stopDown;
33173 this.started = false;
33182 * This private method is factorized from mousemove and touchmove methods
33185 * evt - {Event} The event
33188 * {Boolean} Let the event propagate.
33190 dragmove: function (evt) {
33191 this.lastMoveEvt = evt;
33192 if (this.started && !this.timeoutId && (evt.xy.x != this.last.x ||
33193 evt.xy.y != this.last.y)) {
33194 if(this.documentDrag === true && this.documentEvents) {
33195 if(evt.element === document) {
33196 this.adjustXY(evt);
33197 // do setEvent manually because the documentEvents are not
33198 // registered with the map
33199 this.setEvent(evt);
33201 this.removeDocumentEvents();
33204 if (this.interval > 0) {
33205 this.timeoutId = setTimeout(
33206 OpenLayers.Function.bind(this.removeTimeout, this),
33209 this.dragging = true;
33212 this.callback("move", [evt.xy]);
33213 if(!this.oldOnselectstart) {
33214 this.oldOnselectstart = document.onselectstart;
33215 document.onselectstart = OpenLayers.Function.False;
33217 this.last = evt.xy;
33224 * This private method is factorized from mouseup and touchend methods
33227 * evt - {Event} The event
33230 * {Boolean} Let the event propagate.
33232 dragend: function (evt) {
33233 if (this.started) {
33234 if(this.documentDrag === true && this.documentEvents) {
33235 this.adjustXY(evt);
33236 this.removeDocumentEvents();
33238 var dragged = (this.start != this.last);
33239 this.started = false;
33240 this.dragging = false;
33241 OpenLayers.Element.removeClass(
33242 this.map.viewPortDiv, "olDragDown"
33245 this.callback("up", [evt.xy]);
33247 this.callback("done", [evt.xy]);
33249 document.onselectstart = this.oldOnselectstart;
33255 * The four methods below (down, move, up, and out) are used by subclasses
33256 * to do their own processing related to these mouse events.
33261 * This method is called during the handling of the mouse down event.
33262 * Subclasses can do their own processing here.
33265 * evt - {Event} The mouse down event
33267 down: function(evt) {
33272 * This method is called during the handling of the mouse move event.
33273 * Subclasses can do their own processing here.
33276 * evt - {Event} The mouse move event
33279 move: function(evt) {
33284 * This method is called during the handling of the mouse up event.
33285 * Subclasses can do their own processing here.
33288 * evt - {Event} The mouse up event
33290 up: function(evt) {
33295 * This method is called during the handling of the mouse out event.
33296 * Subclasses can do their own processing here.
33299 * evt - {Event} The mouse out event
33301 out: function(evt) {
33305 * The methods below are part of the magic of event handling. Because
33306 * they are named like browser events, they are registered as listeners
33307 * for the events they represent.
33311 * Method: mousedown
33312 * Handle mousedown events
33318 * {Boolean} Let the event propagate.
33320 mousedown: function(evt) {
33321 return this.dragstart(evt);
33325 * Method: touchstart
33326 * Handle touchstart events
33332 * {Boolean} Let the event propagate.
33334 touchstart: function(evt) {
33336 return this.dragstart(evt);
33340 * Method: mousemove
33341 * Handle mousemove events
33347 * {Boolean} Let the event propagate.
33349 mousemove: function(evt) {
33350 return this.dragmove(evt);
33354 * Method: touchmove
33355 * Handle touchmove events
33361 * {Boolean} Let the event propagate.
33363 touchmove: function(evt) {
33364 return this.dragmove(evt);
33368 * Method: removeTimeout
33369 * Private. Called by mousemove() to remove the drag timeout.
33371 removeTimeout: function() {
33372 this.timeoutId = null;
33373 // if timeout expires while we're still dragging (mouseup
33374 // hasn't occurred) then call mousemove to move to the
33375 // correct position
33376 if(this.dragging) {
33377 this.mousemove(this.lastMoveEvt);
33383 * Handle mouseup events
33389 * {Boolean} Let the event propagate.
33391 mouseup: function(evt) {
33392 return this.dragend(evt);
33397 * Handle touchend events
33403 * {Boolean} Let the event propagate.
33405 touchend: function(evt) {
33406 // override evt.xy with last position since touchend does not have
33407 // any touch position
33408 evt.xy = this.last;
33409 return this.dragend(evt);
33414 * Handle mouseout events
33420 * {Boolean} Let the event propagate.
33422 mouseout: function (evt) {
33423 if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {
33424 if(this.documentDrag === true) {
33425 this.addDocumentEvents();
33427 var dragged = (this.start != this.last);
33428 this.started = false;
33429 this.dragging = false;
33430 OpenLayers.Element.removeClass(
33431 this.map.viewPortDiv, "olDragDown"
33434 this.callback("out", []);
33436 this.callback("done", [evt.xy]);
33438 if(document.onselectstart) {
33439 document.onselectstart = this.oldOnselectstart;
33448 * The drag handler captures the click event. If something else registers
33449 * for clicks on the same element, its listener will not be called
33456 * {Boolean} Let the event propagate.
33458 click: function (evt) {
33459 // let the click event propagate only if the mouse moved
33460 return (this.start == this.last);
33465 * Activate the handler.
33468 * {Boolean} The handler was successfully activated.
33470 activate: function() {
33471 var activated = false;
33472 if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
33473 this.dragging = false;
33480 * Method: deactivate
33481 * Deactivate the handler.
33484 * {Boolean} The handler was successfully deactivated.
33486 deactivate: function() {
33487 var deactivated = false;
33488 if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
33489 this.started = false;
33490 this.dragging = false;
33493 deactivated = true;
33494 OpenLayers.Element.removeClass(
33495 this.map.viewPortDiv, "olDragDown"
33498 return deactivated;
33503 * Converts event coordinates that are relative to the document body to
33504 * ones that are relative to the map viewport. The latter is the default in
33510 adjustXY: function(evt) {
33511 var pos = OpenLayers.Util.pagePosition(this.map.viewPortDiv);
33512 evt.xy.x -= pos[0];
33513 evt.xy.y -= pos[1];
33517 * Method: addDocumentEvents
33518 * Start observing document events when documentDrag is true and the mouse
33519 * cursor leaves the map viewport while dragging.
33521 addDocumentEvents: function() {
33522 OpenLayers.Element.addClass(document.body, "olDragDown");
33523 this.documentEvents = true;
33524 OpenLayers.Event.observe(document, "mousemove", this._docMove);
33525 OpenLayers.Event.observe(document, "mouseup", this._docUp);
33529 * Method: removeDocumentEvents
33530 * Stops observing document events when documentDrag is true and the mouse
33531 * cursor re-enters the map viewport while dragging.
33533 removeDocumentEvents: function() {
33534 OpenLayers.Element.removeClass(document.body, "olDragDown");
33535 this.documentEvents = false;
33536 OpenLayers.Event.stopObserving(document, "mousemove", this._docMove);
33537 OpenLayers.Event.stopObserving(document, "mouseup", this._docUp);
33540 CLASS_NAME: "OpenLayers.Handler.Drag"
33542 /* ======================================================================
33543 OpenLayers/Handler/Box.js
33544 ====================================================================== */
33546 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
33547 * full list of contributors). Published under the 2-clause BSD license.
33548 * See license.txt in the OpenLayers distribution or repository for the
33549 * full text of the license. */
33552 * @requires OpenLayers/Handler.js
33553 * @requires OpenLayers/Handler/Drag.js
33557 * Class: OpenLayers.Handler.Box
33558 * Handler for dragging a rectangle across the map. Box is displayed
33559 * on mouse down, moves on mouse move, and is finished on mouse up.
33562 * - <OpenLayers.Handler>
33564 OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, {
33567 * Property: dragHandler
33568 * {<OpenLayers.Handler.Drag>}
33573 * APIProperty: boxDivClassName
33574 * {String} The CSS class to use for drawing the box. Default is
33575 * olHandlerBoxZoomBox
33577 boxDivClassName: 'olHandlerBoxZoomBox',
33580 * Property: boxOffsets
33581 * {Object} Caches box offsets from css. This is used by the getBoxOffsets
33587 * Constructor: OpenLayers.Handler.Box
33590 * control - {<OpenLayers.Control>}
33591 * callbacks - {Object} An object with a properties whose values are
33592 * functions. Various callbacks described below.
33593 * options - {Object}
33596 * start - Called when the box drag operation starts.
33597 * done - Called when the box drag operation is finished.
33598 * The callback should expect to receive a single argument, the box
33599 * bounds or a pixel. If the box dragging didn't span more than a 5
33600 * pixel distance, a pixel will be returned instead of a bounds object.
33602 initialize: function(control, callbacks, options) {
33603 OpenLayers.Handler.prototype.initialize.apply(this, arguments);
33604 this.dragHandler = new OpenLayers.Handler.Drag(
33607 down: this.startBox,
33608 move: this.moveBox,
33609 out: this.removeBox,
33612 {keyMask: this.keyMask}
33619 destroy: function() {
33620 OpenLayers.Handler.prototype.destroy.apply(this, arguments);
33621 if (this.dragHandler) {
33622 this.dragHandler.destroy();
33623 this.dragHandler = null;
33630 setMap: function (map) {
33631 OpenLayers.Handler.prototype.setMap.apply(this, arguments);
33632 if (this.dragHandler) {
33633 this.dragHandler.setMap(map);
33641 * xy - {<OpenLayers.Pixel>}
33643 startBox: function (xy) {
33644 this.callback("start", []);
33645 this.zoomBox = OpenLayers.Util.createDiv('zoomBox', {
33648 this.zoomBox.className = this.boxDivClassName;
33649 this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
33651 this.map.viewPortDiv.appendChild(this.zoomBox);
33653 OpenLayers.Element.addClass(
33654 this.map.viewPortDiv, "olDrawBox"
33661 moveBox: function (xy) {
33662 var startX = this.dragHandler.start.x;
33663 var startY = this.dragHandler.start.y;
33664 var deltaX = Math.abs(startX - xy.x);
33665 var deltaY = Math.abs(startY - xy.y);
33667 var offset = this.getBoxOffsets();
33668 this.zoomBox.style.width = (deltaX + offset.width + 1) + "px";
33669 this.zoomBox.style.height = (deltaY + offset.height + 1) + "px";
33670 this.zoomBox.style.left = (xy.x < startX ?
33671 startX - deltaX - offset.left : startX - offset.left) + "px";
33672 this.zoomBox.style.top = (xy.y < startY ?
33673 startY - deltaY - offset.top : startY - offset.top) + "px";
33679 endBox: function(end) {
33681 if (Math.abs(this.dragHandler.start.x - end.x) > 5 ||
33682 Math.abs(this.dragHandler.start.y - end.y) > 5) {
33683 var start = this.dragHandler.start;
33684 var top = Math.min(start.y, end.y);
33685 var bottom = Math.max(start.y, end.y);
33686 var left = Math.min(start.x, end.x);
33687 var right = Math.max(start.x, end.x);
33688 result = new OpenLayers.Bounds(left, bottom, right, top);
33690 result = this.dragHandler.start.clone(); // i.e. OL.Pixel
33694 this.callback("done", [result]);
33698 * Method: removeBox
33699 * Remove the zoombox from the screen and nullify our reference to it.
33701 removeBox: function() {
33702 this.map.viewPortDiv.removeChild(this.zoomBox);
33703 this.zoomBox = null;
33704 this.boxOffsets = null;
33705 OpenLayers.Element.removeClass(
33706 this.map.viewPortDiv, "olDrawBox"
33714 activate: function () {
33715 if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
33716 this.dragHandler.activate();
33724 * Method: deactivate
33726 deactivate: function () {
33727 if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
33728 if (this.dragHandler.deactivate()) {
33729 if (this.zoomBox) {
33740 * Method: getBoxOffsets
33741 * Determines border offsets for a box, according to the box model.
33744 * {Object} an object with the following offsets:
33752 getBoxOffsets: function() {
33753 if (!this.boxOffsets) {
33754 // Determine the box model. If the testDiv's clientWidth is 3, then
33755 // the borders are outside and we are dealing with the w3c box
33756 // model. Otherwise, the browser uses the traditional box model and
33757 // the borders are inside the box bounds, leaving us with a
33758 // clientWidth of 1.
33759 var testDiv = document.createElement("div");
33760 //testDiv.style.visibility = "hidden";
33761 testDiv.style.position = "absolute";
33762 testDiv.style.border = "1px solid black";
33763 testDiv.style.width = "3px";
33764 document.body.appendChild(testDiv);
33765 var w3cBoxModel = testDiv.clientWidth == 3;
33766 document.body.removeChild(testDiv);
33768 var left = parseInt(OpenLayers.Element.getStyle(this.zoomBox,
33769 "border-left-width"));
33770 var right = parseInt(OpenLayers.Element.getStyle(
33771 this.zoomBox, "border-right-width"));
33772 var top = parseInt(OpenLayers.Element.getStyle(this.zoomBox,
33773 "border-top-width"));
33774 var bottom = parseInt(OpenLayers.Element.getStyle(
33775 this.zoomBox, "border-bottom-width"));
33776 this.boxOffsets = {
33781 width: w3cBoxModel === false ? left + right : 0,
33782 height: w3cBoxModel === false ? top + bottom : 0
33785 return this.boxOffsets;
33788 CLASS_NAME: "OpenLayers.Handler.Box"
33790 /* ======================================================================
33791 OpenLayers/Control/ZoomBox.js
33792 ====================================================================== */
33794 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
33795 * full list of contributors). Published under the 2-clause BSD license.
33796 * See license.txt in the OpenLayers distribution or repository for the
33797 * full text of the license. */
33800 * @requires OpenLayers/Control.js
33801 * @requires OpenLayers/Handler/Box.js
33805 * Class: OpenLayers.Control.ZoomBox
33806 * The ZoomBox control enables zooming directly to a given extent, by drawing
33807 * a box on the map. The box is drawn by holding down shift, whilst dragging
33811 * - <OpenLayers.Control>
33813 OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, {
33816 * {OpenLayers.Control.TYPE}
33818 type: OpenLayers.Control.TYPE_TOOL,
33822 * {Boolean} Should the control be used for zooming out?
33827 * APIProperty: keyMask
33828 * {Integer} Zoom only occurs if the keyMask matches the combination of
33829 * keys down. Use bitwise operators and one or more of the
33830 * <OpenLayers.Handler> constants to construct a keyMask. Leave null if
33831 * not used mask. Default is null.
33836 * APIProperty: alwaysZoom
33837 * {Boolean} Always zoom in/out when box drawn, even if the zoom level does
33843 * APIProperty: zoomOnClick
33844 * {Boolean} Should we zoom when no box was dragged, i.e. the user only
33845 * clicked? Default is true.
33853 this.handler = new OpenLayers.Handler.Box( this,
33854 {done: this.zoomBox}, {keyMask: this.keyMask} );
33861 * position - {<OpenLayers.Bounds>} or {<OpenLayers.Pixel>}
33863 zoomBox: function (position) {
33864 if (position instanceof OpenLayers.Bounds) {
33866 targetCenterPx = position.getCenterPixel();
33868 var minXY = this.map.getLonLatFromPixel({
33872 var maxXY = this.map.getLonLatFromPixel({
33876 bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat,
33877 maxXY.lon, maxXY.lat);
33879 var pixWidth = position.right - position.left;
33880 var pixHeight = position.bottom - position.top;
33881 var zoomFactor = Math.min((this.map.size.h / pixHeight),
33882 (this.map.size.w / pixWidth));
33883 var extent = this.map.getExtent();
33884 var center = this.map.getLonLatFromPixel(targetCenterPx);
33885 var xmin = center.lon - (extent.getWidth()/2)*zoomFactor;
33886 var xmax = center.lon + (extent.getWidth()/2)*zoomFactor;
33887 var ymin = center.lat - (extent.getHeight()/2)*zoomFactor;
33888 var ymax = center.lat + (extent.getHeight()/2)*zoomFactor;
33889 bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax);
33891 // always zoom in/out
33892 var lastZoom = this.map.getZoom(),
33893 size = this.map.getSize(),
33894 centerPx = {x: size.w / 2, y: size.h / 2},
33895 zoom = this.map.getZoomForExtent(bounds),
33896 oldRes = this.map.getResolution(),
33897 newRes = this.map.getResolutionForZoom(zoom);
33898 if (oldRes == newRes) {
33899 this.map.setCenter(this.map.getLonLatFromPixel(targetCenterPx));
33901 var zoomOriginPx = {
33902 x: (oldRes * targetCenterPx.x - newRes * centerPx.x) /
33904 y: (oldRes * targetCenterPx.y - newRes * centerPx.y) /
33907 this.map.zoomTo(zoom, zoomOriginPx);
33909 if (lastZoom == this.map.getZoom() && this.alwaysZoom == true){
33910 this.map.zoomTo(lastZoom + (this.out ? -1 : 1));
33912 } else if (this.zoomOnClick) { // it's a pixel
33914 this.map.zoomTo(this.map.getZoom() + 1, position);
33916 this.map.zoomTo(this.map.getZoom() - 1, position);
33921 CLASS_NAME: "OpenLayers.Control.ZoomBox"
33923 /* ======================================================================
33924 OpenLayers/Control/DragPan.js
33925 ====================================================================== */
33927 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
33928 * full list of contributors). Published under the 2-clause BSD license.
33929 * See license.txt in the OpenLayers distribution or repository for the
33930 * full text of the license. */
33933 * @requires OpenLayers/Control.js
33934 * @requires OpenLayers/Handler/Drag.js
33938 * Class: OpenLayers.Control.DragPan
33939 * The DragPan control pans the map with a drag of the mouse.
33942 * - <OpenLayers.Control>
33944 OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, {
33948 * {OpenLayers.Control.TYPES}
33950 type: OpenLayers.Control.TYPE_TOOL,
33954 * {Boolean} The map moved.
33959 * Property: interval
33960 * {Integer} The number of milliseconds that should ellapse before
33961 * panning the map again. Defaults to 0 milliseconds, which means that
33962 * no separate cycle is used for panning. In most cases you won't want
33963 * to change this value. For slow machines/devices larger values can be
33969 * APIProperty: documentDrag
33970 * {Boolean} If set to true, mouse dragging will continue even if the
33971 * mouse cursor leaves the map viewport. Default is false.
33973 documentDrag: false,
33976 * Property: kinetic
33977 * {<OpenLayers.Kinetic>} The OpenLayers.Kinetic object.
33982 * APIProperty: enableKinetic
33983 * {Boolean} Set this option to enable "kinetic dragging". Can be
33984 * set to true or to an object. If set to an object this
33985 * object will be passed to the {<OpenLayers.Kinetic>}
33986 * constructor. Defaults to true.
33987 * To get kinetic dragging, ensure that OpenLayers/Kinetic.js is
33988 * included in your build config.
33990 enableKinetic: true,
33993 * APIProperty: kineticInterval
33994 * {Integer} Interval in milliseconds between 2 steps in the "kinetic
33995 * scrolling". Applies only if enableKinetic is set. Defaults
33996 * to 10 milliseconds.
33998 kineticInterval: 10,
34003 * Creates a Drag handler, using <panMap> and
34004 * <panMapDone> as callbacks.
34007 if (this.enableKinetic && OpenLayers.Kinetic) {
34008 var config = {interval: this.kineticInterval};
34009 if(typeof this.enableKinetic === "object") {
34010 config = OpenLayers.Util.extend(config, this.enableKinetic);
34012 this.kinetic = new OpenLayers.Kinetic(config);
34014 this.handler = new OpenLayers.Handler.Drag(this, {
34015 "move": this.panMap,
34016 "done": this.panMapDone,
34017 "down": this.panMapStart
34019 interval: this.interval,
34020 documentDrag: this.documentDrag
34026 * Method: panMapStart
34028 panMapStart: function() {
34030 this.kinetic.begin();
34038 * xy - {<OpenLayers.Pixel>} Pixel of the mouse position
34040 panMap: function(xy) {
34042 this.kinetic.update(xy);
34044 this.panned = true;
34046 this.handler.last.x - xy.x,
34047 this.handler.last.y - xy.y,
34048 {dragging: true, animate: false}
34053 * Method: panMapDone
34054 * Finish the panning operation. Only call setCenter (through <panMap>)
34055 * if the map has actually been moved.
34058 * xy - {<OpenLayers.Pixel>} Pixel of the mouse position
34060 panMapDone: function(xy) {
34063 if (this.kinetic) {
34064 res = this.kinetic.end(xy);
34067 this.handler.last.x - xy.x,
34068 this.handler.last.y - xy.y,
34069 {dragging: !!res, animate: false}
34073 this.kinetic.move(res, function(x, y, end) {
34074 self.map.pan(x, y, {dragging: !end, animate: false});
34077 this.panned = false;
34081 CLASS_NAME: "OpenLayers.Control.DragPan"
34083 /* ======================================================================
34084 OpenLayers/Handler/Click.js
34085 ====================================================================== */
34087 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
34088 * full list of contributors). Published under the 2-clause BSD license.
34089 * See license.txt in the OpenLayers distribution or repository for the
34090 * full text of the license. */
34093 * @requires OpenLayers/Handler.js
34097 * Class: OpenLayers.Handler.Click
34098 * A handler for mouse clicks. The intention of this handler is to give
34099 * controls more flexibility with handling clicks. Browsers trigger
34100 * click events twice for a double-click. In addition, the mousedown,
34101 * mousemove, mouseup sequence fires a click event. With this handler,
34102 * controls can decide whether to ignore clicks associated with a double
34103 * click. By setting a <pixelTolerance>, controls can also ignore clicks
34104 * that include a drag. Create a new instance with the
34105 * <OpenLayers.Handler.Click> constructor.
34108 * - <OpenLayers.Handler>
34110 OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, {
34112 * APIProperty: delay
34113 * {Number} Number of milliseconds between clicks before the event is
34114 * considered a double-click.
34119 * APIProperty: single
34120 * {Boolean} Handle single clicks. Default is true. If false, clicks
34121 * will not be reported. If true, single-clicks will be reported.
34126 * APIProperty: double
34127 * {Boolean} Handle double-clicks. Default is false.
34132 * APIProperty: pixelTolerance
34133 * {Number} Maximum number of pixels between mouseup and mousedown for an
34134 * event to be considered a click. Default is 0. If set to an
34135 * integer value, clicks with a drag greater than the value will be
34136 * ignored. This property can only be set when the handler is
34142 * APIProperty: dblclickTolerance
34143 * {Number} Maximum distance in pixels between clicks for a sequence of
34144 * events to be considered a double click. Default is 13. If the
34145 * distance between two clicks is greater than this value, a double-
34146 * click will not be fired.
34148 dblclickTolerance: 13,
34151 * APIProperty: stopSingle
34152 * {Boolean} Stop other listeners from being notified of clicks. Default
34153 * is false. If true, any listeners registered before this one for
34154 * click or rightclick events will not be notified.
34159 * APIProperty: stopDouble
34160 * {Boolean} Stop other listeners from being notified of double-clicks.
34161 * Default is false. If true, any click listeners registered before
34162 * this one will not be notified of *any* double-click events.
34164 * The one caveat with stopDouble is that given a map with two click
34165 * handlers, one with stopDouble true and the other with stopSingle
34166 * true, the stopSingle handler should be activated last to get
34167 * uniform cross-browser performance. Since IE triggers one click
34168 * with a dblclick and FF triggers two, if a stopSingle handler is
34169 * activated first, all it gets in IE is a single click when the
34170 * second handler stops propagation on the dblclick.
34175 * Property: timerId
34176 * {Number} The id of the timeout waiting to clear the <delayedCall>.
34182 * {Object} Object that store relevant information about the last
34183 * mousedown or touchstart. Its 'xy' OpenLayers.Pixel property gives
34184 * the average location of the mouse/touch event. Its 'touches'
34185 * property records clientX/clientY of each touches.
34191 * {Object} Object that store relevant information about the last
34192 * mousemove or touchmove. Its 'xy' OpenLayers.Pixel property gives
34193 * the average location of the mouse/touch event. Its 'touches'
34194 * property records clientX/clientY of each touches.
34200 * {Object} When waiting for double clicks, this object will store
34201 * information about the first click in a two click sequence.
34206 * Property: rightclickTimerId
34207 * {Number} The id of the right mouse timeout waiting to clear the
34210 rightclickTimerId: null,
34213 * Constructor: OpenLayers.Handler.Click
34214 * Create a new click handler.
34217 * control - {<OpenLayers.Control>} The control that is making use of
34218 * this handler. If a handler is being used without a control, the
34219 * handler's setMap method must be overridden to deal properly with
34221 * callbacks - {Object} An object with keys corresponding to callbacks
34222 * that will be called by the handler. The callbacks should
34223 * expect to recieve a single argument, the click event.
34224 * Callbacks for 'click' and 'dblclick' are supported.
34225 * options - {Object} Optional object whose properties will be set on the
34230 * Method: touchstart
34231 * Handle touchstart.
34234 * {Boolean} Continue propagating this event.
34236 touchstart: function(evt) {
34238 this.down = this.getEventInfo(evt);
34239 this.last = this.getEventInfo(evt);
34244 * Method: touchmove
34245 * Store position of last move, because touchend event can have
34246 * an empty "touches" property.
34249 * {Boolean} Continue propagating this event.
34251 touchmove: function(evt) {
34252 this.last = this.getEventInfo(evt);
34258 * Correctly set event xy property, and add lastTouches to have
34259 * touches property from last touchstart or touchmove
34262 * {Boolean} Continue propagating this event.
34264 touchend: function(evt) {
34265 // touchstart may not have been allowed to propagate
34267 evt.xy = this.last.xy;
34268 evt.lastTouches = this.last.touches;
34269 this.handleSingle(evt);
34276 * Method: mousedown
34277 * Handle mousedown.
34280 * {Boolean} Continue propagating this event.
34282 mousedown: function(evt) {
34283 this.down = this.getEventInfo(evt);
34284 this.last = this.getEventInfo(evt);
34290 * Handle mouseup. Installed to support collection of right mouse events.
34293 * {Boolean} Continue propagating this event.
34295 mouseup: function (evt) {
34296 var propagate = true;
34298 // Collect right mouse clicks from the mouseup
34299 // IE - ignores the second right click in mousedown so using
34301 if (this.checkModifiers(evt) && this.control.handleRightClicks &&
34302 OpenLayers.Event.isRightClick(evt)) {
34303 propagate = this.rightclick(evt);
34310 * Method: rightclick
34311 * Handle rightclick. For a dblrightclick, we get two clicks so we need
34312 * to always register for dblrightclick to properly handle single
34316 * {Boolean} Continue propagating this event.
34318 rightclick: function(evt) {
34319 if(this.passesTolerance(evt)) {
34320 if(this.rightclickTimerId != null) {
34321 //Second click received before timeout this must be
34324 this.callback('dblrightclick', [evt]);
34325 return !this.stopDouble;
34327 //Set the rightclickTimerId, send evt only if double is
34328 // true else trigger single
34329 var clickEvent = this['double'] ?
34330 OpenLayers.Util.extend({}, evt) :
34331 this.callback('rightclick', [evt]);
34333 var delayedRightCall = OpenLayers.Function.bind(
34334 this.delayedRightCall,
34338 this.rightclickTimerId = window.setTimeout(
34339 delayedRightCall, this.delay
34343 return !this.stopSingle;
34347 * Method: delayedRightCall
34348 * Sets <rightclickTimerId> to null. And optionally triggers the
34349 * rightclick callback if evt is set.
34351 delayedRightCall: function(evt) {
34352 this.rightclickTimerId = null;
34354 this.callback('rightclick', [evt]);
34360 * Handle click events from the browser. This is registered as a listener
34361 * for click events and should not be called from other events in this
34365 * {Boolean} Continue propagating this event.
34367 click: function(evt) {
34369 this.last = this.getEventInfo(evt);
34371 this.handleSingle(evt);
34372 return !this.stopSingle;
34377 * Handle dblclick. For a dblclick, we get two clicks in some browsers
34378 * (FF) and one in others (IE). So we need to always register for
34379 * dblclick to properly handle single clicks. This method is registered
34380 * as a listener for the dblclick browser event. It should *not* be
34381 * called by other methods in this handler.
34384 * {Boolean} Continue propagating this event.
34386 dblclick: function(evt) {
34387 this.handleDouble(evt);
34388 return !this.stopDouble;
34392 * Method: handleDouble
34393 * Handle double-click sequence.
34395 handleDouble: function(evt) {
34396 if (this.passesDblclickTolerance(evt)) {
34397 if (this["double"]) {
34398 this.callback("dblclick", [evt]);
34400 // to prevent a dblclick from firing the click callback in IE
34406 * Method: handleSingle
34407 * Handle single click sequence.
34409 handleSingle: function(evt) {
34410 if (this.passesTolerance(evt)) {
34411 if (this.timerId != null) {
34412 // already received a click
34413 if (this.last.touches && this.last.touches.length === 1) {
34414 // touch device, no dblclick event - this may be a double
34415 if (this["double"]) {
34416 // on Android don't let the browser zoom on the page
34417 OpenLayers.Event.preventDefault(evt);
34419 this.handleDouble(evt);
34421 // if we're not in a touch environment we clear the click timer
34422 // if we've got a second touch, we'll get two touchend events
34423 if (!this.last.touches || this.last.touches.length !== 2) {
34427 // remember the first click info so we can compare to the second
34428 this.first = this.getEventInfo(evt);
34429 // set the timer, send evt only if single is true
34430 //use a clone of the event object because it will no longer
34431 //be a valid event object in IE in the timer callback
34432 var clickEvent = this.single ?
34433 OpenLayers.Util.extend({}, evt) : null;
34434 this.queuePotentialClick(clickEvent);
34440 * Method: queuePotentialClick
34441 * This method is separated out largely to make testing easier (so we
34442 * don't have to override window.setTimeout)
34444 queuePotentialClick: function(evt) {
34445 this.timerId = window.setTimeout(
34446 OpenLayers.Function.bind(this.delayedCall, this, evt),
34452 * Method: passesTolerance
34453 * Determine whether the event is within the optional pixel tolerance. Note
34454 * that the pixel tolerance check only works if mousedown events get to
34455 * the listeners registered here. If they are stopped by other elements,
34456 * the <pixelTolerance> will have no effect here (this method will always
34460 * {Boolean} The click is within the pixel tolerance (if specified).
34462 passesTolerance: function(evt) {
34464 if (this.pixelTolerance != null && this.down && this.down.xy) {
34465 passes = this.pixelTolerance >= this.down.xy.distanceTo(evt.xy);
34466 // for touch environments, we also enforce that all touches
34467 // start and end within the given tolerance to be considered a click
34468 if (passes && this.touch &&
34469 this.down.touches.length === this.last.touches.length) {
34470 // the touchend event doesn't come with touches, so we check
34472 for (var i=0, ii=this.down.touches.length; i<ii; ++i) {
34473 if (this.getTouchDistance(
34474 this.down.touches[i],
34475 this.last.touches[i]
34476 ) > this.pixelTolerance) {
34487 * Method: getTouchDistance
34490 * {Boolean} The pixel displacement between two touches.
34492 getTouchDistance: function(from, to) {
34494 Math.pow(from.clientX - to.clientX, 2) +
34495 Math.pow(from.clientY - to.clientY, 2)
34500 * Method: passesDblclickTolerance
34501 * Determine whether the event is within the optional double-cick pixel
34505 * {Boolean} The click is within the double-click pixel tolerance.
34507 passesDblclickTolerance: function(evt) {
34509 if (this.down && this.first) {
34510 passes = this.down.xy.distanceTo(this.first.xy) <= this.dblclickTolerance;
34516 * Method: clearTimer
34517 * Clear the timer and set <timerId> to null.
34519 clearTimer: function() {
34520 if (this.timerId != null) {
34521 window.clearTimeout(this.timerId);
34522 this.timerId = null;
34524 if (this.rightclickTimerId != null) {
34525 window.clearTimeout(this.rightclickTimerId);
34526 this.rightclickTimerId = null;
34531 * Method: delayedCall
34532 * Sets <timerId> to null. And optionally triggers the click callback if
34535 delayedCall: function(evt) {
34536 this.timerId = null;
34538 this.callback("click", [evt]);
34543 * Method: getEventInfo
34544 * This method allows us to store event information without storing the
34545 * actual event. In touch devices (at least), the same event is
34546 * modified between touchstart, touchmove, and touchend.
34549 * {Object} An object with event related info.
34551 getEventInfo: function(evt) {
34554 var len = evt.touches.length;
34555 touches = new Array(len);
34557 for (var i=0; i<len; i++) {
34558 touch = evt.touches[i];
34560 clientX: touch.olClientX,
34561 clientY: touch.olClientY
34572 * APIMethod: deactivate
34573 * Deactivate the handler.
34576 * {Boolean} The handler was successfully deactivated.
34578 deactivate: function() {
34579 var deactivated = false;
34580 if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
34585 deactivated = true;
34587 return deactivated;
34590 CLASS_NAME: "OpenLayers.Handler.Click"
34592 /* ======================================================================
34593 OpenLayers/Control/Navigation.js
34594 ====================================================================== */
34596 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
34597 * full list of contributors). Published under the 2-clause BSD license.
34598 * See license.txt in the OpenLayers distribution or repository for the
34599 * full text of the license. */
34602 * @requires OpenLayers/Control/ZoomBox.js
34603 * @requires OpenLayers/Control/DragPan.js
34604 * @requires OpenLayers/Handler/MouseWheel.js
34605 * @requires OpenLayers/Handler/Click.js
34609 * Class: OpenLayers.Control.Navigation
34610 * The navigation control handles map browsing with mouse events (dragging,
34611 * double-clicking, and scrolling the wheel). Create a new navigation
34612 * control with the <OpenLayers.Control.Navigation> control.
34614 * Note that this control is added to the map by default (if no controls
34615 * array is sent in the options object to the <OpenLayers.Map>
34619 * - <OpenLayers.Control>
34621 OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, {
34624 * Property: dragPan
34625 * {<OpenLayers.Control.DragPan>}
34630 * APIProperty: dragPanOptions
34631 * {Object} Options passed to the DragPan control.
34633 dragPanOptions: null,
34636 * Property: pinchZoom
34637 * {<OpenLayers.Control.PinchZoom>}
34642 * APIProperty: pinchZoomOptions
34643 * {Object} Options passed to the PinchZoom control.
34645 pinchZoomOptions: null,
34648 * APIProperty: documentDrag
34649 * {Boolean} Allow panning of the map by dragging outside map viewport.
34650 * Default is false.
34652 documentDrag: false,
34655 * Property: zoomBox
34656 * {<OpenLayers.Control.ZoomBox>}
34661 * APIProperty: zoomBoxEnabled
34662 * {Boolean} Whether the user can draw a box to zoom
34664 zoomBoxEnabled: true,
34667 * APIProperty: zoomWheelEnabled
34668 * {Boolean} Whether the mousewheel should zoom the map
34670 zoomWheelEnabled: true,
34673 * Property: mouseWheelOptions
34674 * {Object} Options passed to the MouseWheel control (only useful if
34675 * <zoomWheelEnabled> is set to true). Default is no options for maps
34676 * with fractionalZoom set to true, otherwise
34677 * {cumulative: false, interval: 50, maxDelta: 6}
34679 mouseWheelOptions: null,
34682 * APIProperty: handleRightClicks
34683 * {Boolean} Whether or not to handle right clicks. Default is false.
34685 handleRightClicks: false,
34688 * APIProperty: zoomBoxKeyMask
34689 * {Integer} <OpenLayers.Handler> key code of the key, which has to be
34690 * pressed, while drawing the zoom box with the mouse on the screen.
34691 * You should probably set handleRightClicks to true if you use this
34692 * with MOD_CTRL, to disable the context menu for machines which use
34693 * CTRL-Click as a right click.
34694 * Default: <OpenLayers.Handler.MOD_SHIFT>
34696 zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT,
34699 * APIProperty: autoActivate
34700 * {Boolean} Activate the control when it is added to a map. Default is
34703 autoActivate: true,
34706 * Constructor: OpenLayers.Control.Navigation
34707 * Create a new navigation control
34710 * options - {Object} An optional object whose properties will be set on
34713 initialize: function(options) {
34714 this.handlers = {};
34715 OpenLayers.Control.prototype.initialize.apply(this, arguments);
34720 * The destroy method is used to perform any clean up before the control
34721 * is dereferenced. Typically this is where event listeners are removed
34722 * to prevent memory leaks.
34724 destroy: function() {
34727 if (this.dragPan) {
34728 this.dragPan.destroy();
34730 this.dragPan = null;
34732 if (this.zoomBox) {
34733 this.zoomBox.destroy();
34735 this.zoomBox = null;
34737 if (this.pinchZoom) {
34738 this.pinchZoom.destroy();
34740 this.pinchZoom = null;
34742 OpenLayers.Control.prototype.destroy.apply(this,arguments);
34748 activate: function() {
34749 this.dragPan.activate();
34750 if (this.zoomWheelEnabled) {
34751 this.handlers.wheel.activate();
34753 this.handlers.click.activate();
34754 if (this.zoomBoxEnabled) {
34755 this.zoomBox.activate();
34757 if (this.pinchZoom) {
34758 this.pinchZoom.activate();
34760 return OpenLayers.Control.prototype.activate.apply(this,arguments);
34764 * Method: deactivate
34766 deactivate: function() {
34767 if (this.pinchZoom) {
34768 this.pinchZoom.deactivate();
34770 this.zoomBox.deactivate();
34771 this.dragPan.deactivate();
34772 this.handlers.click.deactivate();
34773 this.handlers.wheel.deactivate();
34774 return OpenLayers.Control.prototype.deactivate.apply(this,arguments);
34781 // disable right mouse context menu for support of right click events
34782 if (this.handleRightClicks) {
34783 this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False;
34786 var clickCallbacks = {
34787 'click': this.defaultClick,
34788 'dblclick': this.defaultDblClick,
34789 'dblrightclick': this.defaultDblRightClick
34791 var clickOptions = {
34795 this.handlers.click = new OpenLayers.Handler.Click(
34796 this, clickCallbacks, clickOptions
34798 this.dragPan = new OpenLayers.Control.DragPan(
34799 OpenLayers.Util.extend({
34801 documentDrag: this.documentDrag
34802 }, this.dragPanOptions)
34804 this.zoomBox = new OpenLayers.Control.ZoomBox(
34805 {map: this.map, keyMask: this.zoomBoxKeyMask});
34806 this.dragPan.draw();
34807 this.zoomBox.draw();
34808 var wheelOptions = this.map.fractionalZoom ? {} : {
34813 this.handlers.wheel = new OpenLayers.Handler.MouseWheel(
34814 this, {up : this.wheelUp, down: this.wheelDown},
34815 OpenLayers.Util.extend(wheelOptions, this.mouseWheelOptions)
34817 if (OpenLayers.Control.PinchZoom) {
34818 this.pinchZoom = new OpenLayers.Control.PinchZoom(
34819 OpenLayers.Util.extend(
34820 {map: this.map}, this.pinchZoomOptions));
34825 * Method: defaultClick
34830 defaultClick: function (evt) {
34831 if (evt.lastTouches && evt.lastTouches.length == 2) {
34832 this.map.zoomOut();
34837 * Method: defaultDblClick
34842 defaultDblClick: function (evt) {
34843 this.map.zoomTo(this.map.zoom + 1, evt.xy);
34847 * Method: defaultDblRightClick
34852 defaultDblRightClick: function (evt) {
34853 this.map.zoomTo(this.map.zoom - 1, evt.xy);
34857 * Method: wheelChange
34861 * deltaZ - {Integer}
34863 wheelChange: function(evt, deltaZ) {
34864 if (!this.map.fractionalZoom) {
34865 deltaZ = Math.round(deltaZ);
34867 var currentZoom = this.map.getZoom(),
34868 newZoom = currentZoom + deltaZ;
34869 newZoom = Math.max(newZoom, 0);
34870 newZoom = Math.min(newZoom, this.map.getNumZoomLevels());
34871 if (newZoom === currentZoom) {
34874 this.map.zoomTo(newZoom, evt.xy);
34879 * User spun scroll wheel up
34883 * delta - {Integer}
34885 wheelUp: function(evt, delta) {
34886 this.wheelChange(evt, delta || 1);
34890 * Method: wheelDown
34891 * User spun scroll wheel down
34895 * delta - {Integer}
34897 wheelDown: function(evt, delta) {
34898 this.wheelChange(evt, delta || -1);
34902 * Method: disableZoomBox
34904 disableZoomBox : function() {
34905 this.zoomBoxEnabled = false;
34906 this.zoomBox.deactivate();
34910 * Method: enableZoomBox
34912 enableZoomBox : function() {
34913 this.zoomBoxEnabled = true;
34915 this.zoomBox.activate();
34920 * Method: disableZoomWheel
34923 disableZoomWheel : function() {
34924 this.zoomWheelEnabled = false;
34925 this.handlers.wheel.deactivate();
34929 * Method: enableZoomWheel
34932 enableZoomWheel : function() {
34933 this.zoomWheelEnabled = true;
34935 this.handlers.wheel.activate();
34939 CLASS_NAME: "OpenLayers.Control.Navigation"
34941 /* ======================================================================
34942 OpenLayers/Renderer/SVG.js
34943 ====================================================================== */
34945 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
34946 * full list of contributors). Published under the 2-clause BSD license.
34947 * See license.txt in the OpenLayers distribution or repository for the
34948 * full text of the license. */
34951 * @requires OpenLayers/Renderer/Elements.js
34955 * Class: OpenLayers.Renderer.SVG
34958 * - <OpenLayers.Renderer.Elements>
34960 OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
34966 xmlns: "http://www.w3.org/2000/svg",
34969 * Property: xlinkns
34972 xlinkns: "http://www.w3.org/1999/xlink",
34975 * Constant: MAX_PIXEL
34976 * {Integer} Firefox has a limitation where values larger or smaller than
34977 * about 15000 in an SVG document lock the browser up. This
34983 * Property: translationParameters
34984 * {Object} Hash with "x" and "y" properties
34986 translationParameters: null,
34989 * Property: symbolMetrics
34990 * {Object} Cache for symbol metrics according to their svg coordinate
34991 * space. This is an object keyed by the symbol's id, and values are
34992 * an array of [width, centerX, centerY].
34994 symbolMetrics: null,
34997 * Constructor: OpenLayers.Renderer.SVG
35000 * containerID - {String}
35002 initialize: function(containerID) {
35003 if (!this.supported()) {
35006 OpenLayers.Renderer.Elements.prototype.initialize.apply(this,
35008 this.translationParameters = {x: 0, y: 0};
35010 this.symbolMetrics = {};
35014 * APIMethod: supported
35017 * {Boolean} Whether or not the browser supports the SVG renderer
35019 supported: function() {
35020 var svgFeature = "http://www.w3.org/TR/SVG11/feature#";
35021 return (document.implementation &&
35022 (document.implementation.hasFeature("org.w3c.svg", "1.0") ||
35023 document.implementation.hasFeature(svgFeature + "SVG", "1.1") ||
35024 document.implementation.hasFeature(svgFeature + "BasicStructure", "1.1") ));
35028 * Method: inValidRange
35029 * See #669 for more information
35034 * xyOnly - {Boolean} whether or not to just check for x and y, which means
35035 * to not take the current translation parameters into account if true.
35038 * {Boolean} Whether or not the 'x' and 'y' coordinates are in the
35041 inValidRange: function(x, y, xyOnly) {
35042 var left = x + (xyOnly ? 0 : this.translationParameters.x);
35043 var top = y + (xyOnly ? 0 : this.translationParameters.y);
35044 return (left >= -this.MAX_PIXEL && left <= this.MAX_PIXEL &&
35045 top >= -this.MAX_PIXEL && top <= this.MAX_PIXEL);
35049 * Method: setExtent
35052 * extent - {<OpenLayers.Bounds>}
35053 * resolutionChanged - {Boolean}
35056 * {Boolean} true to notify the layer that the new extent does not exceed
35057 * the coordinate range, and the features will not need to be redrawn.
35060 setExtent: function(extent, resolutionChanged) {
35061 var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments);
35063 var resolution = this.getResolution(),
35064 left = -extent.left / resolution,
35065 top = extent.top / resolution;
35067 // If the resolution has changed, start over changing the corner, because
35068 // the features will redraw.
35069 if (resolutionChanged) {
35073 var extentString = "0 0 " + this.size.w + " " + this.size.h;
35075 this.rendererRoot.setAttributeNS(null, "viewBox", extentString);
35076 this.translate(this.xOffset, 0);
35079 var inRange = this.translate(left - this.left + this.xOffset, top - this.top);
35081 // recenter the coordinate system
35082 this.setExtent(extent, true);
35084 return coordSysUnchanged && inRange;
35089 * Method: translate
35090 * Transforms the SVG coordinate system
35097 * {Boolean} true if the translation parameters are in the valid coordinates
35098 * range, false otherwise.
35100 translate: function(x, y) {
35101 if (!this.inValidRange(x, y, true)) {
35104 var transformString = "";
35106 transformString = "translate(" + x + "," + y + ")";
35108 this.root.setAttributeNS(null, "transform", transformString);
35109 this.translationParameters = {x: x, y: y};
35116 * Sets the size of the drawing surface.
35119 * size - {<OpenLayers.Size>} The size of the drawing surface
35121 setSize: function(size) {
35122 OpenLayers.Renderer.prototype.setSize.apply(this, arguments);
35124 this.rendererRoot.setAttributeNS(null, "width", this.size.w);
35125 this.rendererRoot.setAttributeNS(null, "height", this.size.h);
35129 * Method: getNodeType
35132 * geometry - {<OpenLayers.Geometry>}
35136 * {String} The corresponding node type for the specified geometry
35138 getNodeType: function(geometry, style) {
35139 var nodeType = null;
35140 switch (geometry.CLASS_NAME) {
35141 case "OpenLayers.Geometry.Point":
35142 if (style.externalGraphic) {
35143 nodeType = "image";
35144 } else if (this.isComplexSymbol(style.graphicName)) {
35147 nodeType = "circle";
35150 case "OpenLayers.Geometry.Rectangle":
35153 case "OpenLayers.Geometry.LineString":
35154 nodeType = "polyline";
35156 case "OpenLayers.Geometry.LinearRing":
35157 nodeType = "polygon";
35159 case "OpenLayers.Geometry.Polygon":
35160 case "OpenLayers.Geometry.Curve":
35171 * Use to set all the style attributes to a SVG node.
35173 * Takes care to adjust stroke width and point radius to be
35174 * resolution-relative
35177 * node - {SVGDomElement} An SVG element to decorate
35179 * options - {Object} Currently supported options include
35180 * 'isFilled' {Boolean} and
35181 * 'isStroked' {Boolean}
35183 setStyle: function(node, style, options) {
35184 style = style || node._style;
35185 options = options || node._options;
35187 var title = style.title || style.graphicTitle;
35189 node.setAttributeNS(null, "title", title);
35190 //Standards-conformant SVG
35191 // Prevent duplicate nodes. See issue https://github.com/openlayers/openlayers/issues/92
35192 var titleNode = node.getElementsByTagName("title");
35193 if (titleNode.length > 0) {
35194 titleNode[0].firstChild.textContent = title;
35196 var label = this.nodeFactory(null, "title");
35197 label.textContent = title;
35198 node.appendChild(label);
35202 var r = parseFloat(node.getAttributeNS(null, "r"));
35203 var widthFactor = 1;
35205 if (node._geometryClass == "OpenLayers.Geometry.Point" && r) {
35206 node.style.visibility = "";
35207 if (style.graphic === false) {
35208 node.style.visibility = "hidden";
35209 } else if (style.externalGraphic) {
35210 pos = this.getPosition(node);
35211 if (style.graphicWidth && style.graphicHeight) {
35212 node.setAttributeNS(null, "preserveAspectRatio", "none");
35214 var width = style.graphicWidth || style.graphicHeight;
35215 var height = style.graphicHeight || style.graphicWidth;
35216 width = width ? width : style.pointRadius*2;
35217 height = height ? height : style.pointRadius*2;
35218 var xOffset = (style.graphicXOffset != undefined) ?
35219 style.graphicXOffset : -(0.5 * width);
35220 var yOffset = (style.graphicYOffset != undefined) ?
35221 style.graphicYOffset : -(0.5 * height);
35223 var opacity = style.graphicOpacity || style.fillOpacity;
35225 node.setAttributeNS(null, "x", (pos.x + xOffset).toFixed());
35226 node.setAttributeNS(null, "y", (pos.y + yOffset).toFixed());
35227 node.setAttributeNS(null, "width", width);
35228 node.setAttributeNS(null, "height", height);
35229 node.setAttributeNS(this.xlinkns, "xlink:href", style.externalGraphic);
35230 node.setAttributeNS(null, "style", "opacity: "+opacity);
35231 node.onclick = OpenLayers.Event.preventDefault;
35232 } else if (this.isComplexSymbol(style.graphicName)) {
35233 // the symbol viewBox is three times as large as the symbol
35234 var offset = style.pointRadius * 3;
35235 var size = offset * 2;
35236 var src = this.importSymbol(style.graphicName);
35237 pos = this.getPosition(node);
35238 widthFactor = this.symbolMetrics[src.id][0] * 3 / size;
35240 // remove the node from the dom before we modify it. This
35241 // prevents various rendering issues in Safari and FF
35242 var parent = node.parentNode;
35243 var nextSibling = node.nextSibling;
35245 parent.removeChild(node);
35248 // The more appropriate way to implement this would be use/defs,
35249 // but due to various issues in several browsers, it is safer to
35250 // copy the symbols instead of referencing them.
35251 // See e.g. ticket http://trac.osgeo.org/openlayers/ticket/2985
35252 // and this email thread
35253 // http://osgeo-org.1803224.n2.nabble.com/Select-Control-Ctrl-click-on-Feature-with-a-graphicName-opens-new-browser-window-tc5846039.html
35254 node.firstChild && node.removeChild(node.firstChild);
35255 node.appendChild(src.firstChild.cloneNode(true));
35256 node.setAttributeNS(null, "viewBox", src.getAttributeNS(null, "viewBox"));
35258 node.setAttributeNS(null, "width", size);
35259 node.setAttributeNS(null, "height", size);
35260 node.setAttributeNS(null, "x", pos.x - offset);
35261 node.setAttributeNS(null, "y", pos.y - offset);
35263 // now that the node has all its new properties, insert it
35264 // back into the dom where it was
35266 parent.insertBefore(node, nextSibling);
35267 } else if(parent) {
35268 parent.appendChild(node);
35271 node.setAttributeNS(null, "r", style.pointRadius);
35274 var rotation = style.rotation;
35276 if ((rotation !== undefined || node._rotation !== undefined) && pos) {
35277 node._rotation = rotation;
35279 if (node.nodeName !== "svg") {
35280 node.setAttributeNS(null, "transform",
35281 "rotate(" + rotation + " " + pos.x + " " +
35284 var metrics = this.symbolMetrics[src.id];
35285 node.firstChild.setAttributeNS(null, "transform", "rotate("
35288 + metrics[2] + ")");
35293 if (options.isFilled) {
35294 node.setAttributeNS(null, "fill", style.fillColor);
35295 node.setAttributeNS(null, "fill-opacity", style.fillOpacity);
35297 node.setAttributeNS(null, "fill", "none");
35300 if (options.isStroked) {
35301 node.setAttributeNS(null, "stroke", style.strokeColor);
35302 node.setAttributeNS(null, "stroke-opacity", style.strokeOpacity);
35303 node.setAttributeNS(null, "stroke-width", style.strokeWidth * widthFactor);
35304 node.setAttributeNS(null, "stroke-linecap", style.strokeLinecap || "round");
35305 // Hard-coded linejoin for now, to make it look the same as in VML.
35306 // There is no strokeLinejoin property yet for symbolizers.
35307 node.setAttributeNS(null, "stroke-linejoin", "round");
35308 style.strokeDashstyle && node.setAttributeNS(null,
35309 "stroke-dasharray", this.dashStyle(style, widthFactor));
35311 node.setAttributeNS(null, "stroke", "none");
35314 if (style.pointerEvents) {
35315 node.setAttributeNS(null, "pointer-events", style.pointerEvents);
35318 if (style.cursor != null) {
35319 node.setAttributeNS(null, "cursor", style.cursor);
35326 * Method: dashStyle
35330 * widthFactor - {Number}
35333 * {String} A SVG compliant 'stroke-dasharray' value
35335 dashStyle: function(style, widthFactor) {
35336 var w = style.strokeWidth * widthFactor;
35337 var str = style.strokeDashstyle;
35342 return [1, 4 * w].join();
35344 return [4 * w, 4 * w].join();
35346 return [4 * w, 4 * w, 1, 4 * w].join();
35348 return [8 * w, 4 * w].join();
35349 case 'longdashdot':
35350 return [8 * w, 4 * w, 1, 4 * w].join();
35352 return OpenLayers.String.trim(str).replace(/\s+/g, ",");
35357 * Method: createNode
35360 * type - {String} Kind of node to draw
35361 * id - {String} Id for node
35364 * {DOMElement} A new node of the given type and id
35366 createNode: function(type, id) {
35367 var node = document.createElementNS(this.xmlns, type);
35369 node.setAttributeNS(null, "id", id);
35375 * Method: nodeTypeCompare
35378 * node - {SVGDomElement} An SVG element
35379 * type - {String} Kind of node
35382 * {Boolean} Whether or not the specified node is of the specified type
35384 nodeTypeCompare: function(node, type) {
35385 return (type == node.nodeName);
35389 * Method: createRenderRoot
35392 * {DOMElement} The specific render engine's root element
35394 createRenderRoot: function() {
35395 var svg = this.nodeFactory(this.container.id + "_svgRoot", "svg");
35396 svg.style.display = "block";
35401 * Method: createRoot
35404 * suffix - {String} suffix to append to the id
35409 createRoot: function(suffix) {
35410 return this.nodeFactory(this.container.id + suffix, "g");
35414 * Method: createDefs
35417 * {DOMElement} The element to which we'll add the symbol definitions
35419 createDefs: function() {
35420 var defs = this.nodeFactory(this.container.id + "_defs", "defs");
35421 this.rendererRoot.appendChild(defs);
35425 /**************************************
35427 * GEOMETRY DRAWING FUNCTIONS *
35429 **************************************/
35432 * Method: drawPoint
35433 * This method is only called by the renderer itself.
35436 * node - {DOMElement}
35437 * geometry - {<OpenLayers.Geometry>}
35440 * {DOMElement} or false if the renderer could not draw the point
35442 drawPoint: function(node, geometry) {
35443 return this.drawCircle(node, geometry, 1);
35447 * Method: drawCircle
35448 * This method is only called by the renderer itself.
35451 * node - {DOMElement}
35452 * geometry - {<OpenLayers.Geometry>}
35456 * {DOMElement} or false if the renderer could not draw the circle
35458 drawCircle: function(node, geometry, radius) {
35459 var resolution = this.getResolution();
35460 var x = ((geometry.x - this.featureDx) / resolution + this.left);
35461 var y = (this.top - geometry.y / resolution);
35463 if (this.inValidRange(x, y)) {
35464 node.setAttributeNS(null, "cx", x);
35465 node.setAttributeNS(null, "cy", y);
35466 node.setAttributeNS(null, "r", radius);
35475 * Method: drawLineString
35476 * This method is only called by the renderer itself.
35479 * node - {DOMElement}
35480 * geometry - {<OpenLayers.Geometry>}
35483 * {DOMElement} or null if the renderer could not draw all components of
35484 * the linestring, or false if nothing could be drawn
35486 drawLineString: function(node, geometry) {
35487 var componentsResult = this.getComponentsString(geometry.components);
35488 if (componentsResult.path) {
35489 node.setAttributeNS(null, "points", componentsResult.path);
35490 return (componentsResult.complete ? node : null);
35497 * Method: drawLinearRing
35498 * This method is only called by the renderer itself.
35501 * node - {DOMElement}
35502 * geometry - {<OpenLayers.Geometry>}
35505 * {DOMElement} or null if the renderer could not draw all components
35506 * of the linear ring, or false if nothing could be drawn
35508 drawLinearRing: function(node, geometry) {
35509 var componentsResult = this.getComponentsString(geometry.components);
35510 if (componentsResult.path) {
35511 node.setAttributeNS(null, "points", componentsResult.path);
35512 return (componentsResult.complete ? node : null);
35519 * Method: drawPolygon
35520 * This method is only called by the renderer itself.
35523 * node - {DOMElement}
35524 * geometry - {<OpenLayers.Geometry>}
35527 * {DOMElement} or null if the renderer could not draw all components
35528 * of the polygon, or false if nothing could be drawn
35530 drawPolygon: function(node, geometry) {
35533 var complete = true;
35534 var linearRingResult, path;
35535 for (var j=0, len=geometry.components.length; j<len; j++) {
35537 linearRingResult = this.getComponentsString(
35538 geometry.components[j].components, " ");
35539 path = linearRingResult.path;
35542 complete = linearRingResult.complete && complete;
35549 node.setAttributeNS(null, "d", d);
35550 node.setAttributeNS(null, "fill-rule", "evenodd");
35551 return complete ? node : null;
35558 * Method: drawRectangle
35559 * This method is only called by the renderer itself.
35562 * node - {DOMElement}
35563 * geometry - {<OpenLayers.Geometry>}
35566 * {DOMElement} or false if the renderer could not draw the rectangle
35568 drawRectangle: function(node, geometry) {
35569 var resolution = this.getResolution();
35570 var x = ((geometry.x - this.featureDx) / resolution + this.left);
35571 var y = (this.top - geometry.y / resolution);
35573 if (this.inValidRange(x, y)) {
35574 node.setAttributeNS(null, "x", x);
35575 node.setAttributeNS(null, "y", y);
35576 node.setAttributeNS(null, "width", geometry.width / resolution);
35577 node.setAttributeNS(null, "height", geometry.height / resolution);
35586 * This method is only called by the renderer itself.
35589 * featureId - {String}
35591 * location - {<OpenLayers.Geometry.Point>}
35593 drawText: function(featureId, style, location) {
35594 var drawOutline = (!!style.labelOutlineWidth);
35595 // First draw text in halo color and size and overlay the
35596 // normal text afterwards
35598 var outlineStyle = OpenLayers.Util.extend({}, style);
35599 outlineStyle.fontColor = outlineStyle.labelOutlineColor;
35600 outlineStyle.fontStrokeColor = outlineStyle.labelOutlineColor;
35601 outlineStyle.fontStrokeWidth = style.labelOutlineWidth;
35602 if (style.labelOutlineOpacity) {
35603 outlineStyle.fontOpacity = style.labelOutlineOpacity;
35605 delete outlineStyle.labelOutlineWidth;
35606 this.drawText(featureId, outlineStyle, location);
35609 var resolution = this.getResolution();
35611 var x = ((location.x - this.featureDx) / resolution + this.left);
35612 var y = (location.y / resolution - this.top);
35614 var suffix = (drawOutline)?this.LABEL_OUTLINE_SUFFIX:this.LABEL_ID_SUFFIX;
35615 var label = this.nodeFactory(featureId + suffix, "text");
35617 label.setAttributeNS(null, "x", x);
35618 label.setAttributeNS(null, "y", -y);
35620 if (style.fontColor) {
35621 label.setAttributeNS(null, "fill", style.fontColor);
35623 if (style.fontStrokeColor) {
35624 label.setAttributeNS(null, "stroke", style.fontStrokeColor);
35626 if (style.fontStrokeWidth) {
35627 label.setAttributeNS(null, "stroke-width", style.fontStrokeWidth);
35629 if (style.fontOpacity) {
35630 label.setAttributeNS(null, "opacity", style.fontOpacity);
35632 if (style.fontFamily) {
35633 label.setAttributeNS(null, "font-family", style.fontFamily);
35635 if (style.fontSize) {
35636 label.setAttributeNS(null, "font-size", style.fontSize);
35638 if (style.fontWeight) {
35639 label.setAttributeNS(null, "font-weight", style.fontWeight);
35641 if (style.fontStyle) {
35642 label.setAttributeNS(null, "font-style", style.fontStyle);
35644 if (style.labelSelect === true) {
35645 label.setAttributeNS(null, "pointer-events", "visible");
35646 label._featureId = featureId;
35648 label.setAttributeNS(null, "pointer-events", "none");
35650 var align = style.labelAlign || OpenLayers.Renderer.defaultSymbolizer.labelAlign;
35651 label.setAttributeNS(null, "text-anchor",
35652 OpenLayers.Renderer.SVG.LABEL_ALIGN[align[0]] || "middle");
35654 if (OpenLayers.IS_GECKO === true) {
35655 label.setAttributeNS(null, "dominant-baseline",
35656 OpenLayers.Renderer.SVG.LABEL_ALIGN[align[1]] || "central");
35659 var labelRows = style.label.split('\n');
35660 var numRows = labelRows.length;
35661 while (label.childNodes.length > numRows) {
35662 label.removeChild(label.lastChild);
35664 for (var i = 0; i < numRows; i++) {
35665 var tspan = this.nodeFactory(featureId + suffix + "_tspan_" + i, "tspan");
35666 if (style.labelSelect === true) {
35667 tspan._featureId = featureId;
35668 tspan._geometry = location;
35669 tspan._geometryClass = location.CLASS_NAME;
35671 if (OpenLayers.IS_GECKO === false) {
35672 tspan.setAttributeNS(null, "baseline-shift",
35673 OpenLayers.Renderer.SVG.LABEL_VSHIFT[align[1]] || "-35%");
35675 tspan.setAttribute("x", x);
35677 var vfactor = OpenLayers.Renderer.SVG.LABEL_VFACTOR[align[1]];
35678 if (vfactor == null) {
35681 tspan.setAttribute("dy", (vfactor*(numRows-1)) + "em");
35683 tspan.setAttribute("dy", "1em");
35685 tspan.textContent = (labelRows[i] === '') ? ' ' : labelRows[i];
35686 if (!tspan.parentNode) {
35687 label.appendChild(tspan);
35691 if (!label.parentNode) {
35692 this.textRoot.appendChild(label);
35697 * Method: getComponentString
35700 * components - {Array(<OpenLayers.Geometry.Point>)} Array of points
35701 * separator - {String} character between coordinate pairs. Defaults to ","
35704 * {Object} hash with properties "path" (the string created from the
35705 * components and "complete" (false if the renderer was unable to
35706 * draw all components)
35708 getComponentsString: function(components, separator) {
35709 var renderCmp = [];
35710 var complete = true;
35711 var len = components.length;
35713 var str, component;
35714 for(var i=0; i<len; i++) {
35715 component = components[i];
35716 renderCmp.push(component);
35717 str = this.getShortString(component);
35721 // The current component is outside the valid range. Let's
35722 // see if the previous or next component is inside the range.
35723 // If so, add the coordinate of the intersection with the
35724 // valid range bounds.
35726 if (this.getShortString(components[i - 1])) {
35727 strings.push(this.clipLine(components[i],
35732 if (this.getShortString(components[i + 1])) {
35733 strings.push(this.clipLine(components[i],
35742 path: strings.join(separator || ","),
35749 * Given two points (one inside the valid range, and one outside),
35750 * clips the line betweeen the two points so that the new points are both
35751 * inside the valid range.
35754 * badComponent - {<OpenLayers.Geometry.Point>} original geometry of the
35756 * goodComponent - {<OpenLayers.Geometry.Point>} original geometry of the
35759 * {String} the SVG coordinate pair of the clipped point (like
35760 * getShortString), or an empty string if both passed componets are at
35763 clipLine: function(badComponent, goodComponent) {
35764 if (goodComponent.equals(badComponent)) {
35767 var resolution = this.getResolution();
35768 var maxX = this.MAX_PIXEL - this.translationParameters.x;
35769 var maxY = this.MAX_PIXEL - this.translationParameters.y;
35770 var x1 = (goodComponent.x - this.featureDx) / resolution + this.left;
35771 var y1 = this.top - goodComponent.y / resolution;
35772 var x2 = (badComponent.x - this.featureDx) / resolution + this.left;
35773 var y2 = this.top - badComponent.y / resolution;
35775 if (x2 < -maxX || x2 > maxX) {
35776 k = (y2 - y1) / (x2 - x1);
35777 x2 = x2 < 0 ? -maxX : maxX;
35778 y2 = y1 + (x2 - x1) * k;
35780 if (y2 < -maxY || y2 > maxY) {
35781 k = (x2 - x1) / (y2 - y1);
35782 y2 = y2 < 0 ? -maxY : maxY;
35783 x2 = x1 + (y2 - y1) * k;
35785 return x2 + "," + y2;
35789 * Method: getShortString
35792 * point - {<OpenLayers.Geometry.Point>}
35795 * {String} or false if point is outside the valid range
35797 getShortString: function(point) {
35798 var resolution = this.getResolution();
35799 var x = ((point.x - this.featureDx) / resolution + this.left);
35800 var y = (this.top - point.y / resolution);
35802 if (this.inValidRange(x, y)) {
35803 return x + "," + y;
35810 * Method: getPosition
35811 * Finds the position of an svg node.
35814 * node - {DOMElement}
35817 * {Object} hash with x and y properties, representing the coordinates
35818 * within the svg coordinate system
35820 getPosition: function(node) {
35822 x: parseFloat(node.getAttributeNS(null, "cx")),
35823 y: parseFloat(node.getAttributeNS(null, "cy"))
35828 * Method: importSymbol
35829 * add a new symbol definition from the rendererer's symbol hash
35832 * graphicName - {String} name of the symbol to import
35835 * {DOMElement} - the imported symbol
35837 importSymbol: function (graphicName) {
35839 // create svg defs tag
35840 this.defs = this.createDefs();
35842 var id = this.container.id + "-" + graphicName;
35844 // check if symbol already exists in the defs
35845 var existing = document.getElementById(id);
35846 if (existing != null) {
35850 var symbol = OpenLayers.Renderer.symbol[graphicName];
35852 throw new Error(graphicName + ' is not a valid symbol name');
35855 var symbolNode = this.nodeFactory(id, "symbol");
35856 var node = this.nodeFactory(null, "polygon");
35857 symbolNode.appendChild(node);
35858 var symbolExtent = new OpenLayers.Bounds(
35859 Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
35863 for (var i=0; i<symbol.length; i=i+2) {
35866 symbolExtent.left = Math.min(symbolExtent.left, x);
35867 symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
35868 symbolExtent.right = Math.max(symbolExtent.right, x);
35869 symbolExtent.top = Math.max(symbolExtent.top, y);
35870 points.push(x, ",", y);
35873 node.setAttributeNS(null, "points", points.join(" "));
35875 var width = symbolExtent.getWidth();
35876 var height = symbolExtent.getHeight();
35877 // create a viewBox three times as large as the symbol itself,
35878 // to allow for strokeWidth being displayed correctly at the corners.
35879 var viewBox = [symbolExtent.left - width,
35880 symbolExtent.bottom - height, width * 3, height * 3];
35881 symbolNode.setAttributeNS(null, "viewBox", viewBox.join(" "));
35882 this.symbolMetrics[id] = [
35883 Math.max(width, height),
35884 symbolExtent.getCenterLonLat().lon,
35885 symbolExtent.getCenterLonLat().lat
35888 this.defs.appendChild(symbolNode);
35893 * Method: getFeatureIdFromEvent
35896 * evt - {Object} An <OpenLayers.Event> object
35899 * {String} A feature id or undefined.
35901 getFeatureIdFromEvent: function(evt) {
35902 var featureId = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent.apply(this, arguments);
35904 var target = evt.target;
35905 featureId = target.parentNode && target != this.rendererRoot ?
35906 target.parentNode._featureId : undefined;
35911 CLASS_NAME: "OpenLayers.Renderer.SVG"
35915 * Constant: OpenLayers.Renderer.SVG.LABEL_ALIGN
35918 OpenLayers.Renderer.SVG.LABEL_ALIGN = {
35926 * Constant: OpenLayers.Renderer.SVG.LABEL_VSHIFT
35929 OpenLayers.Renderer.SVG.LABEL_VSHIFT = {
35931 // http://www.w3.org/Graphics/SVG/Test/20061213/htmlObjectHarness/full-text-align-02-b.html
35932 // a baseline-shift of -70% shifts the text exactly from the
35933 // bottom to the top of the baseline, so -35% moves the text to
35934 // the center of the baseline.
35940 * Constant: OpenLayers.Renderer.SVG.LABEL_VFACTOR
35943 OpenLayers.Renderer.SVG.LABEL_VFACTOR = {
35949 * Function: OpenLayers.Renderer.SVG.preventDefault
35950 * *Deprecated*. Use <OpenLayers.Event.preventDefault> method instead.
35951 * Used to prevent default events (especially opening images in a new tab on
35952 * ctrl-click) from being executed for externalGraphic symbols
35954 OpenLayers.Renderer.SVG.preventDefault = function(e) {
35955 OpenLayers.Event.preventDefault(e);
35957 /* ======================================================================
35958 OpenLayers/Control/PanZoom.js
35959 ====================================================================== */
35961 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
35962 * full list of contributors). Published under the 2-clause BSD license.
35963 * See license.txt in the OpenLayers distribution or repository for the
35964 * full text of the license. */
35968 * @requires OpenLayers/Control.js
35969 * @requires OpenLayers/Events/buttonclick.js
35973 * Class: OpenLayers.Control.PanZoom
35974 * The PanZoom is a visible control, composed of a
35975 * <OpenLayers.Control.PanPanel> and a <OpenLayers.Control.ZoomPanel>. By
35976 * default it is drawn in the upper left corner of the map.
35979 * - <OpenLayers.Control>
35981 OpenLayers.Control.PanZoom = OpenLayers.Class(OpenLayers.Control, {
35984 * APIProperty: slideFactor
35985 * {Integer} Number of pixels by which we'll pan the map in any direction
35986 * on clicking the arrow buttons. If you want to pan by some ratio
35987 * of the map dimensions, use <slideRatio> instead.
35992 * APIProperty: slideRatio
35993 * {Number} The fraction of map width/height by which we'll pan the map
35994 * on clicking the arrow buttons. Default is null. If set, will
35995 * override <slideFactor>. E.g. if slideRatio is .5, then the Pan Up
35996 * button will pan up half the map height.
36001 * Property: buttons
36002 * {Array(DOMElement)} Array of Button Divs
36007 * Property: position
36008 * {<OpenLayers.Pixel>}
36013 * Constructor: OpenLayers.Control.PanZoom
36016 * options - {Object}
36018 initialize: function(options) {
36019 this.position = new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X,
36020 OpenLayers.Control.PanZoom.Y);
36021 OpenLayers.Control.prototype.initialize.apply(this, arguments);
36025 * APIMethod: destroy
36027 destroy: function() {
36029 this.map.events.unregister("buttonclick", this, this.onButtonClick);
36031 this.removeButtons();
36032 this.buttons = null;
36033 this.position = null;
36034 OpenLayers.Control.prototype.destroy.apply(this, arguments);
36041 * map - {<OpenLayers.Map>}
36043 setMap: function(map) {
36044 OpenLayers.Control.prototype.setMap.apply(this, arguments);
36045 this.map.events.register("buttonclick", this, this.onButtonClick);
36052 * px - {<OpenLayers.Pixel>}
36055 * {DOMElement} A reference to the container div for the PanZoom control.
36057 draw: function(px) {
36058 // initialize our internal div
36059 OpenLayers.Control.prototype.draw.apply(this, arguments);
36060 px = this.position;
36062 // place the controls
36065 var sz = {w: 18, h: 18};
36066 var centered = new OpenLayers.Pixel(px.x+sz.w/2, px.y);
36068 this._addButton("panup", "north-mini.png", centered, sz);
36069 px.y = centered.y+sz.h;
36070 this._addButton("panleft", "west-mini.png", px, sz);
36071 this._addButton("panright", "east-mini.png", px.add(sz.w, 0), sz);
36072 this._addButton("pandown", "south-mini.png",
36073 centered.add(0, sz.h*2), sz);
36074 this._addButton("zoomin", "zoom-plus-mini.png",
36075 centered.add(0, sz.h*3+5), sz);
36076 this._addButton("zoomworld", "zoom-world-mini.png",
36077 centered.add(0, sz.h*4+5), sz);
36078 this._addButton("zoomout", "zoom-minus-mini.png",
36079 centered.add(0, sz.h*5+5), sz);
36084 * Method: _addButton
36089 * xy - {<OpenLayers.Pixel>}
36090 * sz - {<OpenLayers.Size>}
36093 * {DOMElement} A Div (an alphaImageDiv, to be precise) that contains the
36094 * image of the button, and has all the proper event handlers set.
36096 _addButton:function(id, img, xy, sz) {
36097 var imgLocation = OpenLayers.Util.getImageLocation(img);
36098 var btn = OpenLayers.Util.createAlphaImageDiv(
36099 this.id + "_" + id,
36100 xy, sz, imgLocation, "absolute");
36101 btn.style.cursor = "pointer";
36102 //we want to add the outer div
36103 this.div.appendChild(btn);
36105 btn.className = "olButton";
36107 //we want to remember/reference the outer div
36108 this.buttons.push(btn);
36113 * Method: _removeButton
36118 _removeButton: function(btn) {
36119 this.div.removeChild(btn);
36120 OpenLayers.Util.removeItem(this.buttons, btn);
36124 * Method: removeButtons
36126 removeButtons: function() {
36127 for(var i=this.buttons.length-1; i>=0; --i) {
36128 this._removeButton(this.buttons[i]);
36133 * Method: onButtonClick
36138 onButtonClick: function(evt) {
36139 var btn = evt.buttonElement;
36140 switch (btn.action) {
36142 this.map.pan(0, -this.getSlideFactor("h"));
36145 this.map.pan(0, this.getSlideFactor("h"));
36148 this.map.pan(-this.getSlideFactor("w"), 0);
36151 this.map.pan(this.getSlideFactor("w"), 0);
36157 this.map.zoomOut();
36160 this.map.zoomToMaxExtent();
36166 * Method: getSlideFactor
36169 * dim - {String} "w" or "h" (for width or height).
36172 * {Number} The slide factor for panning in the requested direction.
36174 getSlideFactor: function(dim) {
36175 return this.slideRatio ?
36176 this.map.getSize()[dim] * this.slideRatio :
36180 CLASS_NAME: "OpenLayers.Control.PanZoom"
36187 OpenLayers.Control.PanZoom.X = 4;
36193 OpenLayers.Control.PanZoom.Y = 4;
36194 /* ======================================================================
36196 ====================================================================== */
36198 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
36199 * full list of contributors). Published under the 2-clause BSD license.
36200 * See license.txt in the OpenLayers distribution or repository for the
36201 * full text of the license. */
36204 * @requires OpenLayers/BaseTypes/Class.js
36208 * Class: OpenLayers.Icon
36210 * The icon represents a graphical icon on the screen. Typically used in
36211 * conjunction with a <OpenLayers.Marker> to represent markers on a screen.
36213 * An icon has a url, size and position. It also contains an offset which
36214 * allows the center point to be represented correctly. This can be
36215 * provided either as a fixed offset or a function provided to calculate
36216 * the desired offset.
36219 OpenLayers.Icon = OpenLayers.Class({
36223 * {String} image url
36229 * {<OpenLayers.Size>|Object} An OpenLayers.Size or
36230 * an object with a 'w' and 'h' properties.
36236 * {<OpenLayers.Pixel>|Object} distance in pixels to offset the
36237 * image when being rendered. An OpenLayers.Pixel or an object
36238 * with a 'x' and 'y' properties.
36243 * Property: calculateOffset
36244 * {Function} Function to calculate the offset (based on the size)
36246 calculateOffset: null,
36249 * Property: imageDiv
36256 * {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object
36257 * with a 'x' and 'y' properties.
36262 * Constructor: OpenLayers.Icon
36263 * Creates an icon, which is an image tag in a div.
36266 * size - {<OpenLayers.Size>|Object} An OpenLayers.Size or an
36267 * object with a 'w' and 'h'
36269 * offset - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an
36270 * object with a 'x' and 'y'
36272 * calculateOffset - {Function}
36274 initialize: function(url, size, offset, calculateOffset) {
36276 this.size = size || {w: 20, h: 20};
36277 this.offset = offset || {x: -(this.size.w/2), y: -(this.size.h/2)};
36278 this.calculateOffset = calculateOffset;
36280 var id = OpenLayers.Util.createUniqueID("OL_Icon_");
36281 this.imageDiv = OpenLayers.Util.createAlphaImageDiv(id);
36286 * Nullify references and remove event listeners to prevent circular
36287 * references and memory leaks
36289 destroy: function() {
36290 // erase any drawn elements
36293 OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild);
36294 this.imageDiv.innerHTML = "";
36295 this.imageDiv = null;
36302 * {<OpenLayers.Icon>} A fresh copy of the icon.
36304 clone: function() {
36305 return new OpenLayers.Icon(this.url,
36308 this.calculateOffset);
36315 * size - {<OpenLayers.Size>|Object} An OpenLayers.Size or
36316 * an object with a 'w' and 'h' properties.
36318 setSize: function(size) {
36319 if (size != null) {
36331 setUrl: function(url) {
36340 * Move the div to the given pixel.
36343 * px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an
36344 * object with a 'x' and 'y' properties.
36347 * {DOMElement} A new DOM Image of this icon set at the location passed-in
36349 draw: function(px) {
36350 OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv,
36357 return this.imageDiv;
36362 * Erase the underlying image element.
36364 erase: function() {
36365 if (this.imageDiv != null && this.imageDiv.parentNode != null) {
36366 OpenLayers.Element.remove(this.imageDiv);
36371 * Method: setOpacity
36372 * Change the icon's opacity
36375 * opacity - {float}
36377 setOpacity: function(opacity) {
36378 OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, null,
36379 null, null, null, null, opacity);
36385 * move icon to passed in px.
36388 * px - {<OpenLayers.Pixel>|Object} the pixel position to move to.
36389 * An OpenLayers.Pixel or an object with a 'x' and 'y' properties.
36391 moveTo: function (px) {
36392 //if no px passed in, use stored location
36397 if (this.imageDiv != null) {
36398 if (this.px == null) {
36399 this.display(false);
36401 if (this.calculateOffset) {
36402 this.offset = this.calculateOffset(this.size);
36404 OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, {
36405 x: this.px.x + this.offset.x,
36406 y: this.px.y + this.offset.y
36414 * Hide or show the icon
36417 * display - {Boolean}
36419 display: function(display) {
36420 this.imageDiv.style.display = (display) ? "" : "none";
36425 * APIMethod: isDrawn
36428 * {Boolean} Whether or not the icon is drawn.
36430 isDrawn: function() {
36431 // nodeType 11 for ie, whose nodes *always* have a parentNode
36432 // (of type document fragment)
36433 var isDrawn = (this.imageDiv && this.imageDiv.parentNode &&
36434 (this.imageDiv.parentNode.nodeType != 11));
36439 CLASS_NAME: "OpenLayers.Icon"
36441 /* ======================================================================
36442 OpenLayers/Marker.js
36443 ====================================================================== */
36445 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
36446 * full list of contributors). Published under the 2-clause BSD license.
36447 * See license.txt in the OpenLayers distribution or repository for the
36448 * full text of the license. */
36452 * @requires OpenLayers/BaseTypes/Class.js
36453 * @requires OpenLayers/Events.js
36454 * @requires OpenLayers/Icon.js
36458 * Class: OpenLayers.Marker
36459 * Instances of OpenLayers.Marker are a combination of a
36460 * <OpenLayers.LonLat> and an <OpenLayers.Icon>.
36462 * Markers are generally added to a special layer called
36463 * <OpenLayers.Layer.Markers>.
36467 * var markers = new OpenLayers.Layer.Markers( "Markers" );
36468 * map.addLayer(markers);
36470 * var size = new OpenLayers.Size(21,25);
36471 * var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
36472 * var icon = new OpenLayers.Icon('http://www.openlayers.org/dev/img/marker.png', size, offset);
36473 * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon));
36474 * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon.clone()));
36478 * Note that if you pass an icon into the Marker constructor, it will take
36479 * that icon and use it. This means that you should not share icons between
36480 * markers -- you use them once, but you should clone() for any additional
36481 * markers using that same icon.
36483 OpenLayers.Marker = OpenLayers.Class({
36487 * {<OpenLayers.Icon>} The icon used by this marker.
36493 * {<OpenLayers.LonLat>} location of object
36499 * {<OpenLayers.Events>} the event handler.
36505 * {<OpenLayers.Map>} the map this marker is attached to
36510 * Constructor: OpenLayers.Marker
36513 * lonlat - {<OpenLayers.LonLat>} the position of this marker
36514 * icon - {<OpenLayers.Icon>} the icon for this marker
36516 initialize: function(lonlat, icon) {
36517 this.lonlat = lonlat;
36519 var newIcon = (icon) ? icon : OpenLayers.Marker.defaultIcon();
36520 if (this.icon == null) {
36521 this.icon = newIcon;
36523 this.icon.url = newIcon.url;
36524 this.icon.size = newIcon.size;
36525 this.icon.offset = newIcon.offset;
36526 this.icon.calculateOffset = newIcon.calculateOffset;
36528 this.events = new OpenLayers.Events(this, this.icon.imageDiv);
36532 * APIMethod: destroy
36533 * Destroy the marker. You must first remove the marker from any
36534 * layer which it has been added to, or you will get buggy behavior.
36535 * (This can not be done within the marker since the marker does not
36536 * know which layer it is attached to.)
36538 destroy: function() {
36539 // erase any drawn features
36544 this.events.destroy();
36545 this.events = null;
36547 if (this.icon != null) {
36548 this.icon.destroy();
36555 * Calls draw on the icon, and returns that output.
36558 * px - {<OpenLayers.Pixel>}
36561 * {DOMElement} A new DOM Image with this marker's icon set at the
36562 * location passed-in
36564 draw: function(px) {
36565 return this.icon.draw(px);
36570 * Erases any drawn elements for this marker.
36572 erase: function() {
36573 if (this.icon != null) {
36580 * Move the marker to the new location.
36583 * px - {<OpenLayers.Pixel>|Object} the pixel position to move to.
36584 * An OpenLayers.Pixel or an object with a 'x' and 'y' properties.
36586 moveTo: function (px) {
36587 if ((px != null) && (this.icon != null)) {
36588 this.icon.moveTo(px);
36590 this.lonlat = this.map.getLonLatFromLayerPx(px);
36594 * APIMethod: isDrawn
36597 * {Boolean} Whether or not the marker is drawn.
36599 isDrawn: function() {
36600 var isDrawn = (this.icon && this.icon.isDrawn());
36608 * {Boolean} Whether or not the marker is currently visible on screen.
36610 onScreen:function() {
36612 var onScreen = false;
36614 var screenBounds = this.map.getExtent();
36615 onScreen = screenBounds.containsLonLat(this.lonlat);
36622 * Englarges the markers icon by the specified ratio.
36625 * inflate - {float} the ratio to enlarge the marker by (passing 2
36626 * will double the size).
36628 inflate: function(inflate) {
36630 this.icon.setSize({
36631 w: this.icon.size.w * inflate,
36632 h: this.icon.size.h * inflate
36638 * Method: setOpacity
36639 * Change the opacity of the marker by changin the opacity of
36643 * opacity - {float} Specified as fraction (0.4, etc)
36645 setOpacity: function(opacity) {
36646 this.icon.setOpacity(opacity);
36651 * Change URL of the Icon Image.
36655 setUrl: function(url) {
36656 this.icon.setUrl(url);
36661 * Hide or show the icon
36663 * display - {Boolean}
36665 display: function(display) {
36666 this.icon.display(display);
36669 CLASS_NAME: "OpenLayers.Marker"
36674 * Function: defaultIcon
36675 * Creates a default <OpenLayers.Icon>.
36678 * {<OpenLayers.Icon>} A default OpenLayers.Icon to use for a marker
36680 OpenLayers.Marker.defaultIcon = function() {
36681 return new OpenLayers.Icon(OpenLayers.Util.getImageLocation("marker.png"),
36682 {w: 21, h: 25}, {x: -10.5, y: -25});
36686 /* ======================================================================
36687 OpenLayers/Popup.js
36688 ====================================================================== */
36690 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
36691 * full list of contributors). Published under the 2-clause BSD license.
36692 * See license.txt in the OpenLayers distribution or repository for the
36693 * full text of the license. */
36696 * @requires OpenLayers/BaseTypes/Class.js
36701 * Class: OpenLayers.Popup
36702 * A popup is a small div that can opened and closed on the map.
36703 * Typically opened in response to clicking on a marker.
36704 * See <OpenLayers.Marker>. Popup's don't require their own
36705 * layer and are added the the map using the <OpenLayers.Map.addPopup>
36710 * popup = new OpenLayers.Popup("chicken",
36711 * new OpenLayers.LonLat(5,40),
36712 * new OpenLayers.Size(200,200),
36716 * map.addPopup(popup);
36719 OpenLayers.Popup = OpenLayers.Class({
36723 * {<OpenLayers.Events>} custom event manager
36728 * {String} the unique identifier assigned to this popup.
36734 * {<OpenLayers.LonLat>} the position of this popup on the map
36740 * {DOMElement} the div that contains this popup.
36745 * Property: contentSize
36746 * {<OpenLayers.Size>} the width and height of the content.
36752 * {<OpenLayers.Size>} the width and height of the popup.
36757 * Property: contentHTML
36758 * {String} An HTML string for this popup to display.
36763 * Property: backgroundColor
36764 * {String} the background color used by the popup.
36766 backgroundColor: "",
36769 * Property: opacity
36770 * {float} the opacity of this popup (between 0.0 and 1.0)
36776 * {String} the border size of the popup. (eg 2px)
36781 * Property: contentDiv
36782 * {DOMElement} a reference to the element that holds the content of
36788 * Property: groupDiv
36789 * {DOMElement} First and only child of 'div'. The group Div contains the
36790 * 'contentDiv' and the 'closeDiv'.
36795 * Property: closeDiv
36796 * {DOMElement} the optional closer image
36801 * APIProperty: autoSize
36802 * {Boolean} Resize the popup to auto-fit the contents.
36803 * Default is false.
36808 * APIProperty: minSize
36809 * {<OpenLayers.Size>} Minimum size allowed for the popup's contents.
36814 * APIProperty: maxSize
36815 * {<OpenLayers.Size>} Maximum size allowed for the popup's contents.
36820 * Property: displayClass
36821 * {String} The CSS class of the popup.
36823 displayClass: "olPopup",
36826 * Property: contentDisplayClass
36827 * {String} The CSS class of the popup content div.
36829 contentDisplayClass: "olPopupContent",
36832 * Property: padding
36833 * {int or <OpenLayers.Bounds>} An extra opportunity to specify internal
36834 * padding of the content div inside the popup. This was originally
36835 * confused with the css padding as specified in style.css's
36836 * 'olPopupContent' class. We would like to get rid of this altogether,
36837 * except that it does come in handy for the framed and anchoredbubble
36838 * popups, who need to maintain yet another barrier between their
36839 * content and the outer border of the popup itself.
36841 * Note that in order to not break API, we must continue to support
36842 * this property being set as an integer. Really, though, we'd like to
36843 * have this specified as a Bounds object so that user can specify
36844 * distinct left, top, right, bottom paddings. With the 3.0 release
36845 * we can make this only a bounds.
36850 * Property: disableFirefoxOverflowHack
36851 * {Boolean} The hack for overflow in Firefox causes all elements
36852 * to be re-drawn, which causes Flash elements to be
36853 * re-initialized, which is troublesome.
36854 * With this property the hack can be disabled.
36856 disableFirefoxOverflowHack: false,
36859 * Method: fixPadding
36860 * To be removed in 3.0, this function merely helps us to deal with the
36861 * case where the user may have set an integer value for padding,
36862 * instead of an <OpenLayers.Bounds> object.
36864 fixPadding: function() {
36865 if (typeof this.padding == "number") {
36866 this.padding = new OpenLayers.Bounds(
36867 this.padding, this.padding, this.padding, this.padding
36873 * APIProperty: panMapIfOutOfView
36874 * {Boolean} When drawn, pan map such that the entire popup is visible in
36875 * the current viewport (if necessary).
36876 * Default is false.
36878 panMapIfOutOfView: false,
36881 * APIProperty: keepInMap
36882 * {Boolean} If panMapIfOutOfView is false, and this property is true,
36883 * contrain the popup such that it always fits in the available map
36884 * space. By default, this is not set on the base class. If you are
36885 * creating popups that are near map edges and not allowing pannning,
36886 * and especially if you have a popup which has a
36887 * fixedRelativePosition, setting this to false may be a smart thing to
36888 * do. Subclasses may want to override this setting.
36890 * Default is false.
36895 * APIProperty: closeOnMove
36896 * {Boolean} When map pans, close the popup.
36897 * Default is false.
36899 closeOnMove: false,
36903 * {<OpenLayers.Map>} this gets set in Map.js when the popup is added to the map
36908 * Constructor: OpenLayers.Popup
36912 * id - {String} a unqiue identifier for this popup. If null is passed
36913 * an identifier will be automatically generated.
36914 * lonlat - {<OpenLayers.LonLat>} The position on the map the popup will
36916 * contentSize - {<OpenLayers.Size>} The size of the content.
36917 * contentHTML - {String} An HTML string to display inside the
36919 * closeBox - {Boolean} Whether to display a close box inside
36921 * closeBoxCallback - {Function} Function to be called on closeBox click.
36923 initialize:function(id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback) {
36925 id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
36929 this.lonlat = lonlat;
36931 this.contentSize = (contentSize != null) ? contentSize
36932 : new OpenLayers.Size(
36933 OpenLayers.Popup.WIDTH,
36934 OpenLayers.Popup.HEIGHT);
36935 if (contentHTML != null) {
36936 this.contentHTML = contentHTML;
36938 this.backgroundColor = OpenLayers.Popup.COLOR;
36939 this.opacity = OpenLayers.Popup.OPACITY;
36940 this.border = OpenLayers.Popup.BORDER;
36942 this.div = OpenLayers.Util.createDiv(this.id, null, null,
36943 null, null, null, "hidden");
36944 this.div.className = this.displayClass;
36946 var groupDivId = this.id + "_GroupDiv";
36947 this.groupDiv = OpenLayers.Util.createDiv(groupDivId, null, null,
36948 null, "relative", null,
36951 var id = this.div.id + "_contentDiv";
36952 this.contentDiv = OpenLayers.Util.createDiv(id, null, this.contentSize.clone(),
36954 this.contentDiv.className = this.contentDisplayClass;
36955 this.groupDiv.appendChild(this.contentDiv);
36956 this.div.appendChild(this.groupDiv);
36959 this.addCloseBox(closeBoxCallback);
36962 this.registerEvents();
36967 * nullify references to prevent circular references and memory leaks
36969 destroy: function() {
36972 this.lonlat = null;
36974 this.contentHTML = null;
36976 this.backgroundColor = null;
36977 this.opacity = null;
36978 this.border = null;
36980 if (this.closeOnMove && this.map) {
36981 this.map.events.unregister("movestart", this, this.hide);
36984 this.events.destroy();
36985 this.events = null;
36987 if (this.closeDiv) {
36988 OpenLayers.Event.stopObservingElement(this.closeDiv);
36989 this.groupDiv.removeChild(this.closeDiv);
36991 this.closeDiv = null;
36993 this.div.removeChild(this.groupDiv);
36994 this.groupDiv = null;
36996 if (this.map != null) {
36997 this.map.removePopup(this);
37002 this.autoSize = null;
37003 this.minSize = null;
37004 this.maxSize = null;
37005 this.padding = null;
37006 this.panMapIfOutOfView = null;
37011 * Constructs the elements that make up the popup.
37014 * px - {<OpenLayers.Pixel>} the position the popup in pixels.
37017 * {DOMElement} Reference to a div that contains the drawn popup
37019 draw: function(px) {
37021 if ((this.lonlat != null) && (this.map != null)) {
37022 px = this.map.getLayerPxFromLonLat(this.lonlat);
37026 // this assumes that this.map already exists, which is okay because
37027 // this.draw is only called once the popup has been added to the map.
37028 if (this.closeOnMove) {
37029 this.map.events.register("movestart", this, this.hide);
37032 //listen to movestart, moveend to disable overflow (FF bug)
37033 if (!this.disableFirefoxOverflowHack && OpenLayers.BROWSER_NAME == 'firefox') {
37034 this.map.events.register("movestart", this, function() {
37035 var style = document.defaultView.getComputedStyle(
37036 this.contentDiv, null
37038 var currentOverflow = style.getPropertyValue("overflow");
37039 if (currentOverflow != "hidden") {
37040 this.contentDiv._oldOverflow = currentOverflow;
37041 this.contentDiv.style.overflow = "hidden";
37044 this.map.events.register("moveend", this, function() {
37045 var oldOverflow = this.contentDiv._oldOverflow;
37047 this.contentDiv.style.overflow = oldOverflow;
37048 this.contentDiv._oldOverflow = null;
37054 if (!this.autoSize && !this.size) {
37055 this.setSize(this.contentSize);
37057 this.setBackgroundColor();
37060 this.setContentHTML();
37062 if (this.panMapIfOutOfView) {
37063 this.panIntoView();
37070 * Method: updatePosition
37071 * if the popup has a lonlat and its map members set,
37072 * then have it move itself to its proper position
37074 updatePosition: function() {
37075 if ((this.lonlat) && (this.map)) {
37076 var px = this.map.getLayerPxFromLonLat(this.lonlat);
37087 * px - {<OpenLayers.Pixel>} the top and left position of the popup div.
37089 moveTo: function(px) {
37090 if ((px != null) && (this.div != null)) {
37091 this.div.style.left = px.x + "px";
37092 this.div.style.top = px.y + "px";
37100 * {Boolean} Boolean indicating whether or not the popup is visible
37102 visible: function() {
37103 return OpenLayers.Element.visible(this.div);
37108 * Toggles visibility of the popup.
37110 toggle: function() {
37111 if (this.visible()) {
37120 * Makes the popup visible.
37123 this.div.style.display = '';
37125 if (this.panMapIfOutOfView) {
37126 this.panIntoView();
37132 * Makes the popup invisible.
37135 this.div.style.display = 'none';
37140 * Used to adjust the size of the popup.
37143 * contentSize - {<OpenLayers.Size>} the new size for the popup's
37144 * contents div (in pixels).
37146 setSize:function(contentSize) {
37147 this.size = contentSize.clone();
37149 // if our contentDiv has a css 'padding' set on it by a stylesheet, we
37150 // must add that to the desired "size".
37151 var contentDivPadding = this.getContentDivPadding();
37152 var wPadding = contentDivPadding.left + contentDivPadding.right;
37153 var hPadding = contentDivPadding.top + contentDivPadding.bottom;
37155 // take into account the popup's 'padding' property
37157 wPadding += this.padding.left + this.padding.right;
37158 hPadding += this.padding.top + this.padding.bottom;
37160 // make extra space for the close div
37161 if (this.closeDiv) {
37162 var closeDivWidth = parseInt(this.closeDiv.style.width);
37163 wPadding += closeDivWidth + contentDivPadding.right;
37166 //increase size of the main popup div to take into account the
37167 // users's desired padding and close div.
37168 this.size.w += wPadding;
37169 this.size.h += hPadding;
37171 //now if our browser is IE, we need to actually make the contents
37172 // div itself bigger to take its own padding into effect. this makes
37173 // me want to shoot someone, but so it goes.
37174 if (OpenLayers.BROWSER_NAME == "msie") {
37175 this.contentSize.w +=
37176 contentDivPadding.left + contentDivPadding.right;
37177 this.contentSize.h +=
37178 contentDivPadding.bottom + contentDivPadding.top;
37181 if (this.div != null) {
37182 this.div.style.width = this.size.w + "px";
37183 this.div.style.height = this.size.h + "px";
37185 if (this.contentDiv != null){
37186 this.contentDiv.style.width = contentSize.w + "px";
37187 this.contentDiv.style.height = contentSize.h + "px";
37192 * APIMethod: updateSize
37193 * Auto size the popup so that it precisely fits its contents (as
37194 * determined by this.contentDiv.innerHTML). Popup size will, of
37195 * course, be limited by the available space on the current map
37197 updateSize: function() {
37199 // determine actual render dimensions of the contents by putting its
37200 // contents into a fake contentDiv (for the CSS) and then measuring it
37201 var preparedHTML = "<div class='" + this.contentDisplayClass+ "'>" +
37202 this.contentDiv.innerHTML +
37205 var containerElement = (this.map) ? this.map.div : document.body;
37206 var realSize = OpenLayers.Util.getRenderedDimensions(
37207 preparedHTML, null, {
37208 displayClass: this.displayClass,
37209 containerElement: containerElement
37213 // is the "real" size of the div is safe to display in our map?
37214 var safeSize = this.getSafeContentSize(realSize);
37216 var newSize = null;
37217 if (safeSize.equals(realSize)) {
37218 //real size of content is small enough to fit on the map,
37219 // so we use real size.
37220 newSize = realSize;
37224 // make a new 'size' object with the clipped dimensions
37225 // set or null if not clipped.
37227 w: (safeSize.w < realSize.w) ? safeSize.w : null,
37228 h: (safeSize.h < realSize.h) ? safeSize.h : null
37231 if (fixedSize.w && fixedSize.h) {
37232 //content is too big in both directions, so we will use
37233 // max popup size (safeSize), knowing well that it will
37234 // overflow both ways.
37235 newSize = safeSize;
37237 //content is clipped in only one direction, so we need to
37238 // run getRenderedDimensions() again with a fixed dimension
37239 var clippedSize = OpenLayers.Util.getRenderedDimensions(
37240 preparedHTML, fixedSize, {
37241 displayClass: this.contentDisplayClass,
37242 containerElement: containerElement
37246 //if the clipped size is still the same as the safeSize,
37247 // that means that our content must be fixed in the
37248 // offending direction. If overflow is 'auto', this means
37249 // we are going to have a scrollbar for sure, so we must
37250 // adjust for that.
37252 var currentOverflow = OpenLayers.Element.getStyle(
37253 this.contentDiv, "overflow"
37255 if ( (currentOverflow != "hidden") &&
37256 (clippedSize.equals(safeSize)) ) {
37257 var scrollBar = OpenLayers.Util.getScrollbarWidth();
37259 clippedSize.h += scrollBar;
37261 clippedSize.w += scrollBar;
37265 newSize = this.getSafeContentSize(clippedSize);
37268 this.setSize(newSize);
37272 * Method: setBackgroundColor
37273 * Sets the background color of the popup.
37276 * color - {String} the background color. eg "#FFBBBB"
37278 setBackgroundColor:function(color) {
37279 if (color != undefined) {
37280 this.backgroundColor = color;
37283 if (this.div != null) {
37284 this.div.style.backgroundColor = this.backgroundColor;
37289 * Method: setOpacity
37290 * Sets the opacity of the popup.
37293 * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid).
37295 setOpacity:function(opacity) {
37296 if (opacity != undefined) {
37297 this.opacity = opacity;
37300 if (this.div != null) {
37301 // for Mozilla and Safari
37302 this.div.style.opacity = this.opacity;
37305 this.div.style.filter = 'alpha(opacity=' + this.opacity*100 + ')';
37310 * Method: setBorder
37311 * Sets the border style of the popup.
37314 * border - {String} The border style value. eg 2px
37316 setBorder:function(border) {
37317 if (border != undefined) {
37318 this.border = border;
37321 if (this.div != null) {
37322 this.div.style.border = this.border;
37327 * Method: setContentHTML
37328 * Allows the user to set the HTML content of the popup.
37331 * contentHTML - {String} HTML for the div.
37333 setContentHTML:function(contentHTML) {
37335 if (contentHTML != null) {
37336 this.contentHTML = contentHTML;
37339 if ((this.contentDiv != null) &&
37340 (this.contentHTML != null) &&
37341 (this.contentHTML != this.contentDiv.innerHTML)) {
37343 this.contentDiv.innerHTML = this.contentHTML;
37345 if (this.autoSize) {
37347 //if popup has images, listen for when they finish
37348 // loading and resize accordingly
37349 this.registerImageListeners();
37351 //auto size the popup to its current contents
37359 * Method: registerImageListeners
37360 * Called when an image contained by the popup loaded. this function
37361 * updates the popup size, then unregisters the image load listener.
37363 registerImageListeners: function() {
37365 // As the images load, this function will call updateSize() to
37366 // resize the popup to fit the content div (which presumably is now
37367 // bigger than when the image was not loaded).
37369 // If the 'panMapIfOutOfView' property is set, we will pan the newly
37370 // resized popup back into view.
37372 // Note that this function, when called, will have 'popup' and
37373 // 'img' properties in the context.
37375 var onImgLoad = function() {
37376 if (this.popup.id === null) { // this.popup has been destroyed!
37379 this.popup.updateSize();
37381 if ( this.popup.visible() && this.popup.panMapIfOutOfView ) {
37382 this.popup.panIntoView();
37385 OpenLayers.Event.stopObserving(
37386 this.img, "load", this.img._onImgLoad
37391 //cycle through the images and if their size is 0x0, that means that
37392 // they haven't been loaded yet, so we attach the listener, which
37393 // will fire when the images finish loading and will resize the
37394 // popup accordingly to its new size.
37395 var images = this.contentDiv.getElementsByTagName("img");
37396 for (var i = 0, len = images.length; i < len; i++) {
37397 var img = images[i];
37398 if (img.width == 0 || img.height == 0) {
37405 //expando this function to the image itself before registering
37406 // it. This way we can easily and properly unregister it.
37407 img._onImgLoad = OpenLayers.Function.bind(onImgLoad, context);
37409 OpenLayers.Event.observe(img, 'load', img._onImgLoad);
37415 * APIMethod: getSafeContentSize
37418 * size - {<OpenLayers.Size>} Desired size to make the popup.
37421 * {<OpenLayers.Size>} A size to make the popup which is neither smaller
37422 * than the specified minimum size, nor bigger than the maximum
37423 * size (which is calculated relative to the size of the viewport).
37425 getSafeContentSize: function(size) {
37427 var safeContentSize = size.clone();
37429 // if our contentDiv has a css 'padding' set on it by a stylesheet, we
37430 // must add that to the desired "size".
37431 var contentDivPadding = this.getContentDivPadding();
37432 var wPadding = contentDivPadding.left + contentDivPadding.right;
37433 var hPadding = contentDivPadding.top + contentDivPadding.bottom;
37435 // take into account the popup's 'padding' property
37437 wPadding += this.padding.left + this.padding.right;
37438 hPadding += this.padding.top + this.padding.bottom;
37440 if (this.closeDiv) {
37441 var closeDivWidth = parseInt(this.closeDiv.style.width);
37442 wPadding += closeDivWidth + contentDivPadding.right;
37445 // prevent the popup from being smaller than a specified minimal size
37446 if (this.minSize) {
37447 safeContentSize.w = Math.max(safeContentSize.w,
37448 (this.minSize.w - wPadding));
37449 safeContentSize.h = Math.max(safeContentSize.h,
37450 (this.minSize.h - hPadding));
37453 // prevent the popup from being bigger than a specified maximum size
37454 if (this.maxSize) {
37455 safeContentSize.w = Math.min(safeContentSize.w,
37456 (this.maxSize.w - wPadding));
37457 safeContentSize.h = Math.min(safeContentSize.h,
37458 (this.maxSize.h - hPadding));
37461 //make sure the desired size to set doesn't result in a popup that
37462 // is bigger than the map's viewport.
37464 if (this.map && this.map.size) {
37466 var extraX = 0, extraY = 0;
37467 if (this.keepInMap && !this.panMapIfOutOfView) {
37468 var px = this.map.getPixelFromLonLat(this.lonlat);
37469 switch (this.relativePosition) {
37472 extraY = this.map.size.h - px.y;
37475 extraX = this.map.size.w - px.x;
37476 extraY = this.map.size.h - px.y;
37479 extraX = this.map.size.w - px.x;
37488 extraY = this.map.size.h - px.y;
37493 var maxY = this.map.size.h -
37494 this.map.paddingForPopups.top -
37495 this.map.paddingForPopups.bottom -
37498 var maxX = this.map.size.w -
37499 this.map.paddingForPopups.left -
37500 this.map.paddingForPopups.right -
37503 safeContentSize.w = Math.min(safeContentSize.w, maxX);
37504 safeContentSize.h = Math.min(safeContentSize.h, maxY);
37507 return safeContentSize;
37511 * Method: getContentDivPadding
37512 * Glorious, oh glorious hack in order to determine the css 'padding' of
37513 * the contentDiv. IE/Opera return null here unless we actually add the
37514 * popup's main 'div' element (which contains contentDiv) to the DOM.
37515 * So we make it invisible and then add it to the document temporarily.
37517 * Once we've taken the padding readings we need, we then remove it
37518 * from the DOM (it will actually get added to the DOM in
37519 * Map.js's addPopup)
37522 * {<OpenLayers.Bounds>}
37524 getContentDivPadding: function() {
37526 //use cached value if we have it
37527 var contentDivPadding = this._contentDivPadding;
37528 if (!contentDivPadding) {
37530 if (this.div.parentNode == null) {
37531 //make the div invisible and add it to the page
37532 this.div.style.display = "none";
37533 document.body.appendChild(this.div);
37536 //read the padding settings from css, put them in an OL.Bounds
37537 contentDivPadding = new OpenLayers.Bounds(
37538 OpenLayers.Element.getStyle(this.contentDiv, "padding-left"),
37539 OpenLayers.Element.getStyle(this.contentDiv, "padding-bottom"),
37540 OpenLayers.Element.getStyle(this.contentDiv, "padding-right"),
37541 OpenLayers.Element.getStyle(this.contentDiv, "padding-top")
37545 this._contentDivPadding = contentDivPadding;
37547 if (this.div.parentNode == document.body) {
37548 //remove the div from the page and make it visible again
37549 document.body.removeChild(this.div);
37550 this.div.style.display = "";
37553 return contentDivPadding;
37557 * Method: addCloseBox
37560 * callback - {Function} The callback to be called when the close button
37563 addCloseBox: function(callback) {
37565 this.closeDiv = OpenLayers.Util.createDiv(
37566 this.id + "_close", null, {w: 17, h: 17}
37568 this.closeDiv.className = "olPopupCloseBox";
37570 // use the content div's css padding to determine if we should
37571 // padd the close div
37572 var contentDivPadding = this.getContentDivPadding();
37574 this.closeDiv.style.right = contentDivPadding.right + "px";
37575 this.closeDiv.style.top = contentDivPadding.top + "px";
37576 this.groupDiv.appendChild(this.closeDiv);
37578 var closePopup = callback || function(e) {
37580 OpenLayers.Event.stop(e);
37582 OpenLayers.Event.observe(this.closeDiv, "touchend",
37583 OpenLayers.Function.bindAsEventListener(closePopup, this));
37584 OpenLayers.Event.observe(this.closeDiv, "click",
37585 OpenLayers.Function.bindAsEventListener(closePopup, this));
37589 * Method: panIntoView
37590 * Pans the map such that the popup is totaly viewable (if necessary)
37592 panIntoView: function() {
37594 var mapSize = this.map.getSize();
37596 //start with the top left corner of the popup, in px,
37597 // relative to the viewport
37598 var origTL = this.map.getViewPortPxFromLayerPx( new OpenLayers.Pixel(
37599 parseInt(this.div.style.left),
37600 parseInt(this.div.style.top)
37602 var newTL = origTL.clone();
37604 //new left (compare to margins, using this.size to calculate right)
37605 if (origTL.x < this.map.paddingForPopups.left) {
37606 newTL.x = this.map.paddingForPopups.left;
37608 if ( (origTL.x + this.size.w) > (mapSize.w - this.map.paddingForPopups.right)) {
37609 newTL.x = mapSize.w - this.map.paddingForPopups.right - this.size.w;
37612 //new top (compare to margins, using this.size to calculate bottom)
37613 if (origTL.y < this.map.paddingForPopups.top) {
37614 newTL.y = this.map.paddingForPopups.top;
37616 if ( (origTL.y + this.size.h) > (mapSize.h - this.map.paddingForPopups.bottom)) {
37617 newTL.y = mapSize.h - this.map.paddingForPopups.bottom - this.size.h;
37620 var dx = origTL.x - newTL.x;
37621 var dy = origTL.y - newTL.y;
37623 this.map.pan(dx, dy);
37627 * Method: registerEvents
37628 * Registers events on the popup.
37630 * Do this in a separate function so that subclasses can
37631 * choose to override it if they wish to deal differently
37632 * with mouse events
37634 * Note in the following handler functions that some special
37635 * care is needed to deal correctly with mousing and popups.
37637 * Because the user might select the zoom-rectangle option and
37638 * then drag it over a popup, we need a safe way to allow the
37639 * mousemove and mouseup events to pass through the popup when
37640 * they are initiated from outside. The same procedure is needed for
37641 * touchmove and touchend events.
37643 * Otherwise, we want to essentially kill the event propagation
37644 * for all other events, though we have to do so carefully,
37645 * without disabling basic html functionality, like clicking on
37646 * hyperlinks or drag-selecting text.
37648 registerEvents:function() {
37649 this.events = new OpenLayers.Events(this, this.div, null, true);
37651 function onTouchstart(evt) {
37652 OpenLayers.Event.stop(evt, true);
37655 "mousedown": this.onmousedown,
37656 "mousemove": this.onmousemove,
37657 "mouseup": this.onmouseup,
37658 "click": this.onclick,
37659 "mouseout": this.onmouseout,
37660 "dblclick": this.ondblclick,
37661 "touchstart": onTouchstart,
37668 * Method: onmousedown
37669 * When mouse goes down within the popup, make a note of
37670 * it locally, and then do not propagate the mousedown
37671 * (but do so safely so that user can select text inside)
37676 onmousedown: function (evt) {
37677 this.mousedown = true;
37678 OpenLayers.Event.stop(evt, true);
37682 * Method: onmousemove
37683 * If the drag was started within the popup, then
37684 * do not propagate the mousemove (but do so safely
37685 * so that user can select text inside)
37690 onmousemove: function (evt) {
37691 if (this.mousedown) {
37692 OpenLayers.Event.stop(evt, true);
37697 * Method: onmouseup
37698 * When mouse comes up within the popup, after going down
37699 * in it, reset the flag, and then (once again) do not
37700 * propagate the event, but do so safely so that user can
37701 * select text inside
37706 onmouseup: function (evt) {
37707 if (this.mousedown) {
37708 this.mousedown = false;
37709 OpenLayers.Event.stop(evt, true);
37715 * Ignore clicks, but allowing default browser handling
37720 onclick: function (evt) {
37721 OpenLayers.Event.stop(evt, true);
37725 * Method: onmouseout
37726 * When mouse goes out of the popup set the flag to false so that
37727 * if they let go and then drag back in, we won't be confused.
37732 onmouseout: function (evt) {
37733 this.mousedown = false;
37737 * Method: ondblclick
37738 * Ignore double-clicks, but allowing default browser handling
37743 ondblclick: function (evt) {
37744 OpenLayers.Event.stop(evt, true);
37747 CLASS_NAME: "OpenLayers.Popup"
37750 OpenLayers.Popup.WIDTH = 200;
37751 OpenLayers.Popup.HEIGHT = 200;
37752 OpenLayers.Popup.COLOR = "white";
37753 OpenLayers.Popup.OPACITY = 1;
37754 OpenLayers.Popup.BORDER = "0px";
37755 /* ======================================================================
37756 OpenLayers/Popup/Anchored.js
37757 ====================================================================== */
37759 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
37760 * full list of contributors). Published under the 2-clause BSD license.
37761 * See license.txt in the OpenLayers distribution or repository for the
37762 * full text of the license. */
37766 * @requires OpenLayers/Popup.js
37770 * Class: OpenLayers.Popup.Anchored
37773 * - <OpenLayers.Popup>
37775 OpenLayers.Popup.Anchored =
37776 OpenLayers.Class(OpenLayers.Popup, {
37779 * Property: relativePosition
37780 * {String} Relative position of the popup ("br", "tr", "tl" or "bl").
37782 relativePosition: null,
37785 * APIProperty: keepInMap
37786 * {Boolean} If panMapIfOutOfView is false, and this property is true,
37787 * contrain the popup such that it always fits in the available map
37788 * space. By default, this is set. If you are creating popups that are
37789 * near map edges and not allowing pannning, and especially if you have
37790 * a popup which has a fixedRelativePosition, setting this to false may
37791 * be a smart thing to do.
37793 * For anchored popups, default is true, since subclasses will
37794 * usually want this functionality.
37800 * {Object} Object to which we'll anchor the popup. Must expose a
37801 * 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>).
37806 * Constructor: OpenLayers.Popup.Anchored
37810 * lonlat - {<OpenLayers.LonLat>}
37811 * contentSize - {<OpenLayers.Size>}
37812 * contentHTML - {String}
37813 * anchor - {Object} Object which must expose a 'size' <OpenLayers.Size>
37814 * and 'offset' <OpenLayers.Pixel> (generally an <OpenLayers.Icon>).
37815 * closeBox - {Boolean}
37816 * closeBoxCallback - {Function} Function to be called on closeBox click.
37818 initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
37819 closeBoxCallback) {
37820 var newArguments = [
37821 id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback
37823 OpenLayers.Popup.prototype.initialize.apply(this, newArguments);
37825 this.anchor = (anchor != null) ? anchor
37826 : { size: new OpenLayers.Size(0,0),
37827 offset: new OpenLayers.Pixel(0,0)};
37831 * APIMethod: destroy
37833 destroy: function() {
37834 this.anchor = null;
37835 this.relativePosition = null;
37837 OpenLayers.Popup.prototype.destroy.apply(this, arguments);
37842 * Overridden from Popup since user might hide popup and then show() it
37843 * in a new location (meaning we might want to update the relative
37844 * position on the show)
37847 this.updatePosition();
37848 OpenLayers.Popup.prototype.show.apply(this, arguments);
37853 * Since the popup is moving to a new px, it might need also to be moved
37854 * relative to where the marker is. We first calculate the new
37855 * relativePosition, and then we calculate the new px where we will
37856 * put the popup, based on the new relative position.
37858 * If the relativePosition has changed, we must also call
37859 * updateRelativePosition() to make any visual changes to the popup
37860 * which are associated with putting it in a new relativePosition.
37863 * px - {<OpenLayers.Pixel>}
37865 moveTo: function(px) {
37866 var oldRelativePosition = this.relativePosition;
37867 this.relativePosition = this.calculateRelativePosition(px);
37869 OpenLayers.Popup.prototype.moveTo.call(this, this.calculateNewPx(px));
37871 //if this move has caused the popup to change its relative position,
37872 // we need to make the appropriate cosmetic changes.
37873 if (this.relativePosition != oldRelativePosition) {
37874 this.updateRelativePosition();
37879 * APIMethod: setSize
37882 * contentSize - {<OpenLayers.Size>} the new size for the popup's
37883 * contents div (in pixels).
37885 setSize:function(contentSize) {
37886 OpenLayers.Popup.prototype.setSize.apply(this, arguments);
37888 if ((this.lonlat) && (this.map)) {
37889 var px = this.map.getLayerPxFromLonLat(this.lonlat);
37895 * Method: calculateRelativePosition
37898 * px - {<OpenLayers.Pixel>}
37901 * {String} The relative position ("br" "tr" "tl" "bl") at which the popup
37902 * should be placed.
37904 calculateRelativePosition:function(px) {
37905 var lonlat = this.map.getLonLatFromLayerPx(px);
37907 var extent = this.map.getExtent();
37908 var quadrant = extent.determineQuadrant(lonlat);
37910 return OpenLayers.Bounds.oppositeQuadrant(quadrant);
37914 * Method: updateRelativePosition
37915 * The popup has been moved to a new relative location, so we may want to
37916 * make some cosmetic adjustments to it.
37918 * Note that in the classic Anchored popup, there is nothing to do
37919 * here, since the popup looks exactly the same in all four positions.
37920 * Subclasses such as Framed, however, will want to do something
37923 updateRelativePosition: function() {
37924 //to be overridden by subclasses
37928 * Method: calculateNewPx
37931 * px - {<OpenLayers.Pixel>}
37934 * {<OpenLayers.Pixel>} The the new px position of the popup on the screen
37935 * relative to the passed-in px.
37937 calculateNewPx:function(px) {
37938 var newPx = px.offset(this.anchor.offset);
37940 //use contentSize if size is not already set
37941 var size = this.size || this.contentSize;
37943 var top = (this.relativePosition.charAt(0) == 't');
37944 newPx.y += (top) ? -size.h : this.anchor.size.h;
37946 var left = (this.relativePosition.charAt(1) == 'l');
37947 newPx.x += (left) ? -size.w : this.anchor.size.w;
37952 CLASS_NAME: "OpenLayers.Popup.Anchored"
37954 /* ======================================================================
37955 OpenLayers/Popup/Framed.js
37956 ====================================================================== */
37958 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
37959 * full list of contributors). Published under the 2-clause BSD license.
37960 * See license.txt in the OpenLayers distribution or repository for the
37961 * full text of the license. */
37964 * @requires OpenLayers/Popup/Anchored.js
37968 * Class: OpenLayers.Popup.Framed
37971 * - <OpenLayers.Popup.Anchored>
37973 OpenLayers.Popup.Framed =
37974 OpenLayers.Class(OpenLayers.Popup.Anchored, {
37977 * Property: imageSrc
37978 * {String} location of the image to be used as the popup frame
37983 * Property: imageSize
37984 * {<OpenLayers.Size>} Size (measured in pixels) of the image located
37985 * by the 'imageSrc' property.
37990 * APIProperty: isAlphaImage
37991 * {Boolean} The image has some alpha and thus needs to use the alpha
37992 * image hack. Note that setting this to true will have no noticeable
37993 * effect in FF or IE7 browsers, but will all but crush the ie6
37995 * Default is false.
37997 isAlphaImage: false,
38000 * Property: positionBlocks
38001 * {Object} Hash of different position blocks (Object/Hashs). Each block
38002 * will be keyed by a two-character 'relativePosition'
38003 * code string (ie "tl", "tr", "bl", "br"). Block properties are
38004 * 'offset', 'padding' (self-explanatory), and finally the 'blocks'
38005 * parameter, which is an array of the block objects.
38007 * Each block object must have 'size', 'anchor', and 'position'
38010 * Note that positionBlocks should never be modified at runtime.
38012 positionBlocks: null,
38016 * {Array[Object]} Array of objects, each of which is one "block" of the
38017 * popup. Each block has a 'div' and an 'image' property, both of
38018 * which are DOMElements, and the latter of which is appended to the
38019 * former. These are reused as the popup goes changing positions for
38020 * great economy and elegance.
38025 * APIProperty: fixedRelativePosition
38026 * {Boolean} We want the framed popup to work dynamically placed relative
38027 * to its anchor but also in just one fixed position. A well designed
38028 * framed popup will have the pixels and logic to display itself in
38029 * any of the four relative positions, but (understandably), this will
38030 * not be the case for all of them. By setting this property to 'true',
38031 * framed popup will not recalculate for the best placement each time
38032 * it's open, but will always open the same way.
38033 * Note that if this is set to true, it is generally advisable to also
38034 * set the 'panIntoView' property to true so that the popup can be
38035 * scrolled into view (since it will often be offscreen on open)
38036 * Default is false.
38038 fixedRelativePosition: false,
38041 * Constructor: OpenLayers.Popup.Framed
38045 * lonlat - {<OpenLayers.LonLat>}
38046 * contentSize - {<OpenLayers.Size>}
38047 * contentHTML - {String}
38048 * anchor - {Object} Object to which we'll anchor the popup. Must expose
38049 * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>)
38050 * (Note that this is generally an <OpenLayers.Icon>).
38051 * closeBox - {Boolean}
38052 * closeBoxCallback - {Function} Function to be called on closeBox click.
38054 initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
38055 closeBoxCallback) {
38057 OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments);
38059 if (this.fixedRelativePosition) {
38060 //based on our decided relativePostion, set the current padding
38061 // this keeps us from getting into trouble
38062 this.updateRelativePosition();
38064 //make calculateRelativePosition always return the specified
38066 this.calculateRelativePosition = function(px) {
38067 return this.relativePosition;
38071 this.contentDiv.style.position = "absolute";
38072 this.contentDiv.style.zIndex = 1;
38075 this.closeDiv.style.zIndex = 1;
38078 this.groupDiv.style.position = "absolute";
38079 this.groupDiv.style.top = "0px";
38080 this.groupDiv.style.left = "0px";
38081 this.groupDiv.style.height = "100%";
38082 this.groupDiv.style.width = "100%";
38086 * APIMethod: destroy
38088 destroy: function() {
38089 this.imageSrc = null;
38090 this.imageSize = null;
38091 this.isAlphaImage = null;
38093 this.fixedRelativePosition = false;
38094 this.positionBlocks = null;
38096 //remove our blocks
38097 for(var i = 0; i < this.blocks.length; i++) {
38098 var block = this.blocks[i];
38101 block.div.removeChild(block.image);
38103 block.image = null;
38106 this.groupDiv.removeChild(block.div);
38110 this.blocks = null;
38112 OpenLayers.Popup.Anchored.prototype.destroy.apply(this, arguments);
38116 * APIMethod: setBackgroundColor
38118 setBackgroundColor:function(color) {
38119 //does nothing since the framed popup's entire scheme is based on a
38120 // an image -- changing the background color makes no sense.
38124 * APIMethod: setBorder
38126 setBorder:function() {
38127 //does nothing since the framed popup's entire scheme is based on a
38128 // an image -- changing the popup's border makes no sense.
38132 * Method: setOpacity
38133 * Sets the opacity of the popup.
38136 * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid).
38138 setOpacity:function(opacity) {
38139 //does nothing since we suppose that we'll never apply an opacity
38140 // to a framed popup
38144 * APIMethod: setSize
38145 * Overridden here, because we need to update the blocks whenever the size
38146 * of the popup has changed.
38149 * contentSize - {<OpenLayers.Size>} the new size for the popup's
38150 * contents div (in pixels).
38152 setSize:function(contentSize) {
38153 OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments);
38155 this.updateBlocks();
38159 * Method: updateRelativePosition
38160 * When the relative position changes, we need to set the new padding
38161 * BBOX on the popup, reposition the close div, and update the blocks.
38163 updateRelativePosition: function() {
38165 //update the padding
38166 this.padding = this.positionBlocks[this.relativePosition].padding;
38168 //update the position of our close box to new padding
38169 if (this.closeDiv) {
38170 // use the content div's css padding to determine if we should
38171 // padd the close div
38172 var contentDivPadding = this.getContentDivPadding();
38174 this.closeDiv.style.right = contentDivPadding.right +
38175 this.padding.right + "px";
38176 this.closeDiv.style.top = contentDivPadding.top +
38177 this.padding.top + "px";
38180 this.updateBlocks();
38184 * Method: calculateNewPx
38185 * Besides the standard offset as determined by the Anchored class, our
38186 * Framed popups have a special 'offset' property for each of their
38187 * positions, which is used to offset the popup relative to its anchor.
38190 * px - {<OpenLayers.Pixel>}
38193 * {<OpenLayers.Pixel>} The the new px position of the popup on the screen
38194 * relative to the passed-in px.
38196 calculateNewPx:function(px) {
38197 var newPx = OpenLayers.Popup.Anchored.prototype.calculateNewPx.apply(
38201 newPx = newPx.offset(this.positionBlocks[this.relativePosition].offset);
38207 * Method: createBlocks
38209 createBlocks: function() {
38212 //since all positions contain the same number of blocks, we can
38213 // just pick the first position and use its blocks array to create
38214 // our blocks array
38215 var firstPosition = null;
38216 for(var key in this.positionBlocks) {
38217 firstPosition = key;
38221 var position = this.positionBlocks[firstPosition];
38222 for (var i = 0; i < position.blocks.length; i++) {
38225 this.blocks.push(block);
38227 var divId = this.id + '_FrameDecorationDiv_' + i;
38228 block.div = OpenLayers.Util.createDiv(divId,
38229 null, null, null, "absolute", null, "hidden", null
38232 var imgId = this.id + '_FrameDecorationImg_' + i;
38234 (this.isAlphaImage) ? OpenLayers.Util.createAlphaImageDiv
38235 : OpenLayers.Util.createImage;
38237 block.image = imageCreator(imgId,
38238 null, this.imageSize, this.imageSrc,
38239 "absolute", null, null, null
38242 block.div.appendChild(block.image);
38243 this.groupDiv.appendChild(block.div);
38248 * Method: updateBlocks
38249 * Internal method, called on initialize and when the popup's relative
38250 * position has changed. This function takes care of re-positioning
38251 * the popup's blocks in their appropropriate places.
38253 updateBlocks: function() {
38254 if (!this.blocks) {
38255 this.createBlocks();
38258 if (this.size && this.relativePosition) {
38259 var position = this.positionBlocks[this.relativePosition];
38260 for (var i = 0; i < position.blocks.length; i++) {
38262 var positionBlock = position.blocks[i];
38263 var block = this.blocks[i];
38266 var l = positionBlock.anchor.left;
38267 var b = positionBlock.anchor.bottom;
38268 var r = positionBlock.anchor.right;
38269 var t = positionBlock.anchor.top;
38271 //note that we use the isNaN() test here because if the
38272 // size object is initialized with a "auto" parameter, the
38273 // size constructor calls parseFloat() on the string,
38274 // which will turn it into NaN
38276 var w = (isNaN(positionBlock.size.w)) ? this.size.w - (r + l)
38277 : positionBlock.size.w;
38279 var h = (isNaN(positionBlock.size.h)) ? this.size.h - (b + t)
38280 : positionBlock.size.h;
38282 block.div.style.width = (w < 0 ? 0 : w) + 'px';
38283 block.div.style.height = (h < 0 ? 0 : h) + 'px';
38285 block.div.style.left = (l != null) ? l + 'px' : '';
38286 block.div.style.bottom = (b != null) ? b + 'px' : '';
38287 block.div.style.right = (r != null) ? r + 'px' : '';
38288 block.div.style.top = (t != null) ? t + 'px' : '';
38290 block.image.style.left = positionBlock.position.x + 'px';
38291 block.image.style.top = positionBlock.position.y + 'px';
38294 this.contentDiv.style.left = this.padding.left + "px";
38295 this.contentDiv.style.top = this.padding.top + "px";
38299 CLASS_NAME: "OpenLayers.Popup.Framed"
38301 /* ======================================================================
38302 OpenLayers/Format/JSON.js
38303 ====================================================================== */
38305 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
38306 * full list of contributors). Published under the 2-clause BSD license.
38307 * See license.txt in the OpenLayers distribution or repository for the
38308 * full text of the license. */
38312 * This work draws heavily from the public domain JSON serializer/deserializer
38313 * at http://www.json.org/json.js. Rewritten so that it doesn't modify
38314 * basic data prototypes.
38318 * @requires OpenLayers/Format.js
38322 * Class: OpenLayers.Format.JSON
38323 * A parser to read/write JSON safely. Create a new instance with the
38324 * <OpenLayers.Format.JSON> constructor.
38327 * - <OpenLayers.Format>
38329 OpenLayers.Format.JSON = OpenLayers.Class(OpenLayers.Format, {
38332 * APIProperty: indent
38333 * {String} For "pretty" printing, the indent string will be used once for
38334 * each indentation level.
38339 * APIProperty: space
38340 * {String} For "pretty" printing, the space string will be used after
38341 * the ":" separating a name/value pair.
38346 * APIProperty: newline
38347 * {String} For "pretty" printing, the newline string will be used at the
38348 * end of each name/value pair or array item.
38354 * {Integer} For "pretty" printing, this is incremented/decremented during
38361 * {Boolean} Serialize with extra whitespace for structure. This is set
38362 * by the <write> method.
38367 * Property: nativeJSON
38368 * {Boolean} Does the browser support native json?
38370 nativeJSON: (function() {
38371 return !!(window.JSON && typeof JSON.parse == "function" && typeof JSON.stringify == "function");
38375 * Constructor: OpenLayers.Format.JSON
38376 * Create a new parser for JSON.
38379 * options - {Object} An optional object whose properties will be set on
38385 * Deserialize a json string.
38388 * json - {String} A JSON string
38389 * filter - {Function} A function which will be called for every key and
38390 * value at every level of the final result. Each value will be
38391 * replaced by the result of the filter function. This can be used to
38392 * reform generic objects into instances of classes, or to transform
38393 * date strings into Date objects.
38396 * {Object} An object, array, string, or number .
38398 read: function(json, filter) {
38400 if (this.nativeJSON) {
38401 object = JSON.parse(json, filter);
38404 * Parsing happens in three stages. In the first stage, we run the
38405 * text against a regular expression which looks for non-JSON
38406 * characters. We are especially concerned with '()' and 'new'
38407 * because they can cause invocation, and '=' because it can
38408 * cause mutation. But just to be safe, we will reject all
38409 * unexpected characters.
38411 if (/^[\],:{}\s]*$/.test(json.replace(/\\["\\\/bfnrtu]/g, '@').
38412 replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
38413 replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
38416 * In the second stage we use the eval function to compile the
38417 * text into a JavaScript structure. The '{' operator is
38418 * subject to a syntactic ambiguity in JavaScript - it can
38419 * begin a block or an object literal. We wrap the text in
38420 * parens to eliminate the ambiguity.
38422 object = eval('(' + json + ')');
38425 * In the optional third stage, we recursively walk the new
38426 * structure, passing each name/value pair to a filter
38427 * function for possible transformation.
38429 if(typeof filter === 'function') {
38430 function walk(k, v) {
38431 if(v && typeof v === 'object') {
38433 if(v.hasOwnProperty(i)) {
38434 v[i] = walk(i, v[i]);
38438 return filter(k, v);
38440 object = walk('', object);
38444 // Fall through if the regexp test fails.
38447 if(this.keepData) {
38448 this.data = object;
38456 * Serialize an object into a JSON string.
38459 * value - {String} The object, array, string, number, boolean or date
38460 * to be serialized.
38461 * pretty - {Boolean} Structure the output with newlines and indentation.
38462 * Default is false.
38465 * {String} The JSON string representation of the input value.
38467 write: function(value, pretty) {
38468 this.pretty = !!pretty;
38470 var type = typeof value;
38471 if(this.serialize[type]) {
38473 json = (!this.pretty && this.nativeJSON) ?
38474 JSON.stringify(value) :
38475 this.serialize[type].apply(this, [value]);
38477 OpenLayers.Console.error("Trouble serializing: " + err);
38484 * Method: writeIndent
38485 * Output an indentation string depending on the indentation level.
38488 * {String} An appropriate indentation string.
38490 writeIndent: function() {
38493 for(var i=0; i<this.level; ++i) {
38494 pieces.push(this.indent);
38497 return pieces.join('');
38501 * Method: writeNewline
38502 * Output a string representing a newline if in pretty printing mode.
38505 * {String} A string representing a new line.
38507 writeNewline: function() {
38508 return (this.pretty) ? this.newline : '';
38512 * Method: writeSpace
38513 * Output a string representing a space if in pretty printing mode.
38516 * {String} A space.
38518 writeSpace: function() {
38519 return (this.pretty) ? this.space : '';
38523 * Property: serialize
38524 * Object with properties corresponding to the serializable data types.
38525 * Property values are functions that do the actual serializing.
38529 * Method: serialize.object
38530 * Transform an object into a JSON string.
38533 * object - {Object} The object to be serialized.
38536 * {String} A JSON string representing the object.
38538 'object': function(object) {
38539 // three special objects that we want to treat differently
38540 if(object == null) {
38543 if(object.constructor == Date) {
38544 return this.serialize.date.apply(this, [object]);
38546 if(object.constructor == Array) {
38547 return this.serialize.array.apply(this, [object]);
38549 var pieces = ['{'];
38551 var key, keyJSON, valueJSON;
38553 var addComma = false;
38554 for(key in object) {
38555 if(object.hasOwnProperty(key)) {
38556 // recursive calls need to allow for sub-classing
38557 keyJSON = OpenLayers.Format.JSON.prototype.write.apply(this,
38558 [key, this.pretty]);
38559 valueJSON = OpenLayers.Format.JSON.prototype.write.apply(this,
38560 [object[key], this.pretty]);
38561 if(keyJSON != null && valueJSON != null) {
38565 pieces.push(this.writeNewline(), this.writeIndent(),
38566 keyJSON, ':', this.writeSpace(), valueJSON);
38573 pieces.push(this.writeNewline(), this.writeIndent(), '}');
38574 return pieces.join('');
38578 * Method: serialize.array
38579 * Transform an array into a JSON string.
38582 * array - {Array} The array to be serialized
38585 * {String} A JSON string representing the array.
38587 'array': function(array) {
38589 var pieces = ['['];
38592 for(var i=0, len=array.length; i<len; ++i) {
38593 // recursive calls need to allow for sub-classing
38594 json = OpenLayers.Format.JSON.prototype.write.apply(this,
38595 [array[i], this.pretty]);
38600 pieces.push(this.writeNewline(), this.writeIndent(), json);
38605 pieces.push(this.writeNewline(), this.writeIndent(), ']');
38606 return pieces.join('');
38610 * Method: serialize.string
38611 * Transform a string into a JSON string.
38614 * string - {String} The string to be serialized
38617 * {String} A JSON string representing the string.
38619 'string': function(string) {
38620 // If the string contains no control characters, no quote characters, and no
38621 // backslash characters, then we can simply slap some quotes around it.
38622 // Otherwise we must also replace the offending characters with safe
38633 if(/["\\\x00-\x1f]/.test(string)) {
38634 return '"' + string.replace(/([\x00-\x1f\\"])/g, function(a, b) {
38639 c = b.charCodeAt();
38641 Math.floor(c / 16).toString(16) +
38642 (c % 16).toString(16);
38645 return '"' + string + '"';
38649 * Method: serialize.number
38650 * Transform a number into a JSON string.
38653 * number - {Number} The number to be serialized.
38656 * {String} A JSON string representing the number.
38658 'number': function(number) {
38659 return isFinite(number) ? String(number) : "null";
38663 * Method: serialize.boolean
38664 * Transform a boolean into a JSON string.
38667 * bool - {Boolean} The boolean to be serialized.
38670 * {String} A JSON string representing the boolean.
38672 'boolean': function(bool) {
38673 return String(bool);
38677 * Method: serialize.object
38678 * Transform a date into a JSON string.
38681 * date - {Date} The date to be serialized.
38684 * {String} A JSON string representing the date.
38686 'date': function(date) {
38687 function format(number) {
38688 // Format integers to have at least two digits.
38689 return (number < 10) ? '0' + number : number;
38691 return '"' + date.getFullYear() + '-' +
38692 format(date.getMonth() + 1) + '-' +
38693 format(date.getDate()) + 'T' +
38694 format(date.getHours()) + ':' +
38695 format(date.getMinutes()) + ':' +
38696 format(date.getSeconds()) + '"';
38700 CLASS_NAME: "OpenLayers.Format.JSON"
38703 /* ======================================================================
38704 OpenLayers/Format/GeoJSON.js
38705 ====================================================================== */
38707 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
38708 * full list of contributors). Published under the 2-clause BSD license.
38709 * See license.txt in the OpenLayers distribution or repository for the
38710 * full text of the license. */
38713 * @requires OpenLayers/Format/JSON.js
38714 * @requires OpenLayers/Feature/Vector.js
38715 * @requires OpenLayers/Geometry/Point.js
38716 * @requires OpenLayers/Geometry/MultiPoint.js
38717 * @requires OpenLayers/Geometry/LineString.js
38718 * @requires OpenLayers/Geometry/MultiLineString.js
38719 * @requires OpenLayers/Geometry/Polygon.js
38720 * @requires OpenLayers/Geometry/MultiPolygon.js
38721 * @requires OpenLayers/Console.js
38725 * Class: OpenLayers.Format.GeoJSON
38726 * Read and write GeoJSON. Create a new parser with the
38727 * <OpenLayers.Format.GeoJSON> constructor.
38730 * - <OpenLayers.Format.JSON>
38732 OpenLayers.Format.GeoJSON = OpenLayers.Class(OpenLayers.Format.JSON, {
38735 * APIProperty: ignoreExtraDims
38736 * {Boolean} Ignore dimensions higher than 2 when reading geometry
38739 ignoreExtraDims: false,
38742 * Constructor: OpenLayers.Format.GeoJSON
38743 * Create a new parser for GeoJSON.
38746 * options - {Object} An optional object whose properties will be set on
38752 * Deserialize a GeoJSON string.
38755 * json - {String} A GeoJSON string
38756 * type - {String} Optional string that determines the structure of
38757 * the output. Supported values are "Geometry", "Feature", and
38758 * "FeatureCollection". If absent or null, a default of
38759 * "FeatureCollection" is assumed.
38760 * filter - {Function} A function which will be called for every key and
38761 * value at every level of the final result. Each value will be
38762 * replaced by the result of the filter function. This can be used to
38763 * reform generic objects into instances of classes, or to transform
38764 * date strings into Date objects.
38767 * {Object} The return depends on the value of the type argument. If type
38768 * is "FeatureCollection" (the default), the return will be an array
38769 * of <OpenLayers.Feature.Vector>. If type is "Geometry", the input json
38770 * must represent a single geometry, and the return will be an
38771 * <OpenLayers.Geometry>. If type is "Feature", the input json must
38772 * represent a single feature, and the return will be an
38773 * <OpenLayers.Feature.Vector>.
38775 read: function(json, type, filter) {
38776 type = (type) ? type : "FeatureCollection";
38777 var results = null;
38779 if (typeof json == "string") {
38780 obj = OpenLayers.Format.JSON.prototype.read.apply(this,
38786 OpenLayers.Console.error("Bad JSON: " + json);
38787 } else if(typeof(obj.type) != "string") {
38788 OpenLayers.Console.error("Bad GeoJSON - no type: " + json);
38789 } else if(this.isValidType(obj, type)) {
38793 results = this.parseGeometry(obj);
38795 OpenLayers.Console.error(err);
38800 results = this.parseFeature(obj);
38801 results.type = "Feature";
38803 OpenLayers.Console.error(err);
38806 case "FeatureCollection":
38807 // for type FeatureCollection, we allow input to be any type
38812 results.push(this.parseFeature(obj));
38815 OpenLayers.Console.error(err);
38818 case "FeatureCollection":
38819 for(var i=0, len=obj.features.length; i<len; ++i) {
38821 results.push(this.parseFeature(obj.features[i]));
38824 OpenLayers.Console.error(err);
38830 var geom = this.parseGeometry(obj);
38831 results.push(new OpenLayers.Feature.Vector(geom));
38834 OpenLayers.Console.error(err);
38844 * Method: isValidType
38845 * Check if a GeoJSON object is a valid representative of the given type.
38848 * {Boolean} The object is valid GeoJSON object of the given type.
38850 isValidType: function(obj, type) {
38854 if(OpenLayers.Util.indexOf(
38855 ["Point", "MultiPoint", "LineString", "MultiLineString",
38856 "Polygon", "MultiPolygon", "Box", "GeometryCollection"],
38858 // unsupported geometry type
38859 OpenLayers.Console.error("Unsupported geometry type: " +
38865 case "FeatureCollection":
38866 // allow for any type to be converted to a feature collection
38870 // for Feature types must match
38871 if(obj.type == type) {
38874 OpenLayers.Console.error("Cannot convert types from " +
38875 obj.type + " to " + type);
38882 * Method: parseFeature
38883 * Convert a feature object from GeoJSON into an
38884 * <OpenLayers.Feature.Vector>.
38887 * obj - {Object} An object created from a GeoJSON object
38890 * {<OpenLayers.Feature.Vector>} A feature.
38892 parseFeature: function(obj) {
38893 var feature, geometry, attributes, bbox;
38894 attributes = (obj.properties) ? obj.properties : {};
38895 bbox = (obj.geometry && obj.geometry.bbox) || obj.bbox;
38897 geometry = this.parseGeometry(obj.geometry);
38899 // deal with bad geometries
38902 feature = new OpenLayers.Feature.Vector(geometry, attributes);
38904 feature.bounds = OpenLayers.Bounds.fromArray(bbox);
38907 feature.fid = obj.id;
38913 * Method: parseGeometry
38914 * Convert a geometry object from GeoJSON into an <OpenLayers.Geometry>.
38917 * obj - {Object} An object created from a GeoJSON object
38920 * {<OpenLayers.Geometry>} A geometry.
38922 parseGeometry: function(obj) {
38926 var geometry, collection = false;
38927 if(obj.type == "GeometryCollection") {
38928 if(!(OpenLayers.Util.isArray(obj.geometries))) {
38929 throw "GeometryCollection must have geometries array: " + obj;
38931 var numGeom = obj.geometries.length;
38932 var components = new Array(numGeom);
38933 for(var i=0; i<numGeom; ++i) {
38934 components[i] = this.parseGeometry.apply(
38935 this, [obj.geometries[i]]
38938 geometry = new OpenLayers.Geometry.Collection(components);
38941 if(!(OpenLayers.Util.isArray(obj.coordinates))) {
38942 throw "Geometry must have coordinates array: " + obj;
38944 if(!this.parseCoords[obj.type.toLowerCase()]) {
38945 throw "Unsupported geometry type: " + obj.type;
38948 geometry = this.parseCoords[obj.type.toLowerCase()].apply(
38949 this, [obj.coordinates]
38952 // deal with bad coordinates
38956 // We don't reproject collections because the children are reprojected
38957 // for us when they are created.
38958 if (this.internalProjection && this.externalProjection && !collection) {
38959 geometry.transform(this.externalProjection,
38960 this.internalProjection);
38966 * Property: parseCoords
38967 * Object with properties corresponding to the GeoJSON geometry types.
38968 * Property values are functions that do the actual parsing.
38972 * Method: parseCoords.point
38973 * Convert a coordinate array from GeoJSON into an
38974 * <OpenLayers.Geometry>.
38977 * array - {Object} The coordinates array from the GeoJSON fragment.
38980 * {<OpenLayers.Geometry>} A geometry.
38982 "point": function(array) {
38983 if (this.ignoreExtraDims == false &&
38984 array.length != 2) {
38985 throw "Only 2D points are supported: " + array;
38987 return new OpenLayers.Geometry.Point(array[0], array[1]);
38991 * Method: parseCoords.multipoint
38992 * Convert a coordinate array from GeoJSON into an
38993 * <OpenLayers.Geometry>.
38996 * array - {Object} The coordinates array from the GeoJSON fragment.
38999 * {<OpenLayers.Geometry>} A geometry.
39001 "multipoint": function(array) {
39004 for(var i=0, len=array.length; i<len; ++i) {
39006 p = this.parseCoords["point"].apply(this, [array[i]]);
39012 return new OpenLayers.Geometry.MultiPoint(points);
39016 * Method: parseCoords.linestring
39017 * Convert a coordinate array from GeoJSON into an
39018 * <OpenLayers.Geometry>.
39021 * array - {Object} The coordinates array from the GeoJSON fragment.
39024 * {<OpenLayers.Geometry>} A geometry.
39026 "linestring": function(array) {
39029 for(var i=0, len=array.length; i<len; ++i) {
39031 p = this.parseCoords["point"].apply(this, [array[i]]);
39037 return new OpenLayers.Geometry.LineString(points);
39041 * Method: parseCoords.multilinestring
39042 * Convert a coordinate array from GeoJSON into an
39043 * <OpenLayers.Geometry>.
39046 * array - {Object} The coordinates array from the GeoJSON fragment.
39049 * {<OpenLayers.Geometry>} A geometry.
39051 "multilinestring": function(array) {
39054 for(var i=0, len=array.length; i<len; ++i) {
39056 l = this.parseCoords["linestring"].apply(this, [array[i]]);
39062 return new OpenLayers.Geometry.MultiLineString(lines);
39066 * Method: parseCoords.polygon
39067 * Convert a coordinate array from GeoJSON into an
39068 * <OpenLayers.Geometry>.
39071 * {<OpenLayers.Geometry>} A geometry.
39073 "polygon": function(array) {
39076 for(var i=0, len=array.length; i<len; ++i) {
39078 l = this.parseCoords["linestring"].apply(this, [array[i]]);
39082 r = new OpenLayers.Geometry.LinearRing(l.components);
39085 return new OpenLayers.Geometry.Polygon(rings);
39089 * Method: parseCoords.multipolygon
39090 * Convert a coordinate array from GeoJSON into an
39091 * <OpenLayers.Geometry>.
39094 * array - {Object} The coordinates array from the GeoJSON fragment.
39097 * {<OpenLayers.Geometry>} A geometry.
39099 "multipolygon": function(array) {
39102 for(var i=0, len=array.length; i<len; ++i) {
39104 p = this.parseCoords["polygon"].apply(this, [array[i]]);
39110 return new OpenLayers.Geometry.MultiPolygon(polys);
39114 * Method: parseCoords.box
39115 * Convert a coordinate array from GeoJSON into an
39116 * <OpenLayers.Geometry>.
39119 * array - {Object} The coordinates array from the GeoJSON fragment.
39122 * {<OpenLayers.Geometry>} A geometry.
39124 "box": function(array) {
39125 if(array.length != 2) {
39126 throw "GeoJSON box coordinates must have 2 elements";
39128 return new OpenLayers.Geometry.Polygon([
39129 new OpenLayers.Geometry.LinearRing([
39130 new OpenLayers.Geometry.Point(array[0][0], array[0][1]),
39131 new OpenLayers.Geometry.Point(array[1][0], array[0][1]),
39132 new OpenLayers.Geometry.Point(array[1][0], array[1][1]),
39133 new OpenLayers.Geometry.Point(array[0][0], array[1][1]),
39134 new OpenLayers.Geometry.Point(array[0][0], array[0][1])
39143 * Serialize a feature, geometry, array of features into a GeoJSON string.
39146 * obj - {Object} An <OpenLayers.Feature.Vector>, <OpenLayers.Geometry>,
39147 * or an array of features.
39148 * pretty - {Boolean} Structure the output with newlines and indentation.
39149 * Default is false.
39152 * {String} The GeoJSON string representation of the input geometry,
39153 * features, or array of features.
39155 write: function(obj, pretty) {
39159 if(OpenLayers.Util.isArray(obj)) {
39160 geojson.type = "FeatureCollection";
39161 var numFeatures = obj.length;
39162 geojson.features = new Array(numFeatures);
39163 for(var i=0; i<numFeatures; ++i) {
39164 var element = obj[i];
39165 if(!element instanceof OpenLayers.Feature.Vector) {
39166 var msg = "FeatureCollection only supports collections " +
39167 "of features: " + element;
39170 geojson.features[i] = this.extract.feature.apply(
39174 } else if (obj.CLASS_NAME.indexOf("OpenLayers.Geometry") == 0) {
39175 geojson = this.extract.geometry.apply(this, [obj]);
39176 } else if (obj instanceof OpenLayers.Feature.Vector) {
39177 geojson = this.extract.feature.apply(this, [obj]);
39178 if(obj.layer && obj.layer.projection) {
39179 geojson.crs = this.createCRSObject(obj);
39182 return OpenLayers.Format.JSON.prototype.write.apply(this,
39183 [geojson, pretty]);
39187 * Method: createCRSObject
39188 * Create the CRS object for an object.
39191 * object - {<OpenLayers.Feature.Vector>}
39194 * {Object} An object which can be assigned to the crs property
39195 * of a GeoJSON object.
39197 createCRSObject: function(object) {
39198 var proj = object.layer.projection.toString();
39200 if (proj.match(/epsg:/i)) {
39201 var code = parseInt(proj.substring(proj.indexOf(":") + 1));
39202 if (code == 4326) {
39206 "name": "urn:ogc:def:crs:OGC:1.3:CRS84"
39213 "name": "EPSG:" + code
39222 * Property: extract
39223 * Object with properties corresponding to the GeoJSON types.
39224 * Property values are functions that do the actual value extraction.
39228 * Method: extract.feature
39229 * Return a partial GeoJSON object representing a single feature.
39232 * feature - {<OpenLayers.Feature.Vector>}
39235 * {Object} An object representing the point.
39237 'feature': function(feature) {
39238 var geom = this.extract.geometry.apply(this, [feature.geometry]);
39241 "properties": feature.attributes,
39244 if (feature.fid != null) {
39245 json.id = feature.fid;
39251 * Method: extract.geometry
39252 * Return a GeoJSON object representing a single geometry.
39255 * geometry - {<OpenLayers.Geometry>}
39258 * {Object} An object representing the geometry.
39260 'geometry': function(geometry) {
39261 if (geometry == null) {
39264 if (this.internalProjection && this.externalProjection) {
39265 geometry = geometry.clone();
39266 geometry.transform(this.internalProjection,
39267 this.externalProjection);
39269 var geometryType = geometry.CLASS_NAME.split('.')[2];
39270 var data = this.extract[geometryType.toLowerCase()].apply(this, [geometry]);
39272 if(geometryType == "Collection") {
39274 "type": "GeometryCollection",
39279 "type": geometryType,
39280 "coordinates": data
39288 * Method: extract.point
39289 * Return an array of coordinates from a point.
39292 * point - {<OpenLayers.Geometry.Point>}
39295 * {Array} An array of coordinates representing the point.
39297 'point': function(point) {
39298 return [point.x, point.y];
39302 * Method: extract.multipoint
39303 * Return an array of point coordinates from a multipoint.
39306 * multipoint - {<OpenLayers.Geometry.MultiPoint>}
39309 * {Array} An array of point coordinate arrays representing
39312 'multipoint': function(multipoint) {
39314 for(var i=0, len=multipoint.components.length; i<len; ++i) {
39315 array.push(this.extract.point.apply(this, [multipoint.components[i]]));
39321 * Method: extract.linestring
39322 * Return an array of coordinate arrays from a linestring.
39325 * linestring - {<OpenLayers.Geometry.LineString>}
39328 * {Array} An array of coordinate arrays representing
39331 'linestring': function(linestring) {
39333 for(var i=0, len=linestring.components.length; i<len; ++i) {
39334 array.push(this.extract.point.apply(this, [linestring.components[i]]));
39340 * Method: extract.multilinestring
39341 * Return an array of linestring arrays from a linestring.
39344 * multilinestring - {<OpenLayers.Geometry.MultiLineString>}
39347 * {Array} An array of linestring arrays representing
39348 * the multilinestring.
39350 'multilinestring': function(multilinestring) {
39352 for(var i=0, len=multilinestring.components.length; i<len; ++i) {
39353 array.push(this.extract.linestring.apply(this, [multilinestring.components[i]]));
39359 * Method: extract.polygon
39360 * Return an array of linear ring arrays from a polygon.
39363 * polygon - {<OpenLayers.Geometry.Polygon>}
39366 * {Array} An array of linear ring arrays representing the polygon.
39368 'polygon': function(polygon) {
39370 for(var i=0, len=polygon.components.length; i<len; ++i) {
39371 array.push(this.extract.linestring.apply(this, [polygon.components[i]]));
39377 * Method: extract.multipolygon
39378 * Return an array of polygon arrays from a multipolygon.
39381 * multipolygon - {<OpenLayers.Geometry.MultiPolygon>}
39384 * {Array} An array of polygon arrays representing
39387 'multipolygon': function(multipolygon) {
39389 for(var i=0, len=multipolygon.components.length; i<len; ++i) {
39390 array.push(this.extract.polygon.apply(this, [multipolygon.components[i]]));
39396 * Method: extract.collection
39397 * Return an array of geometries from a geometry collection.
39400 * collection - {<OpenLayers.Geometry.Collection>}
39403 * {Array} An array of geometry objects representing the geometry
39406 'collection': function(collection) {
39407 var len = collection.components.length;
39408 var array = new Array(len);
39409 for(var i=0; i<len; ++i) {
39410 array[i] = this.extract.geometry.apply(
39411 this, [collection.components[i]]
39420 CLASS_NAME: "OpenLayers.Format.GeoJSON"
39423 /* ======================================================================
39424 OpenLayers/Protocol/WFS/v1.js
39425 ====================================================================== */
39427 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
39428 * full list of contributors). Published under the 2-clause BSD license.
39429 * See license.txt in the OpenLayers distribution or repository for the
39430 * full text of the license. */
39433 * @requires OpenLayers/Protocol/WFS.js
39437 * Class: OpenLayers.Protocol.WFS.v1
39438 * Abstract class for for v1.0.0 and v1.1.0 protocol.
39441 * - <OpenLayers.Protocol>
39443 OpenLayers.Protocol.WFS.v1 = OpenLayers.Class(OpenLayers.Protocol, {
39446 * Property: version
39447 * {String} WFS version number.
39452 * Property: srsName
39453 * {String} Name of spatial reference system. Default is "EPSG:4326".
39455 srsName: "EPSG:4326",
39458 * Property: featureType
39459 * {String} Local feature typeName.
39464 * Property: featureNS
39465 * {String} Feature namespace.
39470 * Property: geometryName
39471 * {String} Name of the geometry attribute for features. Default is
39472 * "the_geom" for WFS <version> 1.0, and null for higher versions.
39474 geometryName: "the_geom",
39477 * Property: maxFeatures
39478 * {Integer} Optional maximum number of features to retrieve.
39483 * {String} Optional schema location that will be included in the
39484 * schemaLocation attribute value. Note that the feature type schema
39485 * is required for a strict XML validator (on transactions with an
39486 * insert for example), but is *not* required by the WFS specification
39487 * (since the server is supposed to know about feature type schemas).
39492 * Property: featurePrefix
39493 * {String} Namespace alias for feature type. Default is "feature".
39495 featurePrefix: "feature",
39498 * Property: formatOptions
39499 * {Object} Optional options for the format. If a format is not provided,
39500 * this property can be used to extend the default format options.
39502 formatOptions: null,
39505 * Property: readFormat
39506 * {<OpenLayers.Format>} For WFS requests it is possible to get a
39507 * different output format than GML. In that case, we cannot parse
39508 * the response with the default format (WFST) and we need a different
39509 * format for reading.
39514 * Property: readOptions
39515 * {Object} Optional object to pass to format's read.
39520 * Constructor: OpenLayers.Protocol.WFS
39521 * A class for giving layers WFS protocol.
39524 * options - {Object} Optional object whose properties will be set on the
39527 * Valid options properties:
39528 * url - {String} URL to send requests to (required).
39529 * featureType - {String} Local (without prefix) feature typeName (required).
39530 * featureNS - {String} Feature namespace (required, but can be autodetected
39531 * during the first query if GML is used as readFormat and
39532 * featurePrefix is provided and matches the prefix used by the server
39533 * for this featureType).
39534 * featurePrefix - {String} Feature namespace alias (optional - only used
39535 * for writing if featureNS is provided). Default is 'feature'.
39536 * geometryName - {String} Name of geometry attribute. The default is
39537 * 'the_geom' for WFS <version> 1.0, and null for higher versions. If
39538 * null, it will be set to the name of the first geometry found in the
39539 * first read operation.
39540 * multi - {Boolean} If set to true, geometries will be casted to Multi
39541 * geometries before they are written in a transaction. No casting will
39542 * be done when reading features.
39544 initialize: function(options) {
39545 OpenLayers.Protocol.prototype.initialize.apply(this, [options]);
39546 if(!options.format) {
39547 this.format = OpenLayers.Format.WFST(OpenLayers.Util.extend({
39548 version: this.version,
39549 featureType: this.featureType,
39550 featureNS: this.featureNS,
39551 featurePrefix: this.featurePrefix,
39552 geometryName: this.geometryName,
39553 srsName: this.srsName,
39554 schema: this.schema
39555 }, this.formatOptions));
39557 if (!options.geometryName && parseFloat(this.format.version) > 1.0) {
39558 this.setGeometryName(null);
39563 * APIMethod: destroy
39564 * Clean up the protocol.
39566 destroy: function() {
39567 if(this.options && !this.options.format) {
39568 this.format.destroy();
39570 this.format = null;
39571 OpenLayers.Protocol.prototype.destroy.apply(this);
39576 * Construct a request for reading new features. Since WFS splits the
39577 * basic CRUD operations into GetFeature requests (for read) and
39578 * Transactions (for all others), this method does not make use of the
39579 * format's read method (that is only about reading transaction
39583 * options - {Object} Options for the read operation, in addition to the
39584 * options set on the instance (options set here will take precedence).
39586 * To use a configured protocol to get e.g. a WFS hit count, applications
39587 * could do the following:
39591 * readOptions: {output: "object"},
39592 * resultType: "hits",
39593 * maxFeatures: null,
39594 * callback: function(resp) {
39595 * // process resp.numberOfFeatures here
39600 * To use a configured protocol to use WFS paging (if supported by the
39601 * server), applications could do the following:
39610 * To limit the attributes returned by the GetFeature request, applications
39611 * can use the propertyNames option to specify the properties to include in
39616 * propertyNames: ["DURATION", "INTENSITY"]
39620 read: function(options) {
39621 OpenLayers.Protocol.prototype.read.apply(this, arguments);
39622 options = OpenLayers.Util.extend({}, options);
39623 OpenLayers.Util.applyDefaults(options, this.options || {});
39624 var response = new OpenLayers.Protocol.Response({requestType: "read"});
39626 var data = OpenLayers.Format.XML.prototype.write.apply(
39627 this.format, [this.format.writeNode("wfs:GetFeature", options)]
39630 response.priv = OpenLayers.Request.POST({
39632 callback: this.createCallback(this.handleRead, response, options),
39633 params: options.params,
39634 headers: options.headers,
39642 * APIMethod: setFeatureType
39643 * Change the feature type on the fly.
39646 * featureType - {String} Local (without prefix) feature typeName.
39648 setFeatureType: function(featureType) {
39649 this.featureType = featureType;
39650 this.format.featureType = featureType;
39654 * APIMethod: setGeometryName
39655 * Sets the geometryName option after instantiation.
39658 * geometryName - {String} Name of geometry attribute.
39660 setGeometryName: function(geometryName) {
39661 this.geometryName = geometryName;
39662 this.format.geometryName = geometryName;
39666 * Method: handleRead
39667 * Deal with response from the read request.
39670 * response - {<OpenLayers.Protocol.Response>} The response object to pass
39671 * to the user callback.
39672 * options - {Object} The user options passed to the read call.
39674 handleRead: function(response, options) {
39675 options = OpenLayers.Util.extend({}, options);
39676 OpenLayers.Util.applyDefaults(options, this.options);
39678 if(options.callback) {
39679 var request = response.priv;
39680 if(request.status >= 200 && request.status < 300) {
39682 var result = this.parseResponse(request, options.readOptions);
39683 if (result && result.success !== false) {
39684 if (options.readOptions && options.readOptions.output == "object") {
39685 OpenLayers.Util.extend(response, result);
39687 response.features = result;
39689 response.code = OpenLayers.Protocol.Response.SUCCESS;
39691 // failure (service exception)
39692 response.code = OpenLayers.Protocol.Response.FAILURE;
39693 response.error = result;
39697 response.code = OpenLayers.Protocol.Response.FAILURE;
39699 options.callback.call(options.scope, response);
39704 * Method: parseResponse
39705 * Read HTTP response body and return features
39708 * request - {XMLHttpRequest} The request object
39709 * options - {Object} Optional object to pass to format's read
39712 * {Object} or {Array({<OpenLayers.Feature.Vector>})} or
39713 * {<OpenLayers.Feature.Vector>}
39714 * An object with a features property, an array of features or a single
39717 parseResponse: function(request, options) {
39718 var doc = request.responseXML;
39719 if(!doc || !doc.documentElement) {
39720 doc = request.responseText;
39722 if(!doc || doc.length <= 0) {
39725 var result = (this.readFormat !== null) ? this.readFormat.read(doc) :
39726 this.format.read(doc, options);
39727 if (!this.featureNS) {
39728 var format = this.readFormat || this.format;
39729 this.featureNS = format.featureNS;
39730 // no need to auto-configure again on subsequent reads
39731 format.autoConfig = false;
39732 if (!this.geometryName) {
39733 this.setGeometryName(format.geometryName);
39741 * Given a list of feature, assemble a batch request for update, create,
39742 * and delete transactions. A commit call on the prototype amounts
39743 * to writing a WFS transaction - so the write method on the format
39747 * features - {Array(<OpenLayers.Feature.Vector>)}
39748 * options - {Object}
39750 * Valid options properties:
39751 * nativeElements - {Array({Object})} Array of objects with information for writing
39752 * out <Native> elements, these objects have vendorId, safeToIgnore and
39753 * value properties. The <Native> element is intended to allow access to
39754 * vendor specific capabilities of any particular web feature server or
39758 * {<OpenLayers.Protocol.Response>} A response object with a features
39759 * property containing any insertIds and a priv property referencing
39760 * the XMLHttpRequest object.
39762 commit: function(features, options) {
39764 options = OpenLayers.Util.extend({}, options);
39765 OpenLayers.Util.applyDefaults(options, this.options);
39767 var response = new OpenLayers.Protocol.Response({
39768 requestType: "commit",
39769 reqFeatures: features
39771 response.priv = OpenLayers.Request.POST({
39773 headers: options.headers,
39774 data: this.format.write(features, options),
39775 callback: this.createCallback(this.handleCommit, response, options)
39782 * Method: handleCommit
39783 * Called when the commit request returns.
39786 * response - {<OpenLayers.Protocol.Response>} The response object to pass
39787 * to the user callback.
39788 * options - {Object} The user options passed to the commit call.
39790 handleCommit: function(response, options) {
39791 if(options.callback) {
39792 var request = response.priv;
39794 // ensure that we have an xml doc
39795 var data = request.responseXML;
39796 if(!data || !data.documentElement) {
39797 data = request.responseText;
39800 var obj = this.format.read(data) || {};
39802 response.insertIds = obj.insertIds || [];
39804 response.code = OpenLayers.Protocol.Response.SUCCESS;
39806 response.code = OpenLayers.Protocol.Response.FAILURE;
39807 response.error = obj;
39809 options.callback.call(options.scope, response);
39814 * Method: filterDelete
39815 * Send a request that deletes all features by their filter.
39818 * filter - {<OpenLayers.Filter>} filter
39820 filterDelete: function(filter, options) {
39821 options = OpenLayers.Util.extend({}, options);
39822 OpenLayers.Util.applyDefaults(options, this.options);
39824 var response = new OpenLayers.Protocol.Response({
39825 requestType: "commit"
39828 var root = this.format.createElementNSPlus("wfs:Transaction", {
39831 version: this.version
39835 var deleteNode = this.format.createElementNSPlus("wfs:Delete", {
39837 typeName: (options.featureNS ? this.featurePrefix + ":" : "") +
39838 options.featureType
39842 if(options.featureNS) {
39843 deleteNode.setAttribute("xmlns:" + this.featurePrefix, options.featureNS);
39845 var filterNode = this.format.writeNode("ogc:Filter", filter);
39847 deleteNode.appendChild(filterNode);
39849 root.appendChild(deleteNode);
39851 var data = OpenLayers.Format.XML.prototype.write.apply(
39852 this.format, [root]
39855 return OpenLayers.Request.POST({
39857 callback : options.callback || function(){},
39865 * Abort an ongoing request, the response object passed to
39866 * this method must come from this protocol (as a result
39867 * of a read, or commit operation).
39870 * response - {<OpenLayers.Protocol.Response>}
39872 abort: function(response) {
39874 response.priv.abort();
39878 CLASS_NAME: "OpenLayers.Protocol.WFS.v1"
39880 /* ======================================================================
39881 OpenLayers/Layer/Google/v3.js
39882 ====================================================================== */
39884 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
39885 * full list of contributors). Published under the 2-clause BSD license.
39886 * See license.txt in the OpenLayers distribution or repository for the
39887 * full text of the license. */
39891 * @requires OpenLayers/Layer/Google.js
39895 * Constant: OpenLayers.Layer.Google.v3
39897 * Mixin providing functionality specific to the Google Maps API v3.
39899 * To use this layer, you must include the GMaps v3 API in your html.
39901 * Note that this layer configures the google.maps.map object with the
39902 * "disableDefaultUI" option set to true. Using UI controls that the Google
39903 * Maps API provides is not supported by the OpenLayers API.
39905 OpenLayers.Layer.Google.v3 = {
39908 * Constant: DEFAULTS
39909 * {Object} It is not recommended to change the properties set here. Note
39910 * that Google.v3 layers only work when sphericalMercator is set to true.
39914 * sphericalMercator: true,
39915 * projection: "EPSG:900913"
39920 sphericalMercator: true,
39921 projection: "EPSG:900913"
39925 * APIProperty: animationEnabled
39926 * {Boolean} If set to true, the transition between zoom levels will be
39927 * animated (if supported by the GMaps API for the device used). Set to
39928 * false to match the zooming experience of other layer types. Default
39929 * is true. Note that the GMaps API does not give us control over zoom
39930 * animation, so if set to false, when zooming, this will make the
39931 * layer temporarily invisible, wait until GMaps reports the map being
39932 * idle, and make it visible again. The result will be a blank layer
39933 * for a few moments while zooming.
39935 animationEnabled: true,
39938 * Method: loadMapObject
39939 * Load the GMap and register appropriate event listeners.
39941 loadMapObject: function() {
39943 this.type = google.maps.MapTypeId.ROADMAP;
39946 var cache = OpenLayers.Layer.Google.cache[this.map.id];
39948 // there are already Google layers added to this map
39949 mapObject = cache.mapObject;
39950 // increment the layer count
39953 // this is the first Google layer for this map
39955 var center = this.map.getCenter();
39956 var container = document.createElement('div');
39957 container.className = "olForeignContainer";
39958 container.style.width = '100%';
39959 container.style.height = '100%';
39960 mapObject = new google.maps.Map(container, {
39962 new google.maps.LatLng(center.lat, center.lon) :
39963 new google.maps.LatLng(0, 0),
39964 zoom: this.map.getZoom() || 0,
39965 mapTypeId: this.type,
39966 disableDefaultUI: true,
39967 keyboardShortcuts: false,
39969 disableDoubleClickZoom: true,
39970 scrollwheel: false,
39971 streetViewControl: false
39973 var googleControl = document.createElement('div');
39974 googleControl.style.width = '100%';
39975 googleControl.style.height = '100%';
39976 mapObject.controls[google.maps.ControlPosition.TOP_LEFT].push(googleControl);
39978 // cache elements for use by any other google layers added to
39981 googleControl: googleControl,
39982 mapObject: mapObject,
39985 OpenLayers.Layer.Google.cache[this.map.id] = cache;
39987 this.mapObject = mapObject;
39988 this.setGMapVisibility(this.visibility);
39992 * APIMethod: onMapResize
39994 onMapResize: function() {
39995 if (this.visibility) {
39996 google.maps.event.trigger(this.mapObject, "resize");
40001 * Method: setGMapVisibility
40002 * Display the GMap container and associated elements.
40005 * visible - {Boolean} Display the GMap elements.
40007 setGMapVisibility: function(visible) {
40008 var cache = OpenLayers.Layer.Google.cache[this.map.id];
40009 var map = this.map;
40011 var type = this.type;
40012 var layers = map.layers;
40014 for (var i=layers.length-1; i>=0; --i) {
40016 if (layer instanceof OpenLayers.Layer.Google &&
40017 layer.visibility === true && layer.inRange === true) {
40023 var container = this.mapObject.getDiv();
40024 if (visible === true) {
40025 if (container.parentNode !== map.div) {
40026 if (!cache.rendered) {
40028 google.maps.event.addListenerOnce(this.mapObject, 'tilesloaded', function() {
40029 cache.rendered = true;
40030 me.setGMapVisibility(me.getVisibility());
40031 me.moveTo(me.map.getCenter());
40034 map.div.appendChild(container);
40035 cache.googleControl.appendChild(map.viewPortDiv);
40036 google.maps.event.trigger(this.mapObject, 'resize');
40039 this.mapObject.setMapTypeId(type);
40040 } else if (cache.googleControl.hasChildNodes()) {
40041 map.div.appendChild(map.viewPortDiv);
40042 map.div.removeChild(container);
40048 * Method: getMapContainer
40051 * {DOMElement} the GMap container's div
40053 getMapContainer: function() {
40054 return this.mapObject.getDiv();
40058 // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds
40062 * APIMethod: getMapObjectBoundsFromOLBounds
40065 * olBounds - {<OpenLayers.Bounds>}
40068 * {Object} A MapObject Bounds, translated from olBounds
40069 * Returns null if null value is passed in
40071 getMapObjectBoundsFromOLBounds: function(olBounds) {
40072 var moBounds = null;
40073 if (olBounds != null) {
40074 var sw = this.sphericalMercator ?
40075 this.inverseMercator(olBounds.bottom, olBounds.left) :
40076 new OpenLayers.LonLat(olBounds.bottom, olBounds.left);
40077 var ne = this.sphericalMercator ?
40078 this.inverseMercator(olBounds.top, olBounds.right) :
40079 new OpenLayers.LonLat(olBounds.top, olBounds.right);
40080 moBounds = new google.maps.LatLngBounds(
40081 new google.maps.LatLng(sw.lat, sw.lon),
40082 new google.maps.LatLng(ne.lat, ne.lon)
40089 /************************************
40091 * MapObject Interface Controls *
40093 ************************************/
40096 // LonLat - Pixel Translation
40099 * APIMethod: getMapObjectLonLatFromMapObjectPixel
40102 * moPixel - {Object} MapObject Pixel format
40105 * {Object} MapObject LonLat translated from MapObject Pixel
40107 getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
40108 var size = this.map.getSize();
40109 var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center);
40110 var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center);
40111 var res = this.map.getResolution();
40113 var delta_x = moPixel.x - (size.w / 2);
40114 var delta_y = moPixel.y - (size.h / 2);
40116 var lonlat = new OpenLayers.LonLat(
40117 lon + delta_x * res,
40118 lat - delta_y * res
40121 if (this.wrapDateLine) {
40122 lonlat = lonlat.wrapDateLine(this.maxExtent);
40124 return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat);
40128 * APIMethod: getMapObjectPixelFromMapObjectLonLat
40131 * moLonLat - {Object} MapObject LonLat format
40134 * {Object} MapObject Pixel transtlated from MapObject LonLat
40136 getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
40137 var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);
40138 var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);
40139 var res = this.map.getResolution();
40140 var extent = this.map.getExtent();
40141 return this.getMapObjectPixelFromXY((1/res * (lon - extent.left)),
40142 (1/res * (extent.top - lat)));
40147 * APIMethod: setMapObjectCenter
40148 * Set the mapObject to the specified center and zoom
40151 * center - {Object} MapObject LonLat format
40152 * zoom - {int} MapObject zoom format
40154 setMapObjectCenter: function(center, zoom) {
40155 if (this.animationEnabled === false && zoom != this.mapObject.zoom) {
40156 var mapContainer = this.getMapContainer();
40157 google.maps.event.addListenerOnce(
40161 mapContainer.style.visibility = "";
40164 mapContainer.style.visibility = "hidden";
40166 this.mapObject.setOptions({
40176 * APIMethod: getMapObjectZoomFromMapObjectBounds
40179 * moBounds - {Object} MapObject Bounds format
40182 * {Object} MapObject Zoom for specified MapObject Bounds
40184 getMapObjectZoomFromMapObjectBounds: function(moBounds) {
40185 return this.mapObject.getBoundsZoomLevel(moBounds);
40188 /************************************
40190 * MapObject Primitives *
40192 ************************************/
40198 * APIMethod: getMapObjectLonLatFromLonLat
40205 * {Object} MapObject LonLat built from lon and lat params
40207 getMapObjectLonLatFromLonLat: function(lon, lat) {
40209 if(this.sphericalMercator) {
40210 var lonlat = this.inverseMercator(lon, lat);
40211 gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon);
40213 gLatLng = new google.maps.LatLng(lat, lon);
40221 * APIMethod: getMapObjectPixelFromXY
40228 * {Object} MapObject Pixel from x and y parameters
40230 getMapObjectPixelFromXY: function(x, y) {
40231 return new google.maps.Point(x, y);
40235 /* ======================================================================
40236 OpenLayers/Request.js
40237 ====================================================================== */
40239 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
40240 * full list of contributors). Published under the 2-clause BSD license.
40241 * See license.txt in the OpenLayers distribution or repository for the
40242 * full text of the license. */
40245 * @requires OpenLayers/Events.js
40246 * @requires OpenLayers/Request/XMLHttpRequest.js
40250 * TODO: deprecate me
40251 * Use OpenLayers.Request.proxy instead.
40253 OpenLayers.ProxyHost = "";
40256 * Namespace: OpenLayers.Request
40257 * The OpenLayers.Request namespace contains convenience methods for working
40258 * with XMLHttpRequests. These methods work with a cross-browser
40259 * W3C compliant <OpenLayers.Request.XMLHttpRequest> class.
40261 if (!OpenLayers.Request) {
40263 * This allows for OpenLayers/Request/XMLHttpRequest.js to be included
40264 * before or after this script.
40266 OpenLayers.Request = {};
40268 OpenLayers.Util.extend(OpenLayers.Request, {
40271 * Constant: DEFAULT_CONFIG
40272 * {Object} Default configuration for all requests.
40276 url: window.location.href,
40279 password: undefined,
40281 proxy: OpenLayers.ProxyHost,
40284 callback: function() {},
40291 * Constant: URL_SPLIT_REGEX
40293 URL_SPLIT_REGEX: /([^:]*:)\/\/([^:]*:?[^@]*@)?([^:\/\?]*):?([^\/\?]*)/,
40296 * APIProperty: events
40297 * {<OpenLayers.Events>} An events object that handles all
40298 * events on the {<OpenLayers.Request>} object.
40300 * All event listeners will receive an event object with three properties:
40301 * request - {<OpenLayers.Request.XMLHttpRequest>} The request object.
40302 * config - {Object} The config object sent to the specific request method.
40303 * requestUrl - {String} The request url.
40305 * Supported event types:
40306 * complete - Triggered when we have a response from the request, if a
40307 * listener returns false, no further response processing will take
40309 * success - Triggered when the HTTP response has a success code (200-299).
40310 * failure - Triggered when the HTTP response does not have a success code.
40312 events: new OpenLayers.Events(this),
40315 * Method: makeSameOrigin
40316 * Using the specified proxy, returns a same origin url of the provided url.
40319 * url - {String} An arbitrary url
40320 * proxy {String|Function} The proxy to use to make the provided url a
40324 * {String} the same origin url. If no proxy is provided, the returned url
40325 * will be the same as the provided url.
40327 makeSameOrigin: function(url, proxy) {
40328 var sameOrigin = url.indexOf("http") !== 0;
40329 var urlParts = !sameOrigin && url.match(this.URL_SPLIT_REGEX);
40331 var location = window.location;
40333 urlParts[1] == location.protocol &&
40334 urlParts[3] == location.hostname;
40335 var uPort = urlParts[4], lPort = location.port;
40336 if (uPort != 80 && uPort != "" || lPort != "80" && lPort != "") {
40337 sameOrigin = sameOrigin && uPort == lPort;
40342 if (typeof proxy == "function") {
40345 url = proxy + encodeURIComponent(url);
40354 * Create a new XMLHttpRequest object, open it, set any headers, bind
40355 * a callback to done state, and send any data. It is recommended that
40356 * you use one <GET>, <POST>, <PUT>, <DELETE>, <OPTIONS>, or <HEAD>.
40357 * This method is only documented to provide detail on the configuration
40358 * options available to all request methods.
40361 * config - {Object} Object containing properties for configuring the
40362 * request. Allowed configuration properties are described below.
40363 * This object is modified and should not be reused.
40365 * Allowed config properties:
40366 * method - {String} One of GET, POST, PUT, DELETE, HEAD, or
40367 * OPTIONS. Default is GET.
40368 * url - {String} URL for the request.
40369 * async - {Boolean} Open an asynchronous request. Default is true.
40370 * user - {String} User for relevant authentication scheme. Set
40371 * to null to clear current user.
40372 * password - {String} Password for relevant authentication scheme.
40373 * Set to null to clear current password.
40374 * proxy - {String} Optional proxy. Defaults to
40375 * <OpenLayers.ProxyHost>.
40376 * params - {Object} Any key:value pairs to be appended to the
40377 * url as a query string. Assumes url doesn't already include a query
40378 * string or hash. Typically, this is only appropriate for <GET>
40379 * requests where the query string will be appended to the url.
40380 * Parameter values that are arrays will be
40381 * concatenated with a comma (note that this goes against form-encoding)
40382 * as is done with <OpenLayers.Util.getParameterString>.
40383 * headers - {Object} Object with header:value pairs to be set on
40385 * data - {String | Document} Optional data to send with the request.
40386 * Typically, this is only used with <POST> and <PUT> requests.
40387 * Make sure to provide the appropriate "Content-Type" header for your
40388 * data. For <POST> and <PUT> requests, the content type defaults to
40389 * "application-xml". If your data is a different content type, or
40390 * if you are using a different HTTP method, set the "Content-Type"
40391 * header to match your data type.
40392 * callback - {Function} Function to call when request is done.
40393 * To determine if the request failed, check request.status (200
40394 * indicates success).
40395 * success - {Function} Optional function to call if request status is in
40396 * the 200s. This will be called in addition to callback above and
40397 * would typically only be used as an alternative.
40398 * failure - {Function} Optional function to call if request status is not
40399 * in the 200s. This will be called in addition to callback above and
40400 * would typically only be used as an alternative.
40401 * scope - {Object} If callback is a public method on some object,
40402 * set the scope to that object.
40405 * {XMLHttpRequest} Request object. To abort the request before a response
40406 * is received, call abort() on the request object.
40408 issue: function(config) {
40409 // apply default config - proxy host may have changed
40410 var defaultConfig = OpenLayers.Util.extend(
40411 this.DEFAULT_CONFIG,
40412 {proxy: OpenLayers.ProxyHost}
40414 config = config || {};
40415 config.headers = config.headers || {};
40416 config = OpenLayers.Util.applyDefaults(config, defaultConfig);
40417 config.headers = OpenLayers.Util.applyDefaults(config.headers, defaultConfig.headers);
40418 // Always set the "X-Requested-With" header to signal that this request
40419 // was issued through the XHR-object. Since header keys are case
40420 // insensitive and we want to allow overriding of the "X-Requested-With"
40421 // header through the user we cannot use applyDefaults, but have to
40422 // check manually whether we were called with a "X-Requested-With"
40424 var customRequestedWithHeader = false,
40426 for(headerKey in config.headers) {
40427 if (config.headers.hasOwnProperty( headerKey )) {
40428 if (headerKey.toLowerCase() === 'x-requested-with') {
40429 customRequestedWithHeader = true;
40433 if (customRequestedWithHeader === false) {
40434 // we did not have a custom "X-Requested-With" header
40435 config.headers['X-Requested-With'] = 'XMLHttpRequest';
40438 // create request, open, and set headers
40439 var request = new OpenLayers.Request.XMLHttpRequest();
40440 var url = OpenLayers.Util.urlAppend(config.url,
40441 OpenLayers.Util.getParameterString(config.params || {}));
40442 url = OpenLayers.Request.makeSameOrigin(url, config.proxy);
40444 config.method, url, config.async, config.user, config.password
40446 for(var header in config.headers) {
40447 request.setRequestHeader(header, config.headers[header]);
40450 var events = this.events;
40452 // we want to execute runCallbacks with "this" as the
40456 request.onreadystatechange = function() {
40457 if(request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) {
40458 var proceed = events.triggerEvent(
40460 {request: request, config: config, requestUrl: url}
40462 if(proceed !== false) {
40464 {request: request, config: config, requestUrl: url}
40470 // send request (optionally with data) and return
40471 // call in a timeout for asynchronous requests so the return is
40472 // available before readyState == 4 for cached docs
40473 if(config.async === false) {
40474 request.send(config.data);
40476 window.setTimeout(function(){
40477 if (request.readyState !== 0) { // W3C: 0-UNSENT
40478 request.send(config.data);
40486 * Method: runCallbacks
40487 * Calls the complete, success and failure callbacks. Application
40488 * can listen to the "complete" event, have the listener
40489 * display a confirm window and always return false, and
40490 * execute OpenLayers.Request.runCallbacks if the user
40491 * hits "yes" in the confirm window.
40494 * options - {Object} Hash containing request, config and requestUrl keys
40496 runCallbacks: function(options) {
40497 var request = options.request;
40498 var config = options.config;
40500 // bind callbacks to readyState 4 (done)
40501 var complete = (config.scope) ?
40502 OpenLayers.Function.bind(config.callback, config.scope) :
40505 // optional success callback
40507 if(config.success) {
40508 success = (config.scope) ?
40509 OpenLayers.Function.bind(config.success, config.scope) :
40513 // optional failure callback
40515 if(config.failure) {
40516 failure = (config.scope) ?
40517 OpenLayers.Function.bind(config.failure, config.scope) :
40521 if (OpenLayers.Util.createUrlObject(config.url).protocol == "file:" &&
40522 request.responseText) {
40523 request.status = 200;
40527 if (!request.status || (request.status >= 200 && request.status < 300)) {
40528 this.events.triggerEvent("success", options);
40533 if(request.status && (request.status < 200 || request.status >= 300)) {
40534 this.events.triggerEvent("failure", options);
40543 * Send an HTTP GET request. Additional configuration properties are
40544 * documented in the <issue> method, with the method property set
40548 * config - {Object} Object with properties for configuring the request.
40549 * See the <issue> method for documentation of allowed properties.
40550 * This object is modified and should not be reused.
40553 * {XMLHttpRequest} Request object.
40555 GET: function(config) {
40556 config = OpenLayers.Util.extend(config, {method: "GET"});
40557 return OpenLayers.Request.issue(config);
40562 * Send a POST request. Additional configuration properties are
40563 * documented in the <issue> method, with the method property set
40564 * to POST and "Content-Type" header set to "application/xml".
40567 * config - {Object} Object with properties for configuring the request.
40568 * See the <issue> method for documentation of allowed properties. The
40569 * default "Content-Type" header will be set to "application-xml" if
40570 * none is provided. This object is modified and should not be reused.
40573 * {XMLHttpRequest} Request object.
40575 POST: function(config) {
40576 config = OpenLayers.Util.extend(config, {method: "POST"});
40577 // set content type to application/xml if it isn't already set
40578 config.headers = config.headers ? config.headers : {};
40579 if(!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) {
40580 config.headers["Content-Type"] = "application/xml";
40582 return OpenLayers.Request.issue(config);
40587 * Send an HTTP PUT request. Additional configuration properties are
40588 * documented in the <issue> method, with the method property set
40589 * to PUT and "Content-Type" header set to "application/xml".
40592 * config - {Object} Object with properties for configuring the request.
40593 * See the <issue> method for documentation of allowed properties. The
40594 * default "Content-Type" header will be set to "application-xml" if
40595 * none is provided. This object is modified and should not be reused.
40598 * {XMLHttpRequest} Request object.
40600 PUT: function(config) {
40601 config = OpenLayers.Util.extend(config, {method: "PUT"});
40602 // set content type to application/xml if it isn't already set
40603 config.headers = config.headers ? config.headers : {};
40604 if(!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) {
40605 config.headers["Content-Type"] = "application/xml";
40607 return OpenLayers.Request.issue(config);
40611 * APIMethod: DELETE
40612 * Send an HTTP DELETE request. Additional configuration properties are
40613 * documented in the <issue> method, with the method property set
40617 * config - {Object} Object with properties for configuring the request.
40618 * See the <issue> method for documentation of allowed properties.
40619 * This object is modified and should not be reused.
40622 * {XMLHttpRequest} Request object.
40624 DELETE: function(config) {
40625 config = OpenLayers.Util.extend(config, {method: "DELETE"});
40626 return OpenLayers.Request.issue(config);
40631 * Send an HTTP HEAD request. Additional configuration properties are
40632 * documented in the <issue> method, with the method property set
40636 * config - {Object} Object with properties for configuring the request.
40637 * See the <issue> method for documentation of allowed properties.
40638 * This object is modified and should not be reused.
40641 * {XMLHttpRequest} Request object.
40643 HEAD: function(config) {
40644 config = OpenLayers.Util.extend(config, {method: "HEAD"});
40645 return OpenLayers.Request.issue(config);
40649 * APIMethod: OPTIONS
40650 * Send an HTTP OPTIONS request. Additional configuration properties are
40651 * documented in the <issue> method, with the method property set
40655 * config - {Object} Object with properties for configuring the request.
40656 * See the <issue> method for documentation of allowed properties.
40657 * This object is modified and should not be reused.
40660 * {XMLHttpRequest} Request object.
40662 OPTIONS: function(config) {
40663 config = OpenLayers.Util.extend(config, {method: "OPTIONS"});
40664 return OpenLayers.Request.issue(config);
40668 /* ======================================================================
40669 OpenLayers/Request/XMLHttpRequest.js
40670 ====================================================================== */
40672 // XMLHttpRequest.js Copyright (C) 2010 Sergey Ilinsky (http://www.ilinsky.com)
40674 // Licensed under the Apache License, Version 2.0 (the "License");
40675 // you may not use this file except in compliance with the License.
40676 // You may obtain a copy of the License at
40678 // http://www.apache.org/licenses/LICENSE-2.0
40680 // Unless required by applicable law or agreed to in writing, software
40681 // distributed under the License is distributed on an "AS IS" BASIS,
40682 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
40683 // See the License for the specific language governing permissions and
40684 // limitations under the License.
40687 * @requires OpenLayers/Request.js
40692 // Save reference to earlier defined object implementation (if any)
40693 var oXMLHttpRequest = window.XMLHttpRequest;
40695 // Define on browser type
40696 var bGecko = !!window.controllers,
40697 bIE = window.document.all && !window.opera,
40698 bIE7 = bIE && window.navigator.userAgent.match(/MSIE 7.0/);
40700 // Enables "XMLHttpRequest()" call next to "new XMLHttpReques()"
40701 function fXMLHttpRequest() {
40702 this._object = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject("Microsoft.XMLHTTP");
40703 this._listeners = [];
40707 function cXMLHttpRequest() {
40708 return new fXMLHttpRequest;
40710 cXMLHttpRequest.prototype = fXMLHttpRequest.prototype;
40712 // BUGFIX: Firefox with Firebug installed would break pages if not executed
40713 if (bGecko && oXMLHttpRequest.wrapped)
40714 cXMLHttpRequest.wrapped = oXMLHttpRequest.wrapped;
40717 cXMLHttpRequest.UNSENT = 0;
40718 cXMLHttpRequest.OPENED = 1;
40719 cXMLHttpRequest.HEADERS_RECEIVED = 2;
40720 cXMLHttpRequest.LOADING = 3;
40721 cXMLHttpRequest.DONE = 4;
40723 // Public Properties
40724 cXMLHttpRequest.prototype.readyState = cXMLHttpRequest.UNSENT;
40725 cXMLHttpRequest.prototype.responseText = '';
40726 cXMLHttpRequest.prototype.responseXML = null;
40727 cXMLHttpRequest.prototype.status = 0;
40728 cXMLHttpRequest.prototype.statusText = '';
40730 // Priority proposal
40731 cXMLHttpRequest.prototype.priority = "NORMAL";
40733 // Instance-level Events Handlers
40734 cXMLHttpRequest.prototype.onreadystatechange = null;
40736 // Class-level Events Handlers
40737 cXMLHttpRequest.onreadystatechange = null;
40738 cXMLHttpRequest.onopen = null;
40739 cXMLHttpRequest.onsend = null;
40740 cXMLHttpRequest.onabort = null;
40743 cXMLHttpRequest.prototype.open = function(sMethod, sUrl, bAsync, sUser, sPassword) {
40744 // Delete headers, required when object is reused
40745 delete this._headers;
40747 // When bAsync parameter value is omitted, use true as default
40748 if (arguments.length < 3)
40751 // Save async parameter for fixing Gecko bug with missing readystatechange in synchronous requests
40752 this._async = bAsync;
40754 // Set the onreadystatechange handler
40755 var oRequest = this,
40756 nState = this.readyState,
40759 // BUGFIX: IE - memory leak on page unload (inter-page leak)
40760 if (bIE && bAsync) {
40761 fOnUnload = function() {
40762 if (nState != cXMLHttpRequest.DONE) {
40763 fCleanTransport(oRequest);
40764 // Safe to abort here since onreadystatechange handler removed
40768 window.attachEvent("onunload", fOnUnload);
40771 // Add method sniffer
40772 if (cXMLHttpRequest.onopen)
40773 cXMLHttpRequest.onopen.apply(this, arguments);
40775 if (arguments.length > 4)
40776 this._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
40778 if (arguments.length > 3)
40779 this._object.open(sMethod, sUrl, bAsync, sUser);
40781 this._object.open(sMethod, sUrl, bAsync);
40783 this.readyState = cXMLHttpRequest.OPENED;
40784 fReadyStateChange(this);
40786 this._object.onreadystatechange = function() {
40787 if (bGecko && !bAsync)
40790 // Synchronize state
40791 oRequest.readyState = oRequest._object.readyState;
40794 fSynchronizeValues(oRequest);
40796 // BUGFIX: Firefox fires unnecessary DONE when aborting
40797 if (oRequest._aborted) {
40798 // Reset readyState to UNSENT
40799 oRequest.readyState = cXMLHttpRequest.UNSENT;
40805 if (oRequest.readyState == cXMLHttpRequest.DONE) {
40807 delete oRequest._data;
40809 fQueue_remove(oRequest);*/
40811 fCleanTransport(oRequest);
40812 // Uncomment this block if you need a fix for IE cache
40814 // BUGFIX: IE - cache issue
40815 if (!oRequest._object.getResponseHeader("Date")) {
40816 // Save object to cache
40817 oRequest._cached = oRequest._object;
40819 // Instantiate a new transport object
40820 cXMLHttpRequest.call(oRequest);
40825 oRequest._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
40827 oRequest._object.open(sMethod, sUrl, bAsync, sUser);
40830 oRequest._object.open(sMethod, sUrl, bAsync);
40831 oRequest._object.setRequestHeader("If-Modified-Since", oRequest._cached.getResponseHeader("Last-Modified") || new window.Date(0));
40832 // Copy headers set
40833 if (oRequest._headers)
40834 for (var sHeader in oRequest._headers)
40835 if (typeof oRequest._headers[sHeader] == "string") // Some frameworks prototype objects with functions
40836 oRequest._object.setRequestHeader(sHeader, oRequest._headers[sHeader]);
40838 oRequest._object.onreadystatechange = function() {
40839 // Synchronize state
40840 oRequest.readyState = oRequest._object.readyState;
40842 if (oRequest._aborted) {
40844 oRequest.readyState = cXMLHttpRequest.UNSENT;
40850 if (oRequest.readyState == cXMLHttpRequest.DONE) {
40852 fCleanTransport(oRequest);
40854 // get cached request
40855 if (oRequest.status == 304)
40856 oRequest._object = oRequest._cached;
40859 delete oRequest._cached;
40862 fSynchronizeValues(oRequest);
40865 fReadyStateChange(oRequest);
40867 // BUGFIX: IE - memory leak in interrupted
40869 window.detachEvent("onunload", fOnUnload);
40872 oRequest._object.send(null);
40874 // Return now - wait until re-sent request is finished
40878 // BUGFIX: IE - memory leak in interrupted
40880 window.detachEvent("onunload", fOnUnload);
40883 // BUGFIX: Some browsers (Internet Explorer, Gecko) fire OPEN readystate twice
40884 if (nState != oRequest.readyState)
40885 fReadyStateChange(oRequest);
40887 nState = oRequest.readyState;
40890 function fXMLHttpRequest_send(oRequest) {
40891 oRequest._object.send(oRequest._data);
40893 // BUGFIX: Gecko - missing readystatechange calls in synchronous requests
40894 if (bGecko && !oRequest._async) {
40895 oRequest.readyState = cXMLHttpRequest.OPENED;
40897 // Synchronize state
40898 fSynchronizeValues(oRequest);
40900 // Simulate missing states
40901 while (oRequest.readyState < cXMLHttpRequest.DONE) {
40902 oRequest.readyState++;
40903 fReadyStateChange(oRequest);
40904 // Check if we are aborted
40905 if (oRequest._aborted)
40910 cXMLHttpRequest.prototype.send = function(vData) {
40911 // Add method sniffer
40912 if (cXMLHttpRequest.onsend)
40913 cXMLHttpRequest.onsend.apply(this, arguments);
40915 if (!arguments.length)
40918 // BUGFIX: Safari - fails sending documents created/modified dynamically, so an explicit serialization required
40919 // BUGFIX: IE - rewrites any custom mime-type to "text/xml" in case an XMLNode is sent
40920 // BUGFIX: Gecko - fails sending Element (this is up to the implementation either to standard)
40921 if (vData && vData.nodeType) {
40922 vData = window.XMLSerializer ? new window.XMLSerializer().serializeToString(vData) : vData.xml;
40923 if (!this._headers["Content-Type"])
40924 this._object.setRequestHeader("Content-Type", "application/xml");
40927 this._data = vData;
40933 fXMLHttpRequest_send(this);
40935 cXMLHttpRequest.prototype.abort = function() {
40936 // Add method sniffer
40937 if (cXMLHttpRequest.onabort)
40938 cXMLHttpRequest.onabort.apply(this, arguments);
40940 // BUGFIX: Gecko - unnecessary DONE when aborting
40941 if (this.readyState > cXMLHttpRequest.UNSENT)
40942 this._aborted = true;
40944 this._object.abort();
40946 // BUGFIX: IE - memory leak
40947 fCleanTransport(this);
40949 this.readyState = cXMLHttpRequest.UNSENT;
40952 /* if (this._async)
40953 fQueue_remove(this);*/
40955 cXMLHttpRequest.prototype.getAllResponseHeaders = function() {
40956 return this._object.getAllResponseHeaders();
40958 cXMLHttpRequest.prototype.getResponseHeader = function(sName) {
40959 return this._object.getResponseHeader(sName);
40961 cXMLHttpRequest.prototype.setRequestHeader = function(sName, sValue) {
40962 // BUGFIX: IE - cache issue
40963 if (!this._headers)
40964 this._headers = {};
40965 this._headers[sName] = sValue;
40967 return this._object.setRequestHeader(sName, sValue);
40970 // EventTarget interface implementation
40971 cXMLHttpRequest.prototype.addEventListener = function(sName, fHandler, bUseCapture) {
40972 for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
40973 if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)
40976 this._listeners.push([sName, fHandler, bUseCapture]);
40979 cXMLHttpRequest.prototype.removeEventListener = function(sName, fHandler, bUseCapture) {
40980 for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
40981 if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)
40985 this._listeners.splice(nIndex, 1);
40988 cXMLHttpRequest.prototype.dispatchEvent = function(oEvent) {
40989 var oEventPseudo = {
40990 'type': oEvent.type,
40992 'currentTarget':this,
40994 'bubbles': oEvent.bubbles,
40995 'cancelable': oEvent.cancelable,
40996 'timeStamp': oEvent.timeStamp,
40997 'stopPropagation': function() {}, // There is no flow
40998 'preventDefault': function() {}, // There is no default action
40999 'initEvent': function() {} // Original event object should be initialized
41002 // Execute onreadystatechange
41003 if (oEventPseudo.type == "readystatechange" && this.onreadystatechange)
41004 (this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEventPseudo]);
41006 // Execute listeners
41007 for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
41008 if (oListener[0] == oEventPseudo.type && !oListener[2])
41009 (oListener[1].handleEvent || oListener[1]).apply(this, [oEventPseudo]);
41013 cXMLHttpRequest.prototype.toString = function() {
41014 return '[' + "object" + ' ' + "XMLHttpRequest" + ']';
41017 cXMLHttpRequest.toString = function() {
41018 return '[' + "XMLHttpRequest" + ']';
41022 function fReadyStateChange(oRequest) {
41024 if (cXMLHttpRequest.onreadystatechange)
41025 cXMLHttpRequest.onreadystatechange.apply(oRequest);
41028 oRequest.dispatchEvent({
41029 'type': "readystatechange",
41031 'cancelable': false,
41032 'timeStamp': new Date + 0
41036 function fGetDocument(oRequest) {
41037 var oDocument = oRequest.responseXML,
41038 sResponse = oRequest.responseText;
41039 // Try parsing responseText
41040 if (bIE && sResponse && oDocument && !oDocument.documentElement && oRequest.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)) {
41041 oDocument = new window.ActiveXObject("Microsoft.XMLDOM");
41042 oDocument.async = false;
41043 oDocument.validateOnParse = false;
41044 oDocument.loadXML(sResponse);
41046 // Check if there is no error in document
41048 if ((bIE && oDocument.parseError != 0) || !oDocument.documentElement || (oDocument.documentElement && oDocument.documentElement.tagName == "parsererror"))
41053 function fSynchronizeValues(oRequest) {
41054 try { oRequest.responseText = oRequest._object.responseText; } catch (e) {}
41055 try { oRequest.responseXML = fGetDocument(oRequest._object); } catch (e) {}
41056 try { oRequest.status = oRequest._object.status; } catch (e) {}
41057 try { oRequest.statusText = oRequest._object.statusText; } catch (e) {}
41060 function fCleanTransport(oRequest) {
41061 // BUGFIX: IE - memory leak (on-page leak)
41062 oRequest._object.onreadystatechange = new window.Function;
41066 var oQueuePending = {"CRITICAL":[],"HIGH":[],"NORMAL":[],"LOW":[],"LOWEST":[]},
41067 aQueueRunning = [];
41068 function fQueue_add(oRequest) {
41069 oQueuePending[oRequest.priority in oQueuePending ? oRequest.priority : "NORMAL"].push(oRequest);
41071 setTimeout(fQueue_process);
41074 function fQueue_remove(oRequest) {
41075 for (var nIndex = 0, bFound = false; nIndex < aQueueRunning.length; nIndex++)
41077 aQueueRunning[nIndex - 1] = aQueueRunning[nIndex];
41079 if (aQueueRunning[nIndex] == oRequest)
41082 aQueueRunning.length--;
41084 setTimeout(fQueue_process);
41087 function fQueue_process() {
41088 if (aQueueRunning.length < 6) {
41089 for (var sPriority in oQueuePending) {
41090 if (oQueuePending[sPriority].length) {
41091 var oRequest = oQueuePending[sPriority][0];
41092 oQueuePending[sPriority] = oQueuePending[sPriority].slice(1);
41094 aQueueRunning.push(oRequest);
41096 fXMLHttpRequest_send(oRequest);
41103 // Internet Explorer 5.0 (missing apply)
41104 if (!window.Function.prototype.apply) {
41105 window.Function.prototype.apply = function(oRequest, oArguments) {
41108 oRequest.__func = this;
41109 oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]);
41110 delete oRequest.__func;
41114 // Register new object with window
41116 * Class: OpenLayers.Request.XMLHttpRequest
41117 * Standard-compliant (W3C) cross-browser implementation of the
41118 * XMLHttpRequest object. From
41119 * http://code.google.com/p/xmlhttprequest/.
41121 if (!OpenLayers.Request) {
41123 * This allows for OpenLayers/Request.js to be included
41124 * before or after this script.
41126 OpenLayers.Request = {};
41128 OpenLayers.Request.XMLHttpRequest = cXMLHttpRequest;
41130 /* ======================================================================
41131 OpenLayers/Lang/de.js
41132 ====================================================================== */
41134 /* Translators (2009 onwards):
41141 * @requires OpenLayers/Lang.js
41145 * Namespace: OpenLayers.Lang["de"]
41146 * Dictionary for Deutsch. Keys for entries are used in calls to
41147 * <OpenLayers.Lang.translate>. Entry bodies are normal strings or
41148 * strings formatted for use with <OpenLayers.String.format> calls.
41150 OpenLayers.Lang["de"] = OpenLayers.Util.applyDefaults({
41152 'unhandledRequest': "Unbehandelte Anfragerückmeldung ${statusText}",
41154 'Permalink': "Permalink",
41156 'Overlays': "Overlays",
41158 'Base Layer': "Grundkarte",
41160 'noFID': "Ein Feature, für das keine FID existiert, kann nicht aktualisiert werden.",
41162 'browserNotSupported': "Ihr Browser unterstützt keine Vektordarstellung. Aktuell unterstützte Renderer:\n${renderers}",
41164 'minZoomLevelError': "Die \x3ccode\x3eminZoomLevel\x3c/code\x3e-Eigenschaft ist nur für die Verwendung mit \x3ccode\x3eFixedZoomLevels\x3c/code\x3e-untergeordneten Layers vorgesehen. Das dieser \x3ctt\x3ewfs\x3c/tt\x3e-Layer die \x3ccode\x3eminZoomLevel\x3c/code\x3e-Eigenschaft überprüft ist ein Relikt der Vergangenheit. Wir können diese Überprüfung nicht entfernen, ohne das OL basierende Applikationen nicht mehr funktionieren. Daher markieren wir es als veraltet - die \x3ccode\x3eminZoomLevel\x3c/code\x3e-Überprüfung wird in Version 3.0 entfernt werden. Bitte verwenden Sie stattdessen die Min-/Max-Lösung, wie sie unter http://trac.openlayers.org/wiki/SettingZoomLevels beschrieben ist.",
41166 'commitSuccess': "WFS-Transaktion: Erfolgreich ${response}",
41168 'commitFailed': "WFS-Transaktion: Fehlgeschlagen ${response}",
41170 'googleWarning': "Der Google-Layer konnte nicht korrekt geladen werden.\x3cbr\x3e\x3cbr\x3eUm diese Meldung nicht mehr zu erhalten, wählen Sie einen anderen Hintergrundlayer aus dem LayerSwitcher in der rechten oberen Ecke.\x3cbr\x3e\x3cbr\x3eSehr wahrscheinlich tritt dieser Fehler auf, weil das Skript der Google-Maps-Bibliothek nicht eingebunden wurde oder keinen gültigen API-Schlüssel für Ihre URL enthält.\x3cbr\x3e\x3cbr\x3eEntwickler: Besuche \x3ca href=\'http://trac.openlayers.org/wiki/Google\' target=\'_blank\'\x3edas Wiki\x3c/a\x3e für Hilfe zum korrekten Einbinden des Google-Layers",
41172 'getLayerWarning': "Der ${layerType}-Layer konnte nicht korrekt geladen werden.\x3cbr\x3e\x3cbr\x3eUm diese Meldung nicht mehr zu erhalten, wählen Sie einen anderen Hintergrundlayer aus dem LayerSwitcher in der rechten oberen Ecke.\x3cbr\x3e\x3cbr\x3eSehr wahrscheinlich tritt dieser Fehler auf, weil das Skript der \'${layerLib}\'-Bibliothek nicht eingebunden wurde.\x3cbr\x3e\x3cbr\x3eEntwickler: Besuche \x3ca href=\'http://trac.openlayers.org/wiki/${layerLib}\' target=\'_blank\'\x3edas Wiki\x3c/a\x3e für Hilfe zum korrekten Einbinden von Layern",
41174 'Scale = 1 : ${scaleDenom}': "Maßstab = 1 : ${scaleDenom}",
41184 'reprojectDeprecated': "Sie verwenden die „Reproject“-Option des Layers ${layerName}. Diese Option ist veraltet: Sie wurde entwickelt um die Anzeige von Daten auf kommerziellen Basiskarten zu unterstützen, aber diese Funktion sollte jetzt durch Unterstützung der „Spherical Mercator“ erreicht werden. Weitere Informationen sind unter http://trac.openlayers.org/wiki/SphericalMercator verfügbar.",
41186 'methodDeprecated': "Die Methode ist veraltet und wird in 3.0 entfernt. Bitte verwende stattdessen ${newMethod}."
41189 /* ======================================================================
41190 OpenLayers/Popup/FramedCloud.js
41191 ====================================================================== */
41193 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
41194 * full list of contributors). Published under the 2-clause BSD license.
41195 * See license.txt in the OpenLayers distribution or repository for the
41196 * full text of the license. */
41199 * @requires OpenLayers/Popup/Framed.js
41200 * @requires OpenLayers/Util.js
41201 * @requires OpenLayers/BaseTypes/Bounds.js
41202 * @requires OpenLayers/BaseTypes/Pixel.js
41203 * @requires OpenLayers/BaseTypes/Size.js
41207 * Class: OpenLayers.Popup.FramedCloud
41210 * - <OpenLayers.Popup.Framed>
41212 OpenLayers.Popup.FramedCloud =
41213 OpenLayers.Class(OpenLayers.Popup.Framed, {
41216 * Property: contentDisplayClass
41217 * {String} The CSS class of the popup content div.
41219 contentDisplayClass: "olFramedCloudPopupContent",
41222 * APIProperty: autoSize
41223 * {Boolean} Framed Cloud is autosizing by default.
41228 * APIProperty: panMapIfOutOfView
41229 * {Boolean} Framed Cloud does pan into view by default.
41231 panMapIfOutOfView: true,
41234 * APIProperty: imageSize
41235 * {<OpenLayers.Size>}
41237 imageSize: new OpenLayers.Size(1276, 736),
41240 * APIProperty: isAlphaImage
41241 * {Boolean} The FramedCloud does not use an alpha image (in honor of the
41242 * good ie6 folk out there)
41244 isAlphaImage: false,
41247 * APIProperty: fixedRelativePosition
41248 * {Boolean} The Framed Cloud popup works in just one fixed position.
41250 fixedRelativePosition: false,
41253 * Property: positionBlocks
41254 * {Object} Hash of differen position blocks, keyed by relativePosition
41255 * two-character code string (ie "tl", "tr", "bl", "br")
41259 'offset': new OpenLayers.Pixel(44, 0),
41260 'padding': new OpenLayers.Bounds(8, 40, 8, 9),
41263 size: new OpenLayers.Size('auto', 'auto'),
41264 anchor: new OpenLayers.Bounds(0, 51, 22, 0),
41265 position: new OpenLayers.Pixel(0, 0)
41268 size: new OpenLayers.Size(22, 'auto'),
41269 anchor: new OpenLayers.Bounds(null, 50, 0, 0),
41270 position: new OpenLayers.Pixel(-1238, 0)
41273 size: new OpenLayers.Size('auto', 19),
41274 anchor: new OpenLayers.Bounds(0, 32, 22, null),
41275 position: new OpenLayers.Pixel(0, -631)
41278 size: new OpenLayers.Size(22, 18),
41279 anchor: new OpenLayers.Bounds(null, 32, 0, null),
41280 position: new OpenLayers.Pixel(-1238, -632)
41283 size: new OpenLayers.Size(81, 35),
41284 anchor: new OpenLayers.Bounds(null, 0, 0, null),
41285 position: new OpenLayers.Pixel(0, -688)
41290 'offset': new OpenLayers.Pixel(-45, 0),
41291 'padding': new OpenLayers.Bounds(8, 40, 8, 9),
41294 size: new OpenLayers.Size('auto', 'auto'),
41295 anchor: new OpenLayers.Bounds(0, 51, 22, 0),
41296 position: new OpenLayers.Pixel(0, 0)
41299 size: new OpenLayers.Size(22, 'auto'),
41300 anchor: new OpenLayers.Bounds(null, 50, 0, 0),
41301 position: new OpenLayers.Pixel(-1238, 0)
41304 size: new OpenLayers.Size('auto', 19),
41305 anchor: new OpenLayers.Bounds(0, 32, 22, null),
41306 position: new OpenLayers.Pixel(0, -631)
41309 size: new OpenLayers.Size(22, 19),
41310 anchor: new OpenLayers.Bounds(null, 32, 0, null),
41311 position: new OpenLayers.Pixel(-1238, -631)
41314 size: new OpenLayers.Size(81, 35),
41315 anchor: new OpenLayers.Bounds(0, 0, null, null),
41316 position: new OpenLayers.Pixel(-215, -687)
41321 'offset': new OpenLayers.Pixel(45, 0),
41322 'padding': new OpenLayers.Bounds(8, 9, 8, 40),
41325 size: new OpenLayers.Size('auto', 'auto'),
41326 anchor: new OpenLayers.Bounds(0, 21, 22, 32),
41327 position: new OpenLayers.Pixel(0, 0)
41330 size: new OpenLayers.Size(22, 'auto'),
41331 anchor: new OpenLayers.Bounds(null, 21, 0, 32),
41332 position: new OpenLayers.Pixel(-1238, 0)
41335 size: new OpenLayers.Size('auto', 21),
41336 anchor: new OpenLayers.Bounds(0, 0, 22, null),
41337 position: new OpenLayers.Pixel(0, -629)
41340 size: new OpenLayers.Size(22, 21),
41341 anchor: new OpenLayers.Bounds(null, 0, 0, null),
41342 position: new OpenLayers.Pixel(-1238, -629)
41345 size: new OpenLayers.Size(81, 33),
41346 anchor: new OpenLayers.Bounds(null, null, 0, 0),
41347 position: new OpenLayers.Pixel(-101, -674)
41352 'offset': new OpenLayers.Pixel(-44, 0),
41353 'padding': new OpenLayers.Bounds(8, 9, 8, 40),
41356 size: new OpenLayers.Size('auto', 'auto'),
41357 anchor: new OpenLayers.Bounds(0, 21, 22, 32),
41358 position: new OpenLayers.Pixel(0, 0)
41361 size: new OpenLayers.Size(22, 'auto'),
41362 anchor: new OpenLayers.Bounds(null, 21, 0, 32),
41363 position: new OpenLayers.Pixel(-1238, 0)
41366 size: new OpenLayers.Size('auto', 21),
41367 anchor: new OpenLayers.Bounds(0, 0, 22, null),
41368 position: new OpenLayers.Pixel(0, -629)
41371 size: new OpenLayers.Size(22, 21),
41372 anchor: new OpenLayers.Bounds(null, 0, 0, null),
41373 position: new OpenLayers.Pixel(-1238, -629)
41376 size: new OpenLayers.Size(81, 33),
41377 anchor: new OpenLayers.Bounds(0, null, null, 0),
41378 position: new OpenLayers.Pixel(-311, -674)
41385 * APIProperty: minSize
41386 * {<OpenLayers.Size>}
41388 minSize: new OpenLayers.Size(105, 10),
41391 * APIProperty: maxSize
41392 * {<OpenLayers.Size>}
41394 maxSize: new OpenLayers.Size(1200, 660),
41397 * Constructor: OpenLayers.Popup.FramedCloud
41401 * lonlat - {<OpenLayers.LonLat>}
41402 * contentSize - {<OpenLayers.Size>}
41403 * contentHTML - {String}
41404 * anchor - {Object} Object to which we'll anchor the popup. Must expose
41405 * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>)
41406 * (Note that this is generally an <OpenLayers.Icon>).
41407 * closeBox - {Boolean}
41408 * closeBoxCallback - {Function} Function to be called on closeBox click.
41410 initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
41411 closeBoxCallback) {
41413 this.imageSrc = OpenLayers.Util.getImageLocation('cloud-popup-relative.png');
41414 OpenLayers.Popup.Framed.prototype.initialize.apply(this, arguments);
41415 this.contentDiv.className = this.contentDisplayClass;
41418 CLASS_NAME: "OpenLayers.Popup.FramedCloud"
41420 /* ======================================================================
41422 ====================================================================== */
41424 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
41425 * full list of contributors). Published under the 2-clause BSD license.
41426 * See license.txt in the OpenLayers distribution or repository for the
41427 * full text of the license. */
41431 * @requires OpenLayers/BaseTypes/Class.js
41432 * @requires OpenLayers/Util.js
41433 * @requires OpenLayers/Style.js
41437 * Class: OpenLayers.Rule
41438 * This class represents an SLD Rule, as being used for rule-based SLD styling.
41440 OpenLayers.Rule = OpenLayers.Class({
41444 * {String} A unique id for this session.
41449 * APIProperty: name
41450 * {String} name of this rule
41456 * {String} Title of this rule (set if included in SLD)
41461 * Property: description
41462 * {String} Description of this rule (set if abstract is included in SLD)
41467 * Property: context
41468 * {Object} An optional object with properties that the rule should be
41469 * evaluated against. If no context is specified, feature.attributes will
41476 * {<OpenLayers.Filter>} Optional filter for the rule.
41481 * Property: elseFilter
41482 * {Boolean} Determines whether this rule is only to be applied only if
41483 * no other rules match (ElseFilter according to the SLD specification).
41484 * Default is false. For instances of OpenLayers.Rule, if elseFilter is
41485 * false, the rule will always apply. For subclasses, the else property is
41491 * Property: symbolizer
41492 * {Object} Symbolizer or hash of symbolizers for this rule. If hash of
41493 * symbolizers, keys are one or more of ["Point", "Line", "Polygon"]. The
41494 * latter if useful if it is required to style e.g. vertices of a line
41495 * with a point symbolizer. Note, however, that this is not implemented
41496 * yet in OpenLayers, but it is the way how symbolizers are defined in
41502 * Property: symbolizers
41503 * {Array} Collection of symbolizers associated with this rule. If
41504 * provided at construction, the symbolizers array has precedence
41505 * over the deprecated symbolizer property. Note that multiple
41506 * symbolizers are not currently supported by the vector renderers.
41507 * Rules with multiple symbolizers are currently only useful for
41508 * maintaining elements in an SLD document.
41513 * APIProperty: minScaleDenominator
41514 * {Number} or {String} minimum scale at which to draw the feature.
41515 * In the case of a String, this can be a combination of text and
41516 * propertyNames in the form "literal ${propertyName}"
41518 minScaleDenominator: null,
41521 * APIProperty: maxScaleDenominator
41522 * {Number} or {String} maximum scale at which to draw the feature.
41523 * In the case of a String, this can be a combination of text and
41524 * propertyNames in the form "literal ${propertyName}"
41526 maxScaleDenominator: null,
41529 * Constructor: OpenLayers.Rule
41533 * options - {Object} An optional object with properties to set on the
41537 * {<OpenLayers.Rule>}
41539 initialize: function(options) {
41540 this.symbolizer = {};
41541 OpenLayers.Util.extend(this, options);
41542 if (this.symbolizers) {
41543 delete this.symbolizer;
41545 this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
41549 * APIMethod: destroy
41550 * nullify references to prevent circular references and memory leaks
41552 destroy: function() {
41553 for (var i in this.symbolizer) {
41554 this.symbolizer[i] = null;
41556 this.symbolizer = null;
41557 delete this.symbolizers;
41561 * APIMethod: evaluate
41562 * evaluates this rule for a specific feature
41565 * feature - {<OpenLayers.Feature>} feature to apply the rule to.
41568 * {Boolean} true if the rule applies, false if it does not.
41569 * This rule is the default rule and always returns true.
41571 evaluate: function(feature) {
41572 var context = this.getContext(feature);
41573 var applies = true;
41575 if (this.minScaleDenominator || this.maxScaleDenominator) {
41576 var scale = feature.layer.map.getScale();
41579 // check if within minScale/maxScale bounds
41580 if (this.minScaleDenominator) {
41581 applies = scale >= OpenLayers.Style.createLiteral(
41582 this.minScaleDenominator, context);
41584 if (applies && this.maxScaleDenominator) {
41585 applies = scale < OpenLayers.Style.createLiteral(
41586 this.maxScaleDenominator, context);
41589 // check if optional filter applies
41590 if(applies && this.filter) {
41591 // feature id filters get the feature, others get the context
41592 if(this.filter.CLASS_NAME == "OpenLayers.Filter.FeatureId") {
41593 applies = this.filter.evaluate(feature);
41595 applies = this.filter.evaluate(context);
41603 * Method: getContext
41604 * Gets the context for evaluating this rule
41607 * feature - {<OpenLayers.Feature>} feature to take the context from if
41608 * none is specified.
41610 getContext: function(feature) {
41611 var context = this.context;
41613 context = feature.attributes || feature.data;
41615 if (typeof this.context == "function") {
41616 context = this.context(feature);
41623 * Clones this rule.
41626 * {<OpenLayers.Rule>} Clone of this rule.
41628 clone: function() {
41629 var options = OpenLayers.Util.extend({}, this);
41630 if (this.symbolizers) {
41631 // clone symbolizers
41632 var len = this.symbolizers.length;
41633 options.symbolizers = new Array(len);
41634 for (var i=0; i<len; ++i) {
41635 options.symbolizers[i] = this.symbolizers[i].clone();
41638 // clone symbolizer
41639 options.symbolizer = {};
41641 for(var key in this.symbolizer) {
41642 value = this.symbolizer[key];
41643 type = typeof value;
41644 if(type === "object") {
41645 options.symbolizer[key] = OpenLayers.Util.extend({}, value);
41646 } else if(type === "string") {
41647 options.symbolizer[key] = value;
41652 options.filter = this.filter && this.filter.clone();
41654 options.context = this.context && OpenLayers.Util.extend({}, this.context);
41655 return new OpenLayers.Rule(options);
41658 CLASS_NAME: "OpenLayers.Rule"
41660 /* ======================================================================
41661 OpenLayers/Handler/Pinch.js
41662 ====================================================================== */
41664 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
41665 * full list of contributors). Published under the 2-clause BSD license.
41666 * See license.txt in the OpenLayers distribution or repository for the
41667 * full text of the license. */
41670 * @requires OpenLayers/Handler.js
41674 * Class: OpenLayers.Handler.Pinch
41675 * The pinch handler is used to deal with sequences of browser events related
41676 * to pinch gestures. The handler is used by controls that want to know
41677 * when a pinch sequence begins, when a pinch is happening, and when it has
41680 * Controls that use the pinch handler typically construct it with callbacks
41681 * for 'start', 'move', and 'done'. Callbacks for these keys are
41682 * called when the pinch begins, with each change, and when the pinch is
41685 * Create a new pinch handler with the <OpenLayers.Handler.Pinch> constructor.
41688 * - <OpenLayers.Handler>
41690 OpenLayers.Handler.Pinch = OpenLayers.Class(OpenLayers.Handler, {
41693 * Property: started
41694 * {Boolean} When a touchstart event is received, we want to record it,
41695 * but not set 'pinching' until the touchmove get started after
41701 * Property: stopDown
41702 * {Boolean} Stop propagation of touchstart events from getting to
41703 * listeners on the same element. Default is false.
41708 * Property: pinching
41715 * {Object} Object that store informations related to pinch last touch.
41721 * {Object} Object that store informations related to pinch touchstart.
41726 * Constructor: OpenLayers.Handler.Pinch
41727 * Returns OpenLayers.Handler.Pinch
41730 * control - {<OpenLayers.Control>} The control that is making use of
41731 * this handler. If a handler is being used without a control, the
41732 * handlers setMap method must be overridden to deal properly with
41734 * callbacks - {Object} An object containing functions to be called when
41735 * the pinch operation start, change, or is finished. The callbacks
41736 * should expect to receive an object argument, which contains
41737 * information about scale, distance, and position of touch points.
41738 * options - {Object}
41742 * Method: touchstart
41743 * Handle touchstart events
41749 * {Boolean} Let the event propagate.
41751 touchstart: function(evt) {
41752 var propagate = true;
41753 this.pinching = false;
41754 if (OpenLayers.Event.isMultiTouch(evt)) {
41755 this.started = true;
41756 this.last = this.start = {
41757 distance: this.getDistance(evt.touches),
41761 this.callback("start", [evt, this.start]);
41762 propagate = !this.stopDown;
41763 } else if (this.started) {
41764 // Some webkit versions send fake single-touch events during
41765 // multitouch, which cause the drag handler to trigger
41768 this.started = false;
41772 // prevent document dragging
41773 OpenLayers.Event.preventDefault(evt);
41778 * Method: touchmove
41779 * Handle touchmove events
41785 * {Boolean} Let the event propagate.
41787 touchmove: function(evt) {
41788 if (this.started && OpenLayers.Event.isMultiTouch(evt)) {
41789 this.pinching = true;
41790 var current = this.getPinchData(evt);
41791 this.callback("move", [evt, current]);
41792 this.last = current;
41793 // prevent document dragging
41794 OpenLayers.Event.stop(evt);
41795 } else if (this.started) {
41796 // Some webkit versions send fake single-touch events during
41797 // multitouch, which cause the drag handler to trigger
41805 * Handle touchend events
41811 * {Boolean} Let the event propagate.
41813 touchend: function(evt) {
41814 if (this.started && !OpenLayers.Event.isMultiTouch(evt)) {
41815 this.started = false;
41816 this.pinching = false;
41817 this.callback("done", [evt, this.start, this.last]);
41827 * Activate the handler.
41830 * {Boolean} The handler was successfully activated.
41832 activate: function() {
41833 var activated = false;
41834 if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
41835 this.pinching = false;
41842 * Method: deactivate
41843 * Deactivate the handler.
41846 * {Boolean} The handler was successfully deactivated.
41848 deactivate: function() {
41849 var deactivated = false;
41850 if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
41851 this.started = false;
41852 this.pinching = false;
41855 deactivated = true;
41857 return deactivated;
41861 * Method: getDistance
41862 * Get the distance in pixels between two touches.
41865 * touches - {Array(Object)}
41868 * {Number} The distance in pixels.
41870 getDistance: function(touches) {
41871 var t0 = touches[0];
41872 var t1 = touches[1];
41874 Math.pow(t0.olClientX - t1.olClientX, 2) +
41875 Math.pow(t0.olClientY - t1.olClientY, 2)
41881 * Method: getPinchData
41882 * Get informations about the pinch event.
41888 * {Object} Object that contains data about the current pinch.
41890 getPinchData: function(evt) {
41891 var distance = this.getDistance(evt.touches);
41892 var scale = distance / this.start.distance;
41894 distance: distance,
41895 delta: this.last.distance - distance,
41900 CLASS_NAME: "OpenLayers.Handler.Pinch"
41903 /* ======================================================================
41904 OpenLayers/Lang/en.js
41905 ====================================================================== */
41908 * @requires OpenLayers/Lang.js
41912 * Namespace: OpenLayers.Lang["en"]
41913 * Dictionary for English. Keys for entries are used in calls to
41914 * <OpenLayers.Lang.translate>. Entry bodies are normal strings or
41915 * strings formatted for use with <OpenLayers.String.format> calls.
41917 OpenLayers.Lang.en = {
41919 'unhandledRequest': "Unhandled request return ${statusText}",
41921 'Permalink': "Permalink",
41923 'Overlays': "Overlays",
41925 'Base Layer': "Base Layer",
41927 'noFID': "Can't update a feature for which there is no FID.",
41929 'browserNotSupported':
41930 "Your browser does not support vector rendering. Currently supported renderers are:\n${renderers}",
41933 'minZoomLevelError':
41934 "The minZoomLevel property is only intended for use " +
41935 "with the FixedZoomLevels-descendent layers. That this " +
41936 "wfs layer checks for minZoomLevel is a relic of the" +
41937 "past. We cannot, however, remove it without possibly " +
41938 "breaking OL based applications that may depend on it." +
41939 " Therefore we are deprecating it -- the minZoomLevel " +
41940 "check below will be removed at 3.0. Please instead " +
41941 "use min/max resolution setting as described here: " +
41942 "http://trac.openlayers.org/wiki/SettingZoomLevels",
41944 'commitSuccess': "WFS Transaction: SUCCESS ${response}",
41946 'commitFailed': "WFS Transaction: FAILED ${response}",
41949 "The Google Layer was unable to load correctly.<br><br>" +
41950 "To get rid of this message, select a new BaseLayer " +
41951 "in the layer switcher in the upper-right corner.<br><br>" +
41952 "Most likely, this is because the Google Maps library " +
41953 "script was either not included, or does not contain the " +
41954 "correct API key for your site.<br><br>" +
41955 "Developers: For help getting this working correctly, " +
41956 "<a href='http://trac.openlayers.org/wiki/Google' " +
41957 "target='_blank'>click here</a>",
41960 "The ${layerType} Layer was unable to load correctly.<br><br>" +
41961 "To get rid of this message, select a new BaseLayer " +
41962 "in the layer switcher in the upper-right corner.<br><br>" +
41963 "Most likely, this is because the ${layerLib} library " +
41964 "script was not correctly included.<br><br>" +
41965 "Developers: For help getting this working correctly, " +
41966 "<a href='http://trac.openlayers.org/wiki/${layerLib}' " +
41967 "target='_blank'>click here</a>",
41969 'Scale = 1 : ${scaleDenom}': "Scale = 1 : ${scaleDenom}",
41971 //labels for the graticule control
41976 'Graticule': 'Graticule',
41979 'reprojectDeprecated':
41980 "You are using the 'reproject' option " +
41981 "on the ${layerName} layer. This option is deprecated: " +
41982 "its use was designed to support displaying data over commercial " +
41983 "basemaps, but that functionality should now be achieved by using " +
41984 "Spherical Mercator support. More information is available from " +
41985 "http://trac.openlayers.org/wiki/SphericalMercator.",
41988 'methodDeprecated':
41989 "This method has been deprecated and will be removed in 3.0. " +
41990 "Please use ${newMethod} instead.",
41996 /* ======================================================================
41997 OpenLayers/Control/PinchZoom.js
41998 ====================================================================== */
42000 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
42001 * full list of contributors). Published under the 2-clause BSD license.
42002 * See license.txt in the OpenLayers distribution or repository for the
42003 * full text of the license. */
42006 * @requires OpenLayers/Handler/Pinch.js
42010 * Class: OpenLayers.Control.PinchZoom
42013 * - <OpenLayers.Control>
42015 OpenLayers.Control.PinchZoom = OpenLayers.Class(OpenLayers.Control, {
42019 * {OpenLayers.Control.TYPES}
42021 type: OpenLayers.Control.TYPE_TOOL,
42024 * Property: pinchOrigin
42025 * {Object} Cached object representing the pinch start (in pixels).
42030 * Property: currentCenter
42031 * {Object} Cached object representing the latest pinch center (in pixels).
42033 currentCenter: null,
42036 * APIProperty: autoActivate
42037 * {Boolean} Activate the control when it is added to a map. Default is
42040 autoActivate: true,
42043 * APIProperty: preserveCenter
42044 * {Boolean} Set this to true if you don't want the map center to change
42045 * while pinching. For example you may want to set preserveCenter to
42046 * true when the user location is being watched and you want to preserve
42047 * the user location at the center of the map even if he zooms in or
42048 * out using pinch. This property's value can be changed any time on an
42049 * existing instance. Default is false.
42051 preserveCenter: false,
42054 * APIProperty: handlerOptions
42055 * {Object} Used to set non-default properties on the pinch handler
42059 * Constructor: OpenLayers.Control.PinchZoom
42060 * Create a control for zooming with pinch gestures. This works on devices
42061 * with multi-touch support.
42064 * options - {Object} An optional object whose properties will be set on
42067 initialize: function(options) {
42068 OpenLayers.Control.prototype.initialize.apply(this, arguments);
42069 this.handler = new OpenLayers.Handler.Pinch(this, {
42070 start: this.pinchStart,
42071 move: this.pinchMove,
42072 done: this.pinchDone
42073 }, this.handlerOptions);
42077 * Method: pinchStart
42081 * pinchData - {Object} pinch data object related to the current touchmove
42082 * of the pinch gesture. This give us the current scale of the pinch.
42084 pinchStart: function(evt, pinchData) {
42085 var xy = (this.preserveCenter) ?
42086 this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy;
42087 this.pinchOrigin = xy;
42088 this.currentCenter = xy;
42092 * Method: pinchMove
42096 * pinchData - {Object} pinch data object related to the current touchmove
42097 * of the pinch gesture. This give us the current scale of the pinch.
42099 pinchMove: function(evt, pinchData) {
42100 var scale = pinchData.scale;
42101 var containerOrigin = this.map.layerContainerOriginPx;
42102 var pinchOrigin = this.pinchOrigin;
42103 var current = (this.preserveCenter) ?
42104 this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy;
42106 var dx = Math.round((containerOrigin.x + current.x - pinchOrigin.x) + (scale - 1) * (containerOrigin.x - pinchOrigin.x));
42107 var dy = Math.round((containerOrigin.y + current.y - pinchOrigin.y) + (scale - 1) * (containerOrigin.y - pinchOrigin.y));
42109 this.map.applyTransform(dx, dy, scale);
42110 this.currentCenter = current;
42114 * Method: pinchDone
42118 * start - {Object} pinch data object related to the touchstart event that
42119 * started the pinch gesture.
42120 * last - {Object} pinch data object related to the last touchmove event
42121 * of the pinch gesture. This give us the final scale of the pinch.
42123 pinchDone: function(evt, start, last) {
42124 this.map.applyTransform();
42125 var zoom = this.map.getZoomForResolution(this.map.getResolution() / last.scale, true);
42126 if (zoom !== this.map.getZoom() || !this.currentCenter.equals(this.pinchOrigin)) {
42127 var resolution = this.map.getResolutionForZoom(zoom);
42129 var location = this.map.getLonLatFromPixel(this.pinchOrigin);
42130 var zoomPixel = this.currentCenter;
42131 var size = this.map.getSize();
42133 location.lon += resolution * ((size.w / 2) - zoomPixel.x);
42134 location.lat -= resolution * ((size.h / 2) - zoomPixel.y);
42136 // Force a reflow before calling setCenter. This is to work
42137 // around an issue occuring in iOS.
42139 // See https://github.com/openlayers/openlayers/pull/351.
42141 // Without a reflow setting the layer container div's top left
42142 // style properties to "0px" - as done in Map.moveTo when zoom
42143 // is changed - won't actually correctly reposition the layer
42146 // Also, we need to use a statement that the Google Closure
42147 // compiler won't optimize away.
42148 this.map.div.clientWidth = this.map.div.clientWidth;
42150 this.map.setCenter(location, zoom);
42154 CLASS_NAME: "OpenLayers.Control.PinchZoom"
42157 /* ======================================================================
42158 OpenLayers/Control/TouchNavigation.js
42159 ====================================================================== */
42161 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
42162 * full list of contributors). Published under the 2-clause BSD license.
42163 * See license.txt in the OpenLayers distribution or repository for the
42164 * full text of the license. */
42167 * @requires OpenLayers/Control/DragPan.js
42168 * @requires OpenLayers/Control/PinchZoom.js
42169 * @requires OpenLayers/Handler/Click.js
42173 * Class: OpenLayers.Control.TouchNavigation
42174 * The navigation control handles map browsing with touch events (dragging,
42175 * double-tapping, tap with two fingers, and pinch zoom). Create a new
42176 * control with the <OpenLayers.Control.TouchNavigation> constructor.
42178 * If you’re only targeting touch enabled devices with your mapping application,
42179 * you can create a map with only a TouchNavigation control. The
42180 * <OpenLayers.Control.Navigation> control is mobile ready by default, but
42181 * you can generate a smaller build of the library by only including this
42182 * touch navigation control if you aren't concerned about mouse interaction.
42185 * - <OpenLayers.Control>
42187 OpenLayers.Control.TouchNavigation = OpenLayers.Class(OpenLayers.Control, {
42190 * Property: dragPan
42191 * {<OpenLayers.Control.DragPan>}
42196 * APIProperty: dragPanOptions
42197 * {Object} Options passed to the DragPan control.
42199 dragPanOptions: null,
42202 * Property: pinchZoom
42203 * {<OpenLayers.Control.PinchZoom>}
42208 * APIProperty: pinchZoomOptions
42209 * {Object} Options passed to the PinchZoom control.
42211 pinchZoomOptions: null,
42214 * APIProperty: clickHandlerOptions
42215 * {Object} Options passed to the Click handler.
42217 clickHandlerOptions: null,
42220 * APIProperty: documentDrag
42221 * {Boolean} Allow panning of the map by dragging outside map viewport.
42222 * Default is false.
42224 documentDrag: false,
42227 * APIProperty: autoActivate
42228 * {Boolean} Activate the control when it is added to a map. Default is
42231 autoActivate: true,
42234 * Constructor: OpenLayers.Control.TouchNavigation
42235 * Create a new navigation control
42238 * options - {Object} An optional object whose properties will be set on
42241 initialize: function(options) {
42242 this.handlers = {};
42243 OpenLayers.Control.prototype.initialize.apply(this, arguments);
42248 * The destroy method is used to perform any clean up before the control
42249 * is dereferenced. Typically this is where event listeners are removed
42250 * to prevent memory leaks.
42252 destroy: function() {
42255 this.dragPan.destroy();
42257 this.dragPan = null;
42258 if (this.pinchZoom) {
42259 this.pinchZoom.destroy();
42260 delete this.pinchZoom;
42262 OpenLayers.Control.prototype.destroy.apply(this,arguments);
42268 activate: function() {
42269 if(OpenLayers.Control.prototype.activate.apply(this,arguments)) {
42270 this.dragPan.activate();
42271 this.handlers.click.activate();
42272 this.pinchZoom.activate();
42279 * Method: deactivate
42281 deactivate: function() {
42282 if(OpenLayers.Control.prototype.deactivate.apply(this,arguments)) {
42283 this.dragPan.deactivate();
42284 this.handlers.click.deactivate();
42285 this.pinchZoom.deactivate();
42295 var clickCallbacks = {
42296 click: this.defaultClick,
42297 dblclick: this.defaultDblClick
42299 var clickOptions = OpenLayers.Util.extend({
42303 }, this.clickHandlerOptions);
42304 this.handlers.click = new OpenLayers.Handler.Click(
42305 this, clickCallbacks, clickOptions
42307 this.dragPan = new OpenLayers.Control.DragPan(
42308 OpenLayers.Util.extend({
42310 documentDrag: this.documentDrag
42311 }, this.dragPanOptions)
42313 this.dragPan.draw();
42314 this.pinchZoom = new OpenLayers.Control.PinchZoom(
42315 OpenLayers.Util.extend({map: this.map}, this.pinchZoomOptions)
42320 * Method: defaultClick
42325 defaultClick: function (evt) {
42326 if(evt.lastTouches && evt.lastTouches.length == 2) {
42327 this.map.zoomOut();
42332 * Method: defaultDblClick
42337 defaultDblClick: function (evt) {
42338 this.map.zoomTo(this.map.zoom + 1, evt.xy);
42341 CLASS_NAME: "OpenLayers.Control.TouchNavigation"
42343 /* ======================================================================
42344 OpenLayers/Renderer/VML.js
42345 ====================================================================== */
42347 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
42348 * full list of contributors). Published under the 2-clause BSD license.
42349 * See license.txt in the OpenLayers distribution or repository for the
42350 * full text of the license. */
42353 * @requires OpenLayers/Renderer/Elements.js
42357 * Class: OpenLayers.Renderer.VML
42358 * Render vector features in browsers with VML capability. Construct a new
42359 * VML renderer with the <OpenLayers.Renderer.VML> constructor.
42361 * Note that for all calculations in this class, we use (num | 0) to truncate a
42362 * float value to an integer. This is done because it seems that VML doesn't
42363 * support float values.
42366 * - <OpenLayers.Renderer.Elements>
42368 OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
42372 * {String} XML Namespace URN
42374 xmlns: "urn:schemas-microsoft-com:vml",
42377 * Property: symbolCache
42378 * {DOMElement} node holding symbols. This hash is keyed by symbol name,
42379 * and each value is a hash with a "path" and an "extent" property.
42385 * {Object} Hash with "x" and "y" properties
42390 * Constructor: OpenLayers.Renderer.VML
42391 * Create a new VML renderer.
42394 * containerID - {String} The id for the element that contains the renderer
42396 initialize: function(containerID) {
42397 if (!this.supported()) {
42400 if (!document.namespaces.olv) {
42401 document.namespaces.add("olv", this.xmlns);
42402 var style = document.createStyleSheet();
42403 var shapes = ['shape','rect', 'oval', 'fill', 'stroke', 'imagedata', 'group','textbox'];
42404 for (var i = 0, len = shapes.length; i < len; i++) {
42406 style.addRule('olv\\:' + shapes[i], "behavior: url(#default#VML); " +
42407 "position: absolute; display: inline-block;");
42411 OpenLayers.Renderer.Elements.prototype.initialize.apply(this,
42416 * APIMethod: supported
42417 * Determine whether a browser supports this renderer.
42420 * {Boolean} The browser supports the VML renderer
42422 supported: function() {
42423 return !!(document.namespaces);
42427 * Method: setExtent
42428 * Set the renderer's extent
42431 * extent - {<OpenLayers.Bounds>}
42432 * resolutionChanged - {Boolean}
42435 * {Boolean} true to notify the layer that the new extent does not exceed
42436 * the coordinate range, and the features will not need to be redrawn.
42438 setExtent: function(extent, resolutionChanged) {
42439 var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments);
42440 var resolution = this.getResolution();
42442 var left = (extent.left/resolution) | 0;
42443 var top = (extent.top/resolution - this.size.h) | 0;
42444 if (resolutionChanged || !this.offset) {
42445 this.offset = {x: left, y: top};
42449 left = left - this.offset.x;
42450 top = top - this.offset.y;
42454 var org = (left - this.xOffset) + " " + top;
42455 this.root.coordorigin = org;
42456 var roots = [this.root, this.vectorRoot, this.textRoot];
42458 for(var i=0, len=roots.length; i<len; ++i) {
42461 var size = this.size.w + " " + this.size.h;
42462 root.coordsize = size;
42465 // flip the VML display Y axis upside down so it
42466 // matches the display Y axis of the map
42467 this.root.style.flip = "y";
42469 return coordSysUnchanged;
42475 * Set the size of the drawing surface
42478 * size - {<OpenLayers.Size>} the size of the drawing surface
42480 setSize: function(size) {
42481 OpenLayers.Renderer.prototype.setSize.apply(this, arguments);
42483 // setting width and height on all roots to avoid flicker which we
42484 // would get with 100% width and height on child roots
42491 var w = this.size.w + "px";
42492 var h = this.size.h + "px";
42494 for(var i=0, len=roots.length; i<len; ++i) {
42496 root.style.width = w;
42497 root.style.height = h;
42502 * Method: getNodeType
42503 * Get the node type for a geometry and style
42506 * geometry - {<OpenLayers.Geometry>}
42510 * {String} The corresponding node type for the specified geometry
42512 getNodeType: function(geometry, style) {
42513 var nodeType = null;
42514 switch (geometry.CLASS_NAME) {
42515 case "OpenLayers.Geometry.Point":
42516 if (style.externalGraphic) {
42517 nodeType = "olv:rect";
42518 } else if (this.isComplexSymbol(style.graphicName)) {
42519 nodeType = "olv:shape";
42521 nodeType = "olv:oval";
42524 case "OpenLayers.Geometry.Rectangle":
42525 nodeType = "olv:rect";
42527 case "OpenLayers.Geometry.LineString":
42528 case "OpenLayers.Geometry.LinearRing":
42529 case "OpenLayers.Geometry.Polygon":
42530 case "OpenLayers.Geometry.Curve":
42531 nodeType = "olv:shape";
42541 * Use to set all the style attributes to a VML node.
42544 * node - {DOMElement} An VML element to decorate
42546 * options - {Object} Currently supported options include
42547 * 'isFilled' {Boolean} and
42548 * 'isStroked' {Boolean}
42549 * geometry - {<OpenLayers.Geometry>}
42551 setStyle: function(node, style, options, geometry) {
42552 style = style || node._style;
42553 options = options || node._options;
42554 var fillColor = style.fillColor;
42556 var title = style.title || style.graphicTitle;
42558 node.title = title;
42561 if (node._geometryClass === "OpenLayers.Geometry.Point") {
42562 if (style.externalGraphic) {
42563 options.isFilled = true;
42564 var width = style.graphicWidth || style.graphicHeight;
42565 var height = style.graphicHeight || style.graphicWidth;
42566 width = width ? width : style.pointRadius*2;
42567 height = height ? height : style.pointRadius*2;
42569 var resolution = this.getResolution();
42570 var xOffset = (style.graphicXOffset != undefined) ?
42571 style.graphicXOffset : -(0.5 * width);
42572 var yOffset = (style.graphicYOffset != undefined) ?
42573 style.graphicYOffset : -(0.5 * height);
42575 node.style.left = ((((geometry.x - this.featureDx)/resolution - this.offset.x)+xOffset) | 0) + "px";
42576 node.style.top = (((geometry.y/resolution - this.offset.y)-(yOffset+height)) | 0) + "px";
42577 node.style.width = width + "px";
42578 node.style.height = height + "px";
42579 node.style.flip = "y";
42581 // modify fillColor and options for stroke styling below
42582 fillColor = "none";
42583 options.isStroked = false;
42584 } else if (this.isComplexSymbol(style.graphicName)) {
42585 var cache = this.importSymbol(style.graphicName);
42586 node.path = cache.path;
42587 node.coordorigin = cache.left + "," + cache.bottom;
42588 var size = cache.size;
42589 node.coordsize = size + "," + size;
42590 this.drawCircle(node, geometry, style.pointRadius);
42591 node.style.flip = "y";
42593 this.drawCircle(node, geometry, style.pointRadius);
42598 if (options.isFilled) {
42599 node.fillcolor = fillColor;
42601 node.filled = "false";
42603 var fills = node.getElementsByTagName("fill");
42604 var fill = (fills.length == 0) ? null : fills[0];
42605 if (!options.isFilled) {
42607 node.removeChild(fill);
42611 fill = this.createNode('olv:fill', node.id + "_fill");
42613 fill.opacity = style.fillOpacity;
42615 if (node._geometryClass === "OpenLayers.Geometry.Point" &&
42616 style.externalGraphic) {
42618 // override fillOpacity
42619 if (style.graphicOpacity) {
42620 fill.opacity = style.graphicOpacity;
42623 fill.src = style.externalGraphic;
42624 fill.type = "frame";
42626 if (!(style.graphicWidth && style.graphicHeight)) {
42627 fill.aspect = "atmost";
42630 if (fill.parentNode != node) {
42631 node.appendChild(fill);
42635 // additional rendering for rotated graphics or symbols
42636 var rotation = style.rotation;
42637 if ((rotation !== undefined || node._rotation !== undefined)) {
42638 node._rotation = rotation;
42639 if (style.externalGraphic) {
42640 this.graphicRotate(node, xOffset, yOffset, style);
42641 // make the fill fully transparent, because we now have
42642 // the graphic as imagedata element. We cannot just remove
42643 // the fill, because this is part of the hack described
42644 // in graphicRotate
42646 } else if(node._geometryClass === "OpenLayers.Geometry.Point") {
42647 node.style.rotation = rotation || 0;
42652 var strokes = node.getElementsByTagName("stroke");
42653 var stroke = (strokes.length == 0) ? null : strokes[0];
42654 if (!options.isStroked) {
42655 node.stroked = false;
42661 stroke = this.createNode('olv:stroke', node.id + "_stroke");
42662 node.appendChild(stroke);
42665 stroke.color = style.strokeColor;
42666 stroke.weight = style.strokeWidth + "px";
42667 stroke.opacity = style.strokeOpacity;
42668 stroke.endcap = style.strokeLinecap == 'butt' ? 'flat' :
42669 (style.strokeLinecap || 'round');
42670 if (style.strokeDashstyle) {
42671 stroke.dashstyle = this.dashStyle(style);
42675 if (style.cursor != "inherit" && style.cursor != null) {
42676 node.style.cursor = style.cursor;
42682 * Method: graphicRotate
42683 * If a point is to be styled with externalGraphic and rotation, VML fills
42684 * cannot be used to display the graphic, because rotation of graphic
42685 * fills is not supported by the VML implementation of Internet Explorer.
42686 * This method creates a olv:imagedata element inside the VML node,
42687 * DXImageTransform.Matrix and BasicImage filters for rotation and
42688 * opacity, and a 3-step hack to remove rendering artefacts from the
42689 * graphic and preserve the ability of graphics to trigger events.
42690 * Finally, OpenLayers methods are used to determine the correct
42691 * insertion point of the rotated image, because DXImageTransform.Matrix
42692 * does the rotation without the ability to specify a rotation center
42696 * node - {DOMElement}
42697 * xOffset - {Number} rotation center relative to image, x coordinate
42698 * yOffset - {Number} rotation center relative to image, y coordinate
42701 graphicRotate: function(node, xOffset, yOffset, style) {
42702 var style = style || node._style;
42703 var rotation = style.rotation || 0;
42705 var aspectRatio, size;
42706 if (!(style.graphicWidth && style.graphicHeight)) {
42707 // load the image to determine its size
42708 var img = new Image();
42709 img.onreadystatechange = OpenLayers.Function.bind(function() {
42710 if(img.readyState == "complete" ||
42711 img.readyState == "interactive") {
42712 aspectRatio = img.width / img.height;
42713 size = Math.max(style.pointRadius * 2,
42714 style.graphicWidth || 0,
42715 style.graphicHeight || 0);
42716 xOffset = xOffset * aspectRatio;
42717 style.graphicWidth = size * aspectRatio;
42718 style.graphicHeight = size;
42719 this.graphicRotate(node, xOffset, yOffset, style);
42722 img.src = style.externalGraphic;
42724 // will be called again by the onreadystate handler
42727 size = Math.max(style.graphicWidth, style.graphicHeight);
42728 aspectRatio = style.graphicWidth / style.graphicHeight;
42731 var width = Math.round(style.graphicWidth || size * aspectRatio);
42732 var height = Math.round(style.graphicHeight || size);
42733 node.style.width = width + "px";
42734 node.style.height = height + "px";
42736 // Three steps are required to remove artefacts for images with
42737 // transparent backgrounds (resulting from using DXImageTransform
42738 // filters on svg objects), while preserving awareness for browser
42739 // events on images:
42740 // - Use the fill as usual (like for unrotated images) to handle
42742 // - specify an imagedata element with the same src as the fill
42743 // - style the imagedata element with an AlphaImageLoader filter
42745 var image = document.getElementById(node.id + "_image");
42747 image = this.createNode("olv:imagedata", node.id + "_image");
42748 node.appendChild(image);
42750 image.style.width = width + "px";
42751 image.style.height = height + "px";
42752 image.src = style.externalGraphic;
42753 image.style.filter =
42754 "progid:DXImageTransform.Microsoft.AlphaImageLoader(" +
42755 "src='', sizingMethod='scale')";
42757 var rot = rotation * Math.PI / 180;
42758 var sintheta = Math.sin(rot);
42759 var costheta = Math.cos(rot);
42761 // do the rotation on the image
42763 "progid:DXImageTransform.Microsoft.Matrix(M11=" + costheta +
42764 ",M12=" + (-sintheta) + ",M21=" + sintheta + ",M22=" + costheta +
42765 ",SizingMethod='auto expand')\n";
42767 // set the opacity (needed for the imagedata)
42768 var opacity = style.graphicOpacity || style.fillOpacity;
42769 if (opacity && opacity != 1) {
42771 "progid:DXImageTransform.Microsoft.BasicImage(opacity=" +
42774 node.style.filter = filter;
42776 // do the rotation again on a box, so we know the insertion point
42777 var centerPoint = new OpenLayers.Geometry.Point(-xOffset, -yOffset);
42778 var imgBox = new OpenLayers.Bounds(0, 0, width, height).toGeometry();
42779 imgBox.rotate(style.rotation, centerPoint);
42780 var imgBounds = imgBox.getBounds();
42782 node.style.left = Math.round(
42783 parseInt(node.style.left) + imgBounds.left) + "px";
42784 node.style.top = Math.round(
42785 parseInt(node.style.top) - imgBounds.bottom) + "px";
42790 * Does some node postprocessing to work around browser issues:
42791 * - Some versions of Internet Explorer seem to be unable to set fillcolor
42792 * and strokecolor to "none" correctly before the fill node is appended
42793 * to a visible vml node. This method takes care of that and sets
42794 * fillcolor and strokecolor again if needed.
42795 * - In some cases, a node won't become visible after being drawn. Setting
42796 * style.visibility to "visible" works around that.
42799 * node - {DOMElement}
42801 postDraw: function(node) {
42802 node.style.visibility = "visible";
42803 var fillColor = node._style.fillColor;
42804 var strokeColor = node._style.strokeColor;
42805 if (fillColor == "none" &&
42806 node.fillcolor != fillColor) {
42807 node.fillcolor = fillColor;
42809 if (strokeColor == "none" &&
42810 node.strokecolor != strokeColor) {
42811 node.strokecolor = strokeColor;
42817 * Method: setNodeDimension
42818 * Get the geometry's bounds, convert it to our vml coordinate system,
42819 * then set the node's position, size, and local coordinate system.
42822 * node - {DOMElement}
42823 * geometry - {<OpenLayers.Geometry>}
42825 setNodeDimension: function(node, geometry) {
42827 var bbox = geometry.getBounds();
42829 var resolution = this.getResolution();
42832 new OpenLayers.Bounds(((bbox.left - this.featureDx)/resolution - this.offset.x) | 0,
42833 (bbox.bottom/resolution - this.offset.y) | 0,
42834 ((bbox.right - this.featureDx)/resolution - this.offset.x) | 0,
42835 (bbox.top/resolution - this.offset.y) | 0);
42837 // Set the internal coordinate system to draw the path
42838 node.style.left = scaledBox.left + "px";
42839 node.style.top = scaledBox.top + "px";
42840 node.style.width = scaledBox.getWidth() + "px";
42841 node.style.height = scaledBox.getHeight() + "px";
42843 node.coordorigin = scaledBox.left + " " + scaledBox.top;
42844 node.coordsize = scaledBox.getWidth()+ " " + scaledBox.getHeight();
42849 * Method: dashStyle
42855 * {String} A VML compliant 'stroke-dasharray' value
42857 dashStyle: function(style) {
42858 var dash = style.strokeDashstyle;
42865 case 'longdashdot':
42868 // very basic guessing of dash style patterns
42869 var parts = dash.split(/[ ,]/);
42870 if (parts.length == 2) {
42871 if (1*parts[0] >= 2*parts[1]) {
42874 return (parts[0] == 1 || parts[1] == 1) ? "dot" : "dash";
42875 } else if (parts.length == 4) {
42876 return (1*parts[0] >= 2*parts[1]) ? "longdashdot" :
42884 * Method: createNode
42885 * Create a new node
42888 * type - {String} Kind of node to draw
42889 * id - {String} Id for node
42892 * {DOMElement} A new node of the given type and id
42894 createNode: function(type, id) {
42895 var node = document.createElement(type);
42900 // IE hack to make elements unselectable, to prevent 'blue flash'
42901 // while dragging vectors; #1410
42902 node.unselectable = 'on';
42903 node.onselectstart = OpenLayers.Function.False;
42909 * Method: nodeTypeCompare
42910 * Determine whether a node is of a given type
42913 * node - {DOMElement} An VML element
42914 * type - {String} Kind of node
42917 * {Boolean} Whether or not the specified node is of the specified type
42919 nodeTypeCompare: function(node, type) {
42922 var subType = type;
42923 var splitIndex = subType.indexOf(":");
42924 if (splitIndex != -1) {
42925 subType = subType.substr(splitIndex+1);
42929 var nodeName = node.nodeName;
42930 splitIndex = nodeName.indexOf(":");
42931 if (splitIndex != -1) {
42932 nodeName = nodeName.substr(splitIndex+1);
42935 return (subType == nodeName);
42939 * Method: createRenderRoot
42940 * Create the renderer root
42943 * {DOMElement} The specific render engine's root element
42945 createRenderRoot: function() {
42946 return this.nodeFactory(this.container.id + "_vmlRoot", "div");
42950 * Method: createRoot
42951 * Create the main root element
42954 * suffix - {String} suffix to append to the id
42959 createRoot: function(suffix) {
42960 return this.nodeFactory(this.container.id + suffix, "olv:group");
42963 /**************************************
42965 * GEOMETRY DRAWING FUNCTIONS *
42967 **************************************/
42970 * Method: drawPoint
42974 * node - {DOMElement}
42975 * geometry - {<OpenLayers.Geometry>}
42978 * {DOMElement} or false if the point could not be drawn
42980 drawPoint: function(node, geometry) {
42981 return this.drawCircle(node, geometry, 1);
42985 * Method: drawCircle
42987 * Size and Center a circle given geometry (x,y center) and radius
42990 * node - {DOMElement}
42991 * geometry - {<OpenLayers.Geometry>}
42995 * {DOMElement} or false if the circle could not ne drawn
42997 drawCircle: function(node, geometry, radius) {
42998 if(!isNaN(geometry.x)&& !isNaN(geometry.y)) {
42999 var resolution = this.getResolution();
43001 node.style.left = ((((geometry.x - this.featureDx) /resolution - this.offset.x) | 0) - radius) + "px";
43002 node.style.top = (((geometry.y /resolution - this.offset.y) | 0) - radius) + "px";
43004 var diameter = radius * 2;
43006 node.style.width = diameter + "px";
43007 node.style.height = diameter + "px";
43015 * Method: drawLineString
43016 * Render a linestring.
43019 * node - {DOMElement}
43020 * geometry - {<OpenLayers.Geometry>}
43025 drawLineString: function(node, geometry) {
43026 return this.drawLine(node, geometry, false);
43030 * Method: drawLinearRing
43031 * Render a linearring
43034 * node - {DOMElement}
43035 * geometry - {<OpenLayers.Geometry>}
43040 drawLinearRing: function(node, geometry) {
43041 return this.drawLine(node, geometry, true);
43049 * node - {DOMElement}
43050 * geometry - {<OpenLayers.Geometry>}
43051 * closeLine - {Boolean} Close the line? (make it a ring?)
43056 drawLine: function(node, geometry, closeLine) {
43058 this.setNodeDimension(node, geometry);
43060 var resolution = this.getResolution();
43061 var numComponents = geometry.components.length;
43062 var parts = new Array(numComponents);
43065 for (var i = 0; i < numComponents; i++) {
43066 comp = geometry.components[i];
43067 x = ((comp.x - this.featureDx)/resolution - this.offset.x) | 0;
43068 y = (comp.y/resolution - this.offset.y) | 0;
43069 parts[i] = " " + x + "," + y + " l ";
43071 var end = (closeLine) ? " x e" : " e";
43072 node.path = "m" + parts.join("") + end;
43077 * Method: drawPolygon
43081 * node - {DOMElement}
43082 * geometry - {<OpenLayers.Geometry>}
43087 drawPolygon: function(node, geometry) {
43088 this.setNodeDimension(node, geometry);
43090 var resolution = this.getResolution();
43093 var j, jj, points, area, first, second, i, ii, comp, pathComp, x, y;
43094 for (j=0, jj=geometry.components.length; j<jj; j++) {
43096 points = geometry.components[j].components;
43097 // we only close paths of interior rings with area
43101 for (i=0, ii=points.length; i<ii; i++) {
43103 x = ((comp.x - this.featureDx) / resolution - this.offset.x) | 0;
43104 y = (comp.y / resolution - this.offset.y) | 0;
43105 pathComp = " " + x + "," + y;
43106 path.push(pathComp);
43111 // IE improperly renders sub-paths that have no area.
43112 // Instead of checking the area of every ring, we confirm
43113 // the ring has at least three distinct points. This does
43114 // not catch all non-zero area cases, but it greatly improves
43115 // interior ring digitizing and is a minor performance hit
43116 // when rendering rings with many points.
43119 } else if (first != pathComp) {
43122 } else if (second != pathComp) {
43129 path.push(area ? " x " : " ");
43132 node.path = path.join("");
43137 * Method: drawRectangle
43138 * Render a rectangle
43141 * node - {DOMElement}
43142 * geometry - {<OpenLayers.Geometry>}
43147 drawRectangle: function(node, geometry) {
43148 var resolution = this.getResolution();
43150 node.style.left = (((geometry.x - this.featureDx)/resolution - this.offset.x) | 0) + "px";
43151 node.style.top = ((geometry.y/resolution - this.offset.y) | 0) + "px";
43152 node.style.width = ((geometry.width/resolution) | 0) + "px";
43153 node.style.height = ((geometry.height/resolution) | 0) + "px";
43160 * This method is only called by the renderer itself.
43163 * featureId - {String}
43165 * location - {<OpenLayers.Geometry.Point>}
43167 drawText: function(featureId, style, location) {
43168 var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "olv:rect");
43169 var textbox = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_textbox", "olv:textbox");
43171 var resolution = this.getResolution();
43172 label.style.left = (((location.x - this.featureDx)/resolution - this.offset.x) | 0) + "px";
43173 label.style.top = ((location.y/resolution - this.offset.y) | 0) + "px";
43174 label.style.flip = "y";
43176 textbox.innerText = style.label;
43178 if (style.cursor != "inherit" && style.cursor != null) {
43179 textbox.style.cursor = style.cursor;
43181 if (style.fontColor) {
43182 textbox.style.color = style.fontColor;
43184 if (style.fontOpacity) {
43185 textbox.style.filter = 'alpha(opacity=' + (style.fontOpacity * 100) + ')';
43187 if (style.fontFamily) {
43188 textbox.style.fontFamily = style.fontFamily;
43190 if (style.fontSize) {
43191 textbox.style.fontSize = style.fontSize;
43193 if (style.fontWeight) {
43194 textbox.style.fontWeight = style.fontWeight;
43196 if (style.fontStyle) {
43197 textbox.style.fontStyle = style.fontStyle;
43199 if(style.labelSelect === true) {
43200 label._featureId = featureId;
43201 textbox._featureId = featureId;
43202 textbox._geometry = location;
43203 textbox._geometryClass = location.CLASS_NAME;
43205 textbox.style.whiteSpace = "nowrap";
43206 // fun with IE: IE7 in standards compliant mode does not display any
43207 // text with a left inset of 0. So we set this to 1px and subtract one
43208 // pixel later when we set label.style.left
43209 textbox.inset = "1px,0px,0px,0px";
43211 if(!label.parentNode) {
43212 label.appendChild(textbox);
43213 this.textRoot.appendChild(label);
43216 var align = style.labelAlign || "cm";
43217 if (align.length == 1) {
43220 var xshift = textbox.clientWidth *
43221 (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(0,1)]);
43222 var yshift = textbox.clientHeight *
43223 (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(1,1)]);
43224 label.style.left = parseInt(label.style.left)-xshift-1+"px";
43225 label.style.top = parseInt(label.style.top)+yshift+"px";
43231 * moves this renderer's root to a different renderer.
43234 * renderer - {<OpenLayers.Renderer>} target renderer for the moved root
43235 * root - {DOMElement} optional root node. To be used when this renderer
43236 * holds roots from multiple layers to tell this method which one to
43240 * {Boolean} true if successful, false otherwise
43242 moveRoot: function(renderer) {
43243 var layer = this.map.getLayer(renderer.container.id);
43244 if(layer instanceof OpenLayers.Layer.Vector.RootContainer) {
43245 layer = this.map.getLayer(this.container.id);
43247 layer && layer.renderer.clear();
43248 OpenLayers.Renderer.Elements.prototype.moveRoot.apply(this, arguments);
43249 layer && layer.redraw();
43253 * Method: importSymbol
43254 * add a new symbol definition from the rendererer's symbol hash
43257 * graphicName - {String} name of the symbol to import
43260 * {Object} - hash of {DOMElement} "symbol" and {Number} "size"
43262 importSymbol: function (graphicName) {
43263 var id = this.container.id + "-" + graphicName;
43265 // check if symbol already exists in the cache
43266 var cache = this.symbolCache[id];
43271 var symbol = OpenLayers.Renderer.symbol[graphicName];
43273 throw new Error(graphicName + ' is not a valid symbol name');
43276 var symbolExtent = new OpenLayers.Bounds(
43277 Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
43279 var pathitems = ["m"];
43280 for (var i=0; i<symbol.length; i=i+2) {
43282 var y = symbol[i+1];
43283 symbolExtent.left = Math.min(symbolExtent.left, x);
43284 symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
43285 symbolExtent.right = Math.max(symbolExtent.right, x);
43286 symbolExtent.top = Math.max(symbolExtent.top, y);
43291 pathitems.push("l");
43294 pathitems.push("x e");
43295 var path = pathitems.join(" ");
43297 var diff = (symbolExtent.getWidth() - symbolExtent.getHeight()) / 2;
43299 symbolExtent.bottom = symbolExtent.bottom - diff;
43300 symbolExtent.top = symbolExtent.top + diff;
43302 symbolExtent.left = symbolExtent.left + diff;
43303 symbolExtent.right = symbolExtent.right - diff;
43308 size: symbolExtent.getWidth(), // equals getHeight() now
43309 left: symbolExtent.left,
43310 bottom: symbolExtent.bottom
43312 this.symbolCache[id] = cache;
43317 CLASS_NAME: "OpenLayers.Renderer.VML"
43321 * Constant: OpenLayers.Renderer.VML.LABEL_SHIFT
43324 OpenLayers.Renderer.VML.LABEL_SHIFT = {
43332 /* ======================================================================
43333 OpenLayers/Protocol/WFS/v1_0_0.js
43334 ====================================================================== */
43336 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
43337 * full list of contributors). Published under the 2-clause BSD license.
43338 * See license.txt in the OpenLayers distribution or repository for the
43339 * full text of the license. */
43342 * @requires OpenLayers/Protocol/WFS/v1.js
43343 * @requires OpenLayers/Format/WFST/v1_0_0.js
43347 * Class: OpenLayers.Protocol.WFS.v1_0_0
43348 * A WFS v1.0.0 protocol for vector layers. Create a new instance with the
43349 * <OpenLayers.Protocol.WFS.v1_0_0> constructor.
43352 * - <OpenLayers.Protocol.WFS.v1>
43354 OpenLayers.Protocol.WFS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, {
43357 * Property: version
43358 * {String} WFS version number.
43363 * Constructor: OpenLayers.Protocol.WFS.v1_0_0
43364 * A class for giving layers WFS v1.0.0 protocol.
43367 * options - {Object} Optional object whose properties will be set on the
43370 * Valid options properties:
43371 * featureType - {String} Local (without prefix) feature typeName (required).
43372 * featureNS - {String} Feature namespace (optional).
43373 * featurePrefix - {String} Feature namespace alias (optional - only used
43374 * if featureNS is provided). Default is 'feature'.
43375 * geometryName - {String} Name of geometry attribute. Default is 'the_geom'.
43378 CLASS_NAME: "OpenLayers.Protocol.WFS.v1_0_0"