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/Renderer.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. */
18333 * @requires OpenLayers/BaseTypes/Class.js
18337 * Class: OpenLayers.Renderer
18338 * This is the base class for all renderers.
18340 * This is based on a merger code written by Paul Spencer and Bertil Chapuis.
18341 * It is largely composed of virtual functions that are to be implemented
18342 * in technology-specific subclasses, but there is some generic code too.
18344 * The functions that *are* implemented here merely deal with the maintenance
18345 * of the size and extent variables, as well as the cached 'resolution'
18348 * A note to the user that all subclasses should use getResolution() instead
18349 * of directly accessing this.resolution in order to correctly use the
18353 OpenLayers.Renderer = OpenLayers.Class({
18356 * Property: container
18369 * {<OpenLayers.Bounds>}
18375 * {Boolean} If the renderer is currently in a state where many things
18376 * are changing, the 'locked' property is set to true. This means
18377 * that renderers can expect at least one more drawFeature event to be
18378 * called with the 'locked' property set to 'true': In some renderers,
18379 * this might make sense to use as a 'only update local information'
18386 * {<OpenLayers.Size>}
18391 * Property: resolution
18392 * {Float} cache of current map resolution
18398 * {<OpenLayers.Map>} Reference to the map -- this is set in Vector's setMap()
18403 * Property: featureDx
18404 * {Number} Feature offset in x direction. Will be calculated for and
18405 * applied to the current feature while rendering (see
18406 * <calculateFeatureDx>).
18411 * Constructor: OpenLayers.Renderer
18414 * containerID - {<String>}
18415 * options - {Object} options for this renderer. See sublcasses for
18416 * supported options.
18418 initialize: function(containerID, options) {
18419 this.container = OpenLayers.Util.getElement(containerID);
18420 OpenLayers.Util.extend(this, options);
18424 * APIMethod: destroy
18426 destroy: function() {
18427 this.container = null;
18428 this.extent = null;
18430 this.resolution = null;
18435 * APIMethod: supported
18436 * This should be overridden by specific subclasses
18439 * {Boolean} Whether or not the browser supports the renderer class
18441 supported: function() {
18446 * Method: setExtent
18447 * Set the visible part of the layer.
18449 * Resolution has probably changed, so we nullify the resolution
18450 * cache (this.resolution) -- this way it will be re-computed when
18451 * next it is needed.
18452 * We nullify the resolution cache (this.resolution) if resolutionChanged
18453 * is set to true - this way it will be re-computed on the next
18454 * getResolution() request.
18457 * extent - {<OpenLayers.Bounds>}
18458 * resolutionChanged - {Boolean}
18461 * {Boolean} true to notify the layer that the new extent does not exceed
18462 * the coordinate range, and the features will not need to be redrawn.
18465 setExtent: function(extent, resolutionChanged) {
18466 this.extent = extent.clone();
18467 if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
18468 var ratio = extent.getWidth() / this.map.getExtent().getWidth(),
18469 extent = extent.scale(1 / ratio);
18470 this.extent = extent.wrapDateLine(this.map.getMaxExtent()).scale(ratio);
18472 if (resolutionChanged) {
18473 this.resolution = null;
18480 * Sets the size of the drawing surface.
18482 * Resolution has probably changed, so we nullify the resolution
18483 * cache (this.resolution) -- this way it will be re-computed when
18484 * next it is needed.
18487 * size - {<OpenLayers.Size>}
18489 setSize: function(size) {
18490 this.size = size.clone();
18491 this.resolution = null;
18495 * Method: getResolution
18496 * Uses cached copy of resolution if available to minimize computing
18499 * {Float} The current map's resolution
18501 getResolution: function() {
18502 this.resolution = this.resolution || this.map.getResolution();
18503 return this.resolution;
18507 * Method: drawFeature
18508 * Draw the feature. The optional style argument can be used
18509 * to override the feature's own style. This method should only
18510 * be called from layer.drawFeature().
18513 * feature - {<OpenLayers.Feature.Vector>}
18514 * style - {<Object>}
18517 * {Boolean} true if the feature has been drawn completely, false if not,
18518 * undefined if the feature had no geometry
18520 drawFeature: function(feature, style) {
18521 if(style == null) {
18522 style = feature.style;
18524 if (feature.geometry) {
18525 var bounds = feature.geometry.getBounds();
18528 if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
18529 worldBounds = this.map.getMaxExtent();
18531 if (!bounds.intersectsBounds(this.extent, {worldBounds: worldBounds})) {
18532 style = {display: "none"};
18534 this.calculateFeatureDx(bounds, worldBounds);
18536 var rendered = this.drawGeometry(feature.geometry, style, feature.id);
18537 if(style.display != "none" && style.label && rendered !== false) {
18539 var location = feature.geometry.getCentroid();
18540 if(style.labelXOffset || style.labelYOffset) {
18541 var xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset;
18542 var yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset;
18543 var res = this.getResolution();
18544 location.move(xOffset*res, yOffset*res);
18546 this.drawText(feature.id, style, location);
18548 this.removeText(feature.id);
18556 * Method: calculateFeatureDx
18557 * {Number} Calculates the feature offset in x direction. Looking at the
18558 * center of the feature bounds and the renderer extent, we calculate how
18559 * many world widths the two are away from each other. This distance is
18560 * used to shift the feature as close as possible to the center of the
18561 * current enderer extent, which ensures that the feature is visible in the
18562 * current viewport.
18565 * bounds - {<OpenLayers.Bounds>} Bounds of the feature
18566 * worldBounds - {<OpenLayers.Bounds>} Bounds of the world
18568 calculateFeatureDx: function(bounds, worldBounds) {
18569 this.featureDx = 0;
18571 var worldWidth = worldBounds.getWidth(),
18572 rendererCenterX = (this.extent.left + this.extent.right) / 2,
18573 featureCenterX = (bounds.left + bounds.right) / 2,
18574 worldsAway = Math.round((featureCenterX - rendererCenterX) / worldWidth);
18575 this.featureDx = worldsAway * worldWidth;
18580 * Method: drawGeometry
18582 * Draw a geometry. This should only be called from the renderer itself.
18583 * Use layer.drawFeature() from outside the renderer.
18587 * geometry - {<OpenLayers.Geometry>}
18589 * featureId - {<String>}
18591 drawGeometry: function(geometry, style, featureId) {},
18595 * Function for drawing text labels.
18596 * This method is only called by the renderer itself.
18599 * featureId - {String}
18601 * location - {<OpenLayers.Geometry.Point>}
18603 drawText: function(featureId, style, location) {},
18606 * Method: removeText
18607 * Function for removing text labels.
18608 * This method is only called by the renderer itself.
18611 * featureId - {String}
18613 removeText: function(featureId) {},
18617 * Clear all vectors from the renderer.
18618 * virtual function.
18620 clear: function() {},
18623 * Method: getFeatureIdFromEvent
18624 * Returns a feature id from an event on the renderer.
18625 * How this happens is specific to the renderer. This should be
18626 * called from layer.getFeatureFromEvent().
18627 * Virtual function.
18630 * evt - {<OpenLayers.Event>}
18633 * {String} A feature id or undefined.
18635 getFeatureIdFromEvent: function(evt) {},
18638 * Method: eraseFeatures
18639 * This is called by the layer to erase features
18642 * features - {Array(<OpenLayers.Feature.Vector>)}
18644 eraseFeatures: function(features) {
18645 if(!(OpenLayers.Util.isArray(features))) {
18646 features = [features];
18648 for(var i=0, len=features.length; i<len; ++i) {
18649 var feature = features[i];
18650 this.eraseGeometry(feature.geometry, feature.id);
18651 this.removeText(feature.id);
18656 * Method: eraseGeometry
18657 * Remove a geometry from the renderer (by id).
18658 * virtual function.
18661 * geometry - {<OpenLayers.Geometry>}
18662 * featureId - {String}
18664 eraseGeometry: function(geometry, featureId) {},
18668 * moves this renderer's root to a (different) renderer.
18669 * To be implemented by subclasses that require a common renderer root for
18670 * feature selection.
18673 * renderer - {<OpenLayers.Renderer>} target renderer for the moved root
18675 moveRoot: function(renderer) {},
18678 * Method: getRenderLayerId
18679 * Gets the layer that this renderer's output appears on. If moveRoot was
18680 * used, this will be different from the id of the layer containing the
18681 * features rendered by this renderer.
18684 * {String} the id of the output layer.
18686 getRenderLayerId: function() {
18687 return this.container.id;
18691 * Method: applyDefaultSymbolizer
18694 * symbolizer - {Object}
18699 applyDefaultSymbolizer: function(symbolizer) {
18700 var result = OpenLayers.Util.extend({},
18701 OpenLayers.Renderer.defaultSymbolizer);
18702 if(symbolizer.stroke === false) {
18703 delete result.strokeWidth;
18704 delete result.strokeColor;
18706 if(symbolizer.fill === false) {
18707 delete result.fillColor;
18709 OpenLayers.Util.extend(result, symbolizer);
18713 CLASS_NAME: "OpenLayers.Renderer"
18717 * Constant: OpenLayers.Renderer.defaultSymbolizer
18718 * {Object} Properties from this symbolizer will be applied to symbolizers
18719 * with missing properties. This can also be used to set a global
18720 * symbolizer default in OpenLayers. To be SLD 1.x compliant, add the
18721 * following code before rendering any vector features:
18723 * OpenLayers.Renderer.defaultSymbolizer = {
18724 * fillColor: "#808080",
18726 * strokeColor: "#000000",
18727 * strokeOpacity: 1,
18730 * graphicName: "square"
18734 OpenLayers.Renderer.defaultSymbolizer = {
18735 fillColor: "#000000",
18736 strokeColor: "#000000",
18747 * Constant: OpenLayers.Renderer.symbol
18748 * Coordinate arrays for well known (named) symbols.
18750 OpenLayers.Renderer.symbol = {
18751 "star": [350,75, 379,161, 469,161, 397,215, 423,301, 350,250, 277,301,
18752 303,215, 231,161, 321,161, 350,75],
18753 "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,
18755 "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],
18756 "square": [0,0, 0,1, 1,1, 1,0, 0,0],
18757 "triangle": [0,10, 10,10, 5,0, 0,10]
18759 /* ======================================================================
18760 OpenLayers/Renderer/Canvas.js
18761 ====================================================================== */
18763 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
18764 * full list of contributors). Published under the 2-clause BSD license.
18765 * See license.txt in the OpenLayers distribution or repository for the
18766 * full text of the license. */
18769 * @requires OpenLayers/Renderer.js
18773 * Class: OpenLayers.Renderer.Canvas
18774 * A renderer based on the 2D 'canvas' drawing element.
18777 * - <OpenLayers.Renderer>
18779 OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {
18782 * APIProperty: hitDetection
18783 * {Boolean} Allow for hit detection of features. Default is true.
18785 hitDetection: true,
18788 * Property: hitOverflow
18789 * {Number} The method for converting feature identifiers to color values
18790 * supports 16777215 sequential values. Two features cannot be
18791 * predictably detected if their identifiers differ by more than this
18792 * value. The hitOverflow allows for bigger numbers (but the
18793 * difference in values is still limited).
18799 * {Canvas} The canvas context object.
18804 * Property: features
18805 * {Object} Internal object of feature/style pairs for use in redrawing the layer.
18810 * Property: pendingRedraw
18811 * {Boolean} The renderer needs a redraw call to render features added while
18812 * the renderer was locked.
18814 pendingRedraw: false,
18817 * Property: cachedSymbolBounds
18818 * {Object} Internal cache of calculated symbol extents.
18820 cachedSymbolBounds: {},
18823 * Constructor: OpenLayers.Renderer.Canvas
18826 * containerID - {<String>}
18827 * options - {Object} Optional properties to be set on the renderer.
18829 initialize: function(containerID, options) {
18830 OpenLayers.Renderer.prototype.initialize.apply(this, arguments);
18831 this.root = document.createElement("canvas");
18832 this.container.appendChild(this.root);
18833 this.canvas = this.root.getContext("2d");
18834 this.features = {};
18835 if (this.hitDetection) {
18836 this.hitCanvas = document.createElement("canvas");
18837 this.hitContext = this.hitCanvas.getContext("2d");
18842 * Method: setExtent
18843 * Set the visible part of the layer.
18846 * extent - {<OpenLayers.Bounds>}
18847 * resolutionChanged - {Boolean}
18850 * {Boolean} true to notify the layer that the new extent does not exceed
18851 * the coordinate range, and the features will not need to be redrawn.
18854 setExtent: function() {
18855 OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);
18856 // always redraw features
18861 * Method: eraseGeometry
18862 * Erase a geometry from the renderer. Because the Canvas renderer has
18863 * 'memory' of the features that it has drawn, we have to remove the
18864 * feature so it doesn't redraw.
18867 * geometry - {<OpenLayers.Geometry>}
18868 * featureId - {String}
18870 eraseGeometry: function(geometry, featureId) {
18871 this.eraseFeatures(this.features[featureId][0]);
18875 * APIMethod: supported
18878 * {Boolean} Whether or not the browser supports the renderer class
18880 supported: function() {
18881 return OpenLayers.CANVAS_SUPPORTED;
18886 * Sets the size of the drawing surface.
18888 * Once the size is updated, redraw the canvas.
18891 * size - {<OpenLayers.Size>}
18893 setSize: function(size) {
18894 this.size = size.clone();
18895 var root = this.root;
18896 root.style.width = size.w + "px";
18897 root.style.height = size.h + "px";
18898 root.width = size.w;
18899 root.height = size.h;
18900 this.resolution = null;
18901 if (this.hitDetection) {
18902 var hitCanvas = this.hitCanvas;
18903 hitCanvas.style.width = size.w + "px";
18904 hitCanvas.style.height = size.h + "px";
18905 hitCanvas.width = size.w;
18906 hitCanvas.height = size.h;
18911 * Method: drawFeature
18912 * Draw the feature. Stores the feature in the features list,
18913 * then redraws the layer.
18916 * feature - {<OpenLayers.Feature.Vector>}
18917 * style - {<Object>}
18920 * {Boolean} The feature has been drawn completely. If the feature has no
18921 * geometry, undefined will be returned. If the feature is not rendered
18922 * for other reasons, false will be returned.
18924 drawFeature: function(feature, style) {
18926 if (feature.geometry) {
18927 style = this.applyDefaultSymbolizer(style || feature.style);
18928 // don't render if display none or feature outside extent
18929 var bounds = feature.geometry.getBounds();
18932 if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
18933 worldBounds = this.map.getMaxExtent();
18936 var intersects = bounds && bounds.intersectsBounds(this.extent, {worldBounds: worldBounds});
18938 rendered = (style.display !== "none") && !!bounds && intersects;
18940 // keep track of what we have rendered for redraw
18941 this.features[feature.id] = [feature, style];
18944 // remove from features tracked for redraw
18945 delete(this.features[feature.id]);
18947 this.pendingRedraw = true;
18949 if (this.pendingRedraw && !this.locked) {
18951 this.pendingRedraw = false;
18957 * Method: drawGeometry
18958 * Used when looping (in redraw) over the features; draws
18962 * geometry - {<OpenLayers.Geometry>}
18965 drawGeometry: function(geometry, style, featureId) {
18966 var className = geometry.CLASS_NAME;
18967 if ((className == "OpenLayers.Geometry.Collection") ||
18968 (className == "OpenLayers.Geometry.MultiPoint") ||
18969 (className == "OpenLayers.Geometry.MultiLineString") ||
18970 (className == "OpenLayers.Geometry.MultiPolygon")) {
18971 for (var i = 0; i < geometry.components.length; i++) {
18972 this.drawGeometry(geometry.components[i], style, featureId);
18976 switch (geometry.CLASS_NAME) {
18977 case "OpenLayers.Geometry.Point":
18978 this.drawPoint(geometry, style, featureId);
18980 case "OpenLayers.Geometry.LineString":
18981 this.drawLineString(geometry, style, featureId);
18983 case "OpenLayers.Geometry.LinearRing":
18984 this.drawLinearRing(geometry, style, featureId);
18986 case "OpenLayers.Geometry.Polygon":
18987 this.drawPolygon(geometry, style, featureId);
18995 * Method: drawExternalGraphic
18996 * Called to draw External graphics.
18999 * geometry - {<OpenLayers.Geometry>}
19001 * featureId - {String}
19003 drawExternalGraphic: function(geometry, style, featureId) {
19004 var img = new Image();
19006 var title = style.title || style.graphicTitle;
19011 var width = style.graphicWidth || style.graphicHeight;
19012 var height = style.graphicHeight || style.graphicWidth;
19013 width = width ? width : style.pointRadius * 2;
19014 height = height ? height : style.pointRadius * 2;
19015 var xOffset = (style.graphicXOffset != undefined) ?
19016 style.graphicXOffset : -(0.5 * width);
19017 var yOffset = (style.graphicYOffset != undefined) ?
19018 style.graphicYOffset : -(0.5 * height);
19020 var opacity = style.graphicOpacity || style.fillOpacity;
19022 var onLoad = function() {
19023 if(!this.features[featureId]) {
19026 var pt = this.getLocalXY(geometry);
19029 if(!isNaN(p0) && !isNaN(p1)) {
19030 var x = (p0 + xOffset) | 0;
19031 var y = (p1 + yOffset) | 0;
19032 var canvas = this.canvas;
19033 canvas.globalAlpha = opacity;
19034 var factor = OpenLayers.Renderer.Canvas.drawImageScaleFactor ||
19035 (OpenLayers.Renderer.Canvas.drawImageScaleFactor =
19036 /android 2.1/.test(navigator.userAgent.toLowerCase()) ?
19037 // 320 is the screen width of the G1 phone, for
19038 // which drawImage works out of the box.
19039 320 / window.screen.width : 1
19042 img, x*factor, y*factor, width*factor, height*factor
19044 if (this.hitDetection) {
19045 this.setHitContextStyle("fill", featureId);
19046 this.hitContext.fillRect(x, y, width, height);
19051 img.onload = OpenLayers.Function.bind(onLoad, this);
19052 img.src = style.externalGraphic;
19056 * Method: drawNamedSymbol
19057 * Called to draw Well Known Graphic Symbol Name.
19058 * This method is only called by the renderer itself.
19061 * geometry - {<OpenLayers.Geometry>}
19063 * featureId - {String}
19065 drawNamedSymbol: function(geometry, style, featureId) {
19066 var x, y, cx, cy, i, symbolBounds, scaling, angle;
19067 var unscaledStrokeWidth;
19068 var deg2rad = Math.PI / 180.0;
19070 var symbol = OpenLayers.Renderer.symbol[style.graphicName];
19073 throw new Error(style.graphicName + ' is not a valid symbol name');
19076 if (!symbol.length || symbol.length < 2) return;
19078 var pt = this.getLocalXY(geometry);
19082 if (isNaN(p0) || isNaN(p1)) return;
19084 // Use rounded line caps
19085 this.canvas.lineCap = "round";
19086 this.canvas.lineJoin = "round";
19088 if (this.hitDetection) {
19089 this.hitContext.lineCap = "round";
19090 this.hitContext.lineJoin = "round";
19093 // Scale and rotate symbols, using precalculated bounds whenever possible.
19094 if (style.graphicName in this.cachedSymbolBounds) {
19095 symbolBounds = this.cachedSymbolBounds[style.graphicName];
19097 symbolBounds = new OpenLayers.Bounds();
19098 for(i = 0; i < symbol.length; i+=2) {
19099 symbolBounds.extend(new OpenLayers.LonLat(symbol[i], symbol[i+1]));
19101 this.cachedSymbolBounds[style.graphicName] = symbolBounds;
19104 // Push symbol scaling, translation and rotation onto the transformation stack in reverse order.
19105 // Don't forget to apply all canvas transformations to the hitContext canvas as well(!)
19106 this.canvas.save();
19107 if (this.hitDetection) { this.hitContext.save(); }
19109 // Step 3: place symbol at the desired location
19110 this.canvas.translate(p0,p1);
19111 if (this.hitDetection) { this.hitContext.translate(p0,p1); }
19113 // Step 2a. rotate the symbol if necessary
19114 angle = deg2rad * style.rotation; // will be NaN when style.rotation is undefined.
19115 if (!isNaN(angle)) {
19116 this.canvas.rotate(angle);
19117 if (this.hitDetection) { this.hitContext.rotate(angle); }
19120 // // Step 2: scale symbol such that pointRadius equals half the maximum symbol dimension.
19121 scaling = 2.0 * style.pointRadius / Math.max(symbolBounds.getWidth(), symbolBounds.getHeight());
19122 this.canvas.scale(scaling,scaling);
19123 if (this.hitDetection) { this.hitContext.scale(scaling,scaling); }
19125 // Step 1: center the symbol at the origin
19126 cx = symbolBounds.getCenterLonLat().lon;
19127 cy = symbolBounds.getCenterLonLat().lat;
19128 this.canvas.translate(-cx,-cy);
19129 if (this.hitDetection) { this.hitContext.translate(-cx,-cy); }
19131 // Don't forget to scale stroke widths, because they are affected by canvas scale transformations as well(!)
19132 // Alternative: scale symbol coordinates manually, so stroke width scaling is not needed anymore.
19133 unscaledStrokeWidth = style.strokeWidth;
19134 style.strokeWidth = unscaledStrokeWidth / scaling;
19136 if (style.fill !== false) {
19137 this.setCanvasStyle("fill", style);
19138 this.canvas.beginPath();
19139 for (i=0; i<symbol.length; i=i+2) {
19142 if (i == 0) this.canvas.moveTo(x,y);
19143 this.canvas.lineTo(x,y);
19145 this.canvas.closePath();
19146 this.canvas.fill();
19148 if (this.hitDetection) {
19149 this.setHitContextStyle("fill", featureId, style);
19150 this.hitContext.beginPath();
19151 for (i=0; i<symbol.length; i=i+2) {
19154 if (i == 0) this.canvas.moveTo(x,y);
19155 this.hitContext.lineTo(x,y);
19157 this.hitContext.closePath();
19158 this.hitContext.fill();
19162 if (style.stroke !== false) {
19163 this.setCanvasStyle("stroke", style);
19164 this.canvas.beginPath();
19165 for (i=0; i<symbol.length; i=i+2) {
19168 if (i == 0) this.canvas.moveTo(x,y);
19169 this.canvas.lineTo(x,y);
19171 this.canvas.closePath();
19172 this.canvas.stroke();
19175 if (this.hitDetection) {
19176 this.setHitContextStyle("stroke", featureId, style, scaling);
19177 this.hitContext.beginPath();
19178 for (i=0; i<symbol.length; i=i+2) {
19181 if (i == 0) this.hitContext.moveTo(x,y);
19182 this.hitContext.lineTo(x,y);
19184 this.hitContext.closePath();
19185 this.hitContext.stroke();
19190 style.strokeWidth = unscaledStrokeWidth;
19191 this.canvas.restore();
19192 if (this.hitDetection) { this.hitContext.restore(); }
19193 this.setCanvasStyle("reset");
19197 * Method: setCanvasStyle
19198 * Prepare the canvas for drawing by setting various global settings.
19201 * type - {String} one of 'stroke', 'fill', or 'reset'
19202 * style - {Object} Symbolizer hash
19204 setCanvasStyle: function(type, style) {
19205 if (type === "fill") {
19206 this.canvas.globalAlpha = style['fillOpacity'];
19207 this.canvas.fillStyle = style['fillColor'];
19208 } else if (type === "stroke") {
19209 this.canvas.globalAlpha = style['strokeOpacity'];
19210 this.canvas.strokeStyle = style['strokeColor'];
19211 this.canvas.lineWidth = style['strokeWidth'];
19213 this.canvas.globalAlpha = 0;
19214 this.canvas.lineWidth = 1;
19219 * Method: featureIdToHex
19220 * Convert a feature ID string into an RGB hex string.
19223 * featureId - {String} Feature id
19226 * {String} RGB hex string.
19228 featureIdToHex: function(featureId) {
19229 var id = Number(featureId.split("_").pop()) + 1; // zero for no feature
19230 if (id >= 16777216) {
19231 this.hitOverflow = id - 16777215;
19232 id = id % 16777216 + 1;
19234 var hex = "000000" + id.toString(16);
19235 var len = hex.length;
19236 hex = "#" + hex.substring(len-6, len);
19241 * Method: setHitContextStyle
19242 * Prepare the hit canvas for drawing by setting various global settings.
19245 * type - {String} one of 'stroke', 'fill', or 'reset'
19246 * featureId - {String} The feature id.
19247 * symbolizer - {<OpenLayers.Symbolizer>} The symbolizer.
19249 setHitContextStyle: function(type, featureId, symbolizer, strokeScaling) {
19250 var hex = this.featureIdToHex(featureId);
19251 if (type == "fill") {
19252 this.hitContext.globalAlpha = 1.0;
19253 this.hitContext.fillStyle = hex;
19254 } else if (type == "stroke") {
19255 this.hitContext.globalAlpha = 1.0;
19256 this.hitContext.strokeStyle = hex;
19257 // bump up stroke width to deal with antialiasing. If strokeScaling is defined, we're rendering a symbol
19258 // on a transformed canvas, so the antialias width bump has to scale as well.
19259 if (typeof strokeScaling === "undefined") {
19260 this.hitContext.lineWidth = symbolizer.strokeWidth + 2;
19262 if (!isNaN(strokeScaling)) { this.hitContext.lineWidth = symbolizer.strokeWidth + 2.0 / strokeScaling; }
19265 this.hitContext.globalAlpha = 0;
19266 this.hitContext.lineWidth = 1;
19271 * Method: drawPoint
19272 * This method is only called by the renderer itself.
19275 * geometry - {<OpenLayers.Geometry>}
19277 * featureId - {String}
19279 drawPoint: function(geometry, style, featureId) {
19280 if(style.graphic !== false) {
19281 if(style.externalGraphic) {
19282 this.drawExternalGraphic(geometry, style, featureId);
19283 } else if (style.graphicName && (style.graphicName != "circle")) {
19284 this.drawNamedSymbol(geometry, style, featureId);
19286 var pt = this.getLocalXY(geometry);
19289 if(!isNaN(p0) && !isNaN(p1)) {
19290 var twoPi = Math.PI*2;
19291 var radius = style.pointRadius;
19292 if(style.fill !== false) {
19293 this.setCanvasStyle("fill", style);
19294 this.canvas.beginPath();
19295 this.canvas.arc(p0, p1, radius, 0, twoPi, true);
19296 this.canvas.fill();
19297 if (this.hitDetection) {
19298 this.setHitContextStyle("fill", featureId, style);
19299 this.hitContext.beginPath();
19300 this.hitContext.arc(p0, p1, radius, 0, twoPi, true);
19301 this.hitContext.fill();
19305 if(style.stroke !== false) {
19306 this.setCanvasStyle("stroke", style);
19307 this.canvas.beginPath();
19308 this.canvas.arc(p0, p1, radius, 0, twoPi, true);
19309 this.canvas.stroke();
19310 if (this.hitDetection) {
19311 this.setHitContextStyle("stroke", featureId, style);
19312 this.hitContext.beginPath();
19313 this.hitContext.arc(p0, p1, radius, 0, twoPi, true);
19314 this.hitContext.stroke();
19316 this.setCanvasStyle("reset");
19324 * Method: drawLineString
19325 * This method is only called by the renderer itself.
19328 * geometry - {<OpenLayers.Geometry>}
19330 * featureId - {String}
19332 drawLineString: function(geometry, style, featureId) {
19333 style = OpenLayers.Util.applyDefaults({fill: false}, style);
19334 this.drawLinearRing(geometry, style, featureId);
19338 * Method: drawLinearRing
19339 * This method is only called by the renderer itself.
19342 * geometry - {<OpenLayers.Geometry>}
19344 * featureId - {String}
19346 drawLinearRing: function(geometry, style, featureId) {
19347 if (style.fill !== false) {
19348 this.setCanvasStyle("fill", style);
19349 this.renderPath(this.canvas, geometry, style, featureId, "fill");
19350 if (this.hitDetection) {
19351 this.setHitContextStyle("fill", featureId, style);
19352 this.renderPath(this.hitContext, geometry, style, featureId, "fill");
19355 if (style.stroke !== false) {
19356 this.setCanvasStyle("stroke", style);
19357 this.renderPath(this.canvas, geometry, style, featureId, "stroke");
19358 if (this.hitDetection) {
19359 this.setHitContextStyle("stroke", featureId, style);
19360 this.renderPath(this.hitContext, geometry, style, featureId, "stroke");
19363 this.setCanvasStyle("reset");
19367 * Method: renderPath
19368 * Render a path with stroke and optional fill.
19370 renderPath: function(context, geometry, style, featureId, type) {
19371 var components = geometry.components;
19372 var len = components.length;
19373 context.beginPath();
19374 var start = this.getLocalXY(components[0]);
19377 if (!isNaN(x) && !isNaN(y)) {
19378 context.moveTo(start[0], start[1]);
19379 for (var i=1; i<len; ++i) {
19380 var pt = this.getLocalXY(components[i]);
19381 context.lineTo(pt[0], pt[1]);
19383 if (type === "fill") {
19392 * Method: drawPolygon
19393 * This method is only called by the renderer itself.
19396 * geometry - {<OpenLayers.Geometry>}
19398 * featureId - {String}
19400 drawPolygon: function(geometry, style, featureId) {
19401 var components = geometry.components;
19402 var len = components.length;
19403 this.drawLinearRing(components[0], style, featureId);
19404 // erase inner rings
19405 for (var i=1; i<len; ++i) {
19407 * Note that this is overly agressive. Here we punch holes through
19408 * all previously rendered features on the same canvas. A better
19409 * solution for polygons with interior rings would be to draw the
19410 * polygon on a sketch canvas first. We could erase all holes
19411 * there and then copy the drawing to the layer canvas.
19412 * TODO: http://trac.osgeo.org/openlayers/ticket/3130
19414 this.canvas.globalCompositeOperation = "destination-out";
19415 if (this.hitDetection) {
19416 this.hitContext.globalCompositeOperation = "destination-out";
19418 this.drawLinearRing(
19420 OpenLayers.Util.applyDefaults({stroke: false, fillOpacity: 1.0}, style),
19423 this.canvas.globalCompositeOperation = "source-over";
19424 if (this.hitDetection) {
19425 this.hitContext.globalCompositeOperation = "source-over";
19427 this.drawLinearRing(
19429 OpenLayers.Util.applyDefaults({fill: false}, style),
19437 * This method is only called by the renderer itself.
19440 * location - {<OpenLayers.Point>}
19443 drawText: function(location, style) {
19444 var pt = this.getLocalXY(location);
19446 this.setCanvasStyle("reset");
19447 this.canvas.fillStyle = style.fontColor;
19448 this.canvas.globalAlpha = style.fontOpacity || 1.0;
19449 var fontStyle = [style.fontStyle ? style.fontStyle : "normal",
19450 "normal", // "font-variant" not supported
19451 style.fontWeight ? style.fontWeight : "normal",
19452 style.fontSize ? style.fontSize : "1em",
19453 style.fontFamily ? style.fontFamily : "sans-serif"].join(" ");
19454 var labelRows = style.label.split('\n');
19455 var numRows = labelRows.length;
19456 if (this.canvas.fillText) {
19458 this.canvas.font = fontStyle;
19459 this.canvas.textAlign =
19460 OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[0]] ||
19462 this.canvas.textBaseline =
19463 OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[1]] ||
19466 OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];
19467 if (vfactor == null) {
19471 this.canvas.measureText('Mg').height ||
19472 this.canvas.measureText('xx').width;
19473 pt[1] += lineHeight*vfactor*(numRows-1);
19474 for (var i = 0; i < numRows; i++) {
19475 if (style.labelOutlineWidth) {
19476 this.canvas.save();
19477 this.canvas.globalAlpha = style.labelOutlineOpacity || style.fontOpacity || 1.0;
19478 this.canvas.strokeStyle = style.labelOutlineColor;
19479 this.canvas.lineWidth = style.labelOutlineWidth;
19480 this.canvas.strokeText(labelRows[i], pt[0], pt[1] + (lineHeight*i) + 1);
19481 this.canvas.restore();
19483 this.canvas.fillText(labelRows[i], pt[0], pt[1] + (lineHeight*i));
19485 } else if (this.canvas.mozDrawText) {
19486 // Mozilla pre-Gecko1.9.1 (<FF3.1)
19487 this.canvas.mozTextStyle = fontStyle;
19488 // No built-in text alignment, so we measure and adjust the position
19490 OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[0]];
19491 if (hfactor == null) {
19495 OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];
19496 if (vfactor == null) {
19499 var lineHeight = this.canvas.mozMeasureText('xx');
19500 pt[1] += lineHeight*(1 + (vfactor*numRows));
19501 for (var i = 0; i < numRows; i++) {
19502 var x = pt[0] + (hfactor*this.canvas.mozMeasureText(labelRows[i]));
19503 var y = pt[1] + (i*lineHeight);
19504 this.canvas.translate(x, y);
19505 this.canvas.mozDrawText(labelRows[i]);
19506 this.canvas.translate(-x, -y);
19509 this.setCanvasStyle("reset");
19513 * Method: getLocalXY
19514 * transform geographic xy into pixel xy
19517 * point - {<OpenLayers.Geometry.Point>}
19519 getLocalXY: function(point) {
19520 var resolution = this.getResolution();
19521 var extent = this.extent;
19522 var x = ((point.x - this.featureDx) / resolution + (-extent.left / resolution));
19523 var y = ((extent.top / resolution) - point.y / resolution);
19529 * Clear all vectors from the renderer.
19531 clear: function() {
19532 var height = this.root.height;
19533 var width = this.root.width;
19534 this.canvas.clearRect(0, 0, width, height);
19535 this.features = {};
19536 if (this.hitDetection) {
19537 this.hitContext.clearRect(0, 0, width, height);
19542 * Method: getFeatureIdFromEvent
19543 * Returns a feature id from an event on the renderer.
19546 * evt - {<OpenLayers.Event>}
19549 * {<OpenLayers.Feature.Vector} A feature or undefined. This method returns a
19550 * feature instead of a feature id to avoid an unnecessary lookup on the
19553 getFeatureIdFromEvent: function(evt) {
19554 var featureId, feature;
19556 if (this.hitDetection && this.root.style.display !== "none") {
19557 // this dragging check should go in the feature handler
19558 if (!this.map.dragging) {
19562 var data = this.hitContext.getImageData(x, y, 1, 1).data;
19563 if (data[3] === 255) { // antialiased
19564 var id = data[2] + (256 * (data[1] + (256 * data[0])));
19566 featureId = "OpenLayers_Feature_Vector_" + (id - 1 + this.hitOverflow);
19568 feature = this.features[featureId][0];
19570 // Because of antialiasing on the canvas, when the hit location is at a point where the edge of
19571 // one symbol intersects the interior of another symbol, a wrong hit color (and therefore id) results.
19572 // todo: set Antialiasing = 'off' on the hitContext as soon as browsers allow it.
19582 * Method: eraseFeatures
19583 * This is called by the layer to erase features; removes the feature from
19584 * the list, then redraws the layer.
19587 * features - {Array(<OpenLayers.Feature.Vector>)}
19589 eraseFeatures: function(features) {
19590 if(!(OpenLayers.Util.isArray(features))) {
19591 features = [features];
19593 for(var i=0; i<features.length; ++i) {
19594 delete this.features[features[i].id];
19601 * The real 'meat' of the function: any time things have changed,
19602 * redraw() can be called to loop over all the data and (you guessed
19603 * it) redraw it. Unlike Elements-based Renderers, we can't interact
19604 * with things once they're drawn, to remove them, for example, so
19605 * instead we have to just clear everything and draw from scratch.
19607 redraw: function() {
19608 if (!this.locked) {
19609 var height = this.root.height;
19610 var width = this.root.width;
19611 this.canvas.clearRect(0, 0, width, height);
19612 if (this.hitDetection) {
19613 this.hitContext.clearRect(0, 0, width, height);
19616 var feature, geometry, style;
19617 var worldBounds = (this.map.baseLayer && this.map.baseLayer.wrapDateLine) && this.map.getMaxExtent();
19618 for (var id in this.features) {
19619 if (!this.features.hasOwnProperty(id)) { continue; }
19620 feature = this.features[id][0];
19621 geometry = feature.geometry;
19622 this.calculateFeatureDx(geometry.getBounds(), worldBounds);
19623 style = this.features[id][1];
19624 this.drawGeometry(geometry, style, feature.id);
19626 labelMap.push([feature, style]);
19630 for (var i=0, len=labelMap.length; i<len; ++i) {
19631 item = labelMap[i];
19632 this.drawText(item[0].geometry.getCentroid(), item[1]);
19637 CLASS_NAME: "OpenLayers.Renderer.Canvas"
19641 * Constant: OpenLayers.Renderer.Canvas.LABEL_ALIGN
19644 OpenLayers.Renderer.Canvas.LABEL_ALIGN = {
19652 * Constant: OpenLayers.Renderer.Canvas.LABEL_FACTOR
19655 OpenLayers.Renderer.Canvas.LABEL_FACTOR = {
19663 * Constant: OpenLayers.Renderer.Canvas.drawImageScaleFactor
19664 * {Number} Scale factor to apply to the canvas drawImage arguments. This
19665 * is always 1 except for Android 2.1 devices, to work around
19666 * http://code.google.com/p/android/issues/detail?id=5141.
19668 OpenLayers.Renderer.Canvas.drawImageScaleFactor = null;
19669 /* ======================================================================
19670 OpenLayers/Handler.js
19671 ====================================================================== */
19673 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
19674 * full list of contributors). Published under the 2-clause BSD license.
19675 * See license.txt in the OpenLayers distribution or repository for the
19676 * full text of the license. */
19679 * @requires OpenLayers/BaseTypes/Class.js
19680 * @requires OpenLayers/Events.js
19684 * Class: OpenLayers.Handler
19685 * Base class to construct a higher-level handler for event sequences. All
19686 * handlers have activate and deactivate methods. In addition, they have
19687 * methods named like browser events. When a handler is activated, any
19688 * additional methods named like a browser event is registered as a
19689 * listener for the corresponding event. When a handler is deactivated,
19690 * those same methods are unregistered as event listeners.
19692 * Handlers also typically have a callbacks object with keys named like
19693 * the abstracted events or event sequences that they are in charge of
19694 * handling. The controls that wrap handlers define the methods that
19695 * correspond to these abstract events - so instead of listening for
19696 * individual browser events, they only listen for the abstract events
19697 * defined by the handler.
19699 * Handlers are created by controls, which ultimately have the responsibility
19700 * of making changes to the the state of the application. Handlers
19701 * themselves may make temporary changes, but in general are expected to
19702 * return the application in the same state that they found it.
19704 OpenLayers.Handler = OpenLayers.Class({
19713 * APIProperty: control
19714 * {<OpenLayers.Control>}. The control that initialized this handler. The
19715 * control is assumed to have a valid map property - that map is used
19716 * in the handler's own setMap method.
19722 * {<OpenLayers.Map>}
19727 * APIProperty: keyMask
19728 * {Integer} Use bitwise operators and one or more of the OpenLayers.Handler
19729 * constants to construct a keyMask. The keyMask is used by
19730 * <checkModifiers>. If the keyMask matches the combination of keys
19731 * down on an event, checkModifiers returns true.
19735 * // handler only responds if the Shift key is down
19736 * handler.keyMask = OpenLayers.Handler.MOD_SHIFT;
19738 * // handler only responds if Ctrl-Shift is down
19739 * handler.keyMask = OpenLayers.Handler.MOD_SHIFT |
19740 * OpenLayers.Handler.MOD_CTRL;
19753 * {Event} This property references the last event handled by the handler.
19754 * Note that this property is not part of the stable API. Use of the
19755 * evt property should be restricted to controls in the library
19756 * or other applications that are willing to update with changes to
19757 * the OpenLayers code.
19763 * {Boolean} Indicates the support of touch events. When touch events are
19764 * started touch will be true and all mouse related listeners will do
19770 * Constructor: OpenLayers.Handler
19771 * Construct a handler.
19774 * control - {<OpenLayers.Control>} The control that initialized this
19775 * handler. The control is assumed to have a valid map property; that
19776 * map is used in the handler's own setMap method. If a map property
19777 * is present in the options argument it will be used instead.
19778 * callbacks - {Object} An object whose properties correspond to abstracted
19779 * events or sequences of browser events. The values for these
19780 * properties are functions defined by the control that get called by
19782 * options - {Object} An optional object whose properties will be set on
19785 initialize: function(control, callbacks, options) {
19786 OpenLayers.Util.extend(this, options);
19787 this.control = control;
19788 this.callbacks = callbacks;
19790 var map = this.map || control.map;
19795 this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
19801 setMap: function (map) {
19806 * Method: checkModifiers
19807 * Check the keyMask on the handler. If no <keyMask> is set, this always
19808 * returns true. If a <keyMask> is set and it matches the combination
19809 * of keys down on an event, this returns true.
19812 * {Boolean} The keyMask matches the keys down on an event.
19814 checkModifiers: function (evt) {
19815 if(this.keyMask == null) {
19818 /* calculate the keyboard modifier mask for this event */
19820 (evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) |
19821 (evt.ctrlKey ? OpenLayers.Handler.MOD_CTRL : 0) |
19822 (evt.altKey ? OpenLayers.Handler.MOD_ALT : 0) |
19823 (evt.metaKey ? OpenLayers.Handler.MOD_META : 0);
19825 /* if it differs from the handler object's key mask,
19826 bail out of the event handler */
19827 return (keyModifiers == this.keyMask);
19831 * APIMethod: activate
19832 * Turn on the handler. Returns false if the handler was already active.
19835 * {Boolean} The handler was activated.
19837 activate: function() {
19841 // register for event handlers defined on this class.
19842 var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
19843 for (var i=0, len=events.length; i<len; i++) {
19844 if (this[events[i]]) {
19845 this.register(events[i], this[events[i]]);
19848 this.active = true;
19853 * APIMethod: deactivate
19854 * Turn off the handler. Returns false if the handler was already inactive.
19857 * {Boolean} The handler was deactivated.
19859 deactivate: function() {
19863 // unregister event handlers defined on this class.
19864 var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
19865 for (var i=0, len=events.length; i<len; i++) {
19866 if (this[events[i]]) {
19867 this.unregister(events[i], this[events[i]]);
19870 this.touch = false;
19871 this.active = false;
19876 * Method: startTouch
19877 * Start touch events, this method must be called by subclasses in
19878 * "touchstart" method. When touch events are started <touch> will be
19879 * true and all mouse related listeners will do nothing.
19881 startTouch: function() {
19885 "mousedown", "mouseup", "mousemove", "click", "dblclick",
19888 for (var i=0, len=events.length; i<len; i++) {
19889 if (this[events[i]]) {
19890 this.unregister(events[i], this[events[i]]);
19898 * Trigger the control's named callback with the given arguments
19901 * name - {String} The key for the callback that is one of the properties
19902 * of the handler's callbacks object.
19903 * args - {Array(*)} An array of arguments (any type) with which to call
19904 * the callback (defined by the control).
19906 callback: function (name, args) {
19907 if (name && this.callbacks[name]) {
19908 this.callbacks[name].apply(this.control, args);
19914 * register an event on the map
19916 register: function (name, method) {
19917 // TODO: deal with registerPriority in 3.0
19918 this.map.events.registerPriority(name, this, method);
19919 this.map.events.registerPriority(name, this, this.setEvent);
19923 * Method: unregister
19924 * unregister an event from the map
19926 unregister: function (name, method) {
19927 this.map.events.unregister(name, this, method);
19928 this.map.events.unregister(name, this, this.setEvent);
19933 * With each registered browser event, the handler sets its own evt
19934 * property. This property can be accessed by controls if needed
19935 * to get more information about the event that the handler is
19938 * This allows modifier keys on the event to be checked (alt, shift, ctrl,
19939 * and meta cannot be checked with the keyboard handler). For a
19940 * control to determine which modifier keys are associated with the
19941 * event that a handler is currently processing, it should access
19942 * (code)handler.evt.altKey || handler.evt.shiftKey ||
19943 * handler.evt.ctrlKey || handler.evt.metaKey(end).
19946 * evt - {Event} The browser event.
19948 setEvent: function(evt) {
19955 * Deconstruct the handler.
19957 destroy: function () {
19958 // unregister event listeners
19960 // eliminate circular references
19961 this.control = this.map = null;
19964 CLASS_NAME: "OpenLayers.Handler"
19968 * Constant: OpenLayers.Handler.MOD_NONE
19969 * If set as the <keyMask>, <checkModifiers> returns false if any key is down.
19971 OpenLayers.Handler.MOD_NONE = 0;
19974 * Constant: OpenLayers.Handler.MOD_SHIFT
19975 * If set as the <keyMask>, <checkModifiers> returns false if Shift is down.
19977 OpenLayers.Handler.MOD_SHIFT = 1;
19980 * Constant: OpenLayers.Handler.MOD_CTRL
19981 * If set as the <keyMask>, <checkModifiers> returns false if Ctrl is down.
19983 OpenLayers.Handler.MOD_CTRL = 2;
19986 * Constant: OpenLayers.Handler.MOD_ALT
19987 * If set as the <keyMask>, <checkModifiers> returns false if Alt is down.
19989 OpenLayers.Handler.MOD_ALT = 4;
19992 * Constant: OpenLayers.Handler.MOD_META
19993 * If set as the <keyMask>, <checkModifiers> returns false if Cmd is down.
19995 OpenLayers.Handler.MOD_META = 8;
19998 /* ======================================================================
19999 OpenLayers/Handler/MouseWheel.js
20000 ====================================================================== */
20002 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
20003 * full list of contributors). Published under the 2-clause BSD license.
20004 * See license.txt in the OpenLayers distribution or repository for the
20005 * full text of the license. */
20008 * @requires OpenLayers/Handler.js
20012 * Class: OpenLayers.Handler.MouseWheel
20013 * Handler for wheel up/down events.
20016 * - <OpenLayers.Handler>
20018 OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, {
20020 * Property: wheelListener
20023 wheelListener: null,
20026 * Property: interval
20027 * {Integer} In order to increase server performance, an interval (in
20028 * milliseconds) can be set to reduce the number of up/down events
20029 * called. If set, a new up/down event will not be set until the
20030 * interval has passed.
20031 * Defaults to 0, meaning no interval.
20036 * Property: maxDelta
20037 * {Integer} Maximum delta to collect before breaking from the current
20038 * interval. In cumulative mode, this also limits the maximum delta
20039 * returned from the handler. Default is Number.POSITIVE_INFINITY.
20041 maxDelta: Number.POSITIVE_INFINITY,
20045 * {Integer} When interval is set, delta collects the mousewheel z-deltas
20046 * of the events that occur within the interval.
20047 * See also the cumulative option
20052 * Property: cumulative
20053 * {Boolean} When interval is set: true to collect all the mousewheel
20054 * z-deltas, false to only record the delta direction (positive or
20060 * Constructor: OpenLayers.Handler.MouseWheel
20063 * control - {<OpenLayers.Control>}
20064 * callbacks - {Object} An object containing a single function to be
20065 * called when the drag operation is finished.
20066 * The callback should expect to recieve a single
20067 * argument, the point geometry.
20068 * options - {Object}
20070 initialize: function(control, callbacks, options) {
20071 OpenLayers.Handler.prototype.initialize.apply(this, arguments);
20072 this.wheelListener = OpenLayers.Function.bindAsEventListener(
20073 this.onWheelEvent, this
20080 destroy: function() {
20081 OpenLayers.Handler.prototype.destroy.apply(this, arguments);
20082 this.wheelListener = null;
20086 * Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/
20090 * Method: onWheelEvent
20091 * Catch the wheel event and handle it xbrowserly
20096 onWheelEvent: function(e){
20098 // make sure we have a map and check keyboard modifiers
20099 if (!this.map || !this.checkModifiers(e)) {
20103 // Ride up the element's DOM hierarchy to determine if it or any of
20104 // its ancestors was:
20105 // * specifically marked as scrollable (CSS overflow property)
20106 // * one of our layer divs or a div marked as scrollable
20107 // ('olScrollable' CSS class)
20110 var overScrollableDiv = false;
20111 var allowScroll = false;
20112 var overMapDiv = false;
20114 var elem = OpenLayers.Event.element(e);
20115 while((elem != null) && !overMapDiv && !overScrollableDiv) {
20117 if (!overScrollableDiv) {
20120 if (elem.currentStyle) {
20121 overflow = elem.currentStyle["overflow"];
20124 document.defaultView.getComputedStyle(elem, null);
20125 overflow = style.getPropertyValue("overflow");
20127 overScrollableDiv = ( overflow &&
20128 (overflow == "auto") || (overflow == "scroll") );
20130 //sometimes when scrolling in a popup, this causes
20131 // obscure browser error
20135 if (!allowScroll) {
20136 allowScroll = OpenLayers.Element.hasClass(elem, 'olScrollable');
20137 if (!allowScroll) {
20138 for (var i = 0, len = this.map.layers.length; i < len; i++) {
20139 // Are we in the layer div? Note that we have two cases
20140 // here: one is to catch EventPane layers, which have a
20141 // pane above the layer (layer.pane)
20142 var layer = this.map.layers[i];
20143 if (elem == layer.div || elem == layer.pane) {
20144 allowScroll = true;
20150 overMapDiv = (elem == this.map.div);
20152 elem = elem.parentNode;
20155 // Logic below is the following:
20157 // If we are over a scrollable div or not over the map div:
20158 // * do nothing (let the browser handle scrolling)
20162 // If we are over the layer div or a 'olScrollable' div:
20165 // * kill event (so as not to also scroll the page after zooming)
20169 // Kill the event (dont scroll the page if we wheel over the
20170 // layerswitcher or the pan/zoom control)
20172 if (!overScrollableDiv && overMapDiv) {
20176 if (e.wheelDelta) {
20177 delta = e.wheelDelta;
20178 if (delta % 160 === 0) {
20179 // opera have steps of 160 instead of 120
20180 delta = delta * 0.75;
20182 delta = delta / 120;
20183 } else if (e.detail) {
20184 // detail in Firefox on OS X is 1/3 of Windows
20185 // so force delta 1 / -1
20186 delta = - (e.detail / Math.abs(e.detail));
20188 this.delta += delta;
20190 window.clearTimeout(this._timeoutId);
20191 if(this.interval && Math.abs(this.delta) < this.maxDelta) {
20192 // store e because window.event might change during delay
20193 var evt = OpenLayers.Util.extend({}, e);
20194 this._timeoutId = window.setTimeout(
20195 OpenLayers.Function.bind(function(){
20196 this.wheelZoom(evt);
20204 OpenLayers.Event.stop(e);
20209 * Method: wheelZoom
20210 * Given the wheel event, we carry out the appropriate zooming in or out,
20211 * based on the 'wheelDelta' or 'detail' property of the event.
20216 wheelZoom: function(e) {
20217 var delta = this.delta;
20221 e.xy = this.map.events.getMousePosition(e);
20223 this.callback("down",
20224 [e, this.cumulative ? Math.max(-this.maxDelta, delta) : -1]);
20226 this.callback("up",
20227 [e, this.cumulative ? Math.min(this.maxDelta, delta) : 1]);
20235 activate: function (evt) {
20236 if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
20237 //register mousewheel events specifically on the window and document
20238 var wheelListener = this.wheelListener;
20239 OpenLayers.Event.observe(window, "DOMMouseScroll", wheelListener);
20240 OpenLayers.Event.observe(window, "mousewheel", wheelListener);
20241 OpenLayers.Event.observe(document, "mousewheel", wheelListener);
20249 * Method: deactivate
20251 deactivate: function (evt) {
20252 if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
20253 // unregister mousewheel events specifically on the window and document
20254 var wheelListener = this.wheelListener;
20255 OpenLayers.Event.stopObserving(window, "DOMMouseScroll", wheelListener);
20256 OpenLayers.Event.stopObserving(window, "mousewheel", wheelListener);
20257 OpenLayers.Event.stopObserving(document, "mousewheel", wheelListener);
20264 CLASS_NAME: "OpenLayers.Handler.MouseWheel"
20266 /* ======================================================================
20267 OpenLayers/Geometry/MultiLineString.js
20268 ====================================================================== */
20270 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
20271 * full list of contributors). Published under the 2-clause BSD license.
20272 * See license.txt in the OpenLayers distribution or repository for the
20273 * full text of the license. */
20276 * @requires OpenLayers/Geometry/Collection.js
20277 * @requires OpenLayers/Geometry/LineString.js
20281 * Class: OpenLayers.Geometry.MultiLineString
20282 * A MultiLineString is a geometry with multiple <OpenLayers.Geometry.LineString>
20286 * - <OpenLayers.Geometry.Collection>
20287 * - <OpenLayers.Geometry>
20289 OpenLayers.Geometry.MultiLineString = OpenLayers.Class(
20290 OpenLayers.Geometry.Collection, {
20293 * Property: componentTypes
20294 * {Array(String)} An array of class names representing the types of
20295 * components that the collection can include. A null value means the
20296 * component types are not restricted.
20298 componentTypes: ["OpenLayers.Geometry.LineString"],
20301 * Constructor: OpenLayers.Geometry.MultiLineString
20302 * Constructor for a MultiLineString Geometry.
20305 * components - {Array(<OpenLayers.Geometry.LineString>)}
20311 * Use this geometry (the source) to attempt to split a target geometry.
20314 * geometry - {<OpenLayers.Geometry>} The target geometry.
20315 * options - {Object} Properties of this object will be used to determine
20316 * how the split is conducted.
20319 * mutual - {Boolean} Split the source geometry in addition to the target
20320 * geometry. Default is false.
20321 * edge - {Boolean} Allow splitting when only edges intersect. Default is
20322 * true. If false, a vertex on the source must be within the tolerance
20323 * distance of the intersection to be considered a split.
20324 * tolerance - {Number} If a non-null value is provided, intersections
20325 * within the tolerance distance of an existing vertex on the source
20326 * will be assumed to occur at the vertex.
20329 * {Array} A list of geometries (of this same type as the target) that
20330 * result from splitting the target with the source geometry. The
20331 * source and target geometry will remain unmodified. If no split
20332 * results, null will be returned. If mutual is true and a split
20333 * results, return will be an array of two arrays - the first will be
20334 * all geometries that result from splitting the source geometry and
20335 * the second will be all geometries that result from splitting the
20338 split: function(geometry, options) {
20339 var results = null;
20340 var mutual = options && options.mutual;
20341 var splits, sourceLine, sourceLines, sourceSplit, targetSplit;
20342 var sourceParts = [];
20343 var targetParts = [geometry];
20344 for(var i=0, len=this.components.length; i<len; ++i) {
20345 sourceLine = this.components[i];
20346 sourceSplit = false;
20347 for(var j=0; j < targetParts.length; ++j) {
20348 splits = sourceLine.split(targetParts[j], options);
20351 sourceLines = splits[0];
20352 for(var k=0, klen=sourceLines.length; k<klen; ++k) {
20353 if(k===0 && sourceParts.length) {
20354 sourceParts[sourceParts.length-1].addComponent(
20359 new OpenLayers.Geometry.MultiLineString([
20365 sourceSplit = true;
20366 splits = splits[1];
20368 if(splits.length) {
20369 // splice in new target parts
20370 splits.unshift(j, 1);
20371 Array.prototype.splice.apply(targetParts, splits);
20377 // source line was not hit
20378 if(sourceParts.length) {
20379 // add line to existing multi
20380 sourceParts[sourceParts.length-1].addComponent(
20384 // create a fresh multi
20386 new OpenLayers.Geometry.MultiLineString(
20393 if(sourceParts && sourceParts.length > 1) {
20394 sourceSplit = true;
20398 if(targetParts && targetParts.length > 1) {
20399 targetSplit = true;
20403 if(sourceSplit || targetSplit) {
20405 results = [sourceParts, targetParts];
20407 results = targetParts;
20414 * Method: splitWith
20415 * Split this geometry (the target) with the given geometry (the source).
20418 * geometry - {<OpenLayers.Geometry>} A geometry used to split this
20419 * geometry (the source).
20420 * options - {Object} Properties of this object will be used to determine
20421 * how the split is conducted.
20424 * mutual - {Boolean} Split the source geometry in addition to the target
20425 * geometry. Default is false.
20426 * edge - {Boolean} Allow splitting when only edges intersect. Default is
20427 * true. If false, a vertex on the source must be within the tolerance
20428 * distance of the intersection to be considered a split.
20429 * tolerance - {Number} If a non-null value is provided, intersections
20430 * within the tolerance distance of an existing vertex on the source
20431 * will be assumed to occur at the vertex.
20434 * {Array} A list of geometries (of this same type as the target) that
20435 * result from splitting the target with the source geometry. The
20436 * source and target geometry will remain unmodified. If no split
20437 * results, null will be returned. If mutual is true and a split
20438 * results, return will be an array of two arrays - the first will be
20439 * all geometries that result from splitting the source geometry and
20440 * the second will be all geometries that result from splitting the
20443 splitWith: function(geometry, options) {
20444 var results = null;
20445 var mutual = options && options.mutual;
20446 var splits, targetLine, sourceLines, sourceSplit, targetSplit, sourceParts, targetParts;
20447 if(geometry instanceof OpenLayers.Geometry.LineString) {
20449 sourceParts = [geometry];
20450 for(var i=0, len=this.components.length; i<len; ++i) {
20451 targetSplit = false;
20452 targetLine = this.components[i];
20453 for(var j=0; j<sourceParts.length; ++j) {
20454 splits = sourceParts[j].split(targetLine, options);
20457 sourceLines = splits[0];
20458 if(sourceLines.length) {
20459 // splice in new source parts
20460 sourceLines.unshift(j, 1);
20461 Array.prototype.splice.apply(sourceParts, sourceLines);
20462 j += sourceLines.length - 2;
20464 splits = splits[1];
20465 if(splits.length === 0) {
20466 splits = [targetLine.clone()];
20469 for(var k=0, klen=splits.length; k<klen; ++k) {
20470 if(k===0 && targetParts.length) {
20471 targetParts[targetParts.length-1].addComponent(
20476 new OpenLayers.Geometry.MultiLineString([
20482 targetSplit = true;
20486 // target component was not hit
20487 if(targetParts.length) {
20488 // add it to any existing multi-line
20489 targetParts[targetParts.length-1].addComponent(
20493 // or start with a fresh multi-line
20495 new OpenLayers.Geometry.MultiLineString([
20504 results = geometry.split(this);
20506 if(sourceParts && sourceParts.length > 1) {
20507 sourceSplit = true;
20511 if(targetParts && targetParts.length > 1) {
20512 targetSplit = true;
20516 if(sourceSplit || targetSplit) {
20518 results = [sourceParts, targetParts];
20520 results = targetParts;
20526 CLASS_NAME: "OpenLayers.Geometry.MultiLineString"
20528 /* ======================================================================
20529 OpenLayers/Format.js
20530 ====================================================================== */
20532 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
20533 * full list of contributors). Published under the 2-clause BSD license.
20534 * See license.txt in the OpenLayers distribution or repository for the
20535 * full text of the license. */
20538 * @requires OpenLayers/BaseTypes/Class.js
20539 * @requires OpenLayers/Util.js
20543 * Class: OpenLayers.Format
20544 * Base class for format reading/writing a variety of formats. Subclasses
20545 * of OpenLayers.Format are expected to have read and write methods.
20547 OpenLayers.Format = OpenLayers.Class({
20550 * Property: options
20551 * {Object} A reference to options passed to the constructor.
20556 * APIProperty: externalProjection
20557 * {<OpenLayers.Projection>} When passed a externalProjection and
20558 * internalProjection, the format will reproject the geometries it
20559 * reads or writes. The externalProjection is the projection used by
20560 * the content which is passed into read or which comes out of write.
20561 * In order to reproject, a projection transformation function for the
20562 * specified projections must be available. This support may be
20563 * provided via proj4js or via a custom transformation function. See
20564 * {<OpenLayers.Projection.addTransform>} for more information on
20565 * custom transformations.
20567 externalProjection: null,
20570 * APIProperty: internalProjection
20571 * {<OpenLayers.Projection>} When passed a externalProjection and
20572 * internalProjection, the format will reproject the geometries it
20573 * reads or writes. The internalProjection is the projection used by
20574 * the geometries which are returned by read or which are passed into
20575 * write. In order to reproject, a projection transformation function
20576 * for the specified projections must be available. This support may be
20577 * provided via proj4js or via a custom transformation function. See
20578 * {<OpenLayers.Projection.addTransform>} for more information on
20579 * custom transformations.
20581 internalProjection: null,
20584 * APIProperty: data
20585 * {Object} When <keepData> is true, this is the parsed string sent to
20591 * APIProperty: keepData
20592 * {Object} Maintain a reference (<data>) to the most recently read data.
20593 * Default is false.
20598 * Constructor: OpenLayers.Format
20599 * Instances of this class are not useful. See one of the subclasses.
20602 * options - {Object} An optional object with properties to set on the
20606 * keepData - {Boolean} If true, upon <read>, the data property will be
20607 * set to the parsed object (e.g. the json or xml object).
20610 * An instance of OpenLayers.Format
20612 initialize: function(options) {
20613 OpenLayers.Util.extend(this, options);
20614 this.options = options;
20618 * APIMethod: destroy
20621 destroy: function() {
20626 * Read data from a string, and return an object whose type depends on the
20630 * data - {string} Data to read/parse.
20633 * Depends on the subclass
20635 read: function(data) {
20636 throw new Error('Read not implemented.');
20641 * Accept an object, and return a string.
20644 * object - {Object} Object to be serialized
20647 * {String} A string representation of the object.
20649 write: function(object) {
20650 throw new Error('Write not implemented.');
20653 CLASS_NAME: "OpenLayers.Format"
20655 /* ======================================================================
20656 OpenLayers/Format/XML.js
20657 ====================================================================== */
20659 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
20660 * full list of contributors). Published under the 2-clause BSD license.
20661 * See license.txt in the OpenLayers distribution or repository for the
20662 * full text of the license. */
20665 * @requires OpenLayers/Format.js
20669 * Class: OpenLayers.Format.XML
20670 * Read and write XML. For cross-browser XML generation, use methods on an
20671 * instance of the XML format class instead of on <code>document<end>.
20672 * The DOM creation and traversing methods exposed here all mimic the
20673 * W3C XML DOM methods. Create a new parser with the
20674 * <OpenLayers.Format.XML> constructor.
20677 * - <OpenLayers.Format>
20679 OpenLayers.Format.XML = OpenLayers.Class(OpenLayers.Format, {
20682 * Property: namespaces
20683 * {Object} Mapping of namespace aliases to namespace URIs. Properties
20684 * of this object should not be set individually. Read-only. All
20685 * XML subclasses should have their own namespaces object. Use
20686 * <setNamespace> to add or set a namespace alias after construction.
20691 * Property: namespaceAlias
20692 * {Object} Mapping of namespace URI to namespace alias. This object
20693 * is read-only. Use <setNamespace> to add or set a namespace alias.
20695 namespaceAlias: null,
20698 * Property: defaultPrefix
20699 * {String} The default namespace alias for creating element nodes.
20701 defaultPrefix: null,
20704 * Property: readers
20705 * Contains public functions, grouped by namespace prefix, that will
20706 * be applied when a namespaced node is found matching the function
20707 * name. The function will be applied in the scope of this parser
20708 * with two arguments: the node being read and a context object passed
20714 * Property: writers
20715 * As a compliment to the <readers> property, this structure contains public
20716 * writing functions grouped by namespace alias and named like the
20717 * node names they produce.
20723 * {XMLDom} If this browser uses ActiveX, this will be set to a XMLDOM
20724 * object. It is not intended to be a browser sniffing property.
20725 * Instead, the xmldom property is used instead of <code>document<end>
20726 * where namespaced node creation methods are not supported. In all
20727 * other browsers, this remains null.
20732 * Constructor: OpenLayers.Format.XML
20733 * Construct an XML parser. The parser is used to read and write XML.
20734 * Reading XML from a string returns a DOM element. Writing XML from
20735 * a DOM element returns a string.
20738 * options - {Object} Optional object whose properties will be set on
20741 initialize: function(options) {
20742 if(window.ActiveXObject) {
20743 this.xmldom = new ActiveXObject("Microsoft.XMLDOM");
20745 OpenLayers.Format.prototype.initialize.apply(this, [options]);
20746 // clone the namespace object and set all namespace aliases
20747 this.namespaces = OpenLayers.Util.extend({}, this.namespaces);
20748 this.namespaceAlias = {};
20749 for(var alias in this.namespaces) {
20750 this.namespaceAlias[this.namespaces[alias]] = alias;
20755 * APIMethod: destroy
20758 destroy: function() {
20759 this.xmldom = null;
20760 OpenLayers.Format.prototype.destroy.apply(this, arguments);
20764 * Method: setNamespace
20765 * Set a namespace alias and URI for the format.
20768 * alias - {String} The namespace alias (prefix).
20769 * uri - {String} The namespace URI.
20771 setNamespace: function(alias, uri) {
20772 this.namespaces[alias] = uri;
20773 this.namespaceAlias[uri] = alias;
20778 * Deserialize a XML string and return a DOM node.
20781 * text - {String} A XML string
20784 * {DOMElement} A DOM node
20786 read: function(text) {
20787 var index = text.indexOf('<');
20789 text = text.substring(index);
20791 var node = OpenLayers.Util.Try(
20792 OpenLayers.Function.bind((
20796 * Since we want to be able to call this method on the prototype
20797 * itself, this.xmldom may not exist even if in IE.
20799 if(window.ActiveXObject && !this.xmldom) {
20800 xmldom = new ActiveXObject("Microsoft.XMLDOM");
20802 xmldom = this.xmldom;
20805 xmldom.loadXML(text);
20810 return new DOMParser().parseFromString(text, 'text/xml');
20813 var req = new XMLHttpRequest();
20814 req.open("GET", "data:" + "text/xml" +
20815 ";charset=utf-8," + encodeURIComponent(text), false);
20816 if(req.overrideMimeType) {
20817 req.overrideMimeType("text/xml");
20820 return req.responseXML;
20824 if(this.keepData) {
20833 * Serialize a DOM node into a XML string.
20836 * node - {DOMElement} A DOM node.
20839 * {String} The XML string representation of the input node.
20841 write: function(node) {
20846 var serializer = new XMLSerializer();
20847 if (node.nodeType == 1) {
20848 // Add nodes to a document before serializing. Everything else
20849 // is serialized as is. This may need more work. See #1218 .
20850 var doc = document.implementation.createDocument("", "", null);
20851 if (doc.importNode) {
20852 node = doc.importNode(node, true);
20854 doc.appendChild(node);
20855 data = serializer.serializeToString(doc);
20857 data = serializer.serializeToString(node);
20864 * APIMethod: createElementNS
20865 * Create a new element with namespace. This node can be appended to
20866 * another node with the standard node.appendChild method. For
20867 * cross-browser support, this method must be used instead of
20868 * document.createElementNS.
20871 * uri - {String} Namespace URI for the element.
20872 * name - {String} The qualified name of the element (prefix:localname).
20875 * {Element} A DOM element with namespace.
20877 createElementNS: function(uri, name) {
20880 if(typeof uri == "string") {
20881 element = this.xmldom.createNode(1, name, uri);
20883 element = this.xmldom.createNode(1, name, "");
20886 element = document.createElementNS(uri, name);
20892 * APIMethod: createDocumentFragment
20893 * Create a document fragment node that can be appended to another node
20894 * created by createElementNS. This will call
20895 * document.createDocumentFragment outside of IE. In IE, the ActiveX
20896 * object's createDocumentFragment method is used.
20899 * {Element} A document fragment.
20901 createDocumentFragment: function() {
20904 element = this.xmldom.createDocumentFragment();
20906 element = document.createDocumentFragment();
20912 * APIMethod: createTextNode
20913 * Create a text node. This node can be appended to another node with
20914 * the standard node.appendChild method. For cross-browser support,
20915 * this method must be used instead of document.createTextNode.
20918 * text - {String} The text of the node.
20921 * {DOMElement} A DOM text node.
20923 createTextNode: function(text) {
20925 if (typeof text !== "string") {
20926 text = String(text);
20929 node = this.xmldom.createTextNode(text);
20931 node = document.createTextNode(text);
20937 * APIMethod: getElementsByTagNameNS
20938 * Get a list of elements on a node given the namespace URI and local name.
20939 * To return all nodes in a given namespace, use '*' for the name
20940 * argument. To return all nodes of a given (local) name, regardless
20941 * of namespace, use '*' for the uri argument.
20944 * node - {Element} Node on which to search for other nodes.
20945 * uri - {String} Namespace URI.
20946 * name - {String} Local name of the tag (without the prefix).
20949 * {NodeList} A node list or array of elements.
20951 getElementsByTagNameNS: function(node, uri, name) {
20953 if(node.getElementsByTagNameNS) {
20954 elements = node.getElementsByTagNameNS(uri, name);
20956 // brute force method
20957 var allNodes = node.getElementsByTagName("*");
20958 var potentialNode, fullName;
20959 for(var i=0, len=allNodes.length; i<len; ++i) {
20960 potentialNode = allNodes[i];
20961 fullName = (potentialNode.prefix) ?
20962 (potentialNode.prefix + ":" + name) : name;
20963 if((name == "*") || (fullName == potentialNode.nodeName)) {
20964 if((uri == "*") || (uri == potentialNode.namespaceURI)) {
20965 elements.push(potentialNode);
20974 * APIMethod: getAttributeNodeNS
20975 * Get an attribute node given the namespace URI and local name.
20978 * node - {Element} Node on which to search for attribute nodes.
20979 * uri - {String} Namespace URI.
20980 * name - {String} Local name of the attribute (without the prefix).
20983 * {DOMElement} An attribute node or null if none found.
20985 getAttributeNodeNS: function(node, uri, name) {
20986 var attributeNode = null;
20987 if(node.getAttributeNodeNS) {
20988 attributeNode = node.getAttributeNodeNS(uri, name);
20990 var attributes = node.attributes;
20991 var potentialNode, fullName;
20992 for(var i=0, len=attributes.length; i<len; ++i) {
20993 potentialNode = attributes[i];
20994 if(potentialNode.namespaceURI == uri) {
20995 fullName = (potentialNode.prefix) ?
20996 (potentialNode.prefix + ":" + name) : name;
20997 if(fullName == potentialNode.nodeName) {
20998 attributeNode = potentialNode;
21004 return attributeNode;
21008 * APIMethod: getAttributeNS
21009 * Get an attribute value given the namespace URI and local name.
21012 * node - {Element} Node on which to search for an attribute.
21013 * uri - {String} Namespace URI.
21014 * name - {String} Local name of the attribute (without the prefix).
21017 * {String} An attribute value or and empty string if none found.
21019 getAttributeNS: function(node, uri, name) {
21020 var attributeValue = "";
21021 if(node.getAttributeNS) {
21022 attributeValue = node.getAttributeNS(uri, name) || "";
21024 var attributeNode = this.getAttributeNodeNS(node, uri, name);
21025 if(attributeNode) {
21026 attributeValue = attributeNode.nodeValue;
21029 return attributeValue;
21033 * APIMethod: getChildValue
21034 * Get the textual value of the node if it exists, or return an
21035 * optional default string. Returns an empty string if no first child
21036 * exists and no default value is supplied.
21039 * node - {DOMElement} The element used to look for a first child value.
21040 * def - {String} Optional string to return in the event that no
21041 * first child value exists.
21044 * {String} The value of the first child of the given node.
21046 getChildValue: function(node, def) {
21047 var value = def || "";
21049 for(var child=node.firstChild; child; child=child.nextSibling) {
21050 switch(child.nodeType) {
21051 case 3: // text node
21052 case 4: // cdata section
21053 value += child.nodeValue;
21061 * APIMethod: isSimpleContent
21062 * Test if the given node has only simple content (i.e. no child element
21066 * node - {DOMElement} An element node.
21069 * {Boolean} The node has no child element nodes (nodes of type 1).
21071 isSimpleContent: function(node) {
21073 for(var child=node.firstChild; child; child=child.nextSibling) {
21074 if(child.nodeType === 1) {
21083 * APIMethod: contentType
21084 * Determine the content type for a given node.
21087 * node - {DOMElement}
21090 * {Integer} One of OpenLayers.Format.XML.CONTENT_TYPE.{EMPTY,SIMPLE,COMPLEX,MIXED}
21091 * if the node has no, simple, complex, or mixed content.
21093 contentType: function(node) {
21094 var simple = false,
21097 var type = OpenLayers.Format.XML.CONTENT_TYPE.EMPTY;
21099 for(var child=node.firstChild; child; child=child.nextSibling) {
21100 switch(child.nodeType) {
21109 if(complex && simple) {
21114 if(complex && simple) {
21115 type = OpenLayers.Format.XML.CONTENT_TYPE.MIXED;
21116 } else if(complex) {
21117 return OpenLayers.Format.XML.CONTENT_TYPE.COMPLEX;
21118 } else if(simple) {
21119 return OpenLayers.Format.XML.CONTENT_TYPE.SIMPLE;
21125 * APIMethod: hasAttributeNS
21126 * Determine whether a node has a particular attribute matching the given
21127 * name and namespace.
21130 * node - {Element} Node on which to search for an attribute.
21131 * uri - {String} Namespace URI.
21132 * name - {String} Local name of the attribute (without the prefix).
21135 * {Boolean} The node has an attribute matching the name and namespace.
21137 hasAttributeNS: function(node, uri, name) {
21139 if(node.hasAttributeNS) {
21140 found = node.hasAttributeNS(uri, name);
21142 found = !!this.getAttributeNodeNS(node, uri, name);
21148 * APIMethod: setAttributeNS
21149 * Adds a new attribute or changes the value of an attribute with the given
21150 * namespace and name.
21153 * node - {Element} Element node on which to set the attribute.
21154 * uri - {String} Namespace URI for the attribute.
21155 * name - {String} Qualified name (prefix:localname) for the attribute.
21156 * value - {String} Attribute value.
21158 setAttributeNS: function(node, uri, name, value) {
21159 if(node.setAttributeNS) {
21160 node.setAttributeNS(uri, name, value);
21164 var attribute = node.ownerDocument.createNode(
21167 attribute.nodeValue = value;
21168 node.setAttributeNode(attribute);
21170 node.setAttribute(name, value);
21173 throw "setAttributeNS not implemented";
21179 * Method: createElementNSPlus
21180 * Shorthand for creating namespaced elements with optional attributes and
21181 * child text nodes.
21184 * name - {String} The qualified node name.
21185 * options - {Object} Optional object for node configuration.
21188 * uri - {String} Optional namespace uri for the element - supply a prefix
21189 * instead if the namespace uri is a property of the format's namespace
21191 * attributes - {Object} Optional attributes to be set using the
21192 * <setAttributes> method.
21193 * value - {String} Optional text to be appended as a text node.
21196 * {Element} An element node.
21198 createElementNSPlus: function(name, options) {
21199 options = options || {};
21200 // order of prefix preference
21201 // 1. in the uri option
21202 // 2. in the prefix option
21203 // 3. in the qualified name
21204 // 4. from the defaultPrefix
21205 var uri = options.uri || this.namespaces[options.prefix];
21207 var loc = name.indexOf(":");
21208 uri = this.namespaces[name.substring(0, loc)];
21211 uri = this.namespaces[this.defaultPrefix];
21213 var node = this.createElementNS(uri, name);
21214 if(options.attributes) {
21215 this.setAttributes(node, options.attributes);
21217 var value = options.value;
21218 if(value != null) {
21219 node.appendChild(this.createTextNode(value));
21225 * Method: setAttributes
21226 * Set multiple attributes given key value pairs from an object.
21229 * node - {Element} An element node.
21230 * obj - {Object || Array} An object whose properties represent attribute
21231 * names and values represent attribute values. If an attribute name
21232 * is a qualified name ("prefix:local"), the prefix will be looked up
21233 * in the parsers {namespaces} object. If the prefix is found,
21234 * setAttributeNS will be used instead of setAttribute.
21236 setAttributes: function(node, obj) {
21238 for(var name in obj) {
21239 if(obj[name] != null && obj[name].toString) {
21240 value = obj[name].toString();
21241 // check for qualified attribute name ("prefix:local")
21242 uri = this.namespaces[name.substring(0, name.indexOf(":"))] || null;
21243 this.setAttributeNS(node, uri, name, value);
21250 * Shorthand for applying one of the named readers given the node
21251 * namespace and local name. Readers take two args (node, obj) and
21252 * generally extend or modify the second.
21255 * node - {DOMElement} The node to be read (required).
21256 * obj - {Object} The object to be modified (optional).
21259 * {Object} The input object, modified (or a new one if none was provided).
21261 readNode: function(node, obj) {
21265 var group = this.readers[node.namespaceURI ? this.namespaceAlias[node.namespaceURI]: this.defaultPrefix];
21267 var local = node.localName || node.nodeName.split(":").pop();
21268 var reader = group[local] || group["*"];
21270 reader.apply(this, [node, obj]);
21277 * Method: readChildNodes
21278 * Shorthand for applying the named readers to all children of a node.
21279 * For each child of type 1 (element), <readSelf> is called.
21282 * node - {DOMElement} The node to be read (required).
21283 * obj - {Object} The object to be modified (optional).
21286 * {Object} The input object, modified.
21288 readChildNodes: function(node, obj) {
21292 var children = node.childNodes;
21294 for(var i=0, len=children.length; i<len; ++i) {
21295 child = children[i];
21296 if(child.nodeType == 1) {
21297 this.readNode(child, obj);
21304 * Method: writeNode
21305 * Shorthand for applying one of the named writers and appending the
21306 * results to a node. If a qualified name is not provided for the
21307 * second argument (and a local name is used instead), the namespace
21308 * of the parent node will be assumed.
21311 * name - {String} The name of a node to generate. If a qualified name
21312 * (e.g. "pre:Name") is used, the namespace prefix is assumed to be
21313 * in the <writers> group. If a local name is used (e.g. "Name") then
21314 * the namespace of the parent is assumed. If a local name is used
21315 * and no parent is supplied, then the default namespace is assumed.
21316 * obj - {Object} Structure containing data for the writer.
21317 * parent - {DOMElement} Result will be appended to this node. If no parent
21318 * is supplied, the node will not be appended to anything.
21321 * {DOMElement} The child node.
21323 writeNode: function(name, obj, parent) {
21325 var split = name.indexOf(":");
21327 prefix = name.substring(0, split);
21328 local = name.substring(split + 1);
21331 prefix = this.namespaceAlias[parent.namespaceURI];
21333 prefix = this.defaultPrefix;
21337 var child = this.writers[prefix][local].apply(this, [obj]);
21339 parent.appendChild(child);
21345 * APIMethod: getChildEl
21346 * Get the first child element. Optionally only return the first child
21347 * if it matches the given name and namespace URI.
21350 * node - {DOMElement} The parent node.
21351 * name - {String} Optional node name (local) to search for.
21352 * uri - {String} Optional namespace URI to search for.
21355 * {DOMElement} The first child. Returns null if no element is found, if
21356 * something significant besides an element is found, or if the element
21357 * found does not match the optional name and uri.
21359 getChildEl: function(node, name, uri) {
21360 return node && this.getThisOrNextEl(node.firstChild, name, uri);
21364 * APIMethod: getNextEl
21365 * Get the next sibling element. Optionally get the first sibling only
21366 * if it matches the given local name and namespace URI.
21369 * node - {DOMElement} The node.
21370 * name - {String} Optional local name of the sibling to search for.
21371 * uri - {String} Optional namespace URI of the sibling to search for.
21374 * {DOMElement} The next sibling element. Returns null if no element is
21375 * found, something significant besides an element is found, or the
21376 * found element does not match the optional name and uri.
21378 getNextEl: function(node, name, uri) {
21379 return node && this.getThisOrNextEl(node.nextSibling, name, uri);
21383 * Method: getThisOrNextEl
21384 * Return this node or the next element node. Optionally get the first
21385 * sibling with the given local name or namespace URI.
21388 * node - {DOMElement} The node.
21389 * name - {String} Optional local name of the sibling to search for.
21390 * uri - {String} Optional namespace URI of the sibling to search for.
21393 * {DOMElement} The next sibling element. Returns null if no element is
21394 * found, something significant besides an element is found, or the
21395 * found element does not match the query.
21397 getThisOrNextEl: function(node, name, uri) {
21398 outer: for(var sibling=node; sibling; sibling=sibling.nextSibling) {
21399 switch(sibling.nodeType) {
21401 if((!name || name === (sibling.localName || sibling.nodeName.split(":").pop())) &&
21402 (!uri || uri === sibling.namespaceURI)) {
21409 if(/^\s*$/.test(sibling.nodeValue)) {
21413 case 6: // ENTITY_NODE
21414 case 12: // NOTATION_NODE
21415 case 10: // DOCUMENT_TYPE_NODE
21416 case 11: // DOCUMENT_FRAGMENT_NODE
21419 } // ignore comments and processing instructions
21421 return sibling || null;
21425 * APIMethod: lookupNamespaceURI
21426 * Takes a prefix and returns the namespace URI associated with it on the given
21427 * node if found (and null if not). Supplying null for the prefix will
21428 * return the default namespace.
21430 * For browsers that support it, this calls the native lookupNamesapceURI
21431 * function. In other browsers, this is an implementation of
21432 * http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI.
21434 * For browsers that don't support the attribute.ownerElement property, this
21435 * method cannot be called on attribute nodes.
21438 * node - {DOMElement} The node from which to start looking.
21439 * prefix - {String} The prefix to lookup or null to lookup the default namespace.
21442 * {String} The namespace URI for the given prefix. Returns null if the prefix
21443 * cannot be found or the node is the wrong type.
21445 lookupNamespaceURI: function(node, prefix) {
21448 if(node.lookupNamespaceURI) {
21449 uri = node.lookupNamespaceURI(prefix);
21451 outer: switch(node.nodeType) {
21452 case 1: // ELEMENT_NODE
21453 if(node.namespaceURI !== null && node.prefix === prefix) {
21454 uri = node.namespaceURI;
21457 var len = node.attributes.length;
21460 for(var i=0; i<len; ++i) {
21461 attr = node.attributes[i];
21462 if(attr.prefix === "xmlns" && attr.name === "xmlns:" + prefix) {
21463 uri = attr.value || null;
21465 } else if(attr.name === "xmlns" && prefix === null) {
21466 uri = attr.value || null;
21471 uri = this.lookupNamespaceURI(node.parentNode, prefix);
21473 case 2: // ATTRIBUTE_NODE
21474 uri = this.lookupNamespaceURI(node.ownerElement, prefix);
21476 case 9: // DOCUMENT_NODE
21477 uri = this.lookupNamespaceURI(node.documentElement, prefix);
21479 case 6: // ENTITY_NODE
21480 case 12: // NOTATION_NODE
21481 case 10: // DOCUMENT_TYPE_NODE
21482 case 11: // DOCUMENT_FRAGMENT_NODE
21485 // TEXT_NODE (3), CDATA_SECTION_NODE (4), ENTITY_REFERENCE_NODE (5),
21486 // PROCESSING_INSTRUCTION_NODE (7), COMMENT_NODE (8)
21487 uri = this.lookupNamespaceURI(node.parentNode, prefix);
21496 * Method: getXMLDoc
21497 * Get an XML document for nodes that are not supported in HTML (e.g.
21498 * createCDATASection). On IE, this will either return an existing or
21499 * create a new <xmldom> on the instance. On other browsers, this will
21500 * either return an existing or create a new shared document (see
21501 * <OpenLayers.Format.XML.document>).
21506 getXMLDoc: function() {
21507 if (!OpenLayers.Format.XML.document && !this.xmldom) {
21508 if (document.implementation && document.implementation.createDocument) {
21509 OpenLayers.Format.XML.document =
21510 document.implementation.createDocument("", "", null);
21511 } else if (!this.xmldom && window.ActiveXObject) {
21512 this.xmldom = new ActiveXObject("Microsoft.XMLDOM");
21515 return OpenLayers.Format.XML.document || this.xmldom;
21518 CLASS_NAME: "OpenLayers.Format.XML"
21522 OpenLayers.Format.XML.CONTENT_TYPE = {EMPTY: 0, SIMPLE: 1, COMPLEX: 2, MIXED: 3};
21525 * APIFunction: OpenLayers.Format.XML.lookupNamespaceURI
21526 * Takes a prefix and returns the namespace URI associated with it on the given
21527 * node if found (and null if not). Supplying null for the prefix will
21528 * return the default namespace.
21530 * For browsers that support it, this calls the native lookupNamesapceURI
21531 * function. In other browsers, this is an implementation of
21532 * http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI.
21534 * For browsers that don't support the attribute.ownerElement property, this
21535 * method cannot be called on attribute nodes.
21538 * node - {DOMElement} The node from which to start looking.
21539 * prefix - {String} The prefix to lookup or null to lookup the default namespace.
21542 * {String} The namespace URI for the given prefix. Returns null if the prefix
21543 * cannot be found or the node is the wrong type.
21545 OpenLayers.Format.XML.lookupNamespaceURI = OpenLayers.Function.bind(
21546 OpenLayers.Format.XML.prototype.lookupNamespaceURI,
21547 OpenLayers.Format.XML.prototype
21551 * Property: OpenLayers.Format.XML.document
21552 * {XMLDocument} XML document to reuse for creating non-HTML compliant nodes,
21553 * like document.createCDATASection.
21555 OpenLayers.Format.XML.document = null;
21556 /* ======================================================================
21557 OpenLayers/Format/OGCExceptionReport.js
21558 ====================================================================== */
21560 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
21561 * full list of contributors). Published under the 2-clause BSD license.
21562 * See license.txt in the OpenLayers distribution or repository for the
21563 * full text of the license. */
21566 * @requires OpenLayers/Format/XML.js
21570 * Class: OpenLayers.Format.OGCExceptionReport
21571 * Class to read exception reports for various OGC services and versions.
21574 * - <OpenLayers.Format.XML>
21576 OpenLayers.Format.OGCExceptionReport = OpenLayers.Class(OpenLayers.Format.XML, {
21579 * Property: namespaces
21580 * {Object} Mapping of namespace aliases to namespace URIs.
21583 ogc: "http://www.opengis.net/ogc"
21587 * Property: regExes
21588 * Compiled regular expressions for manipulating strings.
21591 trimSpace: (/^\s*|\s*$/g),
21592 removeSpace: (/\s*/g),
21593 splitSpace: (/\s+/),
21594 trimComma: (/\s*,\s*/g)
21598 * Property: defaultPrefix
21600 defaultPrefix: "ogc",
21603 * Constructor: OpenLayers.Format.OGCExceptionReport
21604 * Create a new parser for OGC exception reports.
21607 * options - {Object} An optional object whose properties will be set on
21613 * Read OGC exception report data from a string, and return an object with
21614 * information about the exceptions.
21617 * data - {String} or {DOMElement} data to read/parse.
21620 * {Object} Information about the exceptions that occurred.
21622 read: function(data) {
21624 if(typeof data == "string") {
21625 data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
21627 var root = data.documentElement;
21628 var exceptionInfo = {exceptionReport: null};
21630 this.readChildNodes(data, exceptionInfo);
21631 if (exceptionInfo.exceptionReport === null) {
21632 // fall-back to OWSCommon since this is a common output format for exceptions
21633 // we cannot easily use the ows readers directly since they differ for 1.0 and 1.1
21634 exceptionInfo = new OpenLayers.Format.OWSCommon().read(data);
21637 return exceptionInfo;
21641 * Property: readers
21642 * Contains public functions, grouped by namespace prefix, that will
21643 * be applied when a namespaced node is found matching the function
21644 * name. The function will be applied in the scope of this parser
21645 * with two arguments: the node being read and a context object passed
21650 "ServiceExceptionReport": function(node, obj) {
21651 obj.exceptionReport = {exceptions: []};
21652 this.readChildNodes(node, obj.exceptionReport);
21654 "ServiceException": function(node, exceptionReport) {
21656 code: node.getAttribute("code"),
21657 locator: node.getAttribute("locator"),
21658 text: this.getChildValue(node)
21660 exceptionReport.exceptions.push(exception);
21665 CLASS_NAME: "OpenLayers.Format.OGCExceptionReport"
21668 /* ======================================================================
21669 OpenLayers/Format/XML/VersionedOGC.js
21670 ====================================================================== */
21672 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
21673 * full list of contributors). Published under the 2-clause BSD license.
21674 * See license.txt in the OpenLayers distribution or repository for the
21675 * full text of the license. */
21678 * @requires OpenLayers/Format/XML.js
21679 * @requires OpenLayers/Format/OGCExceptionReport.js
21683 * Class: OpenLayers.Format.XML.VersionedOGC
21684 * Base class for versioned formats, i.e. a format which supports multiple
21687 * To enable checking if parsing succeeded, you will need to define a property
21688 * called errorProperty on the parser you want to check. The parser will then
21689 * check the returned object to see if that property is present. If it is, it
21690 * assumes the parsing was successful. If it is not present (or is null), it will
21691 * pass the document through an OGCExceptionReport parser.
21693 * If errorProperty is undefined for the parser, this error checking mechanism
21694 * will be disabled.
21699 * - <OpenLayers.Format.XML>
21701 OpenLayers.Format.XML.VersionedOGC = OpenLayers.Class(OpenLayers.Format.XML, {
21704 * APIProperty: defaultVersion
21705 * {String} Version number to assume if none found.
21707 defaultVersion: null,
21710 * APIProperty: version
21711 * {String} Specify a version string if one is known.
21716 * APIProperty: profile
21717 * {String} If provided, use a custom profile.
21722 * APIProperty: allowFallback
21723 * {Boolean} If a profiled parser cannot be found for the returned version,
21724 * use a non-profiled parser as the fallback. Application code using this
21725 * should take into account that the return object structure might be
21726 * missing the specifics of the profile. Defaults to false.
21728 allowFallback: false,
21732 * {String} The name of this parser, this is the part of the CLASS_NAME
21733 * except for "OpenLayers.Format."
21738 * APIProperty: stringifyOutput
21739 * {Boolean} If true, write will return a string otherwise a DOMElement.
21740 * Default is false.
21742 stringifyOutput: false,
21746 * {Object} Instance of the versioned parser. Cached for multiple read and
21747 * write calls of the same version.
21752 * Constructor: OpenLayers.Format.XML.VersionedOGC.
21756 * options - {Object} Optional object whose properties will be set on
21759 initialize: function(options) {
21760 OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
21761 var className = this.CLASS_NAME;
21762 this.name = className.substring(className.lastIndexOf(".")+1);
21766 * Method: getVersion
21767 * Returns the version to use. Subclasses can override this function
21768 * if a different version detection is needed.
21771 * root - {DOMElement}
21772 * options - {Object} Optional configuration object.
21775 * {String} The version to use.
21777 getVersion: function(root, options) {
21781 version = this.version;
21783 version = root.getAttribute("version");
21785 version = this.defaultVersion;
21789 version = (options && options.version) ||
21790 this.version || this.defaultVersion;
21796 * Method: getParser
21797 * Get an instance of the cached parser if available, otherwise create one.
21800 * version - {String}
21803 * {<OpenLayers.Format>}
21805 getParser: function(version) {
21806 version = version || this.defaultVersion;
21807 var profile = this.profile ? "_" + this.profile : "";
21808 if(!this.parser || this.parser.VERSION != version) {
21809 var format = OpenLayers.Format[this.name][
21810 "v" + version.replace(/\./g, "_") + profile
21813 if (profile !== "" && this.allowFallback) {
21814 // fallback to the non-profiled version of the parser
21816 format = OpenLayers.Format[this.name][
21817 "v" + version.replace(/\./g, "_")
21821 throw "Can't find a " + this.name + " parser for version " +
21825 this.parser = new format(this.options);
21827 return this.parser;
21832 * Write a document.
21835 * obj - {Object} An object representing the document.
21836 * options - {Object} Optional configuration object.
21839 * {String} The document as a string
21841 write: function(obj, options) {
21842 var version = this.getVersion(null, options);
21843 this.parser = this.getParser(version);
21844 var root = this.parser.write(obj, options);
21845 if (this.stringifyOutput === false) {
21848 return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
21854 * Read a doc and return an object representing the document.
21857 * data - {String | DOMElement} Data to read.
21858 * options - {Object} Options for the reader.
21861 * {Object} An object representing the document.
21863 read: function(data, options) {
21864 if(typeof data == "string") {
21865 data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
21867 var root = data.documentElement;
21868 var version = this.getVersion(root);
21869 this.parser = this.getParser(version); // Select the parser
21870 var obj = this.parser.read(data, options); // Parse the data
21872 var errorProperty = this.parser.errorProperty || null;
21873 if (errorProperty !== null && obj[errorProperty] === undefined) {
21874 // an error must have happened, so parse it and report back
21875 var format = new OpenLayers.Format.OGCExceptionReport();
21876 obj.error = format.read(data);
21878 obj.version = version;
21882 CLASS_NAME: "OpenLayers.Format.XML.VersionedOGC"
21884 /* ======================================================================
21885 OpenLayers/Filter/FeatureId.js
21886 ====================================================================== */
21888 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
21889 * full list of contributors). Published under the 2-clause BSD license.
21890 * See license.txt in the OpenLayers distribution or repository for the
21891 * full text of the license. */
21895 * @requires OpenLayers/Filter.js
21899 * Class: OpenLayers.Filter.FeatureId
21900 * This class represents a ogc:FeatureId Filter, as being used for rule-based SLD
21904 * - <OpenLayers.Filter>
21906 OpenLayers.Filter.FeatureId = OpenLayers.Class(OpenLayers.Filter, {
21909 * APIProperty: fids
21910 * {Array(String)} Feature Ids to evaluate this rule against.
21911 * To be passed inside the params object.
21917 * {String} Type to identify this filter.
21922 * Constructor: OpenLayers.Filter.FeatureId
21923 * Creates an ogc:FeatureId rule.
21926 * options - {Object} An optional object with properties to set on the
21930 * {<OpenLayers.Filter.FeatureId>}
21932 initialize: function(options) {
21934 OpenLayers.Filter.prototype.initialize.apply(this, [options]);
21938 * APIMethod: evaluate
21939 * evaluates this rule for a specific feature
21942 * feature - {<OpenLayers.Feature>} feature to apply the rule to.
21943 * For vector features, the check is run against the fid,
21944 * for plain features against the id.
21947 * {Boolean} true if the rule applies, false if it does not
21949 evaluate: function(feature) {
21950 for (var i=0, len=this.fids.length; i<len; i++) {
21951 var fid = feature.fid || feature.id;
21952 if (fid == this.fids[i]) {
21961 * Clones this filter.
21964 * {<OpenLayers.Filter.FeatureId>} Clone of this filter.
21966 clone: function() {
21967 var filter = new OpenLayers.Filter.FeatureId();
21968 OpenLayers.Util.extend(filter, this);
21969 filter.fids = this.fids.slice();
21973 CLASS_NAME: "OpenLayers.Filter.FeatureId"
21975 /* ======================================================================
21976 OpenLayers/Filter/Logical.js
21977 ====================================================================== */
21979 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
21980 * full list of contributors). Published under the 2-clause BSD license.
21981 * See license.txt in the OpenLayers distribution or repository for the
21982 * full text of the license. */
21986 * @requires OpenLayers/Filter.js
21990 * Class: OpenLayers.Filter.Logical
21991 * This class represents ogc:And, ogc:Or and ogc:Not rules.
21994 * - <OpenLayers.Filter>
21996 OpenLayers.Filter.Logical = OpenLayers.Class(OpenLayers.Filter, {
21999 * APIProperty: filters
22000 * {Array(<OpenLayers.Filter>)} Child filters for this filter.
22005 * APIProperty: type
22006 * {String} type of logical operator. Available types are:
22007 * - OpenLayers.Filter.Logical.AND = "&&";
22008 * - OpenLayers.Filter.Logical.OR = "||";
22009 * - OpenLayers.Filter.Logical.NOT = "!";
22014 * Constructor: OpenLayers.Filter.Logical
22015 * Creates a logical filter (And, Or, Not).
22018 * options - {Object} An optional object with properties to set on the
22022 * {<OpenLayers.Filter.Logical>}
22024 initialize: function(options) {
22026 OpenLayers.Filter.prototype.initialize.apply(this, [options]);
22030 * APIMethod: destroy
22031 * Remove reference to child filters.
22033 destroy: function() {
22034 this.filters = null;
22035 OpenLayers.Filter.prototype.destroy.apply(this);
22039 * APIMethod: evaluate
22040 * Evaluates this filter in a specific context.
22043 * context - {Object} Context to use in evaluating the filter. A vector
22044 * feature may also be provided to evaluate feature attributes in
22045 * comparison filters or geometries in spatial filters.
22048 * {Boolean} The filter applies.
22050 evaluate: function(context) {
22052 switch(this.type) {
22053 case OpenLayers.Filter.Logical.AND:
22054 for (i=0, len=this.filters.length; i<len; i++) {
22055 if (this.filters[i].evaluate(context) == false) {
22061 case OpenLayers.Filter.Logical.OR:
22062 for (i=0, len=this.filters.length; i<len; i++) {
22063 if (this.filters[i].evaluate(context) == true) {
22069 case OpenLayers.Filter.Logical.NOT:
22070 return (!this.filters[0].evaluate(context));
22077 * Clones this filter.
22080 * {<OpenLayers.Filter.Logical>} Clone of this filter.
22082 clone: function() {
22084 for(var i=0, len=this.filters.length; i<len; ++i) {
22085 filters.push(this.filters[i].clone());
22087 return new OpenLayers.Filter.Logical({
22093 CLASS_NAME: "OpenLayers.Filter.Logical"
22097 OpenLayers.Filter.Logical.AND = "&&";
22098 OpenLayers.Filter.Logical.OR = "||";
22099 OpenLayers.Filter.Logical.NOT = "!";
22100 /* ======================================================================
22101 OpenLayers/Filter/Comparison.js
22102 ====================================================================== */
22104 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
22105 * full list of contributors). Published under the 2-clause BSD license.
22106 * See license.txt in the OpenLayers distribution or repository for the
22107 * full text of the license. */
22110 * @requires OpenLayers/Filter.js
22114 * Class: OpenLayers.Filter.Comparison
22115 * This class represents a comparison filter.
22118 * - <OpenLayers.Filter>
22120 OpenLayers.Filter.Comparison = OpenLayers.Class(OpenLayers.Filter, {
22123 * APIProperty: type
22124 * {String} type: type of the comparison. This is one of
22125 * - OpenLayers.Filter.Comparison.EQUAL_TO = "==";
22126 * - OpenLayers.Filter.Comparison.NOT_EQUAL_TO = "!=";
22127 * - OpenLayers.Filter.Comparison.LESS_THAN = "<";
22128 * - OpenLayers.Filter.Comparison.GREATER_THAN = ">";
22129 * - OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = "<=";
22130 * - OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">=";
22131 * - OpenLayers.Filter.Comparison.BETWEEN = "..";
22132 * - OpenLayers.Filter.Comparison.LIKE = "~";
22133 * - OpenLayers.Filter.Comparison.IS_NULL = "NULL";
22138 * APIProperty: property
22140 * name of the context property to compare
22145 * APIProperty: value
22146 * {Number} or {String}
22147 * comparison value for binary comparisons. In the case of a String, this
22148 * can be a combination of text and propertyNames in the form
22149 * "literal ${propertyName}"
22154 * Property: matchCase
22155 * {Boolean} Force case sensitive searches for EQUAL_TO and NOT_EQUAL_TO
22156 * comparisons. The Filter Encoding 1.1 specification added a matchCase
22157 * attribute to ogc:PropertyIsEqualTo and ogc:PropertyIsNotEqualTo
22158 * elements. This property will be serialized with those elements only
22159 * if using the v1.1.0 filter format. However, when evaluating filters
22160 * here, the matchCase property will always be respected (for EQUAL_TO
22161 * and NOT_EQUAL_TO). Default is true.
22166 * APIProperty: lowerBoundary
22167 * {Number} or {String}
22168 * lower boundary for between comparisons. In the case of a String, this
22169 * can be a combination of text and propertyNames in the form
22170 * "literal ${propertyName}"
22172 lowerBoundary: null,
22175 * APIProperty: upperBoundary
22176 * {Number} or {String}
22177 * upper boundary for between comparisons. In the case of a String, this
22178 * can be a combination of text and propertyNames in the form
22179 * "literal ${propertyName}"
22181 upperBoundary: null,
22184 * Constructor: OpenLayers.Filter.Comparison
22185 * Creates a comparison rule.
22188 * options - {Object} An optional object with properties to set on the
22192 * {<OpenLayers.Filter.Comparison>}
22194 initialize: function(options) {
22195 OpenLayers.Filter.prototype.initialize.apply(this, [options]);
22196 // since matchCase on PropertyIsLike is not schema compliant, we only
22197 // want to use this if explicitly asked for
22198 if (this.type === OpenLayers.Filter.Comparison.LIKE
22199 && options.matchCase === undefined) {
22200 this.matchCase = null;
22205 * APIMethod: evaluate
22206 * Evaluates this filter in a specific context.
22209 * context - {Object} Context to use in evaluating the filter. If a vector
22210 * feature is provided, the feature.attributes will be used as context.
22213 * {Boolean} The filter applies.
22215 evaluate: function(context) {
22216 if (context instanceof OpenLayers.Feature.Vector) {
22217 context = context.attributes;
22219 var result = false;
22220 var got = context[this.property];
22222 switch(this.type) {
22223 case OpenLayers.Filter.Comparison.EQUAL_TO:
22225 if(!this.matchCase &&
22226 typeof got == "string" && typeof exp == "string") {
22227 result = (got.toUpperCase() == exp.toUpperCase());
22229 result = (got == exp);
22232 case OpenLayers.Filter.Comparison.NOT_EQUAL_TO:
22234 if(!this.matchCase &&
22235 typeof got == "string" && typeof exp == "string") {
22236 result = (got.toUpperCase() != exp.toUpperCase());
22238 result = (got != exp);
22241 case OpenLayers.Filter.Comparison.LESS_THAN:
22242 result = got < this.value;
22244 case OpenLayers.Filter.Comparison.GREATER_THAN:
22245 result = got > this.value;
22247 case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO:
22248 result = got <= this.value;
22250 case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO:
22251 result = got >= this.value;
22253 case OpenLayers.Filter.Comparison.BETWEEN:
22254 result = (got >= this.lowerBoundary) &&
22255 (got <= this.upperBoundary);
22257 case OpenLayers.Filter.Comparison.LIKE:
22258 var regexp = new RegExp(this.value, "gi");
22259 result = regexp.test(got);
22261 case OpenLayers.Filter.Comparison.IS_NULL:
22262 result = (got === null);
22269 * APIMethod: value2regex
22270 * Converts the value of this rule into a regular expression string,
22271 * according to the wildcard characters specified. This method has to
22272 * be called after instantiation of this class, if the value is not a
22273 * regular expression already.
22276 * wildCard - {Char} wildcard character in the above value, default
22278 * singleChar - {Char} single-character wildcard in the above value
22280 * escapeChar - {Char} escape character in the above value, default is
22284 * {String} regular expression string
22286 value2regex: function(wildCard, singleChar, escapeChar) {
22287 if (wildCard == ".") {
22288 throw new Error("'.' is an unsupported wildCard character for " +
22289 "OpenLayers.Filter.Comparison");
22293 // set UMN MapServer defaults for unspecified parameters
22294 wildCard = wildCard ? wildCard : "*";
22295 singleChar = singleChar ? singleChar : ".";
22296 escapeChar = escapeChar ? escapeChar : "!";
22298 this.value = this.value.replace(
22299 new RegExp("\\"+escapeChar+"(.|$)", "g"), "\\$1");
22300 this.value = this.value.replace(
22301 new RegExp("\\"+singleChar, "g"), ".");
22302 this.value = this.value.replace(
22303 new RegExp("\\"+wildCard, "g"), ".*");
22304 this.value = this.value.replace(
22305 new RegExp("\\\\.\\*", "g"), "\\"+wildCard);
22306 this.value = this.value.replace(
22307 new RegExp("\\\\\\.", "g"), "\\"+singleChar);
22313 * Method: regex2value
22314 * Convert the value of this rule from a regular expression string into an
22315 * ogc literal string using a wildCard of *, a singleChar of ., and an
22316 * escape of !. Leaves the <value> property unmodified.
22319 * {String} A string value.
22321 regex2value: function() {
22323 var value = this.value;
22325 // replace ! with !!
22326 value = value.replace(/!/g, "!!");
22328 // replace \. with !. (watching out for \\.)
22329 value = value.replace(/(\\)?\\\./g, function($0, $1) {
22330 return $1 ? $0 : "!.";
22333 // replace \* with #* (watching out for \\*)
22334 value = value.replace(/(\\)?\\\*/g, function($0, $1) {
22335 return $1 ? $0 : "!*";
22338 // replace \\ with \
22339 value = value.replace(/\\\\/g, "\\");
22341 // convert .* to * (the sequence #.* is not allowed)
22342 value = value.replace(/\.\*/g, "*");
22349 * Clones this filter.
22352 * {<OpenLayers.Filter.Comparison>} Clone of this filter.
22354 clone: function() {
22355 return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison(), this);
22358 CLASS_NAME: "OpenLayers.Filter.Comparison"
22362 OpenLayers.Filter.Comparison.EQUAL_TO = "==";
22363 OpenLayers.Filter.Comparison.NOT_EQUAL_TO = "!=";
22364 OpenLayers.Filter.Comparison.LESS_THAN = "<";
22365 OpenLayers.Filter.Comparison.GREATER_THAN = ">";
22366 OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = "<=";
22367 OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">=";
22368 OpenLayers.Filter.Comparison.BETWEEN = "..";
22369 OpenLayers.Filter.Comparison.LIKE = "~";
22370 OpenLayers.Filter.Comparison.IS_NULL = "NULL";
22371 /* ======================================================================
22372 OpenLayers/Format/Filter.js
22373 ====================================================================== */
22375 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
22376 * full list of contributors). Published under the 2-clause BSD license.
22377 * See license.txt in the OpenLayers distribution or repository for the
22378 * full text of the license. */
22381 * @requires OpenLayers/Format/XML/VersionedOGC.js
22382 * @requires OpenLayers/Filter/FeatureId.js
22383 * @requires OpenLayers/Filter/Logical.js
22384 * @requires OpenLayers/Filter/Comparison.js
22388 * Class: OpenLayers.Format.Filter
22389 * Read/Write ogc:Filter. Create a new instance with the <OpenLayers.Format.Filter>
22393 * - <OpenLayers.Format.XML.VersionedOGC>
22395 OpenLayers.Format.Filter = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
22398 * APIProperty: defaultVersion
22399 * {String} Version number to assume if none found. Default is "1.0.0".
22401 defaultVersion: "1.0.0",
22405 * Write an ogc:Filter given a filter object.
22408 * filter - {<OpenLayers.Filter>} An filter.
22409 * options - {Object} Optional configuration object.
22412 * {Elment} An ogc:Filter element node.
22417 * Read and Filter doc and return an object representing the Filter.
22420 * data - {String | DOMElement} Data to read.
22423 * {<OpenLayers.Filter>} A filter object.
22426 CLASS_NAME: "OpenLayers.Format.Filter"
22428 /* ======================================================================
22429 OpenLayers/Format/WFST.js
22430 ====================================================================== */
22432 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
22433 * full list of contributors). Published under the 2-clause BSD license.
22434 * See license.txt in the OpenLayers distribution or repository for the
22435 * full text of the license. */
22438 * @requires OpenLayers/Format.js
22442 * Function: OpenLayers.Format.WFST
22443 * Used to create a versioned WFS protocol. Default version is 1.0.0.
22446 * {<OpenLayers.Format>} A WFST format of the given version.
22448 OpenLayers.Format.WFST = function(options) {
22449 options = OpenLayers.Util.applyDefaults(
22450 options, OpenLayers.Format.WFST.DEFAULTS
22452 var cls = OpenLayers.Format.WFST["v"+options.version.replace(/\./g, "_")];
22454 throw "Unsupported WFST version: " + options.version;
22456 return new cls(options);
22460 * Constant: OpenLayers.Format.WFST.DEFAULTS
22461 * {Object} Default properties for the WFST format.
22463 OpenLayers.Format.WFST.DEFAULTS = {
22466 /* ======================================================================
22467 OpenLayers/Filter/Spatial.js
22468 ====================================================================== */
22470 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
22471 * full list of contributors). Published under the 2-clause BSD license.
22472 * See license.txt in the OpenLayers distribution or repository for the
22473 * full text of the license. */
22476 * @requires OpenLayers/Filter.js
22480 * Class: OpenLayers.Filter.Spatial
22481 * This class represents a spatial filter.
22482 * Currently implemented: BBOX, DWithin and Intersects
22485 * - <OpenLayers.Filter>
22487 OpenLayers.Filter.Spatial = OpenLayers.Class(OpenLayers.Filter, {
22490 * APIProperty: type
22491 * {String} Type of spatial filter.
22493 * The type should be one of:
22494 * - OpenLayers.Filter.Spatial.BBOX
22495 * - OpenLayers.Filter.Spatial.INTERSECTS
22496 * - OpenLayers.Filter.Spatial.DWITHIN
22497 * - OpenLayers.Filter.Spatial.WITHIN
22498 * - OpenLayers.Filter.Spatial.CONTAINS
22503 * APIProperty: property
22504 * {String} Name of the context property to compare.
22509 * APIProperty: value
22510 * {<OpenLayers.Bounds> || <OpenLayers.Geometry>} The bounds or geometry
22511 * to be used by the filter. Use bounds for BBOX filters and geometry
22512 * for INTERSECTS or DWITHIN filters.
22517 * APIProperty: distance
22518 * {Number} The distance to use in a DWithin spatial filter.
22523 * APIProperty: distanceUnits
22524 * {String} The units to use for the distance, e.g. 'm'.
22526 distanceUnits: null,
22529 * Constructor: OpenLayers.Filter.Spatial
22530 * Creates a spatial filter.
22533 * options - {Object} An optional object with properties to set on the
22537 * {<OpenLayers.Filter.Spatial>}
22542 * Evaluates this filter for a specific feature.
22545 * feature - {<OpenLayers.Feature.Vector>} feature to apply the filter to.
22548 * {Boolean} The feature meets filter criteria.
22550 evaluate: function(feature) {
22551 var intersect = false;
22552 switch(this.type) {
22553 case OpenLayers.Filter.Spatial.BBOX:
22554 case OpenLayers.Filter.Spatial.INTERSECTS:
22555 if(feature.geometry) {
22556 var geom = this.value;
22557 if(this.value.CLASS_NAME == "OpenLayers.Bounds") {
22558 geom = this.value.toGeometry();
22560 if(feature.geometry.intersects(geom)) {
22566 throw new Error('evaluate is not implemented for this filter type.');
22573 * Clones this filter.
22576 * {<OpenLayers.Filter.Spatial>} Clone of this filter.
22578 clone: function() {
22579 var options = OpenLayers.Util.applyDefaults({
22580 value: this.value && this.value.clone && this.value.clone()
22582 return new OpenLayers.Filter.Spatial(options);
22584 CLASS_NAME: "OpenLayers.Filter.Spatial"
22587 OpenLayers.Filter.Spatial.BBOX = "BBOX";
22588 OpenLayers.Filter.Spatial.INTERSECTS = "INTERSECTS";
22589 OpenLayers.Filter.Spatial.DWITHIN = "DWITHIN";
22590 OpenLayers.Filter.Spatial.WITHIN = "WITHIN";
22591 OpenLayers.Filter.Spatial.CONTAINS = "CONTAINS";
22592 /* ======================================================================
22593 OpenLayers/Format/WFST/v1.js
22594 ====================================================================== */
22596 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
22597 * full list of contributors). Published under the 2-clause BSD license.
22598 * See license.txt in the OpenLayers distribution or repository for the
22599 * full text of the license. */
22602 * @requires OpenLayers/Format/XML.js
22603 * @requires OpenLayers/Format/WFST.js
22604 * @requires OpenLayers/Filter/Spatial.js
22605 * @requires OpenLayers/Filter/FeatureId.js
22609 * Class: OpenLayers.Format.WFST.v1
22610 * Superclass for WFST parsers.
22613 * - <OpenLayers.Format.XML>
22615 OpenLayers.Format.WFST.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
22618 * Property: namespaces
22619 * {Object} Mapping of namespace aliases to namespace URIs.
22622 xlink: "http://www.w3.org/1999/xlink",
22623 xsi: "http://www.w3.org/2001/XMLSchema-instance",
22624 wfs: "http://www.opengis.net/wfs",
22625 gml: "http://www.opengis.net/gml",
22626 ogc: "http://www.opengis.net/ogc",
22627 ows: "http://www.opengis.net/ows"
22631 * Property: defaultPrefix
22633 defaultPrefix: "wfs",
22636 * Property: version
22637 * {String} WFS version number.
22642 * Property: schemaLocation
22643 * {String} Schema location for a particular minor version.
22645 schemaLocations: null,
22648 * APIProperty: srsName
22649 * {String} URI for spatial reference system.
22654 * APIProperty: extractAttributes
22655 * {Boolean} Extract attributes from GML. Default is true.
22657 extractAttributes: true,
22661 * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
22662 * Changing is not recommended, a new Format should be instantiated.
22667 * Property: stateName
22668 * {Object} Maps feature states to node names.
22673 * Constructor: OpenLayers.Format.WFST.v1
22674 * Instances of this class are not created directly. Use the
22675 * <OpenLayers.Format.WFST.v1_0_0> or <OpenLayers.Format.WFST.v1_1_0>
22676 * constructor instead.
22679 * options - {Object} An optional object whose properties will be set on
22682 initialize: function(options) {
22683 // set state name mapping
22684 this.stateName = {};
22685 this.stateName[OpenLayers.State.INSERT] = "wfs:Insert";
22686 this.stateName[OpenLayers.State.UPDATE] = "wfs:Update";
22687 this.stateName[OpenLayers.State.DELETE] = "wfs:Delete";
22688 OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
22692 * Method: getSrsName
22694 getSrsName: function(feature, options) {
22695 var srsName = options && options.srsName;
22697 if(feature && feature.layer) {
22698 srsName = feature.layer.projection.getCode();
22700 srsName = this.srsName;
22708 * Parse the response from a transaction. Because WFS is split into
22709 * Transaction requests (create, update, and delete) and GetFeature
22710 * requests (read), this method handles parsing of both types of
22714 * data - {String | Document} The WFST document to read
22715 * options - {Object} Options for the reader
22717 * Valid options properties:
22718 * output - {String} either "features" or "object". The default is
22719 * "features", which means that the method will return an array of
22720 * features. If set to "object", an object with a "features" property
22721 * and other properties read by the parser will be returned.
22724 * {Array | Object} Output depending on the output option.
22726 read: function(data, options) {
22727 options = options || {};
22728 OpenLayers.Util.applyDefaults(options, {
22732 if(typeof data == "string") {
22733 data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
22735 if(data && data.nodeType == 9) {
22736 data = data.documentElement;
22740 this.readNode(data, obj, true);
22742 if(obj.features && options.output === "features") {
22743 obj = obj.features;
22749 * Property: readers
22750 * Contains public functions, grouped by namespace prefix, that will
22751 * be applied when a namespaced node is found matching the function
22752 * name. The function will be applied in the scope of this parser
22753 * with two arguments: the node being read and a context object passed
22758 "FeatureCollection": function(node, obj) {
22760 this.readChildNodes(node, obj);
22767 * Given an array of features, write a WFS transaction. This assumes
22768 * the features have a state property that determines the operation
22769 * type - insert, update, or delete.
22772 * features - {Array(<OpenLayers.Feature.Vector>)} A list of features. See
22773 * below for a more detailed description of the influence of the
22774 * feature's *modified* property.
22775 * options - {Object}
22777 * feature.modified rules:
22778 * If a feature has a modified property set, the following checks will be
22779 * made before a feature's geometry or attribute is included in an Update
22781 * - *modified* is not set at all: The geometry and all attributes will be
22783 * - *modified.geometry* is set (null or a geometry): The geometry will be
22784 * included. If *modified.attributes* is not set, all attributes will
22786 * - *modified.attributes* is set: Only the attributes set (i.e. to null or
22787 * a value) in *modified.attributes* will be included.
22788 * If *modified.geometry* is not set, the geometry will not be included.
22790 * Valid options include:
22791 * - *multi* {Boolean} If set to true, geometries will be casted to
22792 * Multi geometries before writing.
22795 * {String} A serialized WFS transaction.
22797 write: function(features, options) {
22798 var node = this.writeNode("wfs:Transaction", {
22802 var value = this.schemaLocationAttr();
22804 this.setAttributeNS(
22805 node, this.namespaces["xsi"], "xsi:schemaLocation", value
22808 return OpenLayers.Format.XML.prototype.write.apply(this, [node]);
22812 * Property: writers
22813 * As a compliment to the readers property, this structure contains public
22814 * writing functions grouped by namespace alias and named like the
22815 * node names they produce.
22819 "GetFeature": function(options) {
22820 var node = this.createElementNSPlus("wfs:GetFeature", {
22823 version: this.version,
22824 handle: options && options.handle,
22825 outputFormat: options && options.outputFormat,
22826 maxFeatures: options && options.maxFeatures,
22827 "xsi:schemaLocation": this.schemaLocationAttr(options)
22830 if (typeof this.featureType == "string") {
22831 this.writeNode("Query", options, node);
22833 for (var i=0,len = this.featureType.length; i<len; i++) {
22834 options.featureType = this.featureType[i];
22835 this.writeNode("Query", options, node);
22840 "Transaction": function(obj) {
22842 var options = obj.options || {};
22843 var node = this.createElementNSPlus("wfs:Transaction", {
22846 version: this.version,
22847 handle: options.handle
22851 var features = obj.features;
22853 // temporarily re-assigning geometry types
22854 if (options.multi === true) {
22855 OpenLayers.Util.extend(this.geometryTypes, {
22856 "OpenLayers.Geometry.Point": "MultiPoint",
22857 "OpenLayers.Geometry.LineString": (this.multiCurve === true) ? "MultiCurve": "MultiLineString",
22858 "OpenLayers.Geometry.Polygon": (this.multiSurface === true) ? "MultiSurface" : "MultiPolygon"
22862 for(i=0, len=features.length; i<len; ++i) {
22863 feature = features[i];
22864 name = this.stateName[feature.state];
22866 this.writeNode(name, {
22872 // switch back to original geometry types assignment
22873 if (options.multi === true) {
22874 this.setGeometryTypes();
22877 if (options.nativeElements) {
22878 for (i=0, len=options.nativeElements.length; i<len; ++i) {
22879 this.writeNode("wfs:Native",
22880 options.nativeElements[i], node);
22885 "Native": function(nativeElement) {
22886 var node = this.createElementNSPlus("wfs:Native", {
22888 vendorId: nativeElement.vendorId,
22889 safeToIgnore: nativeElement.safeToIgnore
22891 value: nativeElement.value
22895 "Insert": function(obj) {
22896 var feature = obj.feature;
22897 var options = obj.options;
22898 var node = this.createElementNSPlus("wfs:Insert", {
22900 handle: options && options.handle
22903 this.srsName = this.getSrsName(feature);
22904 this.writeNode("feature:_typeName", feature, node);
22907 "Update": function(obj) {
22908 var feature = obj.feature;
22909 var options = obj.options;
22910 var node = this.createElementNSPlus("wfs:Update", {
22912 handle: options && options.handle,
22913 typeName: (this.featureNS ? this.featurePrefix + ":" : "") +
22917 if(this.featureNS) {
22918 node.setAttribute("xmlns:" + this.featurePrefix, this.featureNS);
22922 var modified = feature.modified;
22923 if (this.geometryName !== null && (!modified || modified.geometry !== undefined)) {
22924 this.srsName = this.getSrsName(feature);
22926 "Property", {name: this.geometryName, value: feature.geometry}, node
22930 // add in attributes
22931 for(var key in feature.attributes) {
22932 if(feature.attributes[key] !== undefined &&
22933 (!modified || !modified.attributes ||
22934 (modified.attributes && modified.attributes[key] !== undefined))) {
22936 "Property", {name: key, value: feature.attributes[key]}, node
22941 // add feature id filter
22942 this.writeNode("ogc:Filter", new OpenLayers.Filter.FeatureId({
22943 fids: [feature.fid]
22948 "Property": function(obj) {
22949 var node = this.createElementNSPlus("wfs:Property");
22950 this.writeNode("Name", obj.name, node);
22951 if(obj.value !== null) {
22952 this.writeNode("Value", obj.value, node);
22956 "Name": function(name) {
22957 return this.createElementNSPlus("wfs:Name", {value: name});
22959 "Value": function(obj) {
22961 if(obj instanceof OpenLayers.Geometry) {
22962 node = this.createElementNSPlus("wfs:Value");
22963 var geom = this.writeNode("feature:_geometry", obj).firstChild;
22964 node.appendChild(geom);
22966 node = this.createElementNSPlus("wfs:Value", {value: obj});
22970 "Delete": function(obj) {
22971 var feature = obj.feature;
22972 var options = obj.options;
22973 var node = this.createElementNSPlus("wfs:Delete", {
22975 handle: options && options.handle,
22976 typeName: (this.featureNS ? this.featurePrefix + ":" : "") +
22980 if(this.featureNS) {
22981 node.setAttribute("xmlns:" + this.featurePrefix, this.featureNS);
22983 this.writeNode("ogc:Filter", new OpenLayers.Filter.FeatureId({
22984 fids: [feature.fid]
22992 * Method: schemaLocationAttr
22993 * Generate the xsi:schemaLocation attribute value.
22996 * {String} The xsi:schemaLocation attribute or undefined if none.
22998 schemaLocationAttr: function(options) {
22999 options = OpenLayers.Util.extend({
23000 featurePrefix: this.featurePrefix,
23001 schema: this.schema
23003 var schemaLocations = OpenLayers.Util.extend({}, this.schemaLocations);
23004 if(options.schema) {
23005 schemaLocations[options.featurePrefix] = options.schema;
23009 for(var key in schemaLocations) {
23010 uri = this.namespaces[key];
23012 parts.push(uri + " " + schemaLocations[key]);
23015 var value = parts.join(" ") || undefined;
23020 * Method: setFilterProperty
23021 * Set the property of each spatial filter.
23024 * filter - {<OpenLayers.Filter>}
23026 setFilterProperty: function(filter) {
23027 if(filter.filters) {
23028 for(var i=0, len=filter.filters.length; i<len; ++i) {
23029 OpenLayers.Format.WFST.v1.prototype.setFilterProperty.call(this, filter.filters[i]);
23032 if(filter instanceof OpenLayers.Filter.Spatial && !filter.property) {
23033 // got a spatial filter without property, so set it
23034 filter.property = this.geometryName;
23039 CLASS_NAME: "OpenLayers.Format.WFST.v1"
23042 /* ======================================================================
23043 OpenLayers/Geometry/Polygon.js
23044 ====================================================================== */
23046 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
23047 * full list of contributors). Published under the 2-clause BSD license.
23048 * See license.txt in the OpenLayers distribution or repository for the
23049 * full text of the license. */
23052 * @requires OpenLayers/Geometry/Collection.js
23053 * @requires OpenLayers/Geometry/LinearRing.js
23057 * Class: OpenLayers.Geometry.Polygon
23058 * Polygon is a collection of Geometry.LinearRings.
23061 * - <OpenLayers.Geometry.Collection>
23062 * - <OpenLayers.Geometry>
23064 OpenLayers.Geometry.Polygon = OpenLayers.Class(
23065 OpenLayers.Geometry.Collection, {
23068 * Property: componentTypes
23069 * {Array(String)} An array of class names representing the types of
23070 * components that the collection can include. A null value means the
23071 * component types are not restricted.
23073 componentTypes: ["OpenLayers.Geometry.LinearRing"],
23076 * Constructor: OpenLayers.Geometry.Polygon
23077 * Constructor for a Polygon geometry.
23078 * The first ring (this.component[0])is the outer bounds of the polygon and
23079 * all subsequent rings (this.component[1-n]) are internal holes.
23083 * components - {Array(<OpenLayers.Geometry.LinearRing>)}
23087 * APIMethod: getArea
23088 * Calculated by subtracting the areas of the internal holes from the
23089 * area of the outer hole.
23092 * {float} The area of the geometry
23094 getArea: function() {
23096 if ( this.components && (this.components.length > 0)) {
23097 area += Math.abs(this.components[0].getArea());
23098 for (var i=1, len=this.components.length; i<len; i++) {
23099 area -= Math.abs(this.components[i].getArea());
23106 * APIMethod: getGeodesicArea
23107 * Calculate the approximate area of the polygon were it projected onto
23111 * projection - {<OpenLayers.Projection>} The spatial reference system
23112 * for the geometry coordinates. If not provided, Geographic/WGS84 is
23116 * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
23117 * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
23118 * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
23121 * {float} The approximate geodesic area of the polygon in square meters.
23123 getGeodesicArea: function(projection) {
23125 if(this.components && (this.components.length > 0)) {
23126 area += Math.abs(this.components[0].getGeodesicArea(projection));
23127 for(var i=1, len=this.components.length; i<len; i++) {
23128 area -= Math.abs(this.components[i].getGeodesicArea(projection));
23135 * Method: containsPoint
23136 * Test if a point is inside a polygon. Points on a polygon edge are
23137 * considered inside.
23140 * point - {<OpenLayers.Geometry.Point>}
23143 * {Boolean | Number} The point is inside the polygon. Returns 1 if the
23144 * point is on an edge. Returns boolean otherwise.
23146 containsPoint: function(point) {
23147 var numRings = this.components.length;
23148 var contained = false;
23150 // check exterior ring - 1 means on edge, boolean otherwise
23151 contained = this.components[0].containsPoint(point);
23152 if(contained !== 1) {
23153 if(contained && numRings > 1) {
23154 // check interior rings
23156 for(var i=1; i<numRings; ++i) {
23157 hole = this.components[i].containsPoint(point);
23176 * APIMethod: intersects
23177 * Determine if the input geometry intersects this one.
23180 * geometry - {<OpenLayers.Geometry>} Any type of geometry.
23183 * {Boolean} The input geometry intersects this one.
23185 intersects: function(geometry) {
23186 var intersect = false;
23188 if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
23189 intersect = this.containsPoint(geometry);
23190 } else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LineString" ||
23191 geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
23192 // check if rings/linestrings intersect
23193 for(i=0, len=this.components.length; i<len; ++i) {
23194 intersect = geometry.intersects(this.components[i]);
23200 // check if this poly contains points of the ring/linestring
23201 for(i=0, len=geometry.components.length; i<len; ++i) {
23202 intersect = this.containsPoint(geometry.components[i]);
23209 for(i=0, len=geometry.components.length; i<len; ++ i) {
23210 intersect = this.intersects(geometry.components[i]);
23216 // check case where this poly is wholly contained by another
23217 if(!intersect && geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") {
23218 // exterior ring points will be contained in the other geometry
23219 var ring = this.components[0];
23220 for(i=0, len=ring.components.length; i<len; ++i) {
23221 intersect = geometry.containsPoint(ring.components[i]);
23231 * APIMethod: distanceTo
23232 * Calculate the closest distance between two geometries (on the x-y plane).
23235 * geometry - {<OpenLayers.Geometry>} The target geometry.
23236 * options - {Object} Optional properties for configuring the distance
23240 * details - {Boolean} Return details from the distance calculation.
23241 * Default is false.
23242 * edge - {Boolean} Calculate the distance from this geometry to the
23243 * nearest edge of the target geometry. Default is true. If true,
23244 * calling distanceTo from a geometry that is wholly contained within
23245 * the target will result in a non-zero distance. If false, whenever
23246 * geometries intersect, calling distanceTo will return 0. If false,
23247 * details cannot be returned.
23250 * {Number | Object} The distance between this geometry and the target.
23251 * If details is true, the return will be an object with distance,
23252 * x0, y0, x1, and y1 properties. The x0 and y0 properties represent
23253 * the coordinates of the closest point on this geometry. The x1 and y1
23254 * properties represent the coordinates of the closest point on the
23257 distanceTo: function(geometry, options) {
23258 var edge = !(options && options.edge === false);
23260 // this is the case where we might not be looking for distance to edge
23261 if(!edge && this.intersects(geometry)) {
23264 result = OpenLayers.Geometry.Collection.prototype.distanceTo.apply(
23265 this, [geometry, options]
23271 CLASS_NAME: "OpenLayers.Geometry.Polygon"
23275 * APIMethod: createRegularPolygon
23276 * Create a regular polygon around a radius. Useful for creating circles
23280 * origin - {<OpenLayers.Geometry.Point>} center of polygon.
23281 * radius - {Float} distance to vertex, in map units.
23282 * sides - {Integer} Number of sides. 20 approximates a circle.
23283 * rotation - {Float} original angle of rotation, in degrees.
23285 OpenLayers.Geometry.Polygon.createRegularPolygon = function(origin, radius, sides, rotation) {
23286 var angle = Math.PI * ((1/sides) - (1/2));
23288 angle += (rotation / 180) * Math.PI;
23290 var rotatedAngle, x, y;
23292 for(var i=0; i<sides; ++i) {
23293 rotatedAngle = angle + (i * 2 * Math.PI / sides);
23294 x = origin.x + (radius * Math.cos(rotatedAngle));
23295 y = origin.y + (radius * Math.sin(rotatedAngle));
23296 points.push(new OpenLayers.Geometry.Point(x, y));
23298 var ring = new OpenLayers.Geometry.LinearRing(points);
23299 return new OpenLayers.Geometry.Polygon([ring]);
23301 /* ======================================================================
23302 OpenLayers/Geometry/MultiPolygon.js
23303 ====================================================================== */
23305 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
23306 * full list of contributors). Published under the 2-clause BSD license.
23307 * See license.txt in the OpenLayers distribution or repository for the
23308 * full text of the license. */
23311 * @requires OpenLayers/Geometry/Collection.js
23312 * @requires OpenLayers/Geometry/Polygon.js
23316 * Class: OpenLayers.Geometry.MultiPolygon
23317 * MultiPolygon is a geometry with multiple <OpenLayers.Geometry.Polygon>
23318 * components. Create a new instance with the <OpenLayers.Geometry.MultiPolygon>
23322 * - <OpenLayers.Geometry.Collection>
23324 OpenLayers.Geometry.MultiPolygon = OpenLayers.Class(
23325 OpenLayers.Geometry.Collection, {
23328 * Property: componentTypes
23329 * {Array(String)} An array of class names representing the types of
23330 * components that the collection can include. A null value means the
23331 * component types are not restricted.
23333 componentTypes: ["OpenLayers.Geometry.Polygon"],
23336 * Constructor: OpenLayers.Geometry.MultiPolygon
23337 * Create a new MultiPolygon geometry
23340 * components - {Array(<OpenLayers.Geometry.Polygon>)} An array of polygons
23341 * used to generate the MultiPolygon
23345 CLASS_NAME: "OpenLayers.Geometry.MultiPolygon"
23347 /* ======================================================================
23348 OpenLayers/Format/GML.js
23349 ====================================================================== */
23351 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
23352 * full list of contributors). Published under the 2-clause BSD license.
23353 * See license.txt in the OpenLayers distribution or repository for the
23354 * full text of the license. */
23357 * @requires OpenLayers/Format/XML.js
23358 * @requires OpenLayers/Feature/Vector.js
23359 * @requires OpenLayers/Geometry/Point.js
23360 * @requires OpenLayers/Geometry/MultiPoint.js
23361 * @requires OpenLayers/Geometry/LineString.js
23362 * @requires OpenLayers/Geometry/MultiLineString.js
23363 * @requires OpenLayers/Geometry/Polygon.js
23364 * @requires OpenLayers/Geometry/MultiPolygon.js
23368 * Class: OpenLayers.Format.GML
23369 * Read/Write GML. Create a new instance with the <OpenLayers.Format.GML>
23370 * constructor. Supports the GML simple features profile.
23373 * - <OpenLayers.Format.XML>
23375 OpenLayers.Format.GML = OpenLayers.Class(OpenLayers.Format.XML, {
23378 * APIProperty: featureNS
23379 * {String} Namespace used for feature attributes. Default is
23380 * "http://mapserver.gis.umn.edu/mapserver".
23382 featureNS: "http://mapserver.gis.umn.edu/mapserver",
23385 * APIProperty: featurePrefix
23386 * {String} Namespace alias (or prefix) for feature nodes. Default is
23389 featurePrefix: "feature",
23392 * APIProperty: featureName
23393 * {String} Element name for features. Default is "featureMember".
23395 featureName: "featureMember",
23398 * APIProperty: layerName
23399 * {String} Name of data layer. Default is "features".
23401 layerName: "features",
23404 * APIProperty: geometryName
23405 * {String} Name of geometry element. Defaults to "geometry".
23407 geometryName: "geometry",
23410 * APIProperty: collectionName
23411 * {String} Name of featureCollection element.
23413 collectionName: "FeatureCollection",
23416 * APIProperty: gmlns
23417 * {String} GML Namespace.
23419 gmlns: "http://www.opengis.net/gml",
23422 * APIProperty: extractAttributes
23423 * {Boolean} Extract attributes from GML.
23425 extractAttributes: true,
23429 * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
23430 * Changing is not recommended, a new Format should be instantiated.
23435 * Constructor: OpenLayers.Format.GML
23436 * Create a new parser for GML.
23439 * options - {Object} An optional object whose properties will be set on
23442 initialize: function(options) {
23443 // compile regular expressions once instead of every time they are used
23445 trimSpace: (/^\s*|\s*$/g),
23446 removeSpace: (/\s*/g),
23447 splitSpace: (/\s+/),
23448 trimComma: (/\s*,\s*/g)
23450 OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
23455 * Read data from a string, and return a list of features.
23458 * data - {String} or {DOMElement} data to read/parse.
23461 * {Array(<OpenLayers.Feature.Vector>)} An array of features.
23463 read: function(data) {
23464 if(typeof data == "string") {
23465 data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
23467 var featureNodes = this.getElementsByTagNameNS(data.documentElement,
23471 for(var i=0; i<featureNodes.length; i++) {
23472 var feature = this.parseFeature(featureNodes[i]);
23474 features.push(feature);
23481 * Method: parseFeature
23482 * This function is the core of the GML parsing code in OpenLayers.
23483 * It creates the geometries that are then attached to the returned
23484 * feature, and calls parseAttributes() to get attribute data out.
23487 * node - {DOMElement} A GML feature node.
23489 parseFeature: function(node) {
23490 // only accept one geometry per feature - look for highest "order"
23491 var order = ["MultiPolygon", "Polygon",
23492 "MultiLineString", "LineString",
23493 "MultiPoint", "Point", "Envelope"];
23494 // FIXME: In case we parse a feature with no geometry, but boundedBy an Envelope,
23495 // this code creates a geometry derived from the Envelope. This is not correct.
23496 var type, nodeList, geometry, parser;
23497 for(var i=0; i<order.length; ++i) {
23499 nodeList = this.getElementsByTagNameNS(node, this.gmlns, type);
23500 if(nodeList.length > 0) {
23501 // only deal with first geometry of this type
23502 parser = this.parseGeometry[type.toLowerCase()];
23504 geometry = parser.apply(this, [nodeList[0]]);
23505 if (this.internalProjection && this.externalProjection) {
23506 geometry.transform(this.externalProjection,
23507 this.internalProjection);
23510 throw new TypeError("Unsupported geometry type: " + type);
23512 // stop looking for different geometry types
23518 var boxNodes = this.getElementsByTagNameNS(node, this.gmlns, "Box");
23519 for(i=0; i<boxNodes.length; ++i) {
23520 var boxNode = boxNodes[i];
23521 var box = this.parseGeometry["box"].apply(this, [boxNode]);
23522 var parentNode = boxNode.parentNode;
23523 var parentName = parentNode.localName ||
23524 parentNode.nodeName.split(":").pop();
23525 if(parentName === "boundedBy") {
23528 geometry = box.toGeometry();
23532 // construct feature (optionally with attributes)
23534 if(this.extractAttributes) {
23535 attributes = this.parseAttributes(node);
23537 var feature = new OpenLayers.Feature.Vector(geometry, attributes);
23538 feature.bounds = bounds;
23541 featureType: node.firstChild.nodeName.split(":")[1],
23542 featureNS: node.firstChild.namespaceURI,
23543 featureNSPrefix: node.firstChild.prefix
23546 // assign fid - this can come from a "fid" or "id" attribute
23547 var childNode = node.firstChild;
23550 if(childNode.nodeType == 1) {
23551 fid = childNode.getAttribute("fid") ||
23552 childNode.getAttribute("id");
23557 childNode = childNode.nextSibling;
23564 * Property: parseGeometry
23565 * Properties of this object are the functions that parse geometries based
23571 * Method: parseGeometry.point
23572 * Given a GML node representing a point geometry, create an OpenLayers
23576 * node - {DOMElement} A GML node.
23579 * {<OpenLayers.Geometry.Point>} A point geometry.
23581 point: function(node) {
23583 * Three coordinate variations to consider:
23584 * 1) <gml:pos>x y z</gml:pos>
23585 * 2) <gml:coordinates>x, y, z</gml:coordinates>
23586 * 3) <gml:coord><gml:X>x</gml:X><gml:Y>y</gml:Y></gml:coord>
23588 var nodeList, coordString;
23591 // look for <gml:pos>
23592 var nodeList = this.getElementsByTagNameNS(node, this.gmlns, "pos");
23593 if(nodeList.length > 0) {
23594 coordString = nodeList[0].firstChild.nodeValue;
23595 coordString = coordString.replace(this.regExes.trimSpace, "");
23596 coords = coordString.split(this.regExes.splitSpace);
23599 // look for <gml:coordinates>
23600 if(coords.length == 0) {
23601 nodeList = this.getElementsByTagNameNS(node, this.gmlns,
23603 if(nodeList.length > 0) {
23604 coordString = nodeList[0].firstChild.nodeValue;
23605 coordString = coordString.replace(this.regExes.removeSpace,
23607 coords = coordString.split(",");
23611 // look for <gml:coord>
23612 if(coords.length == 0) {
23613 nodeList = this.getElementsByTagNameNS(node, this.gmlns,
23615 if(nodeList.length > 0) {
23616 var xList = this.getElementsByTagNameNS(nodeList[0],
23618 var yList = this.getElementsByTagNameNS(nodeList[0],
23620 if(xList.length > 0 && yList.length > 0) {
23621 coords = [xList[0].firstChild.nodeValue,
23622 yList[0].firstChild.nodeValue];
23627 // preserve third dimension
23628 if(coords.length == 2) {
23633 return new OpenLayers.Geometry.Point(coords[0], coords[1],
23637 return new OpenLayers.Geometry.Point(coords[1], coords[0],
23643 * Method: parseGeometry.multipoint
23644 * Given a GML node representing a multipoint geometry, create an
23645 * OpenLayers multipoint geometry.
23648 * node - {DOMElement} A GML node.
23651 * {<OpenLayers.Geometry.MultiPoint>} A multipoint geometry.
23653 multipoint: function(node) {
23654 var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
23656 var components = [];
23657 if(nodeList.length > 0) {
23659 for(var i=0; i<nodeList.length; ++i) {
23660 point = this.parseGeometry.point.apply(this, [nodeList[i]]);
23662 components.push(point);
23666 return new OpenLayers.Geometry.MultiPoint(components);
23670 * Method: parseGeometry.linestring
23671 * Given a GML node representing a linestring geometry, create an
23672 * OpenLayers linestring geometry.
23675 * node - {DOMElement} A GML node.
23678 * {<OpenLayers.Geometry.LineString>} A linestring geometry.
23680 linestring: function(node, ring) {
23682 * Two coordinate variations to consider:
23683 * 1) <gml:posList dimension="d">x0 y0 z0 x1 y1 z1</gml:posList>
23684 * 2) <gml:coordinates>x0, y0, z0 x1, y1, z1</gml:coordinates>
23686 var nodeList, coordString;
23690 // look for <gml:posList>
23691 nodeList = this.getElementsByTagNameNS(node, this.gmlns, "posList");
23692 if(nodeList.length > 0) {
23693 coordString = this.getChildValue(nodeList[0]);
23694 coordString = coordString.replace(this.regExes.trimSpace, "");
23695 coords = coordString.split(this.regExes.splitSpace);
23696 var dim = parseInt(nodeList[0].getAttribute("dimension"));
23698 for(var i=0; i<coords.length/dim; ++i) {
23702 z = (dim == 2) ? null : coords[j+2];
23704 points.push(new OpenLayers.Geometry.Point(x, y, z));
23706 points.push(new OpenLayers.Geometry.Point(y, x, z));
23711 // look for <gml:coordinates>
23712 if(coords.length == 0) {
23713 nodeList = this.getElementsByTagNameNS(node, this.gmlns,
23715 if(nodeList.length > 0) {
23716 coordString = this.getChildValue(nodeList[0]);
23717 coordString = coordString.replace(this.regExes.trimSpace,
23719 coordString = coordString.replace(this.regExes.trimComma,
23721 var pointList = coordString.split(this.regExes.splitSpace);
23722 for(var i=0; i<pointList.length; ++i) {
23723 coords = pointList[i].split(",");
23724 if(coords.length == 2) {
23728 points.push(new OpenLayers.Geometry.Point(coords[0],
23732 points.push(new OpenLayers.Geometry.Point(coords[1],
23741 if(points.length != 0) {
23743 line = new OpenLayers.Geometry.LinearRing(points);
23745 line = new OpenLayers.Geometry.LineString(points);
23752 * Method: parseGeometry.multilinestring
23753 * Given a GML node representing a multilinestring geometry, create an
23754 * OpenLayers multilinestring geometry.
23757 * node - {DOMElement} A GML node.
23760 * {<OpenLayers.Geometry.MultiLineString>} A multilinestring geometry.
23762 multilinestring: function(node) {
23763 var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
23765 var components = [];
23766 if(nodeList.length > 0) {
23768 for(var i=0; i<nodeList.length; ++i) {
23769 line = this.parseGeometry.linestring.apply(this,
23772 components.push(line);
23776 return new OpenLayers.Geometry.MultiLineString(components);
23780 * Method: parseGeometry.polygon
23781 * Given a GML node representing a polygon geometry, create an
23782 * OpenLayers polygon geometry.
23785 * node - {DOMElement} A GML node.
23788 * {<OpenLayers.Geometry.Polygon>} A polygon geometry.
23790 polygon: function(node) {
23791 var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
23793 var components = [];
23794 if(nodeList.length > 0) {
23795 // this assumes exterior ring first, inner rings after
23797 for(var i=0; i<nodeList.length; ++i) {
23798 ring = this.parseGeometry.linestring.apply(this,
23799 [nodeList[i], true]);
23801 components.push(ring);
23805 return new OpenLayers.Geometry.Polygon(components);
23809 * Method: parseGeometry.multipolygon
23810 * Given a GML node representing a multipolygon geometry, create an
23811 * OpenLayers multipolygon geometry.
23814 * node - {DOMElement} A GML node.
23817 * {<OpenLayers.Geometry.MultiPolygon>} A multipolygon geometry.
23819 multipolygon: function(node) {
23820 var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
23822 var components = [];
23823 if(nodeList.length > 0) {
23825 for(var i=0; i<nodeList.length; ++i) {
23826 polygon = this.parseGeometry.polygon.apply(this,
23829 components.push(polygon);
23833 return new OpenLayers.Geometry.MultiPolygon(components);
23836 envelope: function(node) {
23837 var components = [];
23841 var lpoint = this.getElementsByTagNameNS(node, this.gmlns, "lowerCorner");
23842 if (lpoint.length > 0) {
23845 if(lpoint.length > 0) {
23846 coordString = lpoint[0].firstChild.nodeValue;
23847 coordString = coordString.replace(this.regExes.trimSpace, "");
23848 coords = coordString.split(this.regExes.splitSpace);
23851 if(coords.length == 2) {
23855 var lowerPoint = new OpenLayers.Geometry.Point(coords[0], coords[1],coords[2]);
23857 var lowerPoint = new OpenLayers.Geometry.Point(coords[1], coords[0],coords[2]);
23861 var upoint = this.getElementsByTagNameNS(node, this.gmlns, "upperCorner");
23862 if (upoint.length > 0) {
23865 if(upoint.length > 0) {
23866 coordString = upoint[0].firstChild.nodeValue;
23867 coordString = coordString.replace(this.regExes.trimSpace, "");
23868 coords = coordString.split(this.regExes.splitSpace);
23871 if(coords.length == 2) {
23875 var upperPoint = new OpenLayers.Geometry.Point(coords[0], coords[1],coords[2]);
23877 var upperPoint = new OpenLayers.Geometry.Point(coords[1], coords[0],coords[2]);
23881 if (lowerPoint && upperPoint) {
23882 components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y));
23883 components.push(new OpenLayers.Geometry.Point(upperPoint.x, lowerPoint.y));
23884 components.push(new OpenLayers.Geometry.Point(upperPoint.x, upperPoint.y));
23885 components.push(new OpenLayers.Geometry.Point(lowerPoint.x, upperPoint.y));
23886 components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y));
23888 var ring = new OpenLayers.Geometry.LinearRing(components);
23889 envelope = new OpenLayers.Geometry.Polygon([ring]);
23895 * Method: parseGeometry.box
23896 * Given a GML node representing a box geometry, create an
23897 * OpenLayers.Bounds.
23900 * node - {DOMElement} A GML node.
23903 * {<OpenLayers.Bounds>} A bounds representing the box.
23905 box: function(node) {
23906 var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
23909 var coords, beginPoint = null, endPoint = null;
23910 if (nodeList.length > 0) {
23911 coordString = nodeList[0].firstChild.nodeValue;
23912 coords = coordString.split(" ");
23913 if (coords.length == 2) {
23914 beginPoint = coords[0].split(",");
23915 endPoint = coords[1].split(",");
23918 if (beginPoint !== null && endPoint !== null) {
23919 return new OpenLayers.Bounds(parseFloat(beginPoint[0]),
23920 parseFloat(beginPoint[1]),
23921 parseFloat(endPoint[0]),
23922 parseFloat(endPoint[1]) );
23929 * Method: parseAttributes
23932 * node - {DOMElement}
23935 * {Object} An attributes object.
23937 parseAttributes: function(node) {
23938 var attributes = {};
23939 // assume attributes are children of the first type 1 child
23940 var childNode = node.firstChild;
23941 var children, i, child, grandchildren, grandchild, name, value;
23943 if(childNode.nodeType == 1) {
23944 // attributes are type 1 children with one type 3 child
23945 children = childNode.childNodes;
23946 for(i=0; i<children.length; ++i) {
23947 child = children[i];
23948 if(child.nodeType == 1) {
23949 grandchildren = child.childNodes;
23950 if(grandchildren.length == 1) {
23951 grandchild = grandchildren[0];
23952 if(grandchild.nodeType == 3 ||
23953 grandchild.nodeType == 4) {
23954 name = (child.prefix) ?
23955 child.nodeName.split(":")[1] :
23957 value = grandchild.nodeValue.replace(
23958 this.regExes.trimSpace, "");
23959 attributes[name] = value;
23962 // If child has no childNodes (grandchildren),
23963 // set an attribute with null value.
23964 // e.g. <prefix:fieldname/> becomes
23965 // {fieldname: null}
23966 attributes[child.nodeName.split(":").pop()] = null;
23972 childNode = childNode.nextSibling;
23979 * Generate a GML document string given a list of features.
23982 * features - {Array(<OpenLayers.Feature.Vector>)} List of features to
23983 * serialize into a string.
23986 * {String} A string representing the GML document.
23988 write: function(features) {
23989 if(!(OpenLayers.Util.isArray(features))) {
23990 features = [features];
23992 var gml = this.createElementNS("http://www.opengis.net/wfs",
23993 "wfs:" + this.collectionName);
23994 for(var i=0; i<features.length; i++) {
23995 gml.appendChild(this.createFeatureXML(features[i]));
23997 return OpenLayers.Format.XML.prototype.write.apply(this, [gml]);
24001 * Method: createFeatureXML
24002 * Accept an OpenLayers.Feature.Vector, and build a GML node for it.
24005 * feature - {<OpenLayers.Feature.Vector>} The feature to be built as GML.
24008 * {DOMElement} A node reprensting the feature in GML.
24010 createFeatureXML: function(feature) {
24011 var geometry = feature.geometry;
24012 var geometryNode = this.buildGeometryNode(geometry);
24013 var geomContainer = this.createElementNS(this.featureNS,
24014 this.featurePrefix + ":" +
24015 this.geometryName);
24016 geomContainer.appendChild(geometryNode);
24017 var featureNode = this.createElementNS(this.gmlns,
24018 "gml:" + this.featureName);
24019 var featureContainer = this.createElementNS(this.featureNS,
24020 this.featurePrefix + ":" +
24022 var fid = feature.fid || feature.id;
24023 featureContainer.setAttribute("fid", fid);
24024 featureContainer.appendChild(geomContainer);
24025 for(var attr in feature.attributes) {
24026 var attrText = this.createTextNode(feature.attributes[attr]);
24027 var nodename = attr.substring(attr.lastIndexOf(":") + 1);
24028 var attrContainer = this.createElementNS(this.featureNS,
24029 this.featurePrefix + ":" +
24031 attrContainer.appendChild(attrText);
24032 featureContainer.appendChild(attrContainer);
24034 featureNode.appendChild(featureContainer);
24035 return featureNode;
24039 * APIMethod: buildGeometryNode
24041 buildGeometryNode: function(geometry) {
24042 if (this.externalProjection && this.internalProjection) {
24043 geometry = geometry.clone();
24044 geometry.transform(this.internalProjection,
24045 this.externalProjection);
24047 var className = geometry.CLASS_NAME;
24048 var type = className.substring(className.lastIndexOf(".") + 1);
24049 var builder = this.buildGeometry[type.toLowerCase()];
24050 return builder.apply(this, [geometry]);
24054 * Property: buildGeometry
24055 * Object containing methods to do the actual geometry node building
24056 * based on geometry type.
24059 // TBD retrieve the srs from layer
24060 // srsName is non-standard, so not including it until it's right.
24061 // gml.setAttribute("srsName",
24062 // "http://www.opengis.net/gml/srs/epsg.xml#4326");
24065 * Method: buildGeometry.point
24066 * Given an OpenLayers point geometry, create a GML point.
24069 * geometry - {<OpenLayers.Geometry.Point>} A point geometry.
24072 * {DOMElement} A GML point node.
24074 point: function(geometry) {
24075 var gml = this.createElementNS(this.gmlns, "gml:Point");
24076 gml.appendChild(this.buildCoordinatesNode(geometry));
24081 * Method: buildGeometry.multipoint
24082 * Given an OpenLayers multipoint geometry, create a GML multipoint.
24085 * geometry - {<OpenLayers.Geometry.MultiPoint>} A multipoint geometry.
24088 * {DOMElement} A GML multipoint node.
24090 multipoint: function(geometry) {
24091 var gml = this.createElementNS(this.gmlns, "gml:MultiPoint");
24092 var points = geometry.components;
24093 var pointMember, pointGeom;
24094 for(var i=0; i<points.length; i++) {
24095 pointMember = this.createElementNS(this.gmlns,
24096 "gml:pointMember");
24097 pointGeom = this.buildGeometry.point.apply(this,
24099 pointMember.appendChild(pointGeom);
24100 gml.appendChild(pointMember);
24106 * Method: buildGeometry.linestring
24107 * Given an OpenLayers linestring geometry, create a GML linestring.
24110 * geometry - {<OpenLayers.Geometry.LineString>} A linestring geometry.
24113 * {DOMElement} A GML linestring node.
24115 linestring: function(geometry) {
24116 var gml = this.createElementNS(this.gmlns, "gml:LineString");
24117 gml.appendChild(this.buildCoordinatesNode(geometry));
24122 * Method: buildGeometry.multilinestring
24123 * Given an OpenLayers multilinestring geometry, create a GML
24127 * geometry - {<OpenLayers.Geometry.MultiLineString>} A multilinestring
24131 * {DOMElement} A GML multilinestring node.
24133 multilinestring: function(geometry) {
24134 var gml = this.createElementNS(this.gmlns, "gml:MultiLineString");
24135 var lines = geometry.components;
24136 var lineMember, lineGeom;
24137 for(var i=0; i<lines.length; ++i) {
24138 lineMember = this.createElementNS(this.gmlns,
24139 "gml:lineStringMember");
24140 lineGeom = this.buildGeometry.linestring.apply(this,
24142 lineMember.appendChild(lineGeom);
24143 gml.appendChild(lineMember);
24149 * Method: buildGeometry.linearring
24150 * Given an OpenLayers linearring geometry, create a GML linearring.
24153 * geometry - {<OpenLayers.Geometry.LinearRing>} A linearring geometry.
24156 * {DOMElement} A GML linearring node.
24158 linearring: function(geometry) {
24159 var gml = this.createElementNS(this.gmlns, "gml:LinearRing");
24160 gml.appendChild(this.buildCoordinatesNode(geometry));
24165 * Method: buildGeometry.polygon
24166 * Given an OpenLayers polygon geometry, create a GML polygon.
24169 * geometry - {<OpenLayers.Geometry.Polygon>} A polygon geometry.
24172 * {DOMElement} A GML polygon node.
24174 polygon: function(geometry) {
24175 var gml = this.createElementNS(this.gmlns, "gml:Polygon");
24176 var rings = geometry.components;
24177 var ringMember, ringGeom, type;
24178 for(var i=0; i<rings.length; ++i) {
24179 type = (i==0) ? "outerBoundaryIs" : "innerBoundaryIs";
24180 ringMember = this.createElementNS(this.gmlns,
24182 ringGeom = this.buildGeometry.linearring.apply(this,
24184 ringMember.appendChild(ringGeom);
24185 gml.appendChild(ringMember);
24191 * Method: buildGeometry.multipolygon
24192 * Given an OpenLayers multipolygon geometry, create a GML multipolygon.
24195 * geometry - {<OpenLayers.Geometry.MultiPolygon>} A multipolygon
24199 * {DOMElement} A GML multipolygon node.
24201 multipolygon: function(geometry) {
24202 var gml = this.createElementNS(this.gmlns, "gml:MultiPolygon");
24203 var polys = geometry.components;
24204 var polyMember, polyGeom;
24205 for(var i=0; i<polys.length; ++i) {
24206 polyMember = this.createElementNS(this.gmlns,
24207 "gml:polygonMember");
24208 polyGeom = this.buildGeometry.polygon.apply(this,
24210 polyMember.appendChild(polyGeom);
24211 gml.appendChild(polyMember);
24218 * Method: buildGeometry.bounds
24219 * Given an OpenLayers bounds, create a GML box.
24222 * bounds - {<OpenLayers.Geometry.Bounds>} A bounds object.
24225 * {DOMElement} A GML box node.
24227 bounds: function(bounds) {
24228 var gml = this.createElementNS(this.gmlns, "gml:Box");
24229 gml.appendChild(this.buildCoordinatesNode(bounds));
24235 * Method: buildCoordinates
24236 * builds the coordinates XmlNode
24238 * <gml:coordinates decimal="." cs="," ts=" ">...</gml:coordinates>
24242 * geometry - {<OpenLayers.Geometry>}
24245 * {XmlNode} created xmlNode
24247 buildCoordinatesNode: function(geometry) {
24248 var coordinatesNode = this.createElementNS(this.gmlns,
24249 "gml:coordinates");
24250 coordinatesNode.setAttribute("decimal", ".");
24251 coordinatesNode.setAttribute("cs", ",");
24252 coordinatesNode.setAttribute("ts", " ");
24256 if(geometry instanceof OpenLayers.Bounds){
24257 parts.push(geometry.left + "," + geometry.bottom);
24258 parts.push(geometry.right + "," + geometry.top);
24260 var points = (geometry.components) ? geometry.components : [geometry];
24261 for(var i=0; i<points.length; i++) {
24262 parts.push(points[i].x + "," + points[i].y);
24266 var txtNode = this.createTextNode(parts.join(" "));
24267 coordinatesNode.appendChild(txtNode);
24269 return coordinatesNode;
24272 CLASS_NAME: "OpenLayers.Format.GML"
24274 /* ======================================================================
24275 OpenLayers/Format/GML/Base.js
24276 ====================================================================== */
24278 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
24279 * full list of contributors). Published under the 2-clause BSD license.
24280 * See license.txt in the OpenLayers distribution or repository for the
24281 * full text of the license. */
24284 * @requires OpenLayers/Format/XML.js
24285 * @requires OpenLayers/Format/GML.js
24289 * Though required in the full build, if the GML format is excluded, we set
24290 * the namespace here.
24292 if(!OpenLayers.Format.GML) {
24293 OpenLayers.Format.GML = {};
24297 * Class: OpenLayers.Format.GML.Base
24298 * Superclass for GML parsers.
24301 * - <OpenLayers.Format.XML>
24303 OpenLayers.Format.GML.Base = OpenLayers.Class(OpenLayers.Format.XML, {
24306 * Property: namespaces
24307 * {Object} Mapping of namespace aliases to namespace URIs.
24310 gml: "http://www.opengis.net/gml",
24311 xlink: "http://www.w3.org/1999/xlink",
24312 xsi: "http://www.w3.org/2001/XMLSchema-instance",
24313 wfs: "http://www.opengis.net/wfs" // this is a convenience for reading wfs:FeatureCollection
24317 * Property: defaultPrefix
24319 defaultPrefix: "gml",
24322 * Property: schemaLocation
24323 * {String} Schema location for a particular minor version.
24325 schemaLocation: null,
24328 * APIProperty: featureType
24329 * {Array(String) or String} The local (without prefix) feature typeName(s).
24334 * APIProperty: featureNS
24335 * {String} The feature namespace. Must be set in the options at
24341 * APIProperty: geometry
24342 * {String} Name of geometry element. Defaults to "geometry". If null, it
24343 * will be set on <read> when the first geometry is parsed.
24345 geometryName: "geometry",
24348 * APIProperty: extractAttributes
24349 * {Boolean} Extract attributes from GML. Default is true.
24351 extractAttributes: true,
24354 * APIProperty: srsName
24355 * {String} URI for spatial reference system. This is optional for
24356 * single part geometries and mandatory for collections and multis.
24357 * If set, the srsName attribute will be written for all geometries.
24364 * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
24365 * Changing is not recommended, a new Format should be instantiated.
24370 * Property: geometryTypes
24371 * {Object} Maps OpenLayers geometry class names to GML element names.
24372 * Use <setGeometryTypes> before accessing this property.
24374 geometryTypes: null,
24377 * Property: singleFeatureType
24378 * {Boolean} True if there is only 1 featureType, and not an array
24381 singleFeatureType: null,
24384 * Property: autoConfig
24385 * {Boolean} Indicates if the format was configured without a <featureNS>,
24386 * but auto-configured <featureNS> and <featureType> during read.
24387 * Subclasses making use of <featureType> auto-configuration should make
24388 * the first call to the <readNode> method (usually in the read method)
24389 * with true as 3rd argument, so the auto-configured featureType can be
24390 * reset and the format can be reused for subsequent reads with data from
24391 * different featureTypes. Set to false after read if you want to keep the
24392 * auto-configured values.
24396 * Property: regExes
24397 * Compiled regular expressions for manipulating strings.
24400 trimSpace: (/^\s*|\s*$/g),
24401 removeSpace: (/\s*/g),
24402 splitSpace: (/\s+/),
24403 trimComma: (/\s*,\s*/g),
24404 featureMember: (/^(.*:)?featureMembers?$/)
24408 * Constructor: OpenLayers.Format.GML.Base
24409 * Instances of this class are not created directly. Use the
24410 * <OpenLayers.Format.GML.v2> or <OpenLayers.Format.GML.v3> constructor
24414 * options - {Object} An optional object whose properties will be set on
24417 * Valid options properties:
24418 * featureType - {Array(String) or String} Local (without prefix) feature
24419 * typeName(s) (required for write).
24420 * featureNS - {String} Feature namespace (required for write).
24421 * geometryName - {String} Geometry element name (required for write).
24423 initialize: function(options) {
24424 OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
24425 this.setGeometryTypes();
24426 if(options && options.featureNS) {
24427 this.setNamespace("feature", options.featureNS);
24429 this.singleFeatureType = !options || (typeof options.featureType === "string");
24436 * data - {DOMElement} A gml:featureMember element, a gml:featureMembers
24437 * element, or an element containing either of the above at any level.
24440 * {Array(<OpenLayers.Feature.Vector>)} An array of features.
24442 read: function(data) {
24443 if(typeof data == "string") {
24444 data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
24446 if(data && data.nodeType == 9) {
24447 data = data.documentElement;
24450 this.readNode(data, {features: features}, true);
24451 if(features.length == 0) {
24452 // look for gml:featureMember elements
24453 var elements = this.getElementsByTagNameNS(
24454 data, this.namespaces.gml, "featureMember"
24456 if(elements.length) {
24457 for(var i=0, len=elements.length; i<len; ++i) {
24458 this.readNode(elements[i], {features: features}, true);
24461 // look for gml:featureMembers elements (this is v3, but does no harm here)
24462 var elements = this.getElementsByTagNameNS(
24463 data, this.namespaces.gml, "featureMembers"
24465 if(elements.length) {
24466 // there can be only one
24467 this.readNode(elements[0], {features: features}, true);
24476 * Shorthand for applying one of the named readers given the node
24477 * namespace and local name. Readers take two args (node, obj) and
24478 * generally extend or modify the second.
24481 * node - {DOMElement} The node to be read (required).
24482 * obj - {Object} The object to be modified (optional).
24483 * first - {Boolean} Should be set to true for the first node read. This
24484 * is usually the readNode call in the read method. Without this being
24485 * set, auto-configured properties will stick on subsequent reads.
24488 * {Object} The input object, modified (or a new one if none was provided).
24490 readNode: function(node, obj, first) {
24491 // on subsequent calls of format.read(), we want to reset auto-
24492 // configured properties and auto-configure again.
24493 if (first === true && this.autoConfig === true) {
24494 this.featureType = null;
24495 delete this.namespaceAlias[this.featureNS];
24496 delete this.namespaces["feature"];
24497 this.featureNS = null;
24499 // featureType auto-configuration
24500 if (!this.featureNS && (!(node.prefix in this.namespaces) &&
24501 node.parentNode.namespaceURI == this.namespaces["gml"] &&
24502 this.regExes.featureMember.test(node.parentNode.nodeName))) {
24503 this.featureType = node.nodeName.split(":").pop();
24504 this.setNamespace("feature", node.namespaceURI);
24505 this.featureNS = node.namespaceURI;
24506 this.autoConfig = true;
24508 return OpenLayers.Format.XML.prototype.readNode.apply(this, [node, obj]);
24512 * Property: readers
24513 * Contains public functions, grouped by namespace prefix, that will
24514 * be applied when a namespaced node is found matching the function
24515 * name. The function will be applied in the scope of this parser
24516 * with two arguments: the node being read and a context object passed
24521 "_inherit": function(node, obj, container) {
24522 // To be implemented by version specific parsers
24524 "featureMember": function(node, obj) {
24525 this.readChildNodes(node, obj);
24527 "featureMembers": function(node, obj) {
24528 this.readChildNodes(node, obj);
24530 "name": function(node, obj) {
24531 obj.name = this.getChildValue(node);
24533 "boundedBy": function(node, obj) {
24534 var container = {};
24535 this.readChildNodes(node, container);
24536 if(container.components && container.components.length > 0) {
24537 obj.bounds = container.components[0];
24540 "Point": function(node, container) {
24541 var obj = {points: []};
24542 this.readChildNodes(node, obj);
24543 if(!container.components) {
24544 container.components = [];
24546 container.components.push(obj.points[0]);
24548 "coordinates": function(node, obj) {
24549 var str = this.getChildValue(node).replace(
24550 this.regExes.trimSpace, ""
24552 str = str.replace(this.regExes.trimComma, ",");
24553 var pointList = str.split(this.regExes.splitSpace);
24555 var numPoints = pointList.length;
24556 var points = new Array(numPoints);
24557 for(var i=0; i<numPoints; ++i) {
24558 coords = pointList[i].split(",");
24560 points[i] = new OpenLayers.Geometry.Point(
24561 coords[0], coords[1], coords[2]
24564 points[i] = new OpenLayers.Geometry.Point(
24565 coords[1], coords[0], coords[2]
24569 obj.points = points;
24571 "coord": function(node, obj) {
24573 this.readChildNodes(node, coord);
24577 obj.points.push(new OpenLayers.Geometry.Point(
24578 coord.x, coord.y, coord.z
24581 "X": function(node, coord) {
24582 coord.x = this.getChildValue(node);
24584 "Y": function(node, coord) {
24585 coord.y = this.getChildValue(node);
24587 "Z": function(node, coord) {
24588 coord.z = this.getChildValue(node);
24590 "MultiPoint": function(node, container) {
24591 var obj = {components: []};
24592 this.readers.gml._inherit.apply(this, [node, obj, container]);
24593 this.readChildNodes(node, obj);
24594 container.components = [
24595 new OpenLayers.Geometry.MultiPoint(obj.components)
24598 "pointMember": function(node, obj) {
24599 this.readChildNodes(node, obj);
24601 "LineString": function(node, container) {
24603 this.readers.gml._inherit.apply(this, [node, obj, container]);
24604 this.readChildNodes(node, obj);
24605 if(!container.components) {
24606 container.components = [];
24608 container.components.push(
24609 new OpenLayers.Geometry.LineString(obj.points)
24612 "MultiLineString": function(node, container) {
24613 var obj = {components: []};
24614 this.readers.gml._inherit.apply(this, [node, obj, container]);
24615 this.readChildNodes(node, obj);
24616 container.components = [
24617 new OpenLayers.Geometry.MultiLineString(obj.components)
24620 "lineStringMember": function(node, obj) {
24621 this.readChildNodes(node, obj);
24623 "Polygon": function(node, container) {
24624 var obj = {outer: null, inner: []};
24625 this.readers.gml._inherit.apply(this, [node, obj, container]);
24626 this.readChildNodes(node, obj);
24627 obj.inner.unshift(obj.outer);
24628 if(!container.components) {
24629 container.components = [];
24631 container.components.push(
24632 new OpenLayers.Geometry.Polygon(obj.inner)
24635 "LinearRing": function(node, obj) {
24636 var container = {};
24637 this.readers.gml._inherit.apply(this, [node, container]);
24638 this.readChildNodes(node, container);
24639 obj.components = [new OpenLayers.Geometry.LinearRing(
24643 "MultiPolygon": function(node, container) {
24644 var obj = {components: []};
24645 this.readers.gml._inherit.apply(this, [node, obj, container]);
24646 this.readChildNodes(node, obj);
24647 container.components = [
24648 new OpenLayers.Geometry.MultiPolygon(obj.components)
24651 "polygonMember": function(node, obj) {
24652 this.readChildNodes(node, obj);
24654 "GeometryCollection": function(node, container) {
24655 var obj = {components: []};
24656 this.readers.gml._inherit.apply(this, [node, obj, container]);
24657 this.readChildNodes(node, obj);
24658 container.components = [
24659 new OpenLayers.Geometry.Collection(obj.components)
24662 "geometryMember": function(node, obj) {
24663 this.readChildNodes(node, obj);
24667 "*": function(node, obj) {
24668 // The node can either be named like the featureType, or it
24669 // can be a child of the feature:featureType. Children can be
24670 // geometry or attributes.
24672 var local = node.localName || node.nodeName.split(":").pop();
24673 // Since an attribute can have the same name as the feature type
24674 // we only want to read the node as a feature if the parent
24675 // node can have feature nodes as children. In this case, the
24676 // obj.features property is set.
24677 if (obj.features) {
24678 if (!this.singleFeatureType &&
24679 (OpenLayers.Util.indexOf(this.featureType, local) !== -1)) {
24680 name = "_typeName";
24681 } else if(local === this.featureType) {
24682 name = "_typeName";
24685 // Assume attribute elements have one child node and that the child
24686 // is a text node. Otherwise assume it is a geometry node.
24687 if(node.childNodes.length == 0 ||
24688 (node.childNodes.length == 1 && node.firstChild.nodeType == 3)) {
24689 if(this.extractAttributes) {
24690 name = "_attribute";
24693 name = "_geometry";
24697 this.readers.feature[name].apply(this, [node, obj]);
24700 "_typeName": function(node, obj) {
24701 var container = {components: [], attributes: {}};
24702 this.readChildNodes(node, container);
24703 // look for common gml namespaced elements
24704 if(container.name) {
24705 container.attributes.name = container.name;
24707 var feature = new OpenLayers.Feature.Vector(
24708 container.components[0], container.attributes
24710 if (!this.singleFeatureType) {
24711 feature.type = node.nodeName.split(":").pop();
24712 feature.namespace = node.namespaceURI;
24714 var fid = node.getAttribute("fid") ||
24715 this.getAttributeNS(node, this.namespaces["gml"], "id");
24719 if(this.internalProjection && this.externalProjection &&
24720 feature.geometry) {
24721 feature.geometry.transform(
24722 this.externalProjection, this.internalProjection
24725 if(container.bounds) {
24726 feature.bounds = container.bounds;
24728 obj.features.push(feature);
24730 "_geometry": function(node, obj) {
24731 if (!this.geometryName) {
24732 this.geometryName = node.nodeName.split(":").pop();
24734 this.readChildNodes(node, obj);
24736 "_attribute": function(node, obj) {
24737 var local = node.localName || node.nodeName.split(":").pop();
24738 var value = this.getChildValue(node);
24739 obj.attributes[local] = value;
24743 "FeatureCollection": function(node, obj) {
24744 this.readChildNodes(node, obj);
24753 * features - {Array(<OpenLayers.Feature.Vector>) | OpenLayers.Feature.Vector}
24754 * An array of features or a single feature.
24757 * {String} Given an array of features, a doc with a gml:featureMembers
24758 * element will be returned. Given a single feature, a doc with a
24759 * gml:featureMember element will be returned.
24761 write: function(features) {
24763 if(OpenLayers.Util.isArray(features)) {
24764 name = "featureMembers";
24766 name = "featureMember";
24768 var root = this.writeNode("gml:" + name, features);
24769 this.setAttributeNS(
24770 root, this.namespaces["xsi"],
24771 "xsi:schemaLocation", this.schemaLocation
24774 return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
24778 * Property: writers
24779 * As a compliment to the readers property, this structure contains public
24780 * writing functions grouped by namespace alias and named like the
24781 * node names they produce.
24785 "featureMember": function(feature) {
24786 var node = this.createElementNSPlus("gml:featureMember");
24787 this.writeNode("feature:_typeName", feature, node);
24790 "MultiPoint": function(geometry) {
24791 var node = this.createElementNSPlus("gml:MultiPoint");
24792 var components = geometry.components || [geometry];
24793 for(var i=0, ii=components.length; i<ii; ++i) {
24794 this.writeNode("pointMember", components[i], node);
24798 "pointMember": function(geometry) {
24799 var node = this.createElementNSPlus("gml:pointMember");
24800 this.writeNode("Point", geometry, node);
24803 "MultiLineString": function(geometry) {
24804 var node = this.createElementNSPlus("gml:MultiLineString");
24805 var components = geometry.components || [geometry];
24806 for(var i=0, ii=components.length; i<ii; ++i) {
24807 this.writeNode("lineStringMember", components[i], node);
24811 "lineStringMember": function(geometry) {
24812 var node = this.createElementNSPlus("gml:lineStringMember");
24813 this.writeNode("LineString", geometry, node);
24816 "MultiPolygon": function(geometry) {
24817 var node = this.createElementNSPlus("gml:MultiPolygon");
24818 var components = geometry.components || [geometry];
24819 for(var i=0, ii=components.length; i<ii; ++i) {
24821 "polygonMember", components[i], node
24826 "polygonMember": function(geometry) {
24827 var node = this.createElementNSPlus("gml:polygonMember");
24828 this.writeNode("Polygon", geometry, node);
24831 "GeometryCollection": function(geometry) {
24832 var node = this.createElementNSPlus("gml:GeometryCollection");
24833 for(var i=0, len=geometry.components.length; i<len; ++i) {
24834 this.writeNode("geometryMember", geometry.components[i], node);
24838 "geometryMember": function(geometry) {
24839 var node = this.createElementNSPlus("gml:geometryMember");
24840 var child = this.writeNode("feature:_geometry", geometry);
24841 node.appendChild(child.firstChild);
24846 "_typeName": function(feature) {
24847 var node = this.createElementNSPlus("feature:" + this.featureType, {
24848 attributes: {fid: feature.fid}
24850 if(feature.geometry) {
24851 this.writeNode("feature:_geometry", feature.geometry, node);
24853 for(var name in feature.attributes) {
24854 var value = feature.attributes[name];
24855 if(value != null) {
24857 "feature:_attribute",
24858 {name: name, value: value}, node
24864 "_geometry": function(geometry) {
24865 if(this.externalProjection && this.internalProjection) {
24866 geometry = geometry.clone().transform(
24867 this.internalProjection, this.externalProjection
24870 var node = this.createElementNSPlus(
24871 "feature:" + this.geometryName
24873 var type = this.geometryTypes[geometry.CLASS_NAME];
24874 var child = this.writeNode("gml:" + type, geometry, node);
24876 child.setAttribute("srsName", this.srsName);
24880 "_attribute": function(obj) {
24881 return this.createElementNSPlus("feature:" + obj.name, {
24887 "FeatureCollection": function(features) {
24889 * This is only here because GML2 only describes abstract
24890 * feature collections. Typically, you would not be using
24891 * the GML format to write wfs elements. This just provides
24892 * some way to write out lists of features. GML3 defines the
24893 * featureMembers element, so that is used by default instead.
24895 var node = this.createElementNSPlus("wfs:FeatureCollection");
24896 for(var i=0, len=features.length; i<len; ++i) {
24897 this.writeNode("gml:featureMember", features[i], node);
24905 * Method: setGeometryTypes
24906 * Sets the <geometryTypes> mapping.
24908 setGeometryTypes: function() {
24909 this.geometryTypes = {
24910 "OpenLayers.Geometry.Point": "Point",
24911 "OpenLayers.Geometry.MultiPoint": "MultiPoint",
24912 "OpenLayers.Geometry.LineString": "LineString",
24913 "OpenLayers.Geometry.MultiLineString": "MultiLineString",
24914 "OpenLayers.Geometry.Polygon": "Polygon",
24915 "OpenLayers.Geometry.MultiPolygon": "MultiPolygon",
24916 "OpenLayers.Geometry.Collection": "GeometryCollection"
24920 CLASS_NAME: "OpenLayers.Format.GML.Base"
24923 /* ======================================================================
24924 OpenLayers/Format/GML/v2.js
24925 ====================================================================== */
24927 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
24928 * full list of contributors). Published under the 2-clause BSD license.
24929 * See license.txt in the OpenLayers distribution or repository for the
24930 * full text of the license. */
24933 * @requires OpenLayers/Format/GML/Base.js
24937 * Class: OpenLayers.Format.GML.v2
24938 * Parses GML version 2.
24941 * - <OpenLayers.Format.GML.Base>
24943 OpenLayers.Format.GML.v2 = OpenLayers.Class(OpenLayers.Format.GML.Base, {
24946 * Property: schemaLocation
24947 * {String} Schema location for a particular minor version.
24949 schemaLocation: "http://www.opengis.net/gml http://schemas.opengis.net/gml/2.1.2/feature.xsd",
24952 * Constructor: OpenLayers.Format.GML.v2
24953 * Create a parser for GML v2.
24956 * options - {Object} An optional object whose properties will be set on
24959 * Valid options properties:
24960 * featureType - {String} Local (without prefix) feature typeName (required).
24961 * featureNS - {String} Feature namespace (required).
24962 * geometryName - {String} Geometry element name.
24964 initialize: function(options) {
24965 OpenLayers.Format.GML.Base.prototype.initialize.apply(this, [options]);
24969 * Property: readers
24970 * Contains public functions, grouped by namespace prefix, that will
24971 * be applied when a namespaced node is found matching the function
24972 * name. The function will be applied in the scope of this parser
24973 * with two arguments: the node being read and a context object passed
24977 "gml": OpenLayers.Util.applyDefaults({
24978 "outerBoundaryIs": function(node, container) {
24980 this.readChildNodes(node, obj);
24981 container.outer = obj.components[0];
24983 "innerBoundaryIs": function(node, container) {
24985 this.readChildNodes(node, obj);
24986 container.inner.push(obj.components[0]);
24988 "Box": function(node, container) {
24990 this.readChildNodes(node, obj);
24991 if(!container.components) {
24992 container.components = [];
24994 var min = obj.points[0];
24995 var max = obj.points[1];
24996 container.components.push(
24997 new OpenLayers.Bounds(min.x, min.y, max.x, max.y)
25000 }, OpenLayers.Format.GML.Base.prototype.readers["gml"]),
25001 "feature": OpenLayers.Format.GML.Base.prototype.readers["feature"],
25002 "wfs": OpenLayers.Format.GML.Base.prototype.readers["wfs"]
25009 * features - {Array(<OpenLayers.Feature.Vector>) | OpenLayers.Feature.Vector}
25010 * An array of features or a single feature.
25013 * {String} Given an array of features, a doc with a gml:featureMembers
25014 * element will be returned. Given a single feature, a doc with a
25015 * gml:featureMember element will be returned.
25017 write: function(features) {
25019 if(OpenLayers.Util.isArray(features)) {
25020 // GML2 only has abstract feature collections
25021 // wfs provides a feature collection from a well-known schema
25022 name = "wfs:FeatureCollection";
25024 name = "gml:featureMember";
25026 var root = this.writeNode(name, features);
25027 this.setAttributeNS(
25028 root, this.namespaces["xsi"],
25029 "xsi:schemaLocation", this.schemaLocation
25032 return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
25036 * Property: writers
25037 * As a compliment to the readers property, this structure contains public
25038 * writing functions grouped by namespace alias and named like the
25039 * node names they produce.
25042 "gml": OpenLayers.Util.applyDefaults({
25043 "Point": function(geometry) {
25044 var node = this.createElementNSPlus("gml:Point");
25045 this.writeNode("coordinates", [geometry], node);
25048 "coordinates": function(points) {
25049 var numPoints = points.length;
25050 var parts = new Array(numPoints);
25052 for(var i=0; i<numPoints; ++i) {
25055 parts[i] = point.x + "," + point.y;
25057 parts[i] = point.y + "," + point.x;
25059 if(point.z != undefined) { // allow null or undefined
25060 parts[i] += "," + point.z;
25063 return this.createElementNSPlus("gml:coordinates", {
25065 decimal: ".", cs: ",", ts: " "
25067 value: (numPoints == 1) ? parts[0] : parts.join(" ")
25070 "LineString": function(geometry) {
25071 var node = this.createElementNSPlus("gml:LineString");
25072 this.writeNode("coordinates", geometry.components, node);
25075 "Polygon": function(geometry) {
25076 var node = this.createElementNSPlus("gml:Polygon");
25077 this.writeNode("outerBoundaryIs", geometry.components[0], node);
25078 for(var i=1; i<geometry.components.length; ++i) {
25080 "innerBoundaryIs", geometry.components[i], node
25085 "outerBoundaryIs": function(ring) {
25086 var node = this.createElementNSPlus("gml:outerBoundaryIs");
25087 this.writeNode("LinearRing", ring, node);
25090 "innerBoundaryIs": function(ring) {
25091 var node = this.createElementNSPlus("gml:innerBoundaryIs");
25092 this.writeNode("LinearRing", ring, node);
25095 "LinearRing": function(ring) {
25096 var node = this.createElementNSPlus("gml:LinearRing");
25097 this.writeNode("coordinates", ring.components, node);
25100 "Box": function(bounds) {
25101 var node = this.createElementNSPlus("gml:Box");
25102 this.writeNode("coordinates", [
25103 {x: bounds.left, y: bounds.bottom},
25104 {x: bounds.right, y: bounds.top}
25106 // srsName attribute is optional for gml:Box
25108 node.setAttribute("srsName", this.srsName);
25112 }, OpenLayers.Format.GML.Base.prototype.writers["gml"]),
25113 "feature": OpenLayers.Format.GML.Base.prototype.writers["feature"],
25114 "wfs": OpenLayers.Format.GML.Base.prototype.writers["wfs"]
25117 CLASS_NAME: "OpenLayers.Format.GML.v2"
25120 /* ======================================================================
25121 OpenLayers/Filter/Function.js
25122 ====================================================================== */
25124 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
25125 * full list of contributors). Published under the 2-clause BSD license.
25126 * See license.txt in the OpenLayers distribution or repository for the
25127 * full text of the license. */
25130 * @requires OpenLayers/Filter.js
25134 * Class: OpenLayers.Filter.Function
25135 * This class represents a filter function.
25136 * We are using this class for creation of complex
25137 * filters that can contain filter functions as values.
25138 * Nesting function as other functions parameter is supported.
25141 * - <OpenLayers.Filter>
25143 OpenLayers.Filter.Function = OpenLayers.Class(OpenLayers.Filter, {
25146 * APIProperty: name
25147 * {String} Name of the function.
25152 * APIProperty: params
25153 * {Array(<OpenLayers.Filter.Function> || String || Number)} Function parameters
25154 * For now support only other Functions, String or Number
25159 * Constructor: OpenLayers.Filter.Function
25160 * Creates a filter function.
25163 * options - {Object} An optional object with properties to set on the
25167 * {<OpenLayers.Filter.Function>}
25170 CLASS_NAME: "OpenLayers.Filter.Function"
25173 /* ======================================================================
25174 OpenLayers/BaseTypes/Date.js
25175 ====================================================================== */
25177 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
25178 * full list of contributors). Published under the 2-clause BSD license.
25179 * See license.txt in the OpenLayers distribution or repository for the
25180 * full text of the license. */
25183 * @requires OpenLayers/SingleFile.js
25187 * Namespace: OpenLayers.Date
25188 * Contains implementations of Date.parse and date.toISOString that match the
25189 * ECMAScript 5 specification for parsing RFC 3339 dates.
25190 * http://tools.ietf.org/html/rfc3339
25192 OpenLayers.Date = {
25195 * APIProperty: dateRegEx
25196 * The regex to be used for validating dates. You can provide your own
25197 * regex for instance for adding support for years before BC. Default
25198 * value is: /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:(?:T(\d{1,2}):(\d{2}):(\d{2}(?:\.\d+)?)(Z|(?:[+-]\d{1,2}(?::(\d{2}))?)))|Z)?$/
25200 dateRegEx: /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:(?:T(\d{1,2}):(\d{2}):(\d{2}(?:\.\d+)?)(Z|(?:[+-]\d{1,2}(?::(\d{2}))?)))|Z)?$/,
25203 * APIMethod: toISOString
25204 * Generates a string representing a date. The format of the string follows
25205 * the profile of ISO 8601 for date and time on the Internet (see
25206 * http://tools.ietf.org/html/rfc3339). If the toISOString method is
25207 * available on the Date prototype, that is used. The toISOString
25208 * method for Date instances is defined in ECMA-262.
25211 * date - {Date} A date object.
25214 * {String} A string representing the date (e.g.
25215 * "2010-08-07T16:58:23.123Z"). If the date does not have a valid time
25216 * (i.e. isNaN(date.getTime())) this method returns the string "Invalid
25217 * Date". The ECMA standard says the toISOString method should throw
25218 * RangeError in this case, but Firefox returns a string instead. For
25219 * best results, use isNaN(date.getTime()) to determine date validity
25220 * before generating date strings.
25222 toISOString: (function() {
25223 if ("toISOString" in Date.prototype) {
25224 return function(date) {
25225 return date.toISOString();
25228 return function(date) {
25230 if (isNaN(date.getTime())) {
25231 // ECMA-262 says throw RangeError, Firefox returns
25233 str = "Invalid Date";
25236 date.getUTCFullYear() + "-" +
25237 OpenLayers.Number.zeroPad(date.getUTCMonth() + 1, 2) + "-" +
25238 OpenLayers.Number.zeroPad(date.getUTCDate(), 2) + "T" +
25239 OpenLayers.Number.zeroPad(date.getUTCHours(), 2) + ":" +
25240 OpenLayers.Number.zeroPad(date.getUTCMinutes(), 2) + ":" +
25241 OpenLayers.Number.zeroPad(date.getUTCSeconds(), 2) + "." +
25242 OpenLayers.Number.zeroPad(date.getUTCMilliseconds(), 3) + "Z";
25252 * Generate a date object from a string. The format for the string follows
25253 * the profile of ISO 8601 for date and time on the Internet (see
25254 * http://tools.ietf.org/html/rfc3339). We don't call the native
25255 * Date.parse because of inconsistency between implmentations. In
25256 * Chrome, calling Date.parse with a string that doesn't contain any
25257 * indication of the timezone (e.g. "2011"), the date is interpreted
25258 * in local time. On Firefox, the assumption is UTC.
25261 * str - {String} A string representing the date (e.g.
25262 * "2010", "2010-08", "2010-08-07", "2010-08-07T16:58:23.123Z",
25263 * "2010-08-07T11:58:23.123-06").
25266 * {Date} A date object. If the string could not be parsed, an invalid
25267 * date is returned (i.e. isNaN(date.getTime())).
25269 parse: function(str) {
25271 var match = str.match(this.dateRegEx);
25272 if (match && (match[1] || match[7])) { // must have at least year or time
25273 var year = parseInt(match[1], 10) || 0;
25274 var month = (parseInt(match[2], 10) - 1) || 0;
25275 var day = parseInt(match[3], 10) || 1;
25276 date = new Date(Date.UTC(year, month, day));
25278 var type = match[7];
25280 var hours = parseInt(match[4], 10);
25281 var minutes = parseInt(match[5], 10);
25282 var secFrac = parseFloat(match[6]);
25283 var seconds = secFrac | 0;
25284 var milliseconds = Math.round(1000 * (secFrac - seconds));
25285 date.setUTCHours(hours, minutes, seconds, milliseconds);
25287 if (type !== "Z") {
25288 var hoursOffset = parseInt(type, 10);
25289 var minutesOffset = parseInt(match[8], 10) || 0;
25290 var offset = -1000 * (60 * (hoursOffset * 60) + minutesOffset * 60);
25291 date = new Date(date.getTime() + offset);
25295 date = new Date("invalid");
25300 /* ======================================================================
25301 OpenLayers/Format/Filter/v1.js
25302 ====================================================================== */
25304 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
25305 * full list of contributors). Published under the 2-clause BSD license.
25306 * See license.txt in the OpenLayers distribution or repository for the
25307 * full text of the license. */
25309 * @requires OpenLayers/Format/Filter.js
25310 * @requires OpenLayers/Format/XML.js
25311 * @requires OpenLayers/Filter/Function.js
25312 * @requires OpenLayers/BaseTypes/Date.js
25316 * Class: OpenLayers.Format.Filter.v1
25317 * Superclass for Filter version 1 parsers.
25320 * - <OpenLayers.Format.XML>
25322 OpenLayers.Format.Filter.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
25325 * Property: namespaces
25326 * {Object} Mapping of namespace aliases to namespace URIs.
25329 ogc: "http://www.opengis.net/ogc",
25330 gml: "http://www.opengis.net/gml",
25331 xlink: "http://www.w3.org/1999/xlink",
25332 xsi: "http://www.w3.org/2001/XMLSchema-instance"
25336 * Property: defaultPrefix
25338 defaultPrefix: "ogc",
25341 * Property: schemaLocation
25342 * {String} Schema location for a particular minor version.
25344 schemaLocation: null,
25347 * Constructor: OpenLayers.Format.Filter.v1
25348 * Instances of this class are not created directly. Use the
25349 * <OpenLayers.Format.Filter> constructor instead.
25352 * options - {Object} An optional object whose properties will be set on
25355 initialize: function(options) {
25356 OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
25363 * data - {DOMElement} A Filter document element.
25366 * {<OpenLayers.Filter>} A filter object.
25368 read: function(data) {
25370 this.readers.ogc["Filter"].apply(this, [data, obj]);
25375 * Property: readers
25376 * Contains public functions, grouped by namespace prefix, that will
25377 * be applied when a namespaced node is found matching the function
25378 * name. The function will be applied in the scope of this parser
25379 * with two arguments: the node being read and a context object passed
25384 "_expression": function(node) {
25385 // only the simplest of ogc:expression handled
25386 // "some text and an <PropertyName>attribute</PropertyName>"}
25387 var obj, value = "";
25388 for(var child=node.firstChild; child; child=child.nextSibling) {
25389 switch(child.nodeType) {
25391 obj = this.readNode(child);
25392 if (obj.property) {
25393 value += "${" + obj.property + "}";
25394 } else if (obj.value !== undefined) {
25395 value += obj.value;
25398 case 3: // text node
25399 case 4: // cdata section
25400 value += child.nodeValue;
25405 "Filter": function(node, parent) {
25406 // Filters correspond to subclasses of OpenLayers.Filter.
25407 // Since they contain information we don't persist, we
25408 // create a temporary object and then pass on the filter
25409 // (ogc:Filter) to the parent obj.
25414 this.readChildNodes(node, obj);
25415 if(obj.fids.length > 0) {
25416 parent.filter = new OpenLayers.Filter.FeatureId({
25419 } else if(obj.filters.length > 0) {
25420 parent.filter = obj.filters[0];
25423 "FeatureId": function(node, obj) {
25424 var fid = node.getAttribute("fid");
25426 obj.fids.push(fid);
25429 "And": function(node, obj) {
25430 var filter = new OpenLayers.Filter.Logical({
25431 type: OpenLayers.Filter.Logical.AND
25433 this.readChildNodes(node, filter);
25434 obj.filters.push(filter);
25436 "Or": function(node, obj) {
25437 var filter = new OpenLayers.Filter.Logical({
25438 type: OpenLayers.Filter.Logical.OR
25440 this.readChildNodes(node, filter);
25441 obj.filters.push(filter);
25443 "Not": function(node, obj) {
25444 var filter = new OpenLayers.Filter.Logical({
25445 type: OpenLayers.Filter.Logical.NOT
25447 this.readChildNodes(node, filter);
25448 obj.filters.push(filter);
25450 "PropertyIsLessThan": function(node, obj) {
25451 var filter = new OpenLayers.Filter.Comparison({
25452 type: OpenLayers.Filter.Comparison.LESS_THAN
25454 this.readChildNodes(node, filter);
25455 obj.filters.push(filter);
25457 "PropertyIsGreaterThan": function(node, obj) {
25458 var filter = new OpenLayers.Filter.Comparison({
25459 type: OpenLayers.Filter.Comparison.GREATER_THAN
25461 this.readChildNodes(node, filter);
25462 obj.filters.push(filter);
25464 "PropertyIsLessThanOrEqualTo": function(node, obj) {
25465 var filter = new OpenLayers.Filter.Comparison({
25466 type: OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO
25468 this.readChildNodes(node, filter);
25469 obj.filters.push(filter);
25471 "PropertyIsGreaterThanOrEqualTo": function(node, obj) {
25472 var filter = new OpenLayers.Filter.Comparison({
25473 type: OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO
25475 this.readChildNodes(node, filter);
25476 obj.filters.push(filter);
25478 "PropertyIsBetween": function(node, obj) {
25479 var filter = new OpenLayers.Filter.Comparison({
25480 type: OpenLayers.Filter.Comparison.BETWEEN
25482 this.readChildNodes(node, filter);
25483 obj.filters.push(filter);
25485 "Literal": function(node, obj) {
25486 obj.value = OpenLayers.String.numericIf(
25487 this.getChildValue(node), true);
25489 "PropertyName": function(node, filter) {
25490 filter.property = this.getChildValue(node);
25492 "LowerBoundary": function(node, filter) {
25493 filter.lowerBoundary = OpenLayers.String.numericIf(
25494 this.readers.ogc._expression.call(this, node), true);
25496 "UpperBoundary": function(node, filter) {
25497 filter.upperBoundary = OpenLayers.String.numericIf(
25498 this.readers.ogc._expression.call(this, node), true);
25500 "Intersects": function(node, obj) {
25501 this.readSpatial(node, obj, OpenLayers.Filter.Spatial.INTERSECTS);
25503 "Within": function(node, obj) {
25504 this.readSpatial(node, obj, OpenLayers.Filter.Spatial.WITHIN);
25506 "Contains": function(node, obj) {
25507 this.readSpatial(node, obj, OpenLayers.Filter.Spatial.CONTAINS);
25509 "DWithin": function(node, obj) {
25510 this.readSpatial(node, obj, OpenLayers.Filter.Spatial.DWITHIN);
25512 "Distance": function(node, obj) {
25513 obj.distance = parseInt(this.getChildValue(node));
25514 obj.distanceUnits = node.getAttribute("units");
25516 "Function": function(node, obj) {
25517 //TODO write decoder for it
25520 "PropertyIsNull": function(node, obj) {
25521 var filter = new OpenLayers.Filter.Comparison({
25522 type: OpenLayers.Filter.Comparison.IS_NULL
25524 this.readChildNodes(node, filter);
25525 obj.filters.push(filter);
25531 * Method: readSpatial
25533 * Read a {<OpenLayers.Filter.Spatial>} filter.
25536 * node - {DOMElement} A DOM element that contains an ogc:expression.
25537 * obj - {Object} The target object.
25538 * type - {String} One of the OpenLayers.Filter.Spatial.* constants.
25541 * {<OpenLayers.Filter.Spatial>} The created filter.
25543 readSpatial: function(node, obj, type) {
25544 var filter = new OpenLayers.Filter.Spatial({
25547 this.readChildNodes(node, filter);
25548 filter.value = filter.components[0];
25549 delete filter.components;
25550 obj.filters.push(filter);
25554 * APIMethod: encodeLiteral
25555 * Generates the string representation of a value for use in <Literal>
25556 * elements. The default encoder writes Date values as ISO 8601
25560 * value - {Object} Literal value to encode
25563 * {String} String representation of the provided value.
25565 encodeLiteral: function(value) {
25566 if (value instanceof Date) {
25567 value = OpenLayers.Date.toISOString(value);
25573 * Method: writeOgcExpression
25574 * Limited support for writing OGC expressions. Currently it supports
25575 * (<OpenLayers.Filter.Function> || String || Number)
25578 * value - (<OpenLayers.Filter.Function> || String || Number)
25579 * node - {DOMElement} A parent DOM element
25582 * {DOMElement} Updated node element.
25584 writeOgcExpression: function(value, node) {
25585 if (value instanceof OpenLayers.Filter.Function){
25586 this.writeNode("Function", value, node);
25588 this.writeNode("Literal", value, node);
25597 * filter - {<OpenLayers.Filter>} A filter object.
25600 * {DOMElement} An ogc:Filter element.
25602 write: function(filter) {
25603 return this.writers.ogc["Filter"].apply(this, [filter]);
25607 * Property: writers
25608 * As a compliment to the readers property, this structure contains public
25609 * writing functions grouped by namespace alias and named like the
25610 * node names they produce.
25614 "Filter": function(filter) {
25615 var node = this.createElementNSPlus("ogc:Filter");
25616 this.writeNode(this.getFilterType(filter), filter, node);
25619 "_featureIds": function(filter) {
25620 var node = this.createDocumentFragment();
25621 for (var i=0, ii=filter.fids.length; i<ii; ++i) {
25622 this.writeNode("ogc:FeatureId", filter.fids[i], node);
25626 "FeatureId": function(fid) {
25627 return this.createElementNSPlus("ogc:FeatureId", {
25628 attributes: {fid: fid}
25631 "And": function(filter) {
25632 var node = this.createElementNSPlus("ogc:And");
25634 for (var i=0, ii=filter.filters.length; i<ii; ++i) {
25635 childFilter = filter.filters[i];
25637 this.getFilterType(childFilter), childFilter, node
25642 "Or": function(filter) {
25643 var node = this.createElementNSPlus("ogc:Or");
25645 for (var i=0, ii=filter.filters.length; i<ii; ++i) {
25646 childFilter = filter.filters[i];
25648 this.getFilterType(childFilter), childFilter, node
25653 "Not": function(filter) {
25654 var node = this.createElementNSPlus("ogc:Not");
25655 var childFilter = filter.filters[0];
25657 this.getFilterType(childFilter), childFilter, node
25661 "PropertyIsLessThan": function(filter) {
25662 var node = this.createElementNSPlus("ogc:PropertyIsLessThan");
25663 // no ogc:expression handling for PropertyName for now
25664 this.writeNode("PropertyName", filter, node);
25665 // handle Literals or Functions for now
25666 this.writeOgcExpression(filter.value, node);
25669 "PropertyIsGreaterThan": function(filter) {
25670 var node = this.createElementNSPlus("ogc:PropertyIsGreaterThan");
25671 // no ogc:expression handling for PropertyName for now
25672 this.writeNode("PropertyName", filter, node);
25673 // handle Literals or Functions for now
25674 this.writeOgcExpression(filter.value, node);
25677 "PropertyIsLessThanOrEqualTo": function(filter) {
25678 var node = this.createElementNSPlus("ogc:PropertyIsLessThanOrEqualTo");
25679 // no ogc:expression handling for PropertyName for now
25680 this.writeNode("PropertyName", filter, node);
25681 // handle Literals or Functions for now
25682 this.writeOgcExpression(filter.value, node);
25685 "PropertyIsGreaterThanOrEqualTo": function(filter) {
25686 var node = this.createElementNSPlus("ogc:PropertyIsGreaterThanOrEqualTo");
25687 // no ogc:expression handling for PropertyName for now
25688 this.writeNode("PropertyName", filter, node);
25689 // handle Literals or Functions for now
25690 this.writeOgcExpression(filter.value, node);
25693 "PropertyIsBetween": function(filter) {
25694 var node = this.createElementNSPlus("ogc:PropertyIsBetween");
25695 // no ogc:expression handling for PropertyName for now
25696 this.writeNode("PropertyName", filter, node);
25697 this.writeNode("LowerBoundary", filter, node);
25698 this.writeNode("UpperBoundary", filter, node);
25701 "PropertyName": function(filter) {
25702 // no ogc:expression handling for now
25703 return this.createElementNSPlus("ogc:PropertyName", {
25704 value: filter.property
25707 "Literal": function(value) {
25708 var encode = this.encodeLiteral ||
25709 OpenLayers.Format.Filter.v1.prototype.encodeLiteral;
25710 return this.createElementNSPlus("ogc:Literal", {
25711 value: encode(value)
25714 "LowerBoundary": function(filter) {
25715 // handle Literals or Functions for now
25716 var node = this.createElementNSPlus("ogc:LowerBoundary");
25717 this.writeOgcExpression(filter.lowerBoundary, node);
25720 "UpperBoundary": function(filter) {
25721 // handle Literals or Functions for now
25722 var node = this.createElementNSPlus("ogc:UpperBoundary");
25723 this.writeNode("Literal", filter.upperBoundary, node);
25726 "INTERSECTS": function(filter) {
25727 return this.writeSpatial(filter, "Intersects");
25729 "WITHIN": function(filter) {
25730 return this.writeSpatial(filter, "Within");
25732 "CONTAINS": function(filter) {
25733 return this.writeSpatial(filter, "Contains");
25735 "DWITHIN": function(filter) {
25736 var node = this.writeSpatial(filter, "DWithin");
25737 this.writeNode("Distance", filter, node);
25740 "Distance": function(filter) {
25741 return this.createElementNSPlus("ogc:Distance", {
25743 units: filter.distanceUnits
25745 value: filter.distance
25748 "Function": function(filter) {
25749 var node = this.createElementNSPlus("ogc:Function", {
25754 var params = filter.params;
25755 for(var i=0, len=params.length; i<len; i++){
25756 this.writeOgcExpression(params[i], node);
25760 "PropertyIsNull": function(filter) {
25761 var node = this.createElementNSPlus("ogc:PropertyIsNull");
25762 this.writeNode("PropertyName", filter, node);
25769 * Method: getFilterType
25771 getFilterType: function(filter) {
25772 var filterType = this.filterMap[filter.type];
25774 throw "Filter writing not supported for rule type: " + filter.type;
25780 * Property: filterMap
25781 * {Object} Contains a member for each filter type. Values are node names
25782 * for corresponding OGC Filter child elements.
25788 "==": "PropertyIsEqualTo",
25789 "!=": "PropertyIsNotEqualTo",
25790 "<": "PropertyIsLessThan",
25791 ">": "PropertyIsGreaterThan",
25792 "<=": "PropertyIsLessThanOrEqualTo",
25793 ">=": "PropertyIsGreaterThanOrEqualTo",
25794 "..": "PropertyIsBetween",
25795 "~": "PropertyIsLike",
25796 "NULL": "PropertyIsNull",
25798 "DWITHIN": "DWITHIN",
25799 "WITHIN": "WITHIN",
25800 "CONTAINS": "CONTAINS",
25801 "INTERSECTS": "INTERSECTS",
25802 "FID": "_featureIds"
25805 CLASS_NAME: "OpenLayers.Format.Filter.v1"
25808 /* ======================================================================
25809 OpenLayers/Format/Filter/v1_0_0.js
25810 ====================================================================== */
25812 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
25813 * full list of contributors). Published under the 2-clause BSD license.
25814 * See license.txt in the OpenLayers distribution or repository for the
25815 * full text of the license. */
25818 * @requires OpenLayers/Format/GML/v2.js
25819 * @requires OpenLayers/Format/Filter/v1.js
25823 * Class: OpenLayers.Format.Filter.v1_0_0
25824 * Write ogc:Filter version 1.0.0.
25827 * - <OpenLayers.Format.GML.v2>
25828 * - <OpenLayers.Format.Filter.v1>
25830 OpenLayers.Format.Filter.v1_0_0 = OpenLayers.Class(
25831 OpenLayers.Format.GML.v2, OpenLayers.Format.Filter.v1, {
25834 * Constant: VERSION
25840 * Property: schemaLocation
25841 * {String} http://www.opengis.net/ogc/filter/1.0.0/filter.xsd
25843 schemaLocation: "http://www.opengis.net/ogc/filter/1.0.0/filter.xsd",
25846 * Constructor: OpenLayers.Format.Filter.v1_0_0
25847 * Instances of this class are not created directly. Use the
25848 * <OpenLayers.Format.Filter> constructor instead.
25851 * options - {Object} An optional object whose properties will be set on
25854 initialize: function(options) {
25855 OpenLayers.Format.GML.v2.prototype.initialize.apply(
25861 * Property: readers
25862 * Contains public functions, grouped by namespace prefix, that will
25863 * be applied when a namespaced node is found matching the function
25864 * name. The function will be applied in the scope of this parser
25865 * with two arguments: the node being read and a context object passed
25869 "ogc": OpenLayers.Util.applyDefaults({
25870 "PropertyIsEqualTo": function(node, obj) {
25871 var filter = new OpenLayers.Filter.Comparison({
25872 type: OpenLayers.Filter.Comparison.EQUAL_TO
25874 this.readChildNodes(node, filter);
25875 obj.filters.push(filter);
25877 "PropertyIsNotEqualTo": function(node, obj) {
25878 var filter = new OpenLayers.Filter.Comparison({
25879 type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO
25881 this.readChildNodes(node, filter);
25882 obj.filters.push(filter);
25884 "PropertyIsLike": function(node, obj) {
25885 var filter = new OpenLayers.Filter.Comparison({
25886 type: OpenLayers.Filter.Comparison.LIKE
25888 this.readChildNodes(node, filter);
25889 var wildCard = node.getAttribute("wildCard");
25890 var singleChar = node.getAttribute("singleChar");
25891 var esc = node.getAttribute("escape");
25892 filter.value2regex(wildCard, singleChar, esc);
25893 obj.filters.push(filter);
25895 }, OpenLayers.Format.Filter.v1.prototype.readers["ogc"]),
25896 "gml": OpenLayers.Format.GML.v2.prototype.readers["gml"],
25897 "feature": OpenLayers.Format.GML.v2.prototype.readers["feature"]
25901 * Property: writers
25902 * As a compliment to the readers property, this structure contains public
25903 * writing functions grouped by namespace alias and named like the
25904 * node names they produce.
25907 "ogc": OpenLayers.Util.applyDefaults({
25908 "PropertyIsEqualTo": function(filter) {
25909 var node = this.createElementNSPlus("ogc:PropertyIsEqualTo");
25910 // no ogc:expression handling for PropertyName for now
25911 this.writeNode("PropertyName", filter, node);
25912 // handle Literals or Functions for now
25913 this.writeOgcExpression(filter.value, node);
25916 "PropertyIsNotEqualTo": function(filter) {
25917 var node = this.createElementNSPlus("ogc:PropertyIsNotEqualTo");
25918 // no ogc:expression handling for PropertyName for now
25919 this.writeNode("PropertyName", filter, node);
25920 // handle Literals or Functions for now
25921 this.writeOgcExpression(filter.value, node);
25924 "PropertyIsLike": function(filter) {
25925 var node = this.createElementNSPlus("ogc:PropertyIsLike", {
25927 wildCard: "*", singleChar: ".", escape: "!"
25930 // no ogc:expression handling for now
25931 this.writeNode("PropertyName", filter, node);
25932 // convert regex string to ogc string
25933 this.writeNode("Literal", filter.regex2value(), node);
25936 "BBOX": function(filter) {
25937 var node = this.createElementNSPlus("ogc:BBOX");
25938 // PropertyName is mandatory in 1.0.0, but e.g. GeoServer also
25939 // accepts filters without it. When this is used with
25940 // OpenLayers.Protocol.WFS, OpenLayers.Format.WFST will set a
25941 // missing filter.property to the geometryName that is
25942 // configured with the protocol, which defaults to "the_geom".
25943 // So the only way to omit this mandatory property is to not
25944 // set the property on the filter and to set the geometryName
25945 // on the WFS protocol to null. The latter also happens when
25946 // the protocol is configured without a geometryName and a
25948 filter.property && this.writeNode("PropertyName", filter, node);
25949 var box = this.writeNode("gml:Box", filter.value, node);
25950 if(filter.projection) {
25951 box.setAttribute("srsName", filter.projection);
25955 }, OpenLayers.Format.Filter.v1.prototype.writers["ogc"]),
25956 "gml": OpenLayers.Format.GML.v2.prototype.writers["gml"],
25957 "feature": OpenLayers.Format.GML.v2.prototype.writers["feature"]
25961 * Method: writeSpatial
25963 * Read a {<OpenLayers.Filter.Spatial>} filter and converts it into XML.
25966 * filter - {<OpenLayers.Filter.Spatial>} The filter.
25967 * name - {String} Name of the generated XML element.
25970 * {DOMElement} The created XML element.
25972 writeSpatial: function(filter, name) {
25973 var node = this.createElementNSPlus("ogc:"+name);
25974 this.writeNode("PropertyName", filter, node);
25975 if(filter.value instanceof OpenLayers.Filter.Function) {
25976 this.writeNode("Function", filter.value, node);
25979 if(filter.value instanceof OpenLayers.Geometry) {
25980 child = this.writeNode("feature:_geometry", filter.value).firstChild;
25982 child = this.writeNode("gml:Box", filter.value);
25984 if(filter.projection) {
25985 child.setAttribute("srsName", filter.projection);
25987 node.appendChild(child);
25993 CLASS_NAME: "OpenLayers.Format.Filter.v1_0_0"
25996 /* ======================================================================
25997 OpenLayers/Format/WFST/v1_0_0.js
25998 ====================================================================== */
26000 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
26001 * full list of contributors). Published under the 2-clause BSD license.
26002 * See license.txt in the OpenLayers distribution or repository for the
26003 * full text of the license. */
26006 * @requires OpenLayers/Format/WFST/v1.js
26007 * @requires OpenLayers/Format/Filter/v1_0_0.js
26011 * Class: OpenLayers.Format.WFST.v1_0_0
26012 * A format for creating WFS v1.0.0 transactions. Create a new instance with the
26013 * <OpenLayers.Format.WFST.v1_0_0> constructor.
26016 * - <OpenLayers.Format.Filter.v1_0_0>
26017 * - <OpenLayers.Format.WFST.v1>
26019 OpenLayers.Format.WFST.v1_0_0 = OpenLayers.Class(
26020 OpenLayers.Format.Filter.v1_0_0, OpenLayers.Format.WFST.v1, {
26023 * Property: version
26024 * {String} WFS version number.
26029 * APIProperty: srsNameInQuery
26030 * {Boolean} If true the reference system is passed in Query requests
26031 * via the "srsName" attribute to the "wfs:Query" element, this
26032 * property defaults to false as it isn't WFS 1.0.0 compliant.
26034 srsNameInQuery: false,
26037 * Property: schemaLocations
26038 * {Object} Properties are namespace aliases, values are schema locations.
26041 "wfs": "http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd"
26045 * Constructor: OpenLayers.Format.WFST.v1_0_0
26046 * A class for parsing and generating WFS v1.0.0 transactions.
26049 * options - {Object} Optional object whose properties will be set on the
26052 * Valid options properties:
26053 * featureType - {String} Local (without prefix) feature typeName (required).
26054 * featureNS - {String} Feature namespace (optional).
26055 * featurePrefix - {String} Feature namespace alias (optional - only used
26056 * if featureNS is provided). Default is 'feature'.
26057 * geometryName - {String} Name of geometry attribute. Default is 'the_geom'.
26059 initialize: function(options) {
26060 OpenLayers.Format.Filter.v1_0_0.prototype.initialize.apply(this, [options]);
26061 OpenLayers.Format.WFST.v1.prototype.initialize.apply(this, [options]);
26066 * Shorthand for applying one of the named readers given the node
26067 * namespace and local name. Readers take two args (node, obj) and
26068 * generally extend or modify the second.
26071 * node - {DOMElement} The node to be read (required).
26072 * obj - {Object} The object to be modified (optional).
26073 * first - {Boolean} Should be set to true for the first node read. This
26074 * is usually the readNode call in the read method. Without this being
26075 * set, auto-configured properties will stick on subsequent reads.
26078 * {Object} The input object, modified (or a new one if none was provided).
26080 readNode: function(node, obj, first) {
26081 // Not the superclass, only the mixin classes inherit from
26082 // Format.GML.v2. We need this because we don't want to get readNode
26083 // from the superclass's superclass, which is OpenLayers.Format.XML.
26084 return OpenLayers.Format.GML.v2.prototype.readNode.apply(this, arguments);
26088 * Property: readers
26089 * Contains public functions, grouped by namespace prefix, that will
26090 * be applied when a namespaced node is found matching the function
26091 * name. The function will be applied in the scope of this parser
26092 * with two arguments: the node being read and a context object passed
26096 "wfs": OpenLayers.Util.applyDefaults({
26097 "WFS_TransactionResponse": function(node, obj) {
26098 obj.insertIds = [];
26099 obj.success = false;
26100 this.readChildNodes(node, obj);
26102 "InsertResult": function(node, container) {
26103 var obj = {fids: []};
26104 this.readChildNodes(node, obj);
26105 container.insertIds = container.insertIds.concat(obj.fids);
26107 "TransactionResult": function(node, obj) {
26108 this.readChildNodes(node, obj);
26110 "Status": function(node, obj) {
26111 this.readChildNodes(node, obj);
26113 "SUCCESS": function(node, obj) {
26114 obj.success = true;
26116 }, OpenLayers.Format.WFST.v1.prototype.readers["wfs"]),
26117 "gml": OpenLayers.Format.GML.v2.prototype.readers["gml"],
26118 "feature": OpenLayers.Format.GML.v2.prototype.readers["feature"],
26119 "ogc": OpenLayers.Format.Filter.v1_0_0.prototype.readers["ogc"]
26123 * Property: writers
26124 * As a compliment to the readers property, this structure contains public
26125 * writing functions grouped by namespace alias and named like the
26126 * node names they produce.
26129 "wfs": OpenLayers.Util.applyDefaults({
26130 "Query": function(options) {
26131 options = OpenLayers.Util.extend({
26132 featureNS: this.featureNS,
26133 featurePrefix: this.featurePrefix,
26134 featureType: this.featureType,
26135 srsName: this.srsName,
26136 srsNameInQuery: this.srsNameInQuery
26138 var prefix = options.featurePrefix;
26139 var node = this.createElementNSPlus("wfs:Query", {
26141 typeName: (prefix ? prefix + ":" : "") +
26142 options.featureType
26145 if(options.srsNameInQuery && options.srsName) {
26146 node.setAttribute("srsName", options.srsName);
26148 if(options.featureNS) {
26149 node.setAttribute("xmlns:" + prefix, options.featureNS);
26151 if(options.propertyNames) {
26152 for(var i=0,len = options.propertyNames.length; i<len; i++) {
26154 "ogc:PropertyName",
26155 {property: options.propertyNames[i]},
26160 if(options.filter) {
26161 this.setFilterProperty(options.filter);
26162 this.writeNode("ogc:Filter", options.filter, node);
26166 }, OpenLayers.Format.WFST.v1.prototype.writers["wfs"]),
26167 "gml": OpenLayers.Format.GML.v2.prototype.writers["gml"],
26168 "feature": OpenLayers.Format.GML.v2.prototype.writers["feature"],
26169 "ogc": OpenLayers.Format.Filter.v1_0_0.prototype.writers["ogc"]
26172 CLASS_NAME: "OpenLayers.Format.WFST.v1_0_0"
26174 /* ======================================================================
26175 OpenLayers/Renderer/Elements.js
26176 ====================================================================== */
26178 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
26179 * full list of contributors). Published under the 2-clause BSD license.
26180 * See license.txt in the OpenLayers distribution or repository for the
26181 * full text of the license. */
26184 * @requires OpenLayers/Renderer.js
26188 * Class: OpenLayers.ElementsIndexer
26189 * This class takes care of figuring out which order elements should be
26190 * placed in the DOM based on given indexing methods.
26192 OpenLayers.ElementsIndexer = OpenLayers.Class({
26195 * Property: maxZIndex
26196 * {Integer} This is the largest-most z-index value for a node
26197 * contained within the indexer.
26203 * {Array<String>} This is an array of node id's stored in the
26204 * order that they should show up on screen. Id's higher up in the
26205 * array (higher array index) represent nodes with higher z-indeces.
26210 * Property: indices
26211 * {Object} This is a hash that maps node ids to their z-index value
26212 * stored in the indexer. This is done to make finding a nodes z-index
26218 * Property: compare
26219 * {Function} This is the function used to determine placement of
26220 * of a new node within the indexer. If null, this defaults to to
26221 * the Z_ORDER_DRAWING_ORDER comparison method.
26226 * APIMethod: initialize
26227 * Create a new indexer with
26230 * yOrdering - {Boolean} Whether to use y-ordering.
26232 initialize: function(yOrdering) {
26234 this.compare = yOrdering ?
26235 OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER :
26236 OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER;
26242 * APIMethod: insert
26243 * Insert a new node into the indexer. In order to find the correct
26244 * positioning for the node to be inserted, this method uses a binary
26245 * search. This makes inserting O(log(n)).
26248 * newNode - {DOMElement} The new node to be inserted.
26251 * {DOMElement} the node before which we should insert our newNode, or
26252 * null if newNode can just be appended.
26254 insert: function(newNode) {
26255 // If the node is known to the indexer, remove it so we can
26256 // recalculate where it should go.
26257 if (this.exists(newNode)) {
26258 this.remove(newNode);
26261 var nodeId = newNode.id;
26263 this.determineZIndex(newNode);
26265 var leftIndex = -1;
26266 var rightIndex = this.order.length;
26269 while (rightIndex - leftIndex > 1) {
26270 middle = parseInt((leftIndex + rightIndex) / 2);
26272 var placement = this.compare(this, newNode,
26273 OpenLayers.Util.getElement(this.order[middle]));
26275 if (placement > 0) {
26276 leftIndex = middle;
26278 rightIndex = middle;
26282 this.order.splice(rightIndex, 0, nodeId);
26283 this.indices[nodeId] = this.getZIndex(newNode);
26285 // If the new node should be before another in the index
26286 // order, return the node before which we have to insert the new one;
26287 // else, return null to indicate that the new node can be appended.
26288 return this.getNextElement(rightIndex);
26292 * APIMethod: remove
26295 * node - {DOMElement} The node to be removed.
26297 remove: function(node) {
26298 var nodeId = node.id;
26299 var arrayIndex = OpenLayers.Util.indexOf(this.order, nodeId);
26300 if (arrayIndex >= 0) {
26301 // Remove it from the order array, as well as deleting the node
26302 // from the indeces hash.
26303 this.order.splice(arrayIndex, 1);
26304 delete this.indices[nodeId];
26306 // Reset the maxium z-index based on the last item in the
26308 if (this.order.length > 0) {
26309 var lastId = this.order[this.order.length - 1];
26310 this.maxZIndex = this.indices[lastId];
26312 this.maxZIndex = 0;
26320 clear: function() {
26323 this.maxZIndex = 0;
26327 * APIMethod: exists
26330 * node - {DOMElement} The node to test for existence.
26333 * {Boolean} Whether or not the node exists in the indexer?
26335 exists: function(node) {
26336 return (this.indices[node.id] != null);
26340 * APIMethod: getZIndex
26341 * Get the z-index value for the current node from the node data itself.
26344 * node - {DOMElement} The node whose z-index to get.
26347 * {Integer} The z-index value for the specified node (from the node
26350 getZIndex: function(node) {
26351 return node._style.graphicZIndex;
26355 * Method: determineZIndex
26356 * Determine the z-index for the current node if there isn't one,
26357 * and set the maximum value if we've found a new maximum.
26360 * node - {DOMElement}
26362 determineZIndex: function(node) {
26363 var zIndex = node._style.graphicZIndex;
26365 // Everything must have a zIndex. If none is specified,
26366 // this means the user *must* (hint: assumption) want this
26367 // node to succomb to drawing order. To enforce drawing order
26368 // over all indexing methods, we'll create a new z-index that's
26369 // greater than any currently in the indexer.
26370 if (zIndex == null) {
26371 zIndex = this.maxZIndex;
26372 node._style.graphicZIndex = zIndex;
26373 } else if (zIndex > this.maxZIndex) {
26374 this.maxZIndex = zIndex;
26379 * APIMethod: getNextElement
26380 * Get the next element in the order stack.
26383 * index - {Integer} The index of the current node in this.order.
26386 * {DOMElement} the node following the index passed in, or
26389 getNextElement: function(index) {
26390 var nextIndex = index + 1;
26391 if (nextIndex < this.order.length) {
26392 var nextElement = OpenLayers.Util.getElement(this.order[nextIndex]);
26393 if (nextElement == undefined) {
26394 nextElement = this.getNextElement(nextIndex);
26396 return nextElement;
26402 CLASS_NAME: "OpenLayers.ElementsIndexer"
26406 * Namespace: OpenLayers.ElementsIndexer.IndexingMethods
26407 * These are the compare methods for figuring out where a new node should be
26408 * placed within the indexer. These methods are very similar to general
26409 * sorting methods in that they return -1, 0, and 1 to specify the
26410 * direction in which new nodes fall in the ordering.
26412 OpenLayers.ElementsIndexer.IndexingMethods = {
26416 * This compare method is used by other comparison methods.
26417 * It can be used individually for ordering, but is not recommended,
26418 * because it doesn't subscribe to drawing order.
26421 * indexer - {<OpenLayers.ElementsIndexer>}
26422 * newNode - {DOMElement}
26423 * nextNode - {DOMElement}
26428 Z_ORDER: function(indexer, newNode, nextNode) {
26429 var newZIndex = indexer.getZIndex(newNode);
26433 var nextZIndex = indexer.getZIndex(nextNode);
26434 returnVal = newZIndex - nextZIndex;
26441 * APIMethod: Z_ORDER_DRAWING_ORDER
26442 * This method orders nodes by their z-index, but does so in a way
26443 * that, if there are other nodes with the same z-index, the newest
26444 * drawn will be the front most within that z-index. This is the
26445 * default indexing method.
26448 * indexer - {<OpenLayers.ElementsIndexer>}
26449 * newNode - {DOMElement}
26450 * nextNode - {DOMElement}
26455 Z_ORDER_DRAWING_ORDER: function(indexer, newNode, nextNode) {
26456 var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(
26462 // Make Z_ORDER subscribe to drawing order by pushing it above
26463 // all of the other nodes with the same z-index.
26464 if (nextNode && returnVal == 0) {
26472 * APIMethod: Z_ORDER_Y_ORDER
26473 * This one should really be called Z_ORDER_Y_ORDER_DRAWING_ORDER, as it
26474 * best describes which ordering methods have precedence (though, the
26475 * name would be too long). This method orders nodes by their z-index,
26476 * but does so in a way that, if there are other nodes with the same
26477 * z-index, the nodes with the lower y position will be "closer" than
26478 * those with a higher y position. If two nodes have the exact same y
26479 * position, however, then this method will revert to using drawing
26480 * order to decide placement.
26483 * indexer - {<OpenLayers.ElementsIndexer>}
26484 * newNode - {DOMElement}
26485 * nextNode - {DOMElement}
26490 Z_ORDER_Y_ORDER: function(indexer, newNode, nextNode) {
26491 var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(
26497 if (nextNode && returnVal === 0) {
26498 var result = nextNode._boundsBottom - newNode._boundsBottom;
26499 returnVal = (result === 0) ? 1 : result;
26507 * Class: OpenLayers.Renderer.Elements
26508 * This is another virtual class in that it should never be instantiated by
26509 * itself as a Renderer. It exists because there is *tons* of shared
26510 * functionality between different vector libraries which use nodes/elements
26511 * as a base for rendering vectors.
26513 * The highlevel bits of code that are implemented here are the adding and
26514 * removing of geometries, which is essentially the same for any
26515 * element-based renderer. The details of creating each node and drawing the
26516 * paths are of course different, but the machinery is the same.
26519 * - <OpenLayers.Renderer>
26521 OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
26524 * Property: rendererRoot
26527 rendererRoot: null,
26536 * Property: vectorRoot
26542 * Property: textRoot
26554 * Property: xOffset
26555 * {Number} Offset to apply to the renderer viewport translation in x
26556 * direction. If the renderer extent's center is on the right of the
26557 * dateline (i.e. exceeds the world bounds), we shift the viewport to the
26558 * left by one world width. This avoids that features disappear from the
26559 * map viewport. Because our dateline handling logic in other places
26560 * ensures that extents crossing the dateline always have a center
26561 * exceeding the world bounds on the left, we need this offset to make sure
26562 * that the same is true for the renderer extent in pixel space as well.
26567 * Property: rightOfDateLine
26568 * {Boolean} Keeps track of the location of the map extent relative to the
26569 * date line. The <setExtent> method compares this value (which is the one
26570 * from the previous <setExtent> call) with the current position of the map
26571 * extent relative to the date line and updates the xOffset when the extent
26572 * has moved from one side of the date line to the other.
26576 * Property: Indexer
26577 * {<OpenLayers.ElementIndexer>} An instance of OpenLayers.ElementsIndexer
26578 * created upon initialization if the zIndexing or yOrdering options
26579 * passed to this renderer's constructor are set to true.
26584 * Constant: BACKGROUND_ID_SUFFIX
26587 BACKGROUND_ID_SUFFIX: "_background",
26590 * Constant: LABEL_ID_SUFFIX
26593 LABEL_ID_SUFFIX: "_label",
26596 * Constant: LABEL_OUTLINE_SUFFIX
26599 LABEL_OUTLINE_SUFFIX: "_outline",
26602 * Constructor: OpenLayers.Renderer.Elements
26605 * containerID - {String}
26606 * options - {Object} options for this renderer.
26608 * Supported options are:
26609 * yOrdering - {Boolean} Whether to use y-ordering
26610 * zIndexing - {Boolean} Whether to use z-indexing. Will be ignored
26611 * if yOrdering is set to true.
26613 initialize: function(containerID, options) {
26614 OpenLayers.Renderer.prototype.initialize.apply(this, arguments);
26616 this.rendererRoot = this.createRenderRoot();
26617 this.root = this.createRoot("_root");
26618 this.vectorRoot = this.createRoot("_vroot");
26619 this.textRoot = this.createRoot("_troot");
26621 this.root.appendChild(this.vectorRoot);
26622 this.root.appendChild(this.textRoot);
26624 this.rendererRoot.appendChild(this.root);
26625 this.container.appendChild(this.rendererRoot);
26627 if(options && (options.zIndexing || options.yOrdering)) {
26628 this.indexer = new OpenLayers.ElementsIndexer(options.yOrdering);
26635 destroy: function() {
26639 this.rendererRoot = null;
26643 OpenLayers.Renderer.prototype.destroy.apply(this, arguments);
26648 * Remove all the elements from the root
26650 clear: function() {
26652 var root = this.vectorRoot;
26654 while (child = root.firstChild) {
26655 root.removeChild(child);
26658 root = this.textRoot;
26660 while (child = root.firstChild) {
26661 root.removeChild(child);
26664 if (this.indexer) {
26665 this.indexer.clear();
26670 * Method: setExtent
26671 * Set the visible part of the layer.
26674 * extent - {<OpenLayers.Bounds>}
26675 * resolutionChanged - {Boolean}
26678 * {Boolean} true to notify the layer that the new extent does not exceed
26679 * the coordinate range, and the features will not need to be redrawn.
26682 setExtent: function(extent, resolutionChanged) {
26683 var coordSysUnchanged = OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);
26684 var resolution = this.getResolution();
26685 if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
26686 var rightOfDateLine,
26687 ratio = extent.getWidth() / this.map.getExtent().getWidth(),
26688 extent = extent.scale(1 / ratio),
26689 world = this.map.getMaxExtent();
26690 if (world.right > extent.left && world.right < extent.right) {
26691 rightOfDateLine = true;
26692 } else if (world.left > extent.left && world.left < extent.right) {
26693 rightOfDateLine = false;
26695 if (rightOfDateLine !== this.rightOfDateLine || resolutionChanged) {
26696 coordSysUnchanged = false;
26697 this.xOffset = rightOfDateLine === true ?
26698 world.getWidth() / resolution : 0;
26700 this.rightOfDateLine = rightOfDateLine;
26702 return coordSysUnchanged;
26706 * Method: getNodeType
26707 * This function is in charge of asking the specific renderer which type
26708 * of node to create for the given geometry and style. All geometries
26709 * in an Elements-based renderer consist of one node and some
26710 * attributes. We have the nodeFactory() function which creates a node
26711 * for us, but it takes a 'type' as input, and that is precisely what
26712 * this function tells us.
26715 * geometry - {<OpenLayers.Geometry>}
26719 * {String} The corresponding node type for the specified geometry
26721 getNodeType: function(geometry, style) { },
26724 * Method: drawGeometry
26725 * Draw the geometry, creating new nodes, setting paths, setting style,
26726 * setting featureId on the node. This method should only be called
26727 * by the renderer itself.
26730 * geometry - {<OpenLayers.Geometry>}
26732 * featureId - {String}
26735 * {Boolean} true if the geometry has been drawn completely; null if
26736 * incomplete; false otherwise
26738 drawGeometry: function(geometry, style, featureId) {
26739 var className = geometry.CLASS_NAME;
26740 var rendered = true;
26741 if ((className == "OpenLayers.Geometry.Collection") ||
26742 (className == "OpenLayers.Geometry.MultiPoint") ||
26743 (className == "OpenLayers.Geometry.MultiLineString") ||
26744 (className == "OpenLayers.Geometry.MultiPolygon")) {
26745 for (var i = 0, len=geometry.components.length; i<len; i++) {
26746 rendered = this.drawGeometry(
26747 geometry.components[i], style, featureId) && rendered;
26753 var removeBackground = false;
26754 if (style.display != "none") {
26755 if (style.backgroundGraphic) {
26756 this.redrawBackgroundNode(geometry.id, geometry, style,
26759 removeBackground = true;
26761 rendered = this.redrawNode(geometry.id, geometry, style,
26764 if (rendered == false) {
26765 var node = document.getElementById(geometry.id);
26767 if (node._style.backgroundGraphic) {
26768 removeBackground = true;
26770 node.parentNode.removeChild(node);
26773 if (removeBackground) {
26774 var node = document.getElementById(
26775 geometry.id + this.BACKGROUND_ID_SUFFIX);
26777 node.parentNode.removeChild(node);
26784 * Method: redrawNode
26788 * geometry - {<OpenLayers.Geometry>}
26790 * featureId - {String}
26793 * {Boolean} true if the complete geometry could be drawn, null if parts of
26794 * the geometry could not be drawn, false otherwise
26796 redrawNode: function(id, geometry, style, featureId) {
26797 style = this.applyDefaultSymbolizer(style);
26798 // Get the node if it's already on the map.
26799 var node = this.nodeFactory(id, this.getNodeType(geometry, style));
26801 // Set the data for the node, then draw it.
26802 node._featureId = featureId;
26803 node._boundsBottom = geometry.getBounds().bottom;
26804 node._geometryClass = geometry.CLASS_NAME;
26805 node._style = style;
26807 var drawResult = this.drawGeometryNode(node, geometry, style);
26808 if(drawResult === false) {
26812 node = drawResult.node;
26814 // Insert the node into the indexer so it can show us where to
26815 // place it. Note that this operation is O(log(n)). If there's a
26816 // performance problem (when dragging, for instance) this is
26817 // likely where it would be.
26818 if (this.indexer) {
26819 var insert = this.indexer.insert(node);
26821 this.vectorRoot.insertBefore(node, insert);
26823 this.vectorRoot.appendChild(node);
26826 // if there's no indexer, simply append the node to root,
26827 // but only if the node is a new one
26828 if (node.parentNode !== this.vectorRoot){
26829 this.vectorRoot.appendChild(node);
26833 this.postDraw(node);
26835 return drawResult.complete;
26839 * Method: redrawBackgroundNode
26840 * Redraws the node using special 'background' style properties. Basically
26841 * just calls redrawNode(), but instead of directly using the
26842 * 'externalGraphic', 'graphicXOffset', 'graphicYOffset', and
26843 * 'graphicZIndex' properties directly from the specified 'style'
26844 * parameter, we create a new style object and set those properties
26845 * from the corresponding 'background'-prefixed properties from
26846 * specified 'style' parameter.
26850 * geometry - {<OpenLayers.Geometry>}
26852 * featureId - {String}
26855 * {Boolean} true if the complete geometry could be drawn, null if parts of
26856 * the geometry could not be drawn, false otherwise
26858 redrawBackgroundNode: function(id, geometry, style, featureId) {
26859 var backgroundStyle = OpenLayers.Util.extend({}, style);
26861 // Set regular style attributes to apply to the background styles.
26862 backgroundStyle.externalGraphic = backgroundStyle.backgroundGraphic;
26863 backgroundStyle.graphicXOffset = backgroundStyle.backgroundXOffset;
26864 backgroundStyle.graphicYOffset = backgroundStyle.backgroundYOffset;
26865 backgroundStyle.graphicZIndex = backgroundStyle.backgroundGraphicZIndex;
26866 backgroundStyle.graphicWidth = backgroundStyle.backgroundWidth || backgroundStyle.graphicWidth;
26867 backgroundStyle.graphicHeight = backgroundStyle.backgroundHeight || backgroundStyle.graphicHeight;
26869 // Erase background styles.
26870 backgroundStyle.backgroundGraphic = null;
26871 backgroundStyle.backgroundXOffset = null;
26872 backgroundStyle.backgroundYOffset = null;
26873 backgroundStyle.backgroundGraphicZIndex = null;
26875 return this.redrawNode(
26876 id + this.BACKGROUND_ID_SUFFIX,
26884 * Method: drawGeometryNode
26885 * Given a node, draw a geometry on the specified layer.
26886 * node and geometry are required arguments, style is optional.
26887 * This method is only called by the render itself.
26890 * node - {DOMElement}
26891 * geometry - {<OpenLayers.Geometry>}
26895 * {Object} a hash with properties "node" (the drawn node) and "complete"
26896 * (null if parts of the geometry could not be drawn, false if nothing
26899 drawGeometryNode: function(node, geometry, style) {
26900 style = style || node._style;
26903 'isFilled': style.fill === undefined ?
26906 'isStroked': style.stroke === undefined ?
26907 !!style.strokeWidth :
26911 switch (geometry.CLASS_NAME) {
26912 case "OpenLayers.Geometry.Point":
26913 if(style.graphic === false) {
26914 options.isFilled = false;
26915 options.isStroked = false;
26917 drawn = this.drawPoint(node, geometry);
26919 case "OpenLayers.Geometry.LineString":
26920 options.isFilled = false;
26921 drawn = this.drawLineString(node, geometry);
26923 case "OpenLayers.Geometry.LinearRing":
26924 drawn = this.drawLinearRing(node, geometry);
26926 case "OpenLayers.Geometry.Polygon":
26927 drawn = this.drawPolygon(node, geometry);
26929 case "OpenLayers.Geometry.Rectangle":
26930 drawn = this.drawRectangle(node, geometry);
26936 node._options = options;
26939 //TBD simplify this
26940 if (drawn != false) {
26942 node: this.setStyle(node, style, options, geometry),
26952 * Things that have do be done after the geometry node is appended
26953 * to its parent node. To be overridden by subclasses.
26956 * node - {DOMElement}
26958 postDraw: function(node) {},
26961 * Method: drawPoint
26962 * Virtual function for drawing Point Geometry.
26963 * Should be implemented by subclasses.
26964 * This method is only called by the renderer itself.
26967 * node - {DOMElement}
26968 * geometry - {<OpenLayers.Geometry>}
26971 * {DOMElement} or false if the renderer could not draw the point
26973 drawPoint: function(node, geometry) {},
26976 * Method: drawLineString
26977 * Virtual function for drawing LineString Geometry.
26978 * Should be implemented by subclasses.
26979 * This method is only called by the renderer itself.
26982 * node - {DOMElement}
26983 * geometry - {<OpenLayers.Geometry>}
26986 * {DOMElement} or null if the renderer could not draw all components of
26987 * the linestring, or false if nothing could be drawn
26989 drawLineString: function(node, geometry) {},
26992 * Method: drawLinearRing
26993 * Virtual function for drawing LinearRing Geometry.
26994 * Should be implemented by subclasses.
26995 * This method is only called by the renderer itself.
26998 * node - {DOMElement}
26999 * geometry - {<OpenLayers.Geometry>}
27002 * {DOMElement} or null if the renderer could not draw all components
27003 * of the linear ring, or false if nothing could be drawn
27005 drawLinearRing: function(node, geometry) {},
27008 * Method: drawPolygon
27009 * Virtual function for drawing Polygon Geometry.
27010 * Should be implemented by subclasses.
27011 * This method is only called by the renderer itself.
27014 * node - {DOMElement}
27015 * geometry - {<OpenLayers.Geometry>}
27018 * {DOMElement} or null if the renderer could not draw all components
27019 * of the polygon, or false if nothing could be drawn
27021 drawPolygon: function(node, geometry) {},
27024 * Method: drawRectangle
27025 * Virtual function for drawing Rectangle Geometry.
27026 * Should be implemented by subclasses.
27027 * This method is only called by the renderer itself.
27030 * node - {DOMElement}
27031 * geometry - {<OpenLayers.Geometry>}
27034 * {DOMElement} or false if the renderer could not draw the rectangle
27036 drawRectangle: function(node, geometry) {},
27039 * Method: drawCircle
27040 * Virtual function for drawing Circle Geometry.
27041 * Should be implemented by subclasses.
27042 * This method is only called by the renderer itself.
27045 * node - {DOMElement}
27046 * geometry - {<OpenLayers.Geometry>}
27049 * {DOMElement} or false if the renderer could not draw the circle
27051 drawCircle: function(node, geometry) {},
27054 * Method: removeText
27058 * featureId - {String}
27060 removeText: function(featureId) {
27061 var label = document.getElementById(featureId + this.LABEL_ID_SUFFIX);
27063 this.textRoot.removeChild(label);
27065 var outline = document.getElementById(featureId + this.LABEL_OUTLINE_SUFFIX);
27067 this.textRoot.removeChild(outline);
27072 * Method: getFeatureIdFromEvent
27075 * evt - {Object} An <OpenLayers.Event> object
27078 * {String} A feature id or undefined.
27080 getFeatureIdFromEvent: function(evt) {
27081 var target = evt.target;
27082 var useElement = target && target.correspondingUseElement;
27083 var node = useElement ? useElement : (target || evt.srcElement);
27084 return node._featureId;
27088 * Method: eraseGeometry
27089 * Erase a geometry from the renderer. In the case of a multi-geometry,
27090 * we cycle through and recurse on ourselves. Otherwise, we look for a
27091 * node with the geometry.id, destroy its geometry, and remove it from
27095 * geometry - {<OpenLayers.Geometry>}
27096 * featureId - {String}
27098 eraseGeometry: function(geometry, featureId) {
27099 if ((geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPoint") ||
27100 (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiLineString") ||
27101 (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPolygon") ||
27102 (geometry.CLASS_NAME == "OpenLayers.Geometry.Collection")) {
27103 for (var i=0, len=geometry.components.length; i<len; i++) {
27104 this.eraseGeometry(geometry.components[i], featureId);
27107 var element = OpenLayers.Util.getElement(geometry.id);
27108 if (element && element.parentNode) {
27109 if (element.geometry) {
27110 element.geometry.destroy();
27111 element.geometry = null;
27113 element.parentNode.removeChild(element);
27115 if (this.indexer) {
27116 this.indexer.remove(element);
27119 if (element._style.backgroundGraphic) {
27120 var backgroundId = geometry.id + this.BACKGROUND_ID_SUFFIX;
27121 var bElem = OpenLayers.Util.getElement(backgroundId);
27122 if (bElem && bElem.parentNode) {
27123 // No need to destroy the geometry since the element and the background
27124 // node share the same geometry.
27125 bElem.parentNode.removeChild(bElem);
27133 * Method: nodeFactory
27134 * Create new node of the specified type, with the (optional) specified id.
27136 * If node already exists with same ID and a different type, we remove it
27137 * and then call ourselves again to recreate it.
27141 * type - {String} type Kind of node to draw.
27144 * {DOMElement} A new node of the given type and id.
27146 nodeFactory: function(id, type) {
27147 var node = OpenLayers.Util.getElement(id);
27149 if (!this.nodeTypeCompare(node, type)) {
27150 node.parentNode.removeChild(node);
27151 node = this.nodeFactory(id, type);
27154 node = this.createNode(type, id);
27160 * Method: nodeTypeCompare
27163 * node - {DOMElement}
27164 * type - {String} Kind of node
27167 * {Boolean} Whether or not the specified node is of the specified type
27168 * This function must be overridden by subclasses.
27170 nodeTypeCompare: function(node, type) {},
27173 * Method: createNode
27176 * type - {String} Kind of node to draw.
27177 * id - {String} Id for node.
27180 * {DOMElement} A new node of the given type and id.
27181 * This function must be overridden by subclasses.
27183 createNode: function(type, id) {},
27187 * moves this renderer's root to a different renderer.
27190 * renderer - {<OpenLayers.Renderer>} target renderer for the moved root
27192 moveRoot: function(renderer) {
27193 var root = this.root;
27194 if(renderer.root.parentNode == this.rendererRoot) {
27195 root = renderer.root;
27197 root.parentNode.removeChild(root);
27198 renderer.rendererRoot.appendChild(root);
27202 * Method: getRenderLayerId
27203 * Gets the layer that this renderer's output appears on. If moveRoot was
27204 * used, this will be different from the id of the layer containing the
27205 * features rendered by this renderer.
27208 * {String} the id of the output layer.
27210 getRenderLayerId: function() {
27211 return this.root.parentNode.parentNode.id;
27215 * Method: isComplexSymbol
27216 * Determines if a symbol cannot be rendered using drawCircle
27219 * graphicName - {String}
27222 * {Boolean} true if the symbol is complex, false if not
27224 isComplexSymbol: function(graphicName) {
27225 return (graphicName != "circle") && !!graphicName;
27228 CLASS_NAME: "OpenLayers.Renderer.Elements"
27231 /* ======================================================================
27232 OpenLayers/Control/Zoom.js
27233 ====================================================================== */
27235 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
27236 * full list of contributors). Published under the 2-clause BSD license.
27237 * See license.txt in the OpenLayers distribution or repository for the
27238 * full text of the license. */
27241 * @requires OpenLayers/Control.js
27242 * @requires OpenLayers/Events/buttonclick.js
27246 * Class: OpenLayers.Control.Zoom
27247 * The Zoom control is a pair of +/- links for zooming in and out.
27250 * - <OpenLayers.Control>
27252 OpenLayers.Control.Zoom = OpenLayers.Class(OpenLayers.Control, {
27255 * APIProperty: zoomInText
27257 * Text for zoom-in link. Default is "+".
27262 * APIProperty: zoomInId
27264 * Instead of having the control create a zoom in link, you can provide
27265 * the identifier for an anchor element already added to the document.
27266 * By default, an element with id "olZoomInLink" will be searched for
27267 * and used if it exists.
27269 zoomInId: "olZoomInLink",
27272 * APIProperty: zoomOutText
27274 * Text for zoom-out link. Default is "\u2212".
27276 zoomOutText: "\u2212",
27279 * APIProperty: zoomOutId
27281 * Instead of having the control create a zoom out link, you can provide
27282 * the identifier for an anchor element already added to the document.
27283 * By default, an element with id "olZoomOutLink" will be searched for
27284 * and used if it exists.
27286 zoomOutId: "olZoomOutLink",
27292 * {DOMElement} A reference to the DOMElement containing the zoom links.
27295 var div = OpenLayers.Control.prototype.draw.apply(this),
27296 links = this.getOrCreateLinks(div),
27297 zoomIn = links.zoomIn,
27298 zoomOut = links.zoomOut,
27299 eventsInstance = this.map.events;
27301 if (zoomOut.parentNode !== div) {
27302 eventsInstance = this.events;
27303 eventsInstance.attachToElement(zoomOut.parentNode);
27305 eventsInstance.register("buttonclick", this, this.onZoomClick);
27307 this.zoomInLink = zoomIn;
27308 this.zoomOutLink = zoomOut;
27313 * Method: getOrCreateLinks
27316 * el - {DOMElement}
27319 * {Object} Object with zoomIn and zoomOut properties referencing links.
27321 getOrCreateLinks: function(el) {
27322 var zoomIn = document.getElementById(this.zoomInId),
27323 zoomOut = document.getElementById(this.zoomOutId);
27325 zoomIn = document.createElement("a");
27326 zoomIn.href = "#zoomIn";
27327 zoomIn.appendChild(document.createTextNode(this.zoomInText));
27328 zoomIn.className = "olControlZoomIn";
27329 el.appendChild(zoomIn);
27331 OpenLayers.Element.addClass(zoomIn, "olButton");
27333 zoomOut = document.createElement("a");
27334 zoomOut.href = "#zoomOut";
27335 zoomOut.appendChild(document.createTextNode(this.zoomOutText));
27336 zoomOut.className = "olControlZoomOut";
27337 el.appendChild(zoomOut);
27339 OpenLayers.Element.addClass(zoomOut, "olButton");
27341 zoomIn: zoomIn, zoomOut: zoomOut
27346 * Method: onZoomClick
27347 * Called when zoomin/out link is clicked.
27349 onZoomClick: function(evt) {
27350 var button = evt.buttonElement;
27351 if (button === this.zoomInLink) {
27353 } else if (button === this.zoomOutLink) {
27354 this.map.zoomOut();
27362 destroy: function() {
27364 this.map.events.unregister("buttonclick", this, this.onZoomClick);
27366 delete this.zoomInLink;
27367 delete this.zoomOutLink;
27368 OpenLayers.Control.prototype.destroy.apply(this);
27371 CLASS_NAME: "OpenLayers.Control.Zoom"
27373 /* ======================================================================
27374 OpenLayers/Protocol.js
27375 ====================================================================== */
27377 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
27378 * full list of contributors). Published under the 2-clause BSD license.
27379 * See license.txt in the OpenLayers distribution or repository for the
27380 * full text of the license. */
27383 * @requires OpenLayers/BaseTypes/Class.js
27387 * Class: OpenLayers.Protocol
27388 * Abstract vector layer protocol class. Not to be instantiated directly. Use
27389 * one of the protocol subclasses instead.
27391 OpenLayers.Protocol = OpenLayers.Class({
27395 * {<OpenLayers.Format>} The format used by this protocol.
27400 * Property: options
27401 * {Object} Any options sent to the constructor.
27406 * Property: autoDestroy
27407 * {Boolean} The creator of the protocol can set autoDestroy to false
27408 * to fully control when the protocol is destroyed. Defaults to
27414 * Property: defaultFilter
27415 * {<OpenLayers.Filter>} Optional default filter to read requests
27417 defaultFilter: null,
27420 * Constructor: OpenLayers.Protocol
27421 * Abstract class for vector protocols. Create instances of a subclass.
27424 * options - {Object} Optional object whose properties will be set on the
27427 initialize: function(options) {
27428 options = options || {};
27429 OpenLayers.Util.extend(this, options);
27430 this.options = options;
27434 * Method: mergeWithDefaultFilter
27435 * Merge filter passed to the read method with the default one
27438 * filter - {<OpenLayers.Filter>}
27440 mergeWithDefaultFilter: function(filter) {
27442 if (filter && this.defaultFilter) {
27443 merged = new OpenLayers.Filter.Logical({
27444 type: OpenLayers.Filter.Logical.AND,
27445 filters: [this.defaultFilter, filter]
27448 merged = filter || this.defaultFilter || undefined;
27454 * APIMethod: destroy
27455 * Clean up the protocol.
27457 destroy: function() {
27458 this.options = null;
27459 this.format = null;
27464 * Construct a request for reading new features.
27467 * options - {Object} Optional object for configuring the request.
27470 * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
27471 * object, the same object will be passed to the callback function passed
27472 * if one exists in the options object.
27474 read: function(options) {
27475 options = options || {};
27476 options.filter = this.mergeWithDefaultFilter(options.filter);
27481 * APIMethod: create
27482 * Construct a request for writing newly created features.
27485 * features - {Array({<OpenLayers.Feature.Vector>})} or
27486 * {<OpenLayers.Feature.Vector>}
27487 * options - {Object} Optional object for configuring the request.
27490 * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
27491 * object, the same object will be passed to the callback function passed
27492 * if one exists in the options object.
27494 create: function() {
27498 * APIMethod: update
27499 * Construct a request updating modified features.
27502 * features - {Array({<OpenLayers.Feature.Vector>})} or
27503 * {<OpenLayers.Feature.Vector>}
27504 * options - {Object} Optional object for configuring the request.
27507 * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
27508 * object, the same object will be passed to the callback function passed
27509 * if one exists in the options object.
27511 update: function() {
27515 * APIMethod: delete
27516 * Construct a request deleting a removed feature.
27519 * feature - {<OpenLayers.Feature.Vector>}
27520 * options - {Object} Optional object for configuring the request.
27523 * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
27524 * object, the same object will be passed to the callback function passed
27525 * if one exists in the options object.
27527 "delete": function() {
27531 * APIMethod: commit
27532 * Go over the features and for each take action
27533 * based on the feature state. Possible actions are create,
27534 * update and delete.
27537 * features - {Array({<OpenLayers.Feature.Vector>})}
27538 * options - {Object} Object whose possible keys are "create", "update",
27539 * "delete", "callback" and "scope", the values referenced by the
27540 * first three are objects as passed to the "create", "update", and
27541 * "delete" methods, the value referenced by the "callback" key is
27542 * a function which is called when the commit operation is complete
27543 * using the scope referenced by the "scope" key.
27546 * {Array({<OpenLayers.Protocol.Response>})} An array of
27547 * <OpenLayers.Protocol.Response> objects.
27549 commit: function() {
27554 * Abort an ongoing request.
27557 * response - {<OpenLayers.Protocol.Response>}
27559 abort: function(response) {
27563 * Method: createCallback
27564 * Returns a function that applies the given public method with resp and
27565 * options arguments.
27568 * method - {Function} The method to be applied by the callback.
27569 * response - {<OpenLayers.Protocol.Response>} The protocol response object.
27570 * options - {Object} Options sent to the protocol method
27572 createCallback: function(method, response, options) {
27573 return OpenLayers.Function.bind(function() {
27574 method.apply(this, [response, options]);
27578 CLASS_NAME: "OpenLayers.Protocol"
27582 * Class: OpenLayers.Protocol.Response
27583 * Protocols return Response objects to their users.
27585 OpenLayers.Protocol.Response = OpenLayers.Class({
27588 * {Number} - OpenLayers.Protocol.Response.SUCCESS or
27589 * OpenLayers.Protocol.Response.FAILURE
27594 * Property: requestType
27595 * {String} The type of request this response corresponds to. Either
27596 * "create", "read", "update" or "delete".
27602 * {Boolean} - true if this is the last response expected in a commit,
27603 * false otherwise, defaults to true.
27608 * Property: features
27609 * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}
27610 * The features returned in the response by the server. Depending on the
27611 * protocol's read payload, either features or data will be populated.
27618 * The data returned in the response by the server. Depending on the
27619 * protocol's read payload, either features or data will be populated.
27624 * Property: reqFeatures
27625 * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}
27626 * The features provided by the user and placed in the request by the
27638 * {Object} The error object in case a service exception was encountered.
27643 * Constructor: OpenLayers.Protocol.Response
27646 * options - {Object} Optional object whose properties will be set on the
27649 initialize: function(options) {
27650 OpenLayers.Util.extend(this, options);
27657 * {Boolean} - true on success, false otherwise
27659 success: function() {
27660 return this.code > 0;
27663 CLASS_NAME: "OpenLayers.Protocol.Response"
27666 OpenLayers.Protocol.Response.SUCCESS = 1;
27667 OpenLayers.Protocol.Response.FAILURE = 0;
27668 /* ======================================================================
27669 OpenLayers/Protocol/WFS.js
27670 ====================================================================== */
27672 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
27673 * full list of contributors). Published under the 2-clause BSD license.
27674 * See license.txt in the OpenLayers distribution or repository for the
27675 * full text of the license. */
27678 * @requires OpenLayers/Protocol.js
27682 * Class: OpenLayers.Protocol.WFS
27683 * Used to create a versioned WFS protocol. Default version is 1.0.0.
27686 * {<OpenLayers.Protocol>} A WFS protocol of the given version.
27690 * var protocol = new OpenLayers.Protocol.WFS({
27691 * version: "1.1.0",
27692 * url: "http://demo.opengeo.org/geoserver/wfs",
27693 * featureType: "tasmania_roads",
27694 * featureNS: "http://www.openplans.org/topp",
27695 * geometryName: "the_geom"
27699 * See the protocols for specific WFS versions for more detail.
27701 OpenLayers.Protocol.WFS = function(options) {
27702 options = OpenLayers.Util.applyDefaults(
27703 options, OpenLayers.Protocol.WFS.DEFAULTS
27705 var cls = OpenLayers.Protocol.WFS["v"+options.version.replace(/\./g, "_")];
27707 throw "Unsupported WFS version: " + options.version;
27709 return new cls(options);
27713 * Function: fromWMSLayer
27714 * Convenience function to create a WFS protocol from a WMS layer. This makes
27715 * the assumption that a WFS requests can be issued at the same URL as
27716 * WMS requests and that a WFS featureType exists with the same name as the
27719 * This function is designed to auto-configure <url>, <featureType>,
27720 * <featurePrefix> and <srsName> for WFS <version> 1.1.0. Note that
27721 * srsName matching with the WMS layer will not work with WFS 1.0.0.
27724 * layer - {<OpenLayers.Layer.WMS>} WMS layer that has a matching WFS
27725 * FeatureType at the same server url with the same typename.
27726 * options - {Object} Default properties to be set on the protocol.
27729 * {<OpenLayers.Protocol.WFS>}
27731 OpenLayers.Protocol.WFS.fromWMSLayer = function(layer, options) {
27732 var typeName, featurePrefix;
27733 var param = layer.params["LAYERS"];
27734 var parts = (OpenLayers.Util.isArray(param) ? param[0] : param).split(":");
27735 if(parts.length > 1) {
27736 featurePrefix = parts[0];
27738 typeName = parts.pop();
27739 var protocolOptions = {
27741 featureType: typeName,
27742 featurePrefix: featurePrefix,
27743 srsName: layer.projection && layer.projection.getCode() ||
27744 layer.map && layer.map.getProjectionObject().getCode(),
27747 return new OpenLayers.Protocol.WFS(OpenLayers.Util.applyDefaults(
27748 options, protocolOptions
27753 * Constant: OpenLayers.Protocol.WFS.DEFAULTS
27755 OpenLayers.Protocol.WFS.DEFAULTS = {
27758 /* ======================================================================
27759 OpenLayers/Layer/Markers.js
27760 ====================================================================== */
27762 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
27763 * full list of contributors). Published under the 2-clause BSD license.
27764 * See license.txt in the OpenLayers distribution or repository for the
27765 * full text of the license. */
27769 * @requires OpenLayers/Layer.js
27773 * Class: OpenLayers.Layer.Markers
27776 * - <OpenLayers.Layer>
27778 OpenLayers.Layer.Markers = OpenLayers.Class(OpenLayers.Layer, {
27781 * APIProperty: isBaseLayer
27782 * {Boolean} Markers layer is never a base layer.
27784 isBaseLayer: false,
27787 * APIProperty: markers
27788 * {Array(<OpenLayers.Marker>)} internal marker list
27795 * {Boolean} internal state of drawing. This is a workaround for the fact
27796 * that the map does not call moveTo with a zoomChanged when the map is
27797 * first starting up. This lets us catch the case where we have *never*
27798 * drawn the layer, and draw it even if the zoom hasn't changed.
27803 * Constructor: OpenLayers.Layer.Markers
27804 * Create a Markers layer.
27808 * options - {Object} Hashtable of extra options to tag onto the layer
27810 initialize: function(name, options) {
27811 OpenLayers.Layer.prototype.initialize.apply(this, arguments);
27816 * APIMethod: destroy
27818 destroy: function() {
27819 this.clearMarkers();
27820 this.markers = null;
27821 OpenLayers.Layer.prototype.destroy.apply(this, arguments);
27825 * APIMethod: setOpacity
27826 * Sets the opacity for all the markers.
27829 * opacity - {Float}
27831 setOpacity: function(opacity) {
27832 if (opacity != this.opacity) {
27833 this.opacity = opacity;
27834 for (var i=0, len=this.markers.length; i<len; i++) {
27835 this.markers[i].setOpacity(this.opacity);
27844 * bounds - {<OpenLayers.Bounds>}
27845 * zoomChanged - {Boolean}
27846 * dragging - {Boolean}
27848 moveTo:function(bounds, zoomChanged, dragging) {
27849 OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
27851 if (zoomChanged || !this.drawn) {
27852 for(var i=0, len=this.markers.length; i<len; i++) {
27853 this.drawMarker(this.markers[i]);
27860 * APIMethod: addMarker
27863 * marker - {<OpenLayers.Marker>}
27865 addMarker: function(marker) {
27866 this.markers.push(marker);
27868 if (this.opacity < 1) {
27869 marker.setOpacity(this.opacity);
27872 if (this.map && this.map.getExtent()) {
27873 marker.map = this.map;
27874 this.drawMarker(marker);
27879 * APIMethod: removeMarker
27882 * marker - {<OpenLayers.Marker>}
27884 removeMarker: function(marker) {
27885 if (this.markers && this.markers.length) {
27886 OpenLayers.Util.removeItem(this.markers, marker);
27892 * Method: clearMarkers
27893 * This method removes all markers from a layer. The markers are not
27894 * destroyed by this function, but are removed from the list of markers.
27896 clearMarkers: function() {
27897 if (this.markers != null) {
27898 while(this.markers.length > 0) {
27899 this.removeMarker(this.markers[0]);
27905 * Method: drawMarker
27906 * Calculate the pixel location for the marker, create it, and
27907 * add it to the layer's div
27910 * marker - {<OpenLayers.Marker>}
27912 drawMarker: function(marker) {
27913 var px = this.map.getLayerPxFromLonLat(marker.lonlat);
27915 marker.display(false);
27917 if (!marker.isDrawn()) {
27918 var markerImg = marker.draw(px);
27919 this.div.appendChild(markerImg);
27920 } else if(marker.icon) {
27921 marker.icon.moveTo(px);
27927 * APIMethod: getDataExtent
27928 * Calculates the max extent which includes all of the markers.
27931 * {<OpenLayers.Bounds>}
27933 getDataExtent: function () {
27934 var maxExtent = null;
27936 if ( this.markers && (this.markers.length > 0)) {
27937 var maxExtent = new OpenLayers.Bounds();
27938 for(var i=0, len=this.markers.length; i<len; i++) {
27939 var marker = this.markers[i];
27940 maxExtent.extend(marker.lonlat);
27947 CLASS_NAME: "OpenLayers.Layer.Markers"
27949 /* ======================================================================
27950 OpenLayers/Strategy/BBOX.js
27951 ====================================================================== */
27953 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
27954 * full list of contributors). Published under the 2-clause BSD license.
27955 * See license.txt in the OpenLayers distribution or repository for the
27956 * full text of the license. */
27959 * @requires OpenLayers/Strategy.js
27960 * @requires OpenLayers/Filter/Spatial.js
27964 * Class: OpenLayers.Strategy.BBOX
27965 * A simple strategy that reads new features when the viewport invalidates
27969 * - <OpenLayers.Strategy>
27971 OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, {
27975 * {<OpenLayers.Bounds>} The current data bounds (in the same projection
27976 * as the layer - not always the same projection as the map).
27981 * Property: resolution
27982 * {Float} The current data resolution.
27987 * APIProperty: ratio
27988 * {Float} The ratio of the data bounds to the viewport bounds (in each
27989 * dimension). Default is 2.
27994 * Property: resFactor
27995 * {Float} Optional factor used to determine when previously requested
27996 * features are invalid. If set, the resFactor will be compared to the
27997 * resolution of the previous request to the current map resolution.
27998 * If resFactor > (old / new) and 1/resFactor < (old / new). If you
27999 * set a resFactor of 1, data will be requested every time the
28000 * resolution changes. If you set a resFactor of 3, data will be
28001 * requested if the old resolution is 3 times the new, or if the new is
28002 * 3 times the old. If the old bounds do not contain the new bounds
28003 * new data will always be requested (with or without considering
28009 * Property: response
28010 * {<OpenLayers.Protocol.Response>} The protocol response object returned
28011 * by the layer protocol.
28016 * Constructor: OpenLayers.Strategy.BBOX
28017 * Create a new BBOX strategy.
28020 * options - {Object} Optional object whose properties will be set on the
28026 * Set up strategy with regard to reading new batches of remote data.
28029 * {Boolean} The strategy was successfully activated.
28031 activate: function() {
28032 var activated = OpenLayers.Strategy.prototype.activate.call(this);
28034 this.layer.events.on({
28035 "moveend": this.update,
28036 "refresh": this.update,
28037 "visibilitychanged": this.update,
28046 * Method: deactivate
28047 * Tear down strategy with regard to reading new batches of remote data.
28050 * {Boolean} The strategy was successfully deactivated.
28052 deactivate: function() {
28053 var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
28055 this.layer.events.un({
28056 "moveend": this.update,
28057 "refresh": this.update,
28058 "visibilitychanged": this.update,
28062 return deactivated;
28067 * Callback function called on "moveend" or "refresh" layer events.
28070 * options - {Object} Optional object whose properties will determine
28071 * the behaviour of this Strategy
28073 * Valid options include:
28074 * force - {Boolean} if true, new data must be unconditionally read.
28075 * noAbort - {Boolean} if true, do not abort previous requests.
28077 update: function(options) {
28078 var mapBounds = this.getMapBounds();
28079 if (mapBounds !== null && ((options && options.force) ||
28080 (this.layer.visibility && this.layer.calculateInRange() && this.invalidBounds(mapBounds)))) {
28081 this.calculateBounds(mapBounds);
28082 this.resolution = this.layer.map.getResolution();
28083 this.triggerRead(options);
28088 * Method: getMapBounds
28089 * Get the map bounds expressed in the same projection as this layer.
28092 * {<OpenLayers.Bounds>} Map bounds in the projection of the layer.
28094 getMapBounds: function() {
28095 if (this.layer.map === null) {
28098 var bounds = this.layer.map.getExtent();
28099 if(bounds && !this.layer.projection.equals(
28100 this.layer.map.getProjectionObject())) {
28101 bounds = bounds.clone().transform(
28102 this.layer.map.getProjectionObject(), this.layer.projection
28109 * Method: invalidBounds
28110 * Determine whether the previously requested set of features is invalid.
28111 * This occurs when the new map bounds do not contain the previously
28112 * requested bounds. In addition, if <resFactor> is set, it will be
28116 * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be
28117 * retrieved from the map object if not provided
28122 invalidBounds: function(mapBounds) {
28124 mapBounds = this.getMapBounds();
28126 var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds);
28127 if(!invalid && this.resFactor) {
28128 var ratio = this.resolution / this.layer.map.getResolution();
28129 invalid = (ratio >= this.resFactor || ratio <= (1 / this.resFactor));
28135 * Method: calculateBounds
28138 * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be
28139 * retrieved from the map object if not provided
28141 calculateBounds: function(mapBounds) {
28143 mapBounds = this.getMapBounds();
28145 var center = mapBounds.getCenterLonLat();
28146 var dataWidth = mapBounds.getWidth() * this.ratio;
28147 var dataHeight = mapBounds.getHeight() * this.ratio;
28148 this.bounds = new OpenLayers.Bounds(
28149 center.lon - (dataWidth / 2),
28150 center.lat - (dataHeight / 2),
28151 center.lon + (dataWidth / 2),
28152 center.lat + (dataHeight / 2)
28157 * Method: triggerRead
28160 * options - {Object} Additional options for the protocol's read method
28164 * {<OpenLayers.Protocol.Response>} The protocol response object
28165 * returned by the layer protocol.
28167 triggerRead: function(options) {
28168 if (this.response && !(options && options.noAbort === true)) {
28169 this.layer.protocol.abort(this.response);
28170 this.layer.events.triggerEvent("loadend");
28172 var evt = {filter: this.createFilter()};
28173 this.layer.events.triggerEvent("loadstart", evt);
28174 this.response = this.layer.protocol.read(
28175 OpenLayers.Util.applyDefaults({
28176 filter: evt.filter,
28177 callback: this.merge,
28183 * Method: createFilter
28184 * Creates a spatial BBOX filter. If the layer that this strategy belongs
28185 * to has a filter property, this filter will be combined with the BBOX
28189 * {<OpenLayers.Filter>} The filter object.
28191 createFilter: function() {
28192 var filter = new OpenLayers.Filter.Spatial({
28193 type: OpenLayers.Filter.Spatial.BBOX,
28194 value: this.bounds,
28195 projection: this.layer.projection
28197 if (this.layer.filter) {
28198 filter = new OpenLayers.Filter.Logical({
28199 type: OpenLayers.Filter.Logical.AND,
28200 filters: [this.layer.filter, filter]
28208 * Given a list of features, determine which ones to add to the layer.
28209 * If the layer projection differs from the map projection, features
28210 * will be transformed from the layer projection to the map projection.
28213 * resp - {<OpenLayers.Protocol.Response>} The response object passed
28216 merge: function(resp) {
28217 this.layer.destroyFeatures();
28218 if (resp.success()) {
28219 var features = resp.features;
28220 if(features && features.length > 0) {
28221 var remote = this.layer.projection;
28222 var local = this.layer.map.getProjectionObject();
28223 if(!local.equals(remote)) {
28225 for(var i=0, len=features.length; i<len; ++i) {
28226 geom = features[i].geometry;
28228 geom.transform(remote, local);
28232 this.layer.addFeatures(features);
28235 this.bounds = null;
28237 this.response = null;
28238 this.layer.events.triggerEvent("loadend", {response: resp});
28241 CLASS_NAME: "OpenLayers.Strategy.BBOX"
28243 /* ======================================================================
28244 OpenLayers/Handler/Feature.js
28245 ====================================================================== */
28247 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
28248 * full list of contributors). Published under the 2-clause BSD license.
28249 * See license.txt in the OpenLayers distribution or repository for the
28250 * full text of the license. */
28254 * @requires OpenLayers/Handler.js
28258 * Class: OpenLayers.Handler.Feature
28259 * Handler to respond to mouse events related to a drawn feature. Callbacks
28260 * with the following keys will be notified of the following events
28261 * associated with features: click, clickout, over, out, and dblclick.
28263 * This handler stops event propagation for mousedown and mouseup if those
28264 * browser events target features that can be selected.
28267 * - <OpenLayers.Handler>
28269 OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, {
28272 * Property: EVENTMAP
28273 * {Object} A object mapping the browser events to objects with callback
28274 * keys for in and out.
28277 'click': {'in': 'click', 'out': 'clickout'},
28278 'mousemove': {'in': 'over', 'out': 'out'},
28279 'dblclick': {'in': 'dblclick', 'out': null},
28280 'mousedown': {'in': null, 'out': null},
28281 'mouseup': {'in': null, 'out': null},
28282 'touchstart': {'in': 'click', 'out': 'clickout'}
28286 * Property: feature
28287 * {<OpenLayers.Feature.Vector>} The last feature that was hovered.
28292 * Property: lastFeature
28293 * {<OpenLayers.Feature.Vector>} The last feature that was handled.
28299 * {<OpenLayers.Pixel>} The location of the last mousedown.
28305 * {<OpenLayers.Pixel>} The location of the last mouseup.
28310 * Property: clickTolerance
28311 * {Number} The number of pixels the mouse can move between mousedown
28312 * and mouseup for the event to still be considered a click.
28313 * Dragging the map should not trigger the click and clickout callbacks
28314 * unless the map is moved by less than this tolerance. Defaults to 4.
28319 * Property: geometryTypes
28320 * To restrict dragging to a limited set of geometry types, send a list
28321 * of strings corresponding to the geometry class names.
28323 * @type Array(String)
28325 geometryTypes: null,
28328 * Property: stopClick
28329 * {Boolean} If stopClick is set to true, handled clicks do not
28330 * propagate to other click listeners. Otherwise, handled clicks
28331 * do propagate. Unhandled clicks always propagate, whatever the
28332 * value of stopClick. Defaults to true.
28337 * Property: stopDown
28338 * {Boolean} If stopDown is set to true, handled mousedowns do not
28339 * propagate to other mousedown listeners. Otherwise, handled
28340 * mousedowns do propagate. Unhandled mousedowns always propagate,
28341 * whatever the value of stopDown. Defaults to true.
28347 * {Boolean} If stopUp is set to true, handled mouseups do not
28348 * propagate to other mouseup listeners. Otherwise, handled mouseups
28349 * do propagate. Unhandled mouseups always propagate, whatever the
28350 * value of stopUp. Defaults to false.
28355 * Constructor: OpenLayers.Handler.Feature
28358 * control - {<OpenLayers.Control>}
28359 * layer - {<OpenLayers.Layer.Vector>}
28360 * callbacks - {Object} An object with a 'over' property whos value is
28361 * a function to be called when the mouse is over a feature. The
28362 * callback should expect to recieve a single argument, the feature.
28363 * options - {Object}
28365 initialize: function(control, layer, callbacks, options) {
28366 OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]);
28367 this.layer = layer;
28371 * Method: touchstart
28372 * Handle touchstart events
28378 * {Boolean} Let the event propagate.
28380 touchstart: function(evt) {
28382 return OpenLayers.Event.isMultiTouch(evt) ?
28383 true : this.mousedown(evt);
28387 * Method: touchmove
28388 * Handle touchmove events. We just prevent the browser default behavior,
28389 * for Android Webkit not to select text when moving the finger after
28390 * selecting a feature.
28395 touchmove: function(evt) {
28396 OpenLayers.Event.preventDefault(evt);
28400 * Method: mousedown
28401 * Handle mouse down. Stop propagation if a feature is targeted by this
28402 * event (stops map dragging during feature selection).
28407 mousedown: function(evt) {
28408 // Feature selection is only done with a left click. Other handlers may stop the
28409 // propagation of left-click mousedown events but not right-click mousedown events.
28410 // This mismatch causes problems when comparing the location of the down and up
28411 // events in the click function so it is important ignore right-clicks.
28412 if (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt)) {
28413 this.down = evt.xy;
28415 return this.handle(evt) ? !this.stopDown : true;
28420 * Handle mouse up. Stop propagation if a feature is targeted by this
28426 mouseup: function(evt) {
28428 return this.handle(evt) ? !this.stopUp : true;
28433 * Handle click. Call the "click" callback if click on a feature,
28434 * or the "clickout" callback if click outside any feature.
28442 click: function(evt) {
28443 return this.handle(evt) ? !this.stopClick : true;
28447 * Method: mousemove
28448 * Handle mouse moves. Call the "over" callback if moving in to a feature,
28449 * or the "out" callback if moving out of a feature.
28457 mousemove: function(evt) {
28458 if (!this.callbacks['over'] && !this.callbacks['out']) {
28467 * Handle dblclick. Call the "dblclick" callback if dblclick on a feature.
28475 dblclick: function(evt) {
28476 return !this.handle(evt);
28480 * Method: geometryTypeMatches
28481 * Return true if the geometry type of the passed feature matches
28482 * one of the geometry types in the geometryTypes array.
28485 * feature - {<OpenLayers.Vector.Feature>}
28490 geometryTypeMatches: function(feature) {
28491 return this.geometryTypes == null ||
28492 OpenLayers.Util.indexOf(this.geometryTypes,
28493 feature.geometry.CLASS_NAME) > -1;
28503 * {Boolean} The event occurred over a relevant feature.
28505 handle: function(evt) {
28506 if(this.feature && !this.feature.layer) {
28507 // feature has been destroyed
28508 this.feature = null;
28510 var type = evt.type;
28511 var handled = false;
28512 var previouslyIn = !!(this.feature); // previously in a feature
28513 var click = (type == "click" || type == "dblclick" || type == "touchstart");
28514 this.feature = this.layer.getFeatureFromEvent(evt);
28515 if(this.feature && !this.feature.layer) {
28516 // feature has been destroyed
28517 this.feature = null;
28519 if(this.lastFeature && !this.lastFeature.layer) {
28520 // last feature has been destroyed
28521 this.lastFeature = null;
28524 if(type === "touchstart") {
28525 // stop the event to prevent Android Webkit from
28526 // "flashing" the map div
28527 OpenLayers.Event.preventDefault(evt);
28529 var inNew = (this.feature != this.lastFeature);
28530 if(this.geometryTypeMatches(this.feature)) {
28532 if(previouslyIn && inNew) {
28533 // out of last feature and in to another
28534 if(this.lastFeature) {
28535 this.triggerCallback(type, 'out', [this.lastFeature]);
28537 this.triggerCallback(type, 'in', [this.feature]);
28538 } else if(!previouslyIn || click) {
28539 // in feature for the first time
28540 this.triggerCallback(type, 'in', [this.feature]);
28542 this.lastFeature = this.feature;
28545 // not in to a feature
28546 if(this.lastFeature && (previouslyIn && inNew || click)) {
28547 // out of last feature for the first time
28548 this.triggerCallback(type, 'out', [this.lastFeature]);
28550 // next time the mouse goes in a feature whose geometry type
28551 // doesn't match we don't want to call the 'out' callback
28552 // again, so let's set this.feature to null so that
28553 // previouslyIn will evaluate to false the next time
28554 // we enter handle. Yes, a bit hackish...
28555 this.feature = null;
28557 } else if(this.lastFeature && (previouslyIn || click)) {
28558 this.triggerCallback(type, 'out', [this.lastFeature]);
28564 * Method: triggerCallback
28565 * Call the callback keyed in the event map with the supplied arguments.
28566 * For click and clickout, the <clickTolerance> is checked first.
28571 triggerCallback: function(type, mode, args) {
28572 var key = this.EVENTMAP[type][mode];
28574 if(type == 'click' && this.up && this.down) {
28575 // for click/clickout, only trigger callback if tolerance is met
28576 var dpx = Math.sqrt(
28577 Math.pow(this.up.x - this.down.x, 2) +
28578 Math.pow(this.up.y - this.down.y, 2)
28580 if(dpx <= this.clickTolerance) {
28581 this.callback(key, args);
28583 // we're done with this set of events now: clear the cached
28584 // positions so we can't trip over them later (this can occur
28585 // if one of the up/down events gets eaten before it gets to us
28586 // but we still get the click)
28587 this.up = this.down = null;
28589 this.callback(key, args);
28596 * Turn on the handler. Returns false if the handler was already active.
28601 activate: function() {
28602 var activated = false;
28603 if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
28604 this.moveLayerToTop();
28605 this.map.events.on({
28606 "removelayer": this.handleMapEvents,
28607 "changelayer": this.handleMapEvents,
28616 * Method: deactivate
28617 * Turn off the handler. Returns false if the handler was already active.
28622 deactivate: function() {
28623 var deactivated = false;
28624 if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
28625 this.moveLayerBack();
28626 this.feature = null;
28627 this.lastFeature = null;
28630 this.map.events.un({
28631 "removelayer": this.handleMapEvents,
28632 "changelayer": this.handleMapEvents,
28635 deactivated = true;
28637 return deactivated;
28641 * Method: handleMapEvents
28646 handleMapEvents: function(evt) {
28647 if (evt.type == "removelayer" || evt.property == "order") {
28648 this.moveLayerToTop();
28653 * Method: moveLayerToTop
28654 * Moves the layer for this handler to the top, so mouse events can reach
28657 moveLayerToTop: function() {
28658 var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1,
28659 this.layer.getZIndex()) + 1;
28660 this.layer.setZIndex(index);
28665 * Method: moveLayerBack
28666 * Moves the layer back to the position determined by the map's layers
28669 moveLayerBack: function() {
28670 var index = this.layer.getZIndex() - 1;
28671 if (index >= this.map.Z_INDEX_BASE['Feature']) {
28672 this.layer.setZIndex(index);
28674 this.map.setLayerZIndex(this.layer,
28675 this.map.getLayerIndex(this.layer));
28679 CLASS_NAME: "OpenLayers.Handler.Feature"
28681 /* ======================================================================
28682 OpenLayers/StyleMap.js
28683 ====================================================================== */
28685 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
28686 * full list of contributors). Published under the 2-clause BSD license.
28687 * See license.txt in the OpenLayers distribution or repository for the
28688 * full text of the license. */
28691 * @requires OpenLayers/BaseTypes/Class.js
28692 * @requires OpenLayers/Style.js
28693 * @requires OpenLayers/Feature/Vector.js
28697 * Class: OpenLayers.StyleMap
28699 OpenLayers.StyleMap = OpenLayers.Class({
28703 * {Object} Hash of {<OpenLayers.Style>}, keyed by names of well known
28704 * rendering intents (e.g. "default", "temporary", "select", "delete").
28709 * Property: extendDefault
28710 * {Boolean} if true, every render intent will extend the symbolizers
28711 * specified for the "default" intent at rendering time. Otherwise, every
28712 * rendering intent will be treated as a completely independent style.
28714 extendDefault: true,
28717 * Constructor: OpenLayers.StyleMap
28720 * style - {Object} Optional. Either a style hash, or a style object, or
28721 * a hash of style objects (style hashes) keyed by rendering
28722 * intent. If just one style hash or style object is passed,
28723 * this will be used for all known render intents (default,
28724 * select, temporary)
28725 * options - {Object} optional hash of additional options for this
28728 initialize: function (style, options) {
28730 "default": new OpenLayers.Style(
28731 OpenLayers.Feature.Vector.style["default"]),
28732 "select": new OpenLayers.Style(
28733 OpenLayers.Feature.Vector.style["select"]),
28734 "temporary": new OpenLayers.Style(
28735 OpenLayers.Feature.Vector.style["temporary"]),
28736 "delete": new OpenLayers.Style(
28737 OpenLayers.Feature.Vector.style["delete"])
28740 // take whatever the user passed as style parameter and convert it
28741 // into parts of stylemap.
28742 if(style instanceof OpenLayers.Style) {
28743 // user passed a style object
28744 this.styles["default"] = style;
28745 this.styles["select"] = style;
28746 this.styles["temporary"] = style;
28747 this.styles["delete"] = style;
28748 } else if(typeof style == "object") {
28749 for(var key in style) {
28750 if(style[key] instanceof OpenLayers.Style) {
28751 // user passed a hash of style objects
28752 this.styles[key] = style[key];
28753 } else if(typeof style[key] == "object") {
28754 // user passsed a hash of style hashes
28755 this.styles[key] = new OpenLayers.Style(style[key]);
28757 // user passed a style hash (i.e. symbolizer)
28758 this.styles["default"] = new OpenLayers.Style(style);
28759 this.styles["select"] = new OpenLayers.Style(style);
28760 this.styles["temporary"] = new OpenLayers.Style(style);
28761 this.styles["delete"] = new OpenLayers.Style(style);
28766 OpenLayers.Util.extend(this, options);
28772 destroy: function() {
28773 for(var key in this.styles) {
28774 this.styles[key].destroy();
28776 this.styles = null;
28780 * Method: createSymbolizer
28781 * Creates the symbolizer for a feature for a render intent.
28784 * feature - {<OpenLayers.Feature>} The feature to evaluate the rules
28785 * of the intended style against.
28786 * intent - {String} The intent determines the symbolizer that will be
28787 * used to draw the feature. Well known intents are "default"
28788 * (for just drawing the features), "select" (for selected
28789 * features) and "temporary" (for drawing features).
28792 * {Object} symbolizer hash
28794 createSymbolizer: function(feature, intent) {
28796 feature = new OpenLayers.Feature.Vector();
28798 if(!this.styles[intent]) {
28799 intent = "default";
28801 feature.renderIntent = intent;
28802 var defaultSymbolizer = {};
28803 if(this.extendDefault && intent != "default") {
28804 defaultSymbolizer = this.styles["default"].createSymbolizer(feature);
28806 return OpenLayers.Util.extend(defaultSymbolizer,
28807 this.styles[intent].createSymbolizer(feature));
28811 * Method: addUniqueValueRules
28812 * Convenience method to create comparison rules for unique values of a
28813 * property. The rules will be added to the style object for a specified
28814 * rendering intent. This method is a shortcut for creating something like
28815 * the "unique value legends" familiar from well known desktop GIS systems
28818 * renderIntent - {String} rendering intent to add the rules to
28819 * property - {String} values of feature attributes to create the
28821 * symbolizers - {Object} Hash of symbolizers, keyed by the desired
28823 * context - {Object} An optional object with properties that
28824 * symbolizers' property values should be evaluated
28825 * against. If no context is specified, feature.attributes
28828 addUniqueValueRules: function(renderIntent, property, symbolizers, context) {
28830 for (var value in symbolizers) {
28831 rules.push(new OpenLayers.Rule({
28832 symbolizer: symbolizers[value],
28834 filter: new OpenLayers.Filter.Comparison({
28835 type: OpenLayers.Filter.Comparison.EQUAL_TO,
28836 property: property,
28841 this.styles[renderIntent].addRules(rules);
28844 CLASS_NAME: "OpenLayers.StyleMap"
28846 /* ======================================================================
28847 OpenLayers/Layer/Vector.js
28848 ====================================================================== */
28850 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
28851 * full list of contributors). Published under the 2-clause BSD license.
28852 * See license.txt in the OpenLayers distribution or repository for the
28853 * full text of the license. */
28856 * @requires OpenLayers/Layer.js
28857 * @requires OpenLayers/Renderer.js
28858 * @requires OpenLayers/StyleMap.js
28859 * @requires OpenLayers/Feature/Vector.js
28860 * @requires OpenLayers/Console.js
28861 * @requires OpenLayers/Lang.js
28865 * Class: OpenLayers.Layer.Vector
28866 * Instances of OpenLayers.Layer.Vector are used to render vector data from
28867 * a variety of sources. Create a new vector layer with the
28868 * <OpenLayers.Layer.Vector> constructor.
28871 * - <OpenLayers.Layer>
28873 OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
28876 * APIProperty: events
28877 * {<OpenLayers.Events>}
28879 * Register a listener for a particular event with the following syntax:
28881 * layer.events.register(type, obj, listener);
28884 * Listeners will be called with a reference to an event object. The
28885 * properties of this event depends on exactly what happened.
28887 * All event objects have at least the following properties:
28888 * object - {Object} A reference to layer.events.object.
28889 * element - {DOMElement} A reference to layer.events.element.
28891 * Supported map event types (in addition to those from <OpenLayers.Layer.events>):
28892 * beforefeatureadded - Triggered before a feature is added. Listeners
28893 * will receive an object with a *feature* property referencing the
28894 * feature to be added. To stop the feature from being added, a
28895 * listener should return false.
28896 * beforefeaturesadded - Triggered before an array of features is added.
28897 * Listeners will receive an object with a *features* property
28898 * referencing the feature to be added. To stop the features from
28899 * being added, a listener should return false.
28900 * featureadded - Triggered after a feature is added. The event
28901 * object passed to listeners will have a *feature* property with a
28902 * reference to the added feature.
28903 * featuresadded - Triggered after features are added. The event
28904 * object passed to listeners will have a *features* property with a
28905 * reference to an array of added features.
28906 * beforefeatureremoved - Triggered before a feature is removed. Listeners
28907 * will receive an object with a *feature* property referencing the
28908 * feature to be removed.
28909 * beforefeaturesremoved - Triggered before multiple features are removed.
28910 * Listeners will receive an object with a *features* property
28911 * referencing the features to be removed.
28912 * featureremoved - Triggerd after a feature is removed. The event
28913 * object passed to listeners will have a *feature* property with a
28914 * reference to the removed feature.
28915 * featuresremoved - Triggered after features are removed. The event
28916 * object passed to listeners will have a *features* property with a
28917 * reference to an array of removed features.
28918 * beforefeatureselected - Triggered before a feature is selected. Listeners
28919 * will receive an object with a *feature* property referencing the
28920 * feature to be selected. To stop the feature from being selectd, a
28921 * listener should return false.
28922 * featureselected - Triggered after a feature is selected. Listeners
28923 * will receive an object with a *feature* property referencing the
28924 * selected feature.
28925 * featureunselected - Triggered after a feature is unselected.
28926 * Listeners will receive an object with a *feature* property
28927 * referencing the unselected feature.
28928 * beforefeaturemodified - Triggered when a feature is selected to
28929 * be modified. Listeners will receive an object with a *feature*
28930 * property referencing the selected feature.
28931 * featuremodified - Triggered when a feature has been modified.
28932 * Listeners will receive an object with a *feature* property referencing
28933 * the modified feature.
28934 * afterfeaturemodified - Triggered when a feature is finished being modified.
28935 * Listeners will receive an object with a *feature* property referencing
28936 * the modified feature.
28937 * vertexmodified - Triggered when a vertex within any feature geometry
28938 * has been modified. Listeners will receive an object with a
28939 * *feature* property referencing the modified feature, a *vertex*
28940 * property referencing the vertex modified (always a point geometry),
28941 * and a *pixel* property referencing the pixel location of the
28943 * vertexremoved - Triggered when a vertex within any feature geometry
28944 * has been deleted. Listeners will receive an object with a
28945 * *feature* property referencing the modified feature, a *vertex*
28946 * property referencing the vertex modified (always a point geometry),
28947 * and a *pixel* property referencing the pixel location of the
28949 * sketchstarted - Triggered when a feature sketch bound for this layer
28950 * is started. Listeners will receive an object with a *feature*
28951 * property referencing the new sketch feature and a *vertex* property
28952 * referencing the creation point.
28953 * sketchmodified - Triggered when a feature sketch bound for this layer
28954 * is modified. Listeners will receive an object with a *vertex*
28955 * property referencing the modified vertex and a *feature* property
28956 * referencing the sketch feature.
28957 * sketchcomplete - Triggered when a feature sketch bound for this layer
28958 * is complete. Listeners will receive an object with a *feature*
28959 * property referencing the sketch feature. By returning false, a
28960 * listener can stop the sketch feature from being added to the layer.
28961 * refresh - Triggered when something wants a strategy to ask the protocol
28962 * for a new set of features.
28966 * APIProperty: isBaseLayer
28967 * {Boolean} The layer is a base layer. Default is false. Set this property
28968 * in the layer options.
28970 isBaseLayer: false,
28973 * APIProperty: isFixed
28974 * {Boolean} Whether the layer remains in one place while dragging the
28975 * map. Note that setting this to true will move the layer to the bottom
28976 * of the layer stack.
28981 * APIProperty: features
28982 * {Array(<OpenLayers.Feature.Vector>)}
28988 * {<OpenLayers.Filter>} The filter set in this layer,
28989 * a strategy launching read requests can combined
28990 * this filter with its own filter.
28995 * Property: selectedFeatures
28996 * {Array(<OpenLayers.Feature.Vector>)}
28998 selectedFeatures: null,
29001 * Property: unrenderedFeatures
29002 * {Object} hash of features, keyed by feature.id, that the renderer
29005 unrenderedFeatures: null,
29008 * APIProperty: reportError
29009 * {Boolean} report friendly error message when loading of renderer
29015 * APIProperty: style
29016 * {Object} Default style for the layer
29021 * Property: styleMap
29022 * {<OpenLayers.StyleMap>}
29027 * Property: strategies
29028 * {Array(<OpenLayers.Strategy>})} Optional list of strategies for the layer.
29033 * Property: protocol
29034 * {<OpenLayers.Protocol>} Optional protocol for the layer.
29039 * Property: renderers
29040 * {Array(String)} List of supported Renderer classes. Add to this list to
29041 * add support for additional renderers. This list is ordered:
29042 * the first renderer which returns true for the 'supported()'
29043 * method will be used, if not defined in the 'renderer' option.
29045 renderers: ['SVG', 'VML', 'Canvas'],
29048 * Property: renderer
29049 * {<OpenLayers.Renderer>}
29054 * APIProperty: rendererOptions
29055 * {Object} Options for the renderer. See {<OpenLayers.Renderer>} for
29056 * supported options.
29058 rendererOptions: null,
29061 * APIProperty: geometryType
29062 * {String} geometryType allows you to limit the types of geometries this
29063 * layer supports. This should be set to something like
29064 * "OpenLayers.Geometry.Point" to limit types.
29066 geometryType: null,
29070 * {Boolean} Whether the Vector Layer features have been drawn yet.
29075 * APIProperty: ratio
29076 * {Float} This specifies the ratio of the size of the visiblity of the Vector Layer features to the size of the map.
29081 * Constructor: OpenLayers.Layer.Vector
29082 * Create a new vector layer
29085 * name - {String} A name for the layer
29086 * options - {Object} Optional object with non-default properties to set on
29090 * {<OpenLayers.Layer.Vector>} A new vector layer
29092 initialize: function(name, options) {
29093 OpenLayers.Layer.prototype.initialize.apply(this, arguments);
29095 // allow user-set renderer, otherwise assign one
29096 if (!this.renderer || !this.renderer.supported()) {
29097 this.assignRenderer();
29100 // if no valid renderer found, display error
29101 if (!this.renderer || !this.renderer.supported()) {
29102 this.renderer = null;
29103 this.displayError();
29106 if (!this.styleMap) {
29107 this.styleMap = new OpenLayers.StyleMap();
29110 this.features = [];
29111 this.selectedFeatures = [];
29112 this.unrenderedFeatures = {};
29114 // Allow for custom layer behavior
29115 if(this.strategies){
29116 for(var i=0, len=this.strategies.length; i<len; i++) {
29117 this.strategies[i].setLayer(this);
29124 * APIMethod: destroy
29125 * Destroy this layer
29127 destroy: function() {
29128 if (this.strategies) {
29129 var strategy, i, len;
29130 for(i=0, len=this.strategies.length; i<len; i++) {
29131 strategy = this.strategies[i];
29132 if(strategy.autoDestroy) {
29133 strategy.destroy();
29136 this.strategies = null;
29138 if (this.protocol) {
29139 if(this.protocol.autoDestroy) {
29140 this.protocol.destroy();
29142 this.protocol = null;
29144 this.destroyFeatures();
29145 this.features = null;
29146 this.selectedFeatures = null;
29147 this.unrenderedFeatures = null;
29148 if (this.renderer) {
29149 this.renderer.destroy();
29151 this.renderer = null;
29152 this.geometryType = null;
29154 OpenLayers.Layer.prototype.destroy.apply(this, arguments);
29159 * Create a clone of this layer.
29161 * Note: Features of the layer are also cloned.
29164 * {<OpenLayers.Layer.Vector>} An exact clone of this layer
29166 clone: function (obj) {
29169 obj = new OpenLayers.Layer.Vector(this.name, this.getOptions());
29172 //get all additions from superclasses
29173 obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
29175 // copy/set any non-init, non-simple values here
29176 var features = this.features;
29177 var len = features.length;
29178 var clonedFeatures = new Array(len);
29179 for(var i=0; i<len; ++i) {
29180 clonedFeatures[i] = features[i].clone();
29182 obj.features = clonedFeatures;
29189 * Ask the layer to request features again and redraw them. Triggers
29190 * the refresh event if the layer is in range and visible.
29193 * obj - {Object} Optional object with properties for any listener of
29194 * the refresh event.
29196 refresh: function(obj) {
29197 if(this.calculateInRange() && this.visibility) {
29198 this.events.triggerEvent("refresh", obj);
29203 * Method: assignRenderer
29204 * Iterates through the available renderer implementations and selects
29205 * and assigns the first one whose "supported()" function returns true.
29207 assignRenderer: function() {
29208 for (var i=0, len=this.renderers.length; i<len; i++) {
29209 var rendererClass = this.renderers[i];
29210 var renderer = (typeof rendererClass == "function") ?
29212 OpenLayers.Renderer[rendererClass];
29213 if (renderer && renderer.prototype.supported()) {
29214 this.renderer = new renderer(this.div, this.rendererOptions);
29221 * Method: displayError
29222 * Let the user know their browser isn't supported.
29224 displayError: function() {
29225 if (this.reportError) {
29226 OpenLayers.Console.userError(OpenLayers.i18n("browserNotSupported",
29227 {renderers: this. renderers.join('\n')}));
29233 * The layer has been added to the map.
29235 * If there is no renderer set, the layer can't be used. Remove it.
29236 * Otherwise, give the renderer a reference to the map and set its size.
29239 * map - {<OpenLayers.Map>}
29241 setMap: function(map) {
29242 OpenLayers.Layer.prototype.setMap.apply(this, arguments);
29244 if (!this.renderer) {
29245 this.map.removeLayer(this);
29247 this.renderer.map = this.map;
29249 var newSize = this.map.getSize();
29250 newSize.w = newSize.w * this.ratio;
29251 newSize.h = newSize.h * this.ratio;
29252 this.renderer.setSize(newSize);
29258 * Called at the end of the map.addLayer sequence. At this point, the map
29259 * will have a base layer. Any autoActivate strategies will be
29262 afterAdd: function() {
29263 if(this.strategies) {
29264 var strategy, i, len;
29265 for(i=0, len=this.strategies.length; i<len; i++) {
29266 strategy = this.strategies[i];
29267 if(strategy.autoActivate) {
29268 strategy.activate();
29275 * Method: removeMap
29276 * The layer has been removed from the map.
29279 * map - {<OpenLayers.Map>}
29281 removeMap: function(map) {
29282 this.drawn = false;
29283 if(this.strategies) {
29284 var strategy, i, len;
29285 for(i=0, len=this.strategies.length; i<len; i++) {
29286 strategy = this.strategies[i];
29287 if(strategy.autoActivate) {
29288 strategy.deactivate();
29295 * Method: onMapResize
29296 * Notify the renderer of the change in size.
29299 onMapResize: function() {
29300 OpenLayers.Layer.prototype.onMapResize.apply(this, arguments);
29302 var newSize = this.map.getSize();
29303 newSize.w = newSize.w * this.ratio;
29304 newSize.h = newSize.h * this.ratio;
29305 this.renderer.setSize(newSize);
29310 * Reset the vector layer's div so that it once again is lined up with
29311 * the map. Notify the renderer of the change of extent, and in the
29312 * case of a change of zoom level (resolution), have the
29313 * renderer redraw features.
29315 * If the layer has not yet been drawn, cycle through the layer's
29316 * features and draw each one.
29319 * bounds - {<OpenLayers.Bounds>}
29320 * zoomChanged - {Boolean}
29321 * dragging - {Boolean}
29323 moveTo: function(bounds, zoomChanged, dragging) {
29324 OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
29326 var coordSysUnchanged = true;
29328 this.renderer.root.style.visibility = 'hidden';
29330 var viewSize = this.map.getSize(),
29331 viewWidth = viewSize.w,
29332 viewHeight = viewSize.h,
29333 offsetLeft = (viewWidth / 2 * this.ratio) - viewWidth / 2,
29334 offsetTop = (viewHeight / 2 * this.ratio) - viewHeight / 2;
29335 offsetLeft += this.map.layerContainerOriginPx.x;
29336 offsetLeft = -Math.round(offsetLeft);
29337 offsetTop += this.map.layerContainerOriginPx.y;
29338 offsetTop = -Math.round(offsetTop);
29340 this.div.style.left = offsetLeft + 'px';
29341 this.div.style.top = offsetTop + 'px';
29343 var extent = this.map.getExtent().scale(this.ratio);
29344 coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged);
29346 this.renderer.root.style.visibility = 'visible';
29348 // Force a reflow on gecko based browsers to prevent jump/flicker.
29349 // This seems to happen on only certain configurations; it was originally
29350 // noticed in FF 2.0 and Linux.
29351 if (OpenLayers.IS_GECKO === true) {
29352 this.div.scrollLeft = this.div.scrollLeft;
29355 if (!zoomChanged && coordSysUnchanged) {
29356 for (var i in this.unrenderedFeatures) {
29357 var feature = this.unrenderedFeatures[i];
29358 this.drawFeature(feature);
29362 if (!this.drawn || zoomChanged || !coordSysUnchanged) {
29365 for(var i=0, len=this.features.length; i<len; i++) {
29366 this.renderer.locked = (i !== (len - 1));
29367 feature = this.features[i];
29368 this.drawFeature(feature);
29374 * APIMethod: display
29375 * Hide or show the Layer
29378 * display - {Boolean}
29380 display: function(display) {
29381 OpenLayers.Layer.prototype.display.apply(this, arguments);
29382 // we need to set the display style of the root in case it is attached
29383 // to a foreign layer
29384 var currentDisplay = this.div.style.display;
29385 if(currentDisplay != this.renderer.root.style.display) {
29386 this.renderer.root.style.display = currentDisplay;
29391 * APIMethod: addFeatures
29392 * Add Features to the layer.
29395 * features - {Array(<OpenLayers.Feature.Vector>)}
29396 * options - {Object}
29398 addFeatures: function(features, options) {
29399 if (!(OpenLayers.Util.isArray(features))) {
29400 features = [features];
29403 var notify = !options || !options.silent;
29405 var event = {features: features};
29406 var ret = this.events.triggerEvent("beforefeaturesadded", event);
29407 if(ret === false) {
29410 features = event.features;
29413 // Track successfully added features for featuresadded event, since
29414 // beforefeatureadded can veto single features.
29415 var featuresAdded = [];
29416 for (var i=0, len=features.length; i<len; i++) {
29417 if (i != (features.length - 1)) {
29418 this.renderer.locked = true;
29420 this.renderer.locked = false;
29422 var feature = features[i];
29424 if (this.geometryType &&
29425 !(feature.geometry instanceof this.geometryType)) {
29426 throw new TypeError('addFeatures: component should be an ' +
29427 this.geometryType.prototype.CLASS_NAME);
29430 //give feature reference to its layer
29431 feature.layer = this;
29433 if (!feature.style && this.style) {
29434 feature.style = OpenLayers.Util.extend({}, this.style);
29438 if(this.events.triggerEvent("beforefeatureadded",
29439 {feature: feature}) === false) {
29442 this.preFeatureInsert(feature);
29445 featuresAdded.push(feature);
29446 this.features.push(feature);
29447 this.drawFeature(feature);
29450 this.events.triggerEvent("featureadded", {
29453 this.onFeatureInsert(feature);
29458 this.events.triggerEvent("featuresadded", {features: featuresAdded});
29464 * APIMethod: removeFeatures
29465 * Remove features from the layer. This erases any drawn features and
29466 * removes them from the layer's control. The beforefeatureremoved
29467 * and featureremoved events will be triggered for each feature. The
29468 * featuresremoved event will be triggered after all features have
29469 * been removed. To supress event triggering, use the silent option.
29472 * features - {Array(<OpenLayers.Feature.Vector>)} List of features to be
29474 * options - {Object} Optional properties for changing behavior of the
29478 * silent - {Boolean} Supress event triggering. Default is false.
29480 removeFeatures: function(features, options) {
29481 if(!features || features.length === 0) {
29484 if (features === this.features) {
29485 return this.removeAllFeatures(options);
29487 if (!(OpenLayers.Util.isArray(features))) {
29488 features = [features];
29490 if (features === this.selectedFeatures) {
29491 features = features.slice();
29494 var notify = !options || !options.silent;
29497 this.events.triggerEvent(
29498 "beforefeaturesremoved", {features: features}
29502 for (var i = features.length - 1; i >= 0; i--) {
29503 // We remain locked so long as we're not at 0
29504 // and the 'next' feature has a geometry. We do the geometry check
29505 // because if all the features after the current one are 'null', we
29506 // won't call eraseGeometry, so we break the 'renderer functions
29507 // will always be called with locked=false *last*' rule. The end result
29508 // is a possible gratiutious unlocking to save a loop through the rest
29509 // of the list checking the remaining features every time. So long as
29510 // null geoms are rare, this is probably okay.
29511 if (i != 0 && features[i-1].geometry) {
29512 this.renderer.locked = true;
29514 this.renderer.locked = false;
29517 var feature = features[i];
29518 delete this.unrenderedFeatures[feature.id];
29521 this.events.triggerEvent("beforefeatureremoved", {
29526 this.features = OpenLayers.Util.removeItem(this.features, feature);
29527 // feature has no layer at this point
29528 feature.layer = null;
29530 if (feature.geometry) {
29531 this.renderer.eraseFeatures(feature);
29534 //in the case that this feature is one of the selected features,
29535 // remove it from that array as well.
29536 if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1){
29537 OpenLayers.Util.removeItem(this.selectedFeatures, feature);
29541 this.events.triggerEvent("featureremoved", {
29548 this.events.triggerEvent("featuresremoved", {features: features});
29553 * APIMethod: removeAllFeatures
29554 * Remove all features from the layer.
29557 * options - {Object} Optional properties for changing behavior of the
29561 * silent - {Boolean} Supress event triggering. Default is false.
29563 removeAllFeatures: function(options) {
29564 var notify = !options || !options.silent;
29565 var features = this.features;
29567 this.events.triggerEvent(
29568 "beforefeaturesremoved", {features: features}
29572 for (var i = features.length-1; i >= 0; i--) {
29573 feature = features[i];
29575 this.events.triggerEvent("beforefeatureremoved", {
29579 feature.layer = null;
29581 this.events.triggerEvent("featureremoved", {
29586 this.renderer.clear();
29587 this.features = [];
29588 this.unrenderedFeatures = {};
29589 this.selectedFeatures = [];
29591 this.events.triggerEvent("featuresremoved", {features: features});
29596 * APIMethod: destroyFeatures
29597 * Erase and destroy features on the layer.
29600 * features - {Array(<OpenLayers.Feature.Vector>)} An optional array of
29601 * features to destroy. If not supplied, all features on the layer
29602 * will be destroyed.
29603 * options - {Object}
29605 destroyFeatures: function(features, options) {
29606 var all = (features == undefined); // evaluates to true if
29607 // features is null
29609 features = this.features;
29612 this.removeFeatures(features, options);
29613 for(var i=features.length-1; i>=0; i--) {
29614 features[i].destroy();
29620 * APIMethod: drawFeature
29621 * Draw (or redraw) a feature on the layer. If the optional style argument
29622 * is included, this style will be used. If no style is included, the
29623 * feature's style will be used. If the feature doesn't have a style,
29624 * the layer's style will be used.
29626 * This function is not designed to be used when adding features to
29627 * the layer (use addFeatures instead). It is meant to be used when
29628 * the style of a feature has changed, or in some other way needs to
29629 * visually updated *after* it has already been added to a layer. You
29630 * must add the feature to the layer for most layer-related events to
29634 * feature - {<OpenLayers.Feature.Vector>}
29635 * style - {String | Object} Named render intent or full symbolizer object.
29637 drawFeature: function(feature, style) {
29638 // don't try to draw the feature with the renderer if the layer is not
29643 if (typeof style != "object") {
29644 if(!style && feature.state === OpenLayers.State.DELETE) {
29647 var renderIntent = style || feature.renderIntent;
29648 style = feature.style || this.style;
29650 style = this.styleMap.createSymbolizer(feature, renderIntent);
29654 var drawn = this.renderer.drawFeature(feature, style);
29655 //TODO remove the check for null when we get rid of Renderer.SVG
29656 if (drawn === false || drawn === null) {
29657 this.unrenderedFeatures[feature.id] = feature;
29659 delete this.unrenderedFeatures[feature.id];
29664 * Method: eraseFeatures
29665 * Erase features from the layer.
29668 * features - {Array(<OpenLayers.Feature.Vector>)}
29670 eraseFeatures: function(features) {
29671 this.renderer.eraseFeatures(features);
29675 * Method: getFeatureFromEvent
29676 * Given an event, return a feature if the event occurred over one.
29677 * Otherwise, return null.
29683 * {<OpenLayers.Feature.Vector>} A feature if one was under the event.
29685 getFeatureFromEvent: function(evt) {
29686 if (!this.renderer) {
29687 throw new Error('getFeatureFromEvent called on layer with no ' +
29688 'renderer. This usually means you destroyed a ' +
29689 'layer, but not some handler which is associated ' +
29692 var feature = null;
29693 var featureId = this.renderer.getFeatureIdFromEvent(evt);
29695 if (typeof featureId === "string") {
29696 feature = this.getFeatureById(featureId);
29698 feature = featureId;
29705 * APIMethod: getFeatureBy
29706 * Given a property value, return the feature if it exists in the features array
29709 * property - {String}
29713 * {<OpenLayers.Feature.Vector>} A feature corresponding to the given
29714 * property value or null if there is no such feature.
29716 getFeatureBy: function(property, value) {
29717 //TBD - would it be more efficient to use a hash for this.features?
29718 var feature = null;
29719 for(var i=0, len=this.features.length; i<len; ++i) {
29720 if(this.features[i][property] == value) {
29721 feature = this.features[i];
29729 * APIMethod: getFeatureById
29730 * Given a feature id, return the feature if it exists in the features array
29733 * featureId - {String}
29736 * {<OpenLayers.Feature.Vector>} A feature corresponding to the given
29737 * featureId or null if there is no such feature.
29739 getFeatureById: function(featureId) {
29740 return this.getFeatureBy('id', featureId);
29744 * APIMethod: getFeatureByFid
29745 * Given a feature fid, return the feature if it exists in the features array
29748 * featureFid - {String}
29751 * {<OpenLayers.Feature.Vector>} A feature corresponding to the given
29752 * featureFid or null if there is no such feature.
29754 getFeatureByFid: function(featureFid) {
29755 return this.getFeatureBy('fid', featureFid);
29759 * APIMethod: getFeaturesByAttribute
29760 * Returns an array of features that have the given attribute key set to the
29761 * given value. Comparison of attribute values takes care of datatypes, e.g.
29762 * the string '1234' is not equal to the number 1234.
29765 * attrName - {String}
29766 * attrValue - {Mixed}
29769 * Array({<OpenLayers.Feature.Vector>}) An array of features that have the
29770 * passed named attribute set to the given value.
29772 getFeaturesByAttribute: function(attrName, attrValue) {
29775 len = this.features.length,
29776 foundFeatures = [];
29777 for(i = 0; i < len; i++) {
29778 feature = this.features[i];
29779 if(feature && feature.attributes) {
29780 if (feature.attributes[attrName] === attrValue) {
29781 foundFeatures.push(feature);
29785 return foundFeatures;
29789 * Unselect the selected features
29790 * i.e. clears the featureSelection array
29791 * change the style back
29792 clearSelection: function() {
29794 var vectorLayer = this.map.vectorLayer;
29795 for (var i = 0; i < this.map.featureSelection.length; i++) {
29796 var featureSelection = this.map.featureSelection[i];
29797 vectorLayer.drawFeature(featureSelection, vectorLayer.style);
29799 this.map.featureSelection = [];
29805 * APIMethod: onFeatureInsert
29806 * method called after a feature is inserted.
29807 * Does nothing by default. Override this if you
29808 * need to do something on feature updates.
29811 * feature - {<OpenLayers.Feature.Vector>}
29813 onFeatureInsert: function(feature) {
29817 * APIMethod: preFeatureInsert
29818 * method called before a feature is inserted.
29819 * Does nothing by default. Override this if you
29820 * need to do something when features are first added to the
29821 * layer, but before they are drawn, such as adjust the style.
29824 * feature - {<OpenLayers.Feature.Vector>}
29826 preFeatureInsert: function(feature) {
29830 * APIMethod: getDataExtent
29831 * Calculates the max extent which includes all of the features.
29834 * {<OpenLayers.Bounds>} or null if the layer has no features with
29837 getDataExtent: function () {
29838 var maxExtent = null;
29839 var features = this.features;
29840 if(features && (features.length > 0)) {
29841 var geometry = null;
29842 for(var i=0, len=features.length; i<len; i++) {
29843 geometry = features[i].geometry;
29845 if (maxExtent === null) {
29846 maxExtent = new OpenLayers.Bounds();
29848 maxExtent.extend(geometry.getBounds());
29855 CLASS_NAME: "OpenLayers.Layer.Vector"
29857 /* ======================================================================
29858 OpenLayers/Layer/Vector/RootContainer.js
29859 ====================================================================== */
29861 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
29862 * full list of contributors). Published under the 2-clause BSD license.
29863 * See license.txt in the OpenLayers distribution or repository for the
29864 * full text of the license. */
29867 * @requires OpenLayers/Layer/Vector.js
29871 * Class: OpenLayers.Layer.Vector.RootContainer
29872 * A special layer type to combine multiple vector layers inside a single
29873 * renderer root container. This class is not supposed to be instantiated
29874 * from user space, it is a helper class for controls that require event
29875 * processing for multiple vector layers.
29878 * - <OpenLayers.Layer.Vector>
29880 OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, {
29883 * Property: displayInLayerSwitcher
29884 * Set to false for this layer type
29886 displayInLayerSwitcher: false,
29889 * APIProperty: layers
29890 * Layers that are attached to this container. Required config option.
29895 * Constructor: OpenLayers.Layer.Vector.RootContainer
29896 * Create a new root container for multiple vector layer. This constructor
29897 * is not supposed to be used from user space, it is only to be used by
29898 * controls that need feature selection across multiple vector layers.
29901 * name - {String} A name for the layer
29902 * options - {Object} Optional object with non-default properties to set on
29905 * Required options properties:
29906 * layers - {Array(<OpenLayers.Layer.Vector>)} The layers managed by this
29910 * {<OpenLayers.Layer.Vector.RootContainer>} A new vector layer root
29917 display: function() {},
29920 * Method: getFeatureFromEvent
29921 * walk through the layers to find the feature returned by the event
29924 * evt - {Object} event object with a feature property
29927 * {<OpenLayers.Feature.Vector>}
29929 getFeatureFromEvent: function(evt) {
29930 var layers = this.layers;
29932 for(var i=0; i<layers.length; i++) {
29933 feature = layers[i].getFeatureFromEvent(evt);
29944 * map - {<OpenLayers.Map>}
29946 setMap: function(map) {
29947 OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);
29948 this.collectRoots();
29949 map.events.register("changelayer", this, this.handleChangeLayer);
29953 * Method: removeMap
29956 * map - {<OpenLayers.Map>}
29958 removeMap: function(map) {
29959 map.events.unregister("changelayer", this, this.handleChangeLayer);
29961 OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments);
29965 * Method: collectRoots
29966 * Collects the root nodes of all layers this control is configured with
29967 * and moveswien the nodes to this control's layer
29969 collectRoots: function() {
29971 // walk through all map layers, because we want to keep the order
29972 for(var i=0; i<this.map.layers.length; ++i) {
29973 layer = this.map.layers[i];
29974 if(OpenLayers.Util.indexOf(this.layers, layer) != -1) {
29975 layer.renderer.moveRoot(this.renderer);
29981 * Method: resetRoots
29982 * Resets the root nodes back into the layers they belong to.
29984 resetRoots: function() {
29986 for(var i=0; i<this.layers.length; ++i) {
29987 layer = this.layers[i];
29988 if(this.renderer && layer.renderer.getRenderLayerId() == this.id) {
29989 this.renderer.moveRoot(layer.renderer);
29995 * Method: handleChangeLayer
29996 * Event handler for the map's changelayer event. We need to rebuild
29997 * this container's layer dom if order of one of its layers changes.
29998 * This handler is added with the setMap method, and removed with the
29999 * removeMap method.
30004 handleChangeLayer: function(evt) {
30005 var layer = evt.layer;
30006 if(evt.property == "order" &&
30007 OpenLayers.Util.indexOf(this.layers, layer) != -1) {
30009 this.collectRoots();
30013 CLASS_NAME: "OpenLayers.Layer.Vector.RootContainer"
30015 /* ======================================================================
30016 OpenLayers/Control/SelectFeature.js
30017 ====================================================================== */
30019 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
30020 * full list of contributors). Published under the 2-clause BSD license.
30021 * See license.txt in the OpenLayers distribution or repository for the
30022 * full text of the license. */
30026 * @requires OpenLayers/Control.js
30027 * @requires OpenLayers/Feature/Vector.js
30028 * @requires OpenLayers/Handler/Feature.js
30029 * @requires OpenLayers/Layer/Vector/RootContainer.js
30033 * Class: OpenLayers.Control.SelectFeature
30034 * The SelectFeature control selects vector features from a given layer on
30038 * - <OpenLayers.Control>
30040 OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {
30043 * APIProperty: events
30044 * {<OpenLayers.Events>} Events instance for listeners and triggering
30045 * control specific events.
30047 * Register a listener for a particular event with the following syntax:
30049 * control.events.register(type, obj, listener);
30052 * Supported event types (in addition to those from <OpenLayers.Control.events>):
30053 * beforefeaturehighlighted - Triggered before a feature is highlighted
30054 * featurehighlighted - Triggered when a feature is highlighted
30055 * featureunhighlighted - Triggered when a feature is unhighlighted
30056 * boxselectionstart - Triggered before box selection starts
30057 * boxselectionend - Triggered after box selection ends
30061 * Property: multipleKey
30062 * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets
30063 * the <multiple> property to true. Default is null.
30068 * Property: toggleKey
30069 * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets
30070 * the <toggle> property to true. Default is null.
30075 * APIProperty: multiple
30076 * {Boolean} Allow selection of multiple geometries. Default is false.
30081 * APIProperty: clickout
30082 * {Boolean} Unselect features when clicking outside any feature.
30088 * APIProperty: toggle
30089 * {Boolean} Unselect a selected feature on click. Default is false. Only
30090 * has meaning if hover is false.
30095 * APIProperty: hover
30096 * {Boolean} Select on mouse over and deselect on mouse out. If true, this
30097 * ignores clicks and only listens to mouse moves.
30102 * APIProperty: highlightOnly
30103 * {Boolean} If true do not actually select features (that is place them in
30104 * the layer's selected features array), just highlight them. This property
30105 * has no effect if hover is false. Defaults to false.
30107 highlightOnly: false,
30111 * {Boolean} Allow feature selection by drawing a box.
30116 * Property: onBeforeSelect
30117 * {Function} Optional function to be called before a feature is selected.
30118 * The function should expect to be called with a feature.
30120 onBeforeSelect: function() {},
30123 * APIProperty: onSelect
30124 * {Function} Optional function to be called when a feature is selected.
30125 * The function should expect to be called with a feature.
30127 onSelect: function() {},
30130 * APIProperty: onUnselect
30131 * {Function} Optional function to be called when a feature is unselected.
30132 * The function should expect to be called with a feature.
30134 onUnselect: function() {},
30138 * {Object} The scope to use with the onBeforeSelect, onSelect, onUnselect
30139 * callbacks. If null the scope will be this control.
30144 * APIProperty: geometryTypes
30145 * {Array(String)} To restrict selecting to a limited set of geometry types,
30146 * send a list of strings corresponding to the geometry class names.
30148 geometryTypes: null,
30152 * {<OpenLayers.Layer.Vector>} The vector layer with a common renderer
30153 * root for all layers this control is configured with (if an array of
30154 * layers was passed to the constructor), or the vector layer the control
30155 * was configured with (if a single layer was passed to the constructor).
30161 * {Array(<OpenLayers.Layer.Vector>)} The layers this control will work on,
30162 * or null if the control was configured with a single layer
30167 * APIProperty: callbacks
30168 * {Object} The functions that are sent to the handlers.feature for callback
30173 * APIProperty: selectStyle
30174 * {Object} Hash of styles
30179 * Property: renderIntent
30180 * {String} key used to retrieve the select style from the layer's
30183 renderIntent: "select",
30186 * Property: handlers
30187 * {Object} Object with references to multiple <OpenLayers.Handler>
30193 * Constructor: OpenLayers.Control.SelectFeature
30194 * Create a new control for selecting features.
30197 * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers. The
30198 * layer(s) this control will select features from.
30199 * options - {Object}
30201 initialize: function(layers, options) {
30202 OpenLayers.Control.prototype.initialize.apply(this, [options]);
30204 if(this.scope === null) {
30207 this.initLayer(layers);
30209 click: this.clickFeature,
30210 clickout: this.clickoutFeature
30213 callbacks.over = this.overFeature;
30214 callbacks.out = this.outFeature;
30217 this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);
30219 feature: new OpenLayers.Handler.Feature(
30220 this, this.layer, this.callbacks,
30221 {geometryTypes: this.geometryTypes}
30226 this.handlers.box = new OpenLayers.Handler.Box(
30227 this, {done: this.selectBox},
30228 {boxDivClassName: "olHandlerBoxSelectFeature"}
30234 * Method: initLayer
30235 * Assign the layer property. If layers is an array, we need to use
30239 * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers.
30241 initLayer: function(layers) {
30242 if(OpenLayers.Util.isArray(layers)) {
30243 this.layers = layers;
30244 this.layer = new OpenLayers.Layer.Vector.RootContainer(
30245 this.id + "_container", {
30250 this.layer = layers;
30257 destroy: function() {
30258 if(this.active && this.layers) {
30259 this.map.removeLayer(this.layer);
30261 OpenLayers.Control.prototype.destroy.apply(this, arguments);
30263 this.layer.destroy();
30269 * Activates the control.
30272 * {Boolean} The control was effectively activated.
30274 activate: function () {
30275 if (!this.active) {
30277 this.map.addLayer(this.layer);
30279 this.handlers.feature.activate();
30280 if(this.box && this.handlers.box) {
30281 this.handlers.box.activate();
30284 return OpenLayers.Control.prototype.activate.apply(
30290 * Method: deactivate
30291 * Deactivates the control.
30294 * {Boolean} The control was effectively deactivated.
30296 deactivate: function () {
30298 this.handlers.feature.deactivate();
30299 if(this.handlers.box) {
30300 this.handlers.box.deactivate();
30303 this.map.removeLayer(this.layer);
30306 return OpenLayers.Control.prototype.deactivate.apply(
30312 * Method: unselectAll
30313 * Unselect all selected features. To unselect all except for a single
30314 * feature, set the options.except property to the feature.
30317 * options - {Object} Optional configuration object.
30319 unselectAll: function(options) {
30320 // we'll want an option to supress notification here
30321 var layers = this.layers || [this.layer],
30322 layer, feature, l, numExcept;
30323 for(l=0; l<layers.length; ++l) {
30326 //layer.selectedFeatures is null when layer is destroyed and
30327 //one of it's preremovelayer listener calls setLayer
30328 //with another layer on this control
30329 if(layer.selectedFeatures != null) {
30330 while(layer.selectedFeatures.length > numExcept) {
30331 feature = layer.selectedFeatures[numExcept];
30332 if(!options || options.except != feature) {
30333 this.unselect(feature);
30343 * Method: clickFeature
30344 * Called on click in a feature
30345 * Only responds if this.hover is false.
30348 * feature - {<OpenLayers.Feature.Vector>}
30350 clickFeature: function(feature) {
30352 var selected = (OpenLayers.Util.indexOf(
30353 feature.layer.selectedFeatures, feature) > -1);
30355 if(this.toggleSelect()) {
30356 this.unselect(feature);
30357 } else if(!this.multipleSelect()) {
30358 this.unselectAll({except: feature});
30361 if(!this.multipleSelect()) {
30362 this.unselectAll({except: feature});
30364 this.select(feature);
30370 * Method: multipleSelect
30371 * Allow for multiple selected features based on <multiple> property and
30372 * <multipleKey> event modifier.
30375 * {Boolean} Allow for multiple selected features.
30377 multipleSelect: function() {
30378 return this.multiple || (this.handlers.feature.evt &&
30379 this.handlers.feature.evt[this.multipleKey]);
30383 * Method: toggleSelect
30384 * Event should toggle the selected state of a feature based on <toggle>
30385 * property and <toggleKey> event modifier.
30388 * {Boolean} Toggle the selected state of a feature.
30390 toggleSelect: function() {
30391 return this.toggle || (this.handlers.feature.evt &&
30392 this.handlers.feature.evt[this.toggleKey]);
30396 * Method: clickoutFeature
30397 * Called on click outside a previously clicked (selected) feature.
30398 * Only responds if this.hover is false.
30401 * feature - {<OpenLayers.Vector.Feature>}
30403 clickoutFeature: function(feature) {
30404 if(!this.hover && this.clickout) {
30405 this.unselectAll();
30410 * Method: overFeature
30411 * Called on over a feature.
30412 * Only responds if this.hover is true.
30415 * feature - {<OpenLayers.Feature.Vector>}
30417 overFeature: function(feature) {
30418 var layer = feature.layer;
30420 if(this.highlightOnly) {
30421 this.highlight(feature);
30422 } else if(OpenLayers.Util.indexOf(
30423 layer.selectedFeatures, feature) == -1) {
30424 this.select(feature);
30430 * Method: outFeature
30431 * Called on out of a selected feature.
30432 * Only responds if this.hover is true.
30435 * feature - {<OpenLayers.Feature.Vector>}
30437 outFeature: function(feature) {
30439 if(this.highlightOnly) {
30440 // we do nothing if we're not the last highlighter of the
30442 if(feature._lastHighlighter == this.id) {
30443 // if another select control had highlighted the feature before
30444 // we did it ourself then we use that control to highlight the
30445 // feature as it was before we highlighted it, else we just
30447 if(feature._prevHighlighter &&
30448 feature._prevHighlighter != this.id) {
30449 delete feature._lastHighlighter;
30450 var control = this.map.getControl(
30451 feature._prevHighlighter);
30453 control.highlight(feature);
30456 this.unhighlight(feature);
30460 this.unselect(feature);
30466 * Method: highlight
30467 * Redraw feature with the select style.
30470 * feature - {<OpenLayers.Feature.Vector>}
30472 highlight: function(feature) {
30473 var layer = feature.layer;
30474 var cont = this.events.triggerEvent("beforefeaturehighlighted", {
30477 if(cont !== false) {
30478 feature._prevHighlighter = feature._lastHighlighter;
30479 feature._lastHighlighter = this.id;
30480 var style = this.selectStyle || this.renderIntent;
30481 layer.drawFeature(feature, style);
30482 this.events.triggerEvent("featurehighlighted", {feature : feature});
30487 * Method: unhighlight
30488 * Redraw feature with the "default" style
30491 * feature - {<OpenLayers.Feature.Vector>}
30493 unhighlight: function(feature) {
30494 var layer = feature.layer;
30496 // 1. there's no other highlighter, in that case _prev is undefined,
30497 // and we just need to undef _last
30498 // 2. another control highlighted the feature after we did it, in
30499 // that case _last references this other control, and we just
30500 // need to undef _prev
30501 // 3. another control highlighted the feature before we did it, in
30502 // that case _prev references this other control, and we need to
30503 // set _last to _prev and undef _prev
30504 if(feature._prevHighlighter == undefined) {
30505 delete feature._lastHighlighter;
30506 } else if(feature._prevHighlighter == this.id) {
30507 delete feature._prevHighlighter;
30509 feature._lastHighlighter = feature._prevHighlighter;
30510 delete feature._prevHighlighter;
30512 layer.drawFeature(feature, feature.style || feature.layer.style ||
30514 this.events.triggerEvent("featureunhighlighted", {feature : feature});
30519 * Add feature to the layer's selectedFeature array, render the feature as
30520 * selected, and call the onSelect function.
30523 * feature - {<OpenLayers.Feature.Vector>}
30525 select: function(feature) {
30526 var cont = this.onBeforeSelect.call(this.scope, feature);
30527 var layer = feature.layer;
30528 if(cont !== false) {
30529 cont = layer.events.triggerEvent("beforefeatureselected", {
30532 if(cont !== false) {
30533 layer.selectedFeatures.push(feature);
30534 this.highlight(feature);
30535 // if the feature handler isn't involved in the feature
30536 // selection (because the box handler is used or the
30537 // feature is selected programatically) we fake the
30538 // feature handler to allow unselecting on click
30539 if(!this.handlers.feature.lastFeature) {
30540 this.handlers.feature.lastFeature = layer.selectedFeatures[0];
30542 layer.events.triggerEvent("featureselected", {feature: feature});
30543 this.onSelect.call(this.scope, feature);
30550 * Remove feature from the layer's selectedFeature array, render the feature as
30551 * normal, and call the onUnselect function.
30554 * feature - {<OpenLayers.Feature.Vector>}
30556 unselect: function(feature) {
30557 var layer = feature.layer;
30558 // Store feature style for restoration later
30559 this.unhighlight(feature);
30560 OpenLayers.Util.removeItem(layer.selectedFeatures, feature);
30561 layer.events.triggerEvent("featureunselected", {feature: feature});
30562 this.onUnselect.call(this.scope, feature);
30566 * Method: selectBox
30567 * Callback from the handlers.box set up when <box> selection is true
30571 * position - {<OpenLayers.Bounds> || <OpenLayers.Pixel> }
30573 selectBox: function(position) {
30574 if (position instanceof OpenLayers.Bounds) {
30575 var minXY = this.map.getLonLatFromPixel({
30579 var maxXY = this.map.getLonLatFromPixel({
30583 var bounds = new OpenLayers.Bounds(
30584 minXY.lon, minXY.lat, maxXY.lon, maxXY.lat
30587 // if multiple is false, first deselect currently selected features
30588 if (!this.multipleSelect()) {
30589 this.unselectAll();
30592 // because we're using a box, we consider we want multiple selection
30593 var prevMultiple = this.multiple;
30594 this.multiple = true;
30595 var layers = this.layers || [this.layer];
30596 this.events.triggerEvent("boxselectionstart", {layers: layers});
30598 for(var l=0; l<layers.length; ++l) {
30600 for(var i=0, len = layer.features.length; i<len; ++i) {
30601 var feature = layer.features[i];
30602 // check if the feature is displayed
30603 if (!feature.getVisibility()) {
30607 if (this.geometryTypes == null || OpenLayers.Util.indexOf(
30608 this.geometryTypes, feature.geometry.CLASS_NAME) > -1) {
30609 if (bounds.toGeometry().intersects(feature.geometry)) {
30610 if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) {
30611 this.select(feature);
30617 this.multiple = prevMultiple;
30618 this.events.triggerEvent("boxselectionend", {layers: layers});
30624 * Set the map property for the control.
30627 * map - {<OpenLayers.Map>}
30629 setMap: function(map) {
30630 this.handlers.feature.setMap(map);
30632 this.handlers.box.setMap(map);
30634 OpenLayers.Control.prototype.setMap.apply(this, arguments);
30638 * APIMethod: setLayer
30639 * Attach a new layer to the control, overriding any existing layers.
30642 * layers - Array of {<OpenLayers.Layer.Vector>} or a single
30643 * {<OpenLayers.Layer.Vector>}
30645 setLayer: function(layers) {
30646 var isActive = this.active;
30647 this.unselectAll();
30650 this.layer.destroy();
30651 this.layers = null;
30653 this.initLayer(layers);
30654 this.handlers.feature.layer = this.layer;
30660 CLASS_NAME: "OpenLayers.Control.SelectFeature"
30662 /* ======================================================================
30663 OpenLayers/Control/Attribution.js
30664 ====================================================================== */
30666 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
30667 * full list of contributors). Published under the 2-clause BSD license.
30668 * See license.txt in the OpenLayers distribution or repository for the
30669 * full text of the license. */
30672 * @requires OpenLayers/Control.js
30676 * Class: OpenLayers.Control.Attribution
30677 * The attribution control adds attribution from layers to the map display.
30678 * It uses 'attribution' property of each layer.
30681 * - <OpenLayers.Control>
30683 OpenLayers.Control.Attribution =
30684 OpenLayers.Class(OpenLayers.Control, {
30687 * APIProperty: separator
30688 * {String} String used to separate layers.
30693 * APIProperty: template
30694 * {String} Template for the attribution. This has to include the substring
30695 * "${layers}", which will be replaced by the layer specific
30696 * attributions, separated by <separator>. The default is "${layers}".
30698 template: "${layers}",
30701 * Constructor: OpenLayers.Control.Attribution
30704 * options - {Object} Options for control.
30711 destroy: function() {
30712 this.map.events.un({
30713 "removelayer": this.updateAttribution,
30714 "addlayer": this.updateAttribution,
30715 "changelayer": this.updateAttribution,
30716 "changebaselayer": this.updateAttribution,
30720 OpenLayers.Control.prototype.destroy.apply(this, arguments);
30725 * Initialize control.
30728 * {DOMElement} A reference to the DIV DOMElement containing the control
30731 OpenLayers.Control.prototype.draw.apply(this, arguments);
30733 this.map.events.on({
30734 'changebaselayer': this.updateAttribution,
30735 'changelayer': this.updateAttribution,
30736 'addlayer': this.updateAttribution,
30737 'removelayer': this.updateAttribution,
30740 this.updateAttribution();
30746 * Method: updateAttribution
30747 * Update attribution string.
30749 updateAttribution: function() {
30750 var attributions = [];
30751 if (this.map && this.map.layers) {
30752 for(var i=0, len=this.map.layers.length; i<len; i++) {
30753 var layer = this.map.layers[i];
30754 if (layer.attribution && layer.getVisibility()) {
30755 // add attribution only if attribution text is unique
30756 if (OpenLayers.Util.indexOf(
30757 attributions, layer.attribution) === -1) {
30758 attributions.push( layer.attribution );
30762 this.div.innerHTML = OpenLayers.String.format(this.template, {
30763 layers: attributions.join(this.separator)
30768 CLASS_NAME: "OpenLayers.Control.Attribution"
30770 /* ======================================================================
30771 OpenLayers/Handler/Drag.js
30772 ====================================================================== */
30774 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
30775 * full list of contributors). Published under the 2-clause BSD license.
30776 * See license.txt in the OpenLayers distribution or repository for the
30777 * full text of the license. */
30780 * @requires OpenLayers/Handler.js
30784 * Class: OpenLayers.Handler.Drag
30785 * The drag handler is used to deal with sequences of browser events related
30786 * to dragging. The handler is used by controls that want to know when
30787 * a drag sequence begins, when a drag is happening, and when it has
30790 * Controls that use the drag handler typically construct it with callbacks
30791 * for 'down', 'move', and 'done'. Callbacks for these keys are called
30792 * when the drag begins, with each move, and when the drag is done. In
30793 * addition, controls can have callbacks keyed to 'up' and 'out' if they
30794 * care to differentiate between the types of events that correspond with
30795 * the end of a drag sequence. If no drag actually occurs (no mouse move)
30796 * the 'down' and 'up' callbacks will be called, but not the 'done'
30799 * Create a new drag handler with the <OpenLayers.Handler.Drag> constructor.
30802 * - <OpenLayers.Handler>
30804 OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, {
30807 * Property: started
30808 * {Boolean} When a mousedown or touchstart event is received, we want to
30809 * record it, but not set 'dragging' until the mouse moves after starting.
30814 * Property: stopDown
30815 * {Boolean} Stop propagation of mousedown events from getting to listeners
30816 * on the same element. Default is true.
30821 * Property: dragging
30828 * {<OpenLayers.Pixel>} The last pixel location of the drag.
30834 * {<OpenLayers.Pixel>} The first pixel location of the drag.
30839 * Property: lastMoveEvt
30840 * {Object} The last mousemove event that occurred. Used to
30841 * position the map correctly when our "delay drag"
30847 * Property: oldOnselectstart
30850 oldOnselectstart: null,
30853 * Property: interval
30854 * {Integer} In order to increase performance, an interval (in
30855 * milliseconds) can be set to reduce the number of drag events
30856 * called. If set, a new drag event will not be set until the
30857 * interval has passed.
30858 * Defaults to 0, meaning no interval.
30863 * Property: timeoutId
30864 * {String} The id of the timeout used for the mousedown interval.
30865 * This is "private", and should be left alone.
30870 * APIProperty: documentDrag
30871 * {Boolean} If set to true, the handler will also handle mouse moves when
30872 * the cursor has moved out of the map viewport. Default is false.
30874 documentDrag: false,
30877 * Property: documentEvents
30878 * {Boolean} Are we currently observing document events?
30880 documentEvents: null,
30883 * Constructor: OpenLayers.Handler.Drag
30884 * Returns OpenLayers.Handler.Drag
30887 * control - {<OpenLayers.Control>} The control that is making use of
30888 * this handler. If a handler is being used without a control, the
30889 * handlers setMap method must be overridden to deal properly with
30891 * callbacks - {Object} An object containing a single function to be
30892 * called when the drag operation is finished. The callback should
30893 * expect to recieve a single argument, the pixel location of the event.
30894 * Callbacks for 'move' and 'done' are supported. You can also speficy
30895 * callbacks for 'down', 'up', and 'out' to respond to those events.
30896 * options - {Object}
30898 initialize: function(control, callbacks, options) {
30899 OpenLayers.Handler.prototype.initialize.apply(this, arguments);
30901 if (this.documentDrag === true) {
30903 this._docMove = function(evt) {
30905 xy: {x: evt.clientX, y: evt.clientY},
30909 this._docUp = function(evt) {
30910 me.mouseup({xy: {x: evt.clientX, y: evt.clientY}});
30917 * Method: dragstart
30918 * This private method is factorized from mousedown and touchstart methods
30921 * evt - {Event} The event
30924 * {Boolean} Let the event propagate.
30926 dragstart: function (evt) {
30927 var propagate = true;
30928 this.dragging = false;
30929 if (this.checkModifiers(evt) &&
30930 (OpenLayers.Event.isLeftClick(evt) ||
30931 OpenLayers.Event.isSingleTouch(evt))) {
30932 this.started = true;
30933 this.start = evt.xy;
30934 this.last = evt.xy;
30935 OpenLayers.Element.addClass(
30936 this.map.viewPortDiv, "olDragDown"
30939 this.callback("down", [evt.xy]);
30941 // prevent document dragging
30942 OpenLayers.Event.preventDefault(evt);
30944 if(!this.oldOnselectstart) {
30945 this.oldOnselectstart = document.onselectstart ?
30946 document.onselectstart : OpenLayers.Function.True;
30948 document.onselectstart = OpenLayers.Function.False;
30950 propagate = !this.stopDown;
30952 this.started = false;
30961 * This private method is factorized from mousemove and touchmove methods
30964 * evt - {Event} The event
30967 * {Boolean} Let the event propagate.
30969 dragmove: function (evt) {
30970 this.lastMoveEvt = evt;
30971 if (this.started && !this.timeoutId && (evt.xy.x != this.last.x ||
30972 evt.xy.y != this.last.y)) {
30973 if(this.documentDrag === true && this.documentEvents) {
30974 if(evt.element === document) {
30975 this.adjustXY(evt);
30976 // do setEvent manually because the documentEvents are not
30977 // registered with the map
30978 this.setEvent(evt);
30980 this.removeDocumentEvents();
30983 if (this.interval > 0) {
30984 this.timeoutId = setTimeout(
30985 OpenLayers.Function.bind(this.removeTimeout, this),
30988 this.dragging = true;
30991 this.callback("move", [evt.xy]);
30992 if(!this.oldOnselectstart) {
30993 this.oldOnselectstart = document.onselectstart;
30994 document.onselectstart = OpenLayers.Function.False;
30996 this.last = evt.xy;
31003 * This private method is factorized from mouseup and touchend methods
31006 * evt - {Event} The event
31009 * {Boolean} Let the event propagate.
31011 dragend: function (evt) {
31012 if (this.started) {
31013 if(this.documentDrag === true && this.documentEvents) {
31014 this.adjustXY(evt);
31015 this.removeDocumentEvents();
31017 var dragged = (this.start != this.last);
31018 this.started = false;
31019 this.dragging = false;
31020 OpenLayers.Element.removeClass(
31021 this.map.viewPortDiv, "olDragDown"
31024 this.callback("up", [evt.xy]);
31026 this.callback("done", [evt.xy]);
31028 document.onselectstart = this.oldOnselectstart;
31034 * The four methods below (down, move, up, and out) are used by subclasses
31035 * to do their own processing related to these mouse events.
31040 * This method is called during the handling of the mouse down event.
31041 * Subclasses can do their own processing here.
31044 * evt - {Event} The mouse down event
31046 down: function(evt) {
31051 * This method is called during the handling of the mouse move event.
31052 * Subclasses can do their own processing here.
31055 * evt - {Event} The mouse move event
31058 move: function(evt) {
31063 * This method is called during the handling of the mouse up event.
31064 * Subclasses can do their own processing here.
31067 * evt - {Event} The mouse up event
31069 up: function(evt) {
31074 * This method is called during the handling of the mouse out event.
31075 * Subclasses can do their own processing here.
31078 * evt - {Event} The mouse out event
31080 out: function(evt) {
31084 * The methods below are part of the magic of event handling. Because
31085 * they are named like browser events, they are registered as listeners
31086 * for the events they represent.
31090 * Method: mousedown
31091 * Handle mousedown events
31097 * {Boolean} Let the event propagate.
31099 mousedown: function(evt) {
31100 return this.dragstart(evt);
31104 * Method: touchstart
31105 * Handle touchstart events
31111 * {Boolean} Let the event propagate.
31113 touchstart: function(evt) {
31115 return this.dragstart(evt);
31119 * Method: mousemove
31120 * Handle mousemove events
31126 * {Boolean} Let the event propagate.
31128 mousemove: function(evt) {
31129 return this.dragmove(evt);
31133 * Method: touchmove
31134 * Handle touchmove events
31140 * {Boolean} Let the event propagate.
31142 touchmove: function(evt) {
31143 return this.dragmove(evt);
31147 * Method: removeTimeout
31148 * Private. Called by mousemove() to remove the drag timeout.
31150 removeTimeout: function() {
31151 this.timeoutId = null;
31152 // if timeout expires while we're still dragging (mouseup
31153 // hasn't occurred) then call mousemove to move to the
31154 // correct position
31155 if(this.dragging) {
31156 this.mousemove(this.lastMoveEvt);
31162 * Handle mouseup events
31168 * {Boolean} Let the event propagate.
31170 mouseup: function(evt) {
31171 return this.dragend(evt);
31176 * Handle touchend events
31182 * {Boolean} Let the event propagate.
31184 touchend: function(evt) {
31185 // override evt.xy with last position since touchend does not have
31186 // any touch position
31187 evt.xy = this.last;
31188 return this.dragend(evt);
31193 * Handle mouseout events
31199 * {Boolean} Let the event propagate.
31201 mouseout: function (evt) {
31202 if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {
31203 if(this.documentDrag === true) {
31204 this.addDocumentEvents();
31206 var dragged = (this.start != this.last);
31207 this.started = false;
31208 this.dragging = false;
31209 OpenLayers.Element.removeClass(
31210 this.map.viewPortDiv, "olDragDown"
31213 this.callback("out", []);
31215 this.callback("done", [evt.xy]);
31217 if(document.onselectstart) {
31218 document.onselectstart = this.oldOnselectstart;
31227 * The drag handler captures the click event. If something else registers
31228 * for clicks on the same element, its listener will not be called
31235 * {Boolean} Let the event propagate.
31237 click: function (evt) {
31238 // let the click event propagate only if the mouse moved
31239 return (this.start == this.last);
31244 * Activate the handler.
31247 * {Boolean} The handler was successfully activated.
31249 activate: function() {
31250 var activated = false;
31251 if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
31252 this.dragging = false;
31259 * Method: deactivate
31260 * Deactivate the handler.
31263 * {Boolean} The handler was successfully deactivated.
31265 deactivate: function() {
31266 var deactivated = false;
31267 if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
31268 this.started = false;
31269 this.dragging = false;
31272 deactivated = true;
31273 OpenLayers.Element.removeClass(
31274 this.map.viewPortDiv, "olDragDown"
31277 return deactivated;
31282 * Converts event coordinates that are relative to the document body to
31283 * ones that are relative to the map viewport. The latter is the default in
31289 adjustXY: function(evt) {
31290 var pos = OpenLayers.Util.pagePosition(this.map.viewPortDiv);
31291 evt.xy.x -= pos[0];
31292 evt.xy.y -= pos[1];
31296 * Method: addDocumentEvents
31297 * Start observing document events when documentDrag is true and the mouse
31298 * cursor leaves the map viewport while dragging.
31300 addDocumentEvents: function() {
31301 OpenLayers.Element.addClass(document.body, "olDragDown");
31302 this.documentEvents = true;
31303 OpenLayers.Event.observe(document, "mousemove", this._docMove);
31304 OpenLayers.Event.observe(document, "mouseup", this._docUp);
31308 * Method: removeDocumentEvents
31309 * Stops observing document events when documentDrag is true and the mouse
31310 * cursor re-enters the map viewport while dragging.
31312 removeDocumentEvents: function() {
31313 OpenLayers.Element.removeClass(document.body, "olDragDown");
31314 this.documentEvents = false;
31315 OpenLayers.Event.stopObserving(document, "mousemove", this._docMove);
31316 OpenLayers.Event.stopObserving(document, "mouseup", this._docUp);
31319 CLASS_NAME: "OpenLayers.Handler.Drag"
31321 /* ======================================================================
31322 OpenLayers/Handler/Box.js
31323 ====================================================================== */
31325 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
31326 * full list of contributors). Published under the 2-clause BSD license.
31327 * See license.txt in the OpenLayers distribution or repository for the
31328 * full text of the license. */
31331 * @requires OpenLayers/Handler.js
31332 * @requires OpenLayers/Handler/Drag.js
31336 * Class: OpenLayers.Handler.Box
31337 * Handler for dragging a rectangle across the map. Box is displayed
31338 * on mouse down, moves on mouse move, and is finished on mouse up.
31341 * - <OpenLayers.Handler>
31343 OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, {
31346 * Property: dragHandler
31347 * {<OpenLayers.Handler.Drag>}
31352 * APIProperty: boxDivClassName
31353 * {String} The CSS class to use for drawing the box. Default is
31354 * olHandlerBoxZoomBox
31356 boxDivClassName: 'olHandlerBoxZoomBox',
31359 * Property: boxOffsets
31360 * {Object} Caches box offsets from css. This is used by the getBoxOffsets
31366 * Constructor: OpenLayers.Handler.Box
31369 * control - {<OpenLayers.Control>}
31370 * callbacks - {Object} An object with a properties whose values are
31371 * functions. Various callbacks described below.
31372 * options - {Object}
31375 * start - Called when the box drag operation starts.
31376 * done - Called when the box drag operation is finished.
31377 * The callback should expect to receive a single argument, the box
31378 * bounds or a pixel. If the box dragging didn't span more than a 5
31379 * pixel distance, a pixel will be returned instead of a bounds object.
31381 initialize: function(control, callbacks, options) {
31382 OpenLayers.Handler.prototype.initialize.apply(this, arguments);
31383 this.dragHandler = new OpenLayers.Handler.Drag(
31386 down: this.startBox,
31387 move: this.moveBox,
31388 out: this.removeBox,
31391 {keyMask: this.keyMask}
31398 destroy: function() {
31399 OpenLayers.Handler.prototype.destroy.apply(this, arguments);
31400 if (this.dragHandler) {
31401 this.dragHandler.destroy();
31402 this.dragHandler = null;
31409 setMap: function (map) {
31410 OpenLayers.Handler.prototype.setMap.apply(this, arguments);
31411 if (this.dragHandler) {
31412 this.dragHandler.setMap(map);
31420 * xy - {<OpenLayers.Pixel>}
31422 startBox: function (xy) {
31423 this.callback("start", []);
31424 this.zoomBox = OpenLayers.Util.createDiv('zoomBox', {
31427 this.zoomBox.className = this.boxDivClassName;
31428 this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
31430 this.map.viewPortDiv.appendChild(this.zoomBox);
31432 OpenLayers.Element.addClass(
31433 this.map.viewPortDiv, "olDrawBox"
31440 moveBox: function (xy) {
31441 var startX = this.dragHandler.start.x;
31442 var startY = this.dragHandler.start.y;
31443 var deltaX = Math.abs(startX - xy.x);
31444 var deltaY = Math.abs(startY - xy.y);
31446 var offset = this.getBoxOffsets();
31447 this.zoomBox.style.width = (deltaX + offset.width + 1) + "px";
31448 this.zoomBox.style.height = (deltaY + offset.height + 1) + "px";
31449 this.zoomBox.style.left = (xy.x < startX ?
31450 startX - deltaX - offset.left : startX - offset.left) + "px";
31451 this.zoomBox.style.top = (xy.y < startY ?
31452 startY - deltaY - offset.top : startY - offset.top) + "px";
31458 endBox: function(end) {
31460 if (Math.abs(this.dragHandler.start.x - end.x) > 5 ||
31461 Math.abs(this.dragHandler.start.y - end.y) > 5) {
31462 var start = this.dragHandler.start;
31463 var top = Math.min(start.y, end.y);
31464 var bottom = Math.max(start.y, end.y);
31465 var left = Math.min(start.x, end.x);
31466 var right = Math.max(start.x, end.x);
31467 result = new OpenLayers.Bounds(left, bottom, right, top);
31469 result = this.dragHandler.start.clone(); // i.e. OL.Pixel
31473 this.callback("done", [result]);
31477 * Method: removeBox
31478 * Remove the zoombox from the screen and nullify our reference to it.
31480 removeBox: function() {
31481 this.map.viewPortDiv.removeChild(this.zoomBox);
31482 this.zoomBox = null;
31483 this.boxOffsets = null;
31484 OpenLayers.Element.removeClass(
31485 this.map.viewPortDiv, "olDrawBox"
31493 activate: function () {
31494 if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
31495 this.dragHandler.activate();
31503 * Method: deactivate
31505 deactivate: function () {
31506 if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
31507 if (this.dragHandler.deactivate()) {
31508 if (this.zoomBox) {
31519 * Method: getBoxOffsets
31520 * Determines border offsets for a box, according to the box model.
31523 * {Object} an object with the following offsets:
31531 getBoxOffsets: function() {
31532 if (!this.boxOffsets) {
31533 // Determine the box model. If the testDiv's clientWidth is 3, then
31534 // the borders are outside and we are dealing with the w3c box
31535 // model. Otherwise, the browser uses the traditional box model and
31536 // the borders are inside the box bounds, leaving us with a
31537 // clientWidth of 1.
31538 var testDiv = document.createElement("div");
31539 //testDiv.style.visibility = "hidden";
31540 testDiv.style.position = "absolute";
31541 testDiv.style.border = "1px solid black";
31542 testDiv.style.width = "3px";
31543 document.body.appendChild(testDiv);
31544 var w3cBoxModel = testDiv.clientWidth == 3;
31545 document.body.removeChild(testDiv);
31547 var left = parseInt(OpenLayers.Element.getStyle(this.zoomBox,
31548 "border-left-width"));
31549 var right = parseInt(OpenLayers.Element.getStyle(
31550 this.zoomBox, "border-right-width"));
31551 var top = parseInt(OpenLayers.Element.getStyle(this.zoomBox,
31552 "border-top-width"));
31553 var bottom = parseInt(OpenLayers.Element.getStyle(
31554 this.zoomBox, "border-bottom-width"));
31555 this.boxOffsets = {
31560 width: w3cBoxModel === false ? left + right : 0,
31561 height: w3cBoxModel === false ? top + bottom : 0
31564 return this.boxOffsets;
31567 CLASS_NAME: "OpenLayers.Handler.Box"
31569 /* ======================================================================
31570 OpenLayers/Control/ZoomBox.js
31571 ====================================================================== */
31573 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
31574 * full list of contributors). Published under the 2-clause BSD license.
31575 * See license.txt in the OpenLayers distribution or repository for the
31576 * full text of the license. */
31579 * @requires OpenLayers/Control.js
31580 * @requires OpenLayers/Handler/Box.js
31584 * Class: OpenLayers.Control.ZoomBox
31585 * The ZoomBox control enables zooming directly to a given extent, by drawing
31586 * a box on the map. The box is drawn by holding down shift, whilst dragging
31590 * - <OpenLayers.Control>
31592 OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, {
31595 * {OpenLayers.Control.TYPE}
31597 type: OpenLayers.Control.TYPE_TOOL,
31601 * {Boolean} Should the control be used for zooming out?
31606 * APIProperty: keyMask
31607 * {Integer} Zoom only occurs if the keyMask matches the combination of
31608 * keys down. Use bitwise operators and one or more of the
31609 * <OpenLayers.Handler> constants to construct a keyMask. Leave null if
31610 * not used mask. Default is null.
31615 * APIProperty: alwaysZoom
31616 * {Boolean} Always zoom in/out when box drawn, even if the zoom level does
31622 * APIProperty: zoomOnClick
31623 * {Boolean} Should we zoom when no box was dragged, i.e. the user only
31624 * clicked? Default is true.
31632 this.handler = new OpenLayers.Handler.Box( this,
31633 {done: this.zoomBox}, {keyMask: this.keyMask} );
31640 * position - {<OpenLayers.Bounds>} or {<OpenLayers.Pixel>}
31642 zoomBox: function (position) {
31643 if (position instanceof OpenLayers.Bounds) {
31645 targetCenterPx = position.getCenterPixel();
31647 var minXY = this.map.getLonLatFromPixel({
31651 var maxXY = this.map.getLonLatFromPixel({
31655 bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat,
31656 maxXY.lon, maxXY.lat);
31658 var pixWidth = position.right - position.left;
31659 var pixHeight = position.bottom - position.top;
31660 var zoomFactor = Math.min((this.map.size.h / pixHeight),
31661 (this.map.size.w / pixWidth));
31662 var extent = this.map.getExtent();
31663 var center = this.map.getLonLatFromPixel(targetCenterPx);
31664 var xmin = center.lon - (extent.getWidth()/2)*zoomFactor;
31665 var xmax = center.lon + (extent.getWidth()/2)*zoomFactor;
31666 var ymin = center.lat - (extent.getHeight()/2)*zoomFactor;
31667 var ymax = center.lat + (extent.getHeight()/2)*zoomFactor;
31668 bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax);
31670 // always zoom in/out
31671 var lastZoom = this.map.getZoom(),
31672 size = this.map.getSize(),
31673 centerPx = {x: size.w / 2, y: size.h / 2},
31674 zoom = this.map.getZoomForExtent(bounds),
31675 oldRes = this.map.getResolution(),
31676 newRes = this.map.getResolutionForZoom(zoom);
31677 if (oldRes == newRes) {
31678 this.map.setCenter(this.map.getLonLatFromPixel(targetCenterPx));
31680 var zoomOriginPx = {
31681 x: (oldRes * targetCenterPx.x - newRes * centerPx.x) /
31683 y: (oldRes * targetCenterPx.y - newRes * centerPx.y) /
31686 this.map.zoomTo(zoom, zoomOriginPx);
31688 if (lastZoom == this.map.getZoom() && this.alwaysZoom == true){
31689 this.map.zoomTo(lastZoom + (this.out ? -1 : 1));
31691 } else if (this.zoomOnClick) { // it's a pixel
31693 this.map.zoomTo(this.map.getZoom() + 1, position);
31695 this.map.zoomTo(this.map.getZoom() - 1, position);
31700 CLASS_NAME: "OpenLayers.Control.ZoomBox"
31702 /* ======================================================================
31703 OpenLayers/Control/DragPan.js
31704 ====================================================================== */
31706 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
31707 * full list of contributors). Published under the 2-clause BSD license.
31708 * See license.txt in the OpenLayers distribution or repository for the
31709 * full text of the license. */
31712 * @requires OpenLayers/Control.js
31713 * @requires OpenLayers/Handler/Drag.js
31717 * Class: OpenLayers.Control.DragPan
31718 * The DragPan control pans the map with a drag of the mouse.
31721 * - <OpenLayers.Control>
31723 OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, {
31727 * {OpenLayers.Control.TYPES}
31729 type: OpenLayers.Control.TYPE_TOOL,
31733 * {Boolean} The map moved.
31738 * Property: interval
31739 * {Integer} The number of milliseconds that should ellapse before
31740 * panning the map again. Defaults to 0 milliseconds, which means that
31741 * no separate cycle is used for panning. In most cases you won't want
31742 * to change this value. For slow machines/devices larger values can be
31748 * APIProperty: documentDrag
31749 * {Boolean} If set to true, mouse dragging will continue even if the
31750 * mouse cursor leaves the map viewport. Default is false.
31752 documentDrag: false,
31755 * Property: kinetic
31756 * {<OpenLayers.Kinetic>} The OpenLayers.Kinetic object.
31761 * APIProperty: enableKinetic
31762 * {Boolean} Set this option to enable "kinetic dragging". Can be
31763 * set to true or to an object. If set to an object this
31764 * object will be passed to the {<OpenLayers.Kinetic>}
31765 * constructor. Defaults to true.
31766 * To get kinetic dragging, ensure that OpenLayers/Kinetic.js is
31767 * included in your build config.
31769 enableKinetic: true,
31772 * APIProperty: kineticInterval
31773 * {Integer} Interval in milliseconds between 2 steps in the "kinetic
31774 * scrolling". Applies only if enableKinetic is set. Defaults
31775 * to 10 milliseconds.
31777 kineticInterval: 10,
31782 * Creates a Drag handler, using <panMap> and
31783 * <panMapDone> as callbacks.
31786 if (this.enableKinetic && OpenLayers.Kinetic) {
31787 var config = {interval: this.kineticInterval};
31788 if(typeof this.enableKinetic === "object") {
31789 config = OpenLayers.Util.extend(config, this.enableKinetic);
31791 this.kinetic = new OpenLayers.Kinetic(config);
31793 this.handler = new OpenLayers.Handler.Drag(this, {
31794 "move": this.panMap,
31795 "done": this.panMapDone,
31796 "down": this.panMapStart
31798 interval: this.interval,
31799 documentDrag: this.documentDrag
31805 * Method: panMapStart
31807 panMapStart: function() {
31809 this.kinetic.begin();
31817 * xy - {<OpenLayers.Pixel>} Pixel of the mouse position
31819 panMap: function(xy) {
31821 this.kinetic.update(xy);
31823 this.panned = true;
31825 this.handler.last.x - xy.x,
31826 this.handler.last.y - xy.y,
31827 {dragging: true, animate: false}
31832 * Method: panMapDone
31833 * Finish the panning operation. Only call setCenter (through <panMap>)
31834 * if the map has actually been moved.
31837 * xy - {<OpenLayers.Pixel>} Pixel of the mouse position
31839 panMapDone: function(xy) {
31842 if (this.kinetic) {
31843 res = this.kinetic.end(xy);
31846 this.handler.last.x - xy.x,
31847 this.handler.last.y - xy.y,
31848 {dragging: !!res, animate: false}
31852 this.kinetic.move(res, function(x, y, end) {
31853 self.map.pan(x, y, {dragging: !end, animate: false});
31856 this.panned = false;
31860 CLASS_NAME: "OpenLayers.Control.DragPan"
31862 /* ======================================================================
31863 OpenLayers/Handler/Click.js
31864 ====================================================================== */
31866 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
31867 * full list of contributors). Published under the 2-clause BSD license.
31868 * See license.txt in the OpenLayers distribution or repository for the
31869 * full text of the license. */
31872 * @requires OpenLayers/Handler.js
31876 * Class: OpenLayers.Handler.Click
31877 * A handler for mouse clicks. The intention of this handler is to give
31878 * controls more flexibility with handling clicks. Browsers trigger
31879 * click events twice for a double-click. In addition, the mousedown,
31880 * mousemove, mouseup sequence fires a click event. With this handler,
31881 * controls can decide whether to ignore clicks associated with a double
31882 * click. By setting a <pixelTolerance>, controls can also ignore clicks
31883 * that include a drag. Create a new instance with the
31884 * <OpenLayers.Handler.Click> constructor.
31887 * - <OpenLayers.Handler>
31889 OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, {
31891 * APIProperty: delay
31892 * {Number} Number of milliseconds between clicks before the event is
31893 * considered a double-click.
31898 * APIProperty: single
31899 * {Boolean} Handle single clicks. Default is true. If false, clicks
31900 * will not be reported. If true, single-clicks will be reported.
31905 * APIProperty: double
31906 * {Boolean} Handle double-clicks. Default is false.
31911 * APIProperty: pixelTolerance
31912 * {Number} Maximum number of pixels between mouseup and mousedown for an
31913 * event to be considered a click. Default is 0. If set to an
31914 * integer value, clicks with a drag greater than the value will be
31915 * ignored. This property can only be set when the handler is
31921 * APIProperty: dblclickTolerance
31922 * {Number} Maximum distance in pixels between clicks for a sequence of
31923 * events to be considered a double click. Default is 13. If the
31924 * distance between two clicks is greater than this value, a double-
31925 * click will not be fired.
31927 dblclickTolerance: 13,
31930 * APIProperty: stopSingle
31931 * {Boolean} Stop other listeners from being notified of clicks. Default
31932 * is false. If true, any listeners registered before this one for
31933 * click or rightclick events will not be notified.
31938 * APIProperty: stopDouble
31939 * {Boolean} Stop other listeners from being notified of double-clicks.
31940 * Default is false. If true, any click listeners registered before
31941 * this one will not be notified of *any* double-click events.
31943 * The one caveat with stopDouble is that given a map with two click
31944 * handlers, one with stopDouble true and the other with stopSingle
31945 * true, the stopSingle handler should be activated last to get
31946 * uniform cross-browser performance. Since IE triggers one click
31947 * with a dblclick and FF triggers two, if a stopSingle handler is
31948 * activated first, all it gets in IE is a single click when the
31949 * second handler stops propagation on the dblclick.
31954 * Property: timerId
31955 * {Number} The id of the timeout waiting to clear the <delayedCall>.
31961 * {Object} Object that store relevant information about the last
31962 * mousedown or touchstart. Its 'xy' OpenLayers.Pixel property gives
31963 * the average location of the mouse/touch event. Its 'touches'
31964 * property records clientX/clientY of each touches.
31970 * {Object} Object that store relevant information about the last
31971 * mousemove or touchmove. Its 'xy' OpenLayers.Pixel property gives
31972 * the average location of the mouse/touch event. Its 'touches'
31973 * property records clientX/clientY of each touches.
31979 * {Object} When waiting for double clicks, this object will store
31980 * information about the first click in a two click sequence.
31985 * Property: rightclickTimerId
31986 * {Number} The id of the right mouse timeout waiting to clear the
31989 rightclickTimerId: null,
31992 * Constructor: OpenLayers.Handler.Click
31993 * Create a new click handler.
31996 * control - {<OpenLayers.Control>} The control that is making use of
31997 * this handler. If a handler is being used without a control, the
31998 * handler's setMap method must be overridden to deal properly with
32000 * callbacks - {Object} An object with keys corresponding to callbacks
32001 * that will be called by the handler. The callbacks should
32002 * expect to recieve a single argument, the click event.
32003 * Callbacks for 'click' and 'dblclick' are supported.
32004 * options - {Object} Optional object whose properties will be set on the
32009 * Method: touchstart
32010 * Handle touchstart.
32013 * {Boolean} Continue propagating this event.
32015 touchstart: function(evt) {
32017 this.down = this.getEventInfo(evt);
32018 this.last = this.getEventInfo(evt);
32023 * Method: touchmove
32024 * Store position of last move, because touchend event can have
32025 * an empty "touches" property.
32028 * {Boolean} Continue propagating this event.
32030 touchmove: function(evt) {
32031 this.last = this.getEventInfo(evt);
32037 * Correctly set event xy property, and add lastTouches to have
32038 * touches property from last touchstart or touchmove
32041 * {Boolean} Continue propagating this event.
32043 touchend: function(evt) {
32044 // touchstart may not have been allowed to propagate
32046 evt.xy = this.last.xy;
32047 evt.lastTouches = this.last.touches;
32048 this.handleSingle(evt);
32055 * Method: mousedown
32056 * Handle mousedown.
32059 * {Boolean} Continue propagating this event.
32061 mousedown: function(evt) {
32062 this.down = this.getEventInfo(evt);
32063 this.last = this.getEventInfo(evt);
32069 * Handle mouseup. Installed to support collection of right mouse events.
32072 * {Boolean} Continue propagating this event.
32074 mouseup: function (evt) {
32075 var propagate = true;
32077 // Collect right mouse clicks from the mouseup
32078 // IE - ignores the second right click in mousedown so using
32080 if (this.checkModifiers(evt) && this.control.handleRightClicks &&
32081 OpenLayers.Event.isRightClick(evt)) {
32082 propagate = this.rightclick(evt);
32089 * Method: rightclick
32090 * Handle rightclick. For a dblrightclick, we get two clicks so we need
32091 * to always register for dblrightclick to properly handle single
32095 * {Boolean} Continue propagating this event.
32097 rightclick: function(evt) {
32098 if(this.passesTolerance(evt)) {
32099 if(this.rightclickTimerId != null) {
32100 //Second click received before timeout this must be
32103 this.callback('dblrightclick', [evt]);
32104 return !this.stopDouble;
32106 //Set the rightclickTimerId, send evt only if double is
32107 // true else trigger single
32108 var clickEvent = this['double'] ?
32109 OpenLayers.Util.extend({}, evt) :
32110 this.callback('rightclick', [evt]);
32112 var delayedRightCall = OpenLayers.Function.bind(
32113 this.delayedRightCall,
32117 this.rightclickTimerId = window.setTimeout(
32118 delayedRightCall, this.delay
32122 return !this.stopSingle;
32126 * Method: delayedRightCall
32127 * Sets <rightclickTimerId> to null. And optionally triggers the
32128 * rightclick callback if evt is set.
32130 delayedRightCall: function(evt) {
32131 this.rightclickTimerId = null;
32133 this.callback('rightclick', [evt]);
32139 * Handle click events from the browser. This is registered as a listener
32140 * for click events and should not be called from other events in this
32144 * {Boolean} Continue propagating this event.
32146 click: function(evt) {
32148 this.last = this.getEventInfo(evt);
32150 this.handleSingle(evt);
32151 return !this.stopSingle;
32156 * Handle dblclick. For a dblclick, we get two clicks in some browsers
32157 * (FF) and one in others (IE). So we need to always register for
32158 * dblclick to properly handle single clicks. This method is registered
32159 * as a listener for the dblclick browser event. It should *not* be
32160 * called by other methods in this handler.
32163 * {Boolean} Continue propagating this event.
32165 dblclick: function(evt) {
32166 this.handleDouble(evt);
32167 return !this.stopDouble;
32171 * Method: handleDouble
32172 * Handle double-click sequence.
32174 handleDouble: function(evt) {
32175 if (this.passesDblclickTolerance(evt)) {
32176 if (this["double"]) {
32177 this.callback("dblclick", [evt]);
32179 // to prevent a dblclick from firing the click callback in IE
32185 * Method: handleSingle
32186 * Handle single click sequence.
32188 handleSingle: function(evt) {
32189 if (this.passesTolerance(evt)) {
32190 if (this.timerId != null) {
32191 // already received a click
32192 if (this.last.touches && this.last.touches.length === 1) {
32193 // touch device, no dblclick event - this may be a double
32194 if (this["double"]) {
32195 // on Android don't let the browser zoom on the page
32196 OpenLayers.Event.preventDefault(evt);
32198 this.handleDouble(evt);
32200 // if we're not in a touch environment we clear the click timer
32201 // if we've got a second touch, we'll get two touchend events
32202 if (!this.last.touches || this.last.touches.length !== 2) {
32206 // remember the first click info so we can compare to the second
32207 this.first = this.getEventInfo(evt);
32208 // set the timer, send evt only if single is true
32209 //use a clone of the event object because it will no longer
32210 //be a valid event object in IE in the timer callback
32211 var clickEvent = this.single ?
32212 OpenLayers.Util.extend({}, evt) : null;
32213 this.queuePotentialClick(clickEvent);
32219 * Method: queuePotentialClick
32220 * This method is separated out largely to make testing easier (so we
32221 * don't have to override window.setTimeout)
32223 queuePotentialClick: function(evt) {
32224 this.timerId = window.setTimeout(
32225 OpenLayers.Function.bind(this.delayedCall, this, evt),
32231 * Method: passesTolerance
32232 * Determine whether the event is within the optional pixel tolerance. Note
32233 * that the pixel tolerance check only works if mousedown events get to
32234 * the listeners registered here. If they are stopped by other elements,
32235 * the <pixelTolerance> will have no effect here (this method will always
32239 * {Boolean} The click is within the pixel tolerance (if specified).
32241 passesTolerance: function(evt) {
32243 if (this.pixelTolerance != null && this.down && this.down.xy) {
32244 passes = this.pixelTolerance >= this.down.xy.distanceTo(evt.xy);
32245 // for touch environments, we also enforce that all touches
32246 // start and end within the given tolerance to be considered a click
32247 if (passes && this.touch &&
32248 this.down.touches.length === this.last.touches.length) {
32249 // the touchend event doesn't come with touches, so we check
32251 for (var i=0, ii=this.down.touches.length; i<ii; ++i) {
32252 if (this.getTouchDistance(
32253 this.down.touches[i],
32254 this.last.touches[i]
32255 ) > this.pixelTolerance) {
32266 * Method: getTouchDistance
32269 * {Boolean} The pixel displacement between two touches.
32271 getTouchDistance: function(from, to) {
32273 Math.pow(from.clientX - to.clientX, 2) +
32274 Math.pow(from.clientY - to.clientY, 2)
32279 * Method: passesDblclickTolerance
32280 * Determine whether the event is within the optional double-cick pixel
32284 * {Boolean} The click is within the double-click pixel tolerance.
32286 passesDblclickTolerance: function(evt) {
32288 if (this.down && this.first) {
32289 passes = this.down.xy.distanceTo(this.first.xy) <= this.dblclickTolerance;
32295 * Method: clearTimer
32296 * Clear the timer and set <timerId> to null.
32298 clearTimer: function() {
32299 if (this.timerId != null) {
32300 window.clearTimeout(this.timerId);
32301 this.timerId = null;
32303 if (this.rightclickTimerId != null) {
32304 window.clearTimeout(this.rightclickTimerId);
32305 this.rightclickTimerId = null;
32310 * Method: delayedCall
32311 * Sets <timerId> to null. And optionally triggers the click callback if
32314 delayedCall: function(evt) {
32315 this.timerId = null;
32317 this.callback("click", [evt]);
32322 * Method: getEventInfo
32323 * This method allows us to store event information without storing the
32324 * actual event. In touch devices (at least), the same event is
32325 * modified between touchstart, touchmove, and touchend.
32328 * {Object} An object with event related info.
32330 getEventInfo: function(evt) {
32333 var len = evt.touches.length;
32334 touches = new Array(len);
32336 for (var i=0; i<len; i++) {
32337 touch = evt.touches[i];
32339 clientX: touch.olClientX,
32340 clientY: touch.olClientY
32351 * APIMethod: deactivate
32352 * Deactivate the handler.
32355 * {Boolean} The handler was successfully deactivated.
32357 deactivate: function() {
32358 var deactivated = false;
32359 if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
32364 deactivated = true;
32366 return deactivated;
32369 CLASS_NAME: "OpenLayers.Handler.Click"
32371 /* ======================================================================
32372 OpenLayers/Control/Navigation.js
32373 ====================================================================== */
32375 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
32376 * full list of contributors). Published under the 2-clause BSD license.
32377 * See license.txt in the OpenLayers distribution or repository for the
32378 * full text of the license. */
32381 * @requires OpenLayers/Control/ZoomBox.js
32382 * @requires OpenLayers/Control/DragPan.js
32383 * @requires OpenLayers/Handler/MouseWheel.js
32384 * @requires OpenLayers/Handler/Click.js
32388 * Class: OpenLayers.Control.Navigation
32389 * The navigation control handles map browsing with mouse events (dragging,
32390 * double-clicking, and scrolling the wheel). Create a new navigation
32391 * control with the <OpenLayers.Control.Navigation> control.
32393 * Note that this control is added to the map by default (if no controls
32394 * array is sent in the options object to the <OpenLayers.Map>
32398 * - <OpenLayers.Control>
32400 OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, {
32403 * Property: dragPan
32404 * {<OpenLayers.Control.DragPan>}
32409 * APIProperty: dragPanOptions
32410 * {Object} Options passed to the DragPan control.
32412 dragPanOptions: null,
32415 * Property: pinchZoom
32416 * {<OpenLayers.Control.PinchZoom>}
32421 * APIProperty: pinchZoomOptions
32422 * {Object} Options passed to the PinchZoom control.
32424 pinchZoomOptions: null,
32427 * APIProperty: documentDrag
32428 * {Boolean} Allow panning of the map by dragging outside map viewport.
32429 * Default is false.
32431 documentDrag: false,
32434 * Property: zoomBox
32435 * {<OpenLayers.Control.ZoomBox>}
32440 * APIProperty: zoomBoxEnabled
32441 * {Boolean} Whether the user can draw a box to zoom
32443 zoomBoxEnabled: true,
32446 * APIProperty: zoomWheelEnabled
32447 * {Boolean} Whether the mousewheel should zoom the map
32449 zoomWheelEnabled: true,
32452 * Property: mouseWheelOptions
32453 * {Object} Options passed to the MouseWheel control (only useful if
32454 * <zoomWheelEnabled> is set to true). Default is no options for maps
32455 * with fractionalZoom set to true, otherwise
32456 * {cumulative: false, interval: 50, maxDelta: 6}
32458 mouseWheelOptions: null,
32461 * APIProperty: handleRightClicks
32462 * {Boolean} Whether or not to handle right clicks. Default is false.
32464 handleRightClicks: false,
32467 * APIProperty: zoomBoxKeyMask
32468 * {Integer} <OpenLayers.Handler> key code of the key, which has to be
32469 * pressed, while drawing the zoom box with the mouse on the screen.
32470 * You should probably set handleRightClicks to true if you use this
32471 * with MOD_CTRL, to disable the context menu for machines which use
32472 * CTRL-Click as a right click.
32473 * Default: <OpenLayers.Handler.MOD_SHIFT>
32475 zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT,
32478 * APIProperty: autoActivate
32479 * {Boolean} Activate the control when it is added to a map. Default is
32482 autoActivate: true,
32485 * Constructor: OpenLayers.Control.Navigation
32486 * Create a new navigation control
32489 * options - {Object} An optional object whose properties will be set on
32492 initialize: function(options) {
32493 this.handlers = {};
32494 OpenLayers.Control.prototype.initialize.apply(this, arguments);
32499 * The destroy method is used to perform any clean up before the control
32500 * is dereferenced. Typically this is where event listeners are removed
32501 * to prevent memory leaks.
32503 destroy: function() {
32506 if (this.dragPan) {
32507 this.dragPan.destroy();
32509 this.dragPan = null;
32511 if (this.zoomBox) {
32512 this.zoomBox.destroy();
32514 this.zoomBox = null;
32516 if (this.pinchZoom) {
32517 this.pinchZoom.destroy();
32519 this.pinchZoom = null;
32521 OpenLayers.Control.prototype.destroy.apply(this,arguments);
32527 activate: function() {
32528 this.dragPan.activate();
32529 if (this.zoomWheelEnabled) {
32530 this.handlers.wheel.activate();
32532 this.handlers.click.activate();
32533 if (this.zoomBoxEnabled) {
32534 this.zoomBox.activate();
32536 if (this.pinchZoom) {
32537 this.pinchZoom.activate();
32539 return OpenLayers.Control.prototype.activate.apply(this,arguments);
32543 * Method: deactivate
32545 deactivate: function() {
32546 if (this.pinchZoom) {
32547 this.pinchZoom.deactivate();
32549 this.zoomBox.deactivate();
32550 this.dragPan.deactivate();
32551 this.handlers.click.deactivate();
32552 this.handlers.wheel.deactivate();
32553 return OpenLayers.Control.prototype.deactivate.apply(this,arguments);
32560 // disable right mouse context menu for support of right click events
32561 if (this.handleRightClicks) {
32562 this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False;
32565 var clickCallbacks = {
32566 'click': this.defaultClick,
32567 'dblclick': this.defaultDblClick,
32568 'dblrightclick': this.defaultDblRightClick
32570 var clickOptions = {
32574 this.handlers.click = new OpenLayers.Handler.Click(
32575 this, clickCallbacks, clickOptions
32577 this.dragPan = new OpenLayers.Control.DragPan(
32578 OpenLayers.Util.extend({
32580 documentDrag: this.documentDrag
32581 }, this.dragPanOptions)
32583 this.zoomBox = new OpenLayers.Control.ZoomBox(
32584 {map: this.map, keyMask: this.zoomBoxKeyMask});
32585 this.dragPan.draw();
32586 this.zoomBox.draw();
32587 var wheelOptions = this.map.fractionalZoom ? {} : {
32592 this.handlers.wheel = new OpenLayers.Handler.MouseWheel(
32593 this, {up : this.wheelUp, down: this.wheelDown},
32594 OpenLayers.Util.extend(wheelOptions, this.mouseWheelOptions)
32596 if (OpenLayers.Control.PinchZoom) {
32597 this.pinchZoom = new OpenLayers.Control.PinchZoom(
32598 OpenLayers.Util.extend(
32599 {map: this.map}, this.pinchZoomOptions));
32604 * Method: defaultClick
32609 defaultClick: function (evt) {
32610 if (evt.lastTouches && evt.lastTouches.length == 2) {
32611 this.map.zoomOut();
32616 * Method: defaultDblClick
32621 defaultDblClick: function (evt) {
32622 this.map.zoomTo(this.map.zoom + 1, evt.xy);
32626 * Method: defaultDblRightClick
32631 defaultDblRightClick: function (evt) {
32632 this.map.zoomTo(this.map.zoom - 1, evt.xy);
32636 * Method: wheelChange
32640 * deltaZ - {Integer}
32642 wheelChange: function(evt, deltaZ) {
32643 if (!this.map.fractionalZoom) {
32644 deltaZ = Math.round(deltaZ);
32646 var currentZoom = this.map.getZoom(),
32647 newZoom = currentZoom + deltaZ;
32648 newZoom = Math.max(newZoom, 0);
32649 newZoom = Math.min(newZoom, this.map.getNumZoomLevels());
32650 if (newZoom === currentZoom) {
32653 this.map.zoomTo(newZoom, evt.xy);
32658 * User spun scroll wheel up
32662 * delta - {Integer}
32664 wheelUp: function(evt, delta) {
32665 this.wheelChange(evt, delta || 1);
32669 * Method: wheelDown
32670 * User spun scroll wheel down
32674 * delta - {Integer}
32676 wheelDown: function(evt, delta) {
32677 this.wheelChange(evt, delta || -1);
32681 * Method: disableZoomBox
32683 disableZoomBox : function() {
32684 this.zoomBoxEnabled = false;
32685 this.zoomBox.deactivate();
32689 * Method: enableZoomBox
32691 enableZoomBox : function() {
32692 this.zoomBoxEnabled = true;
32694 this.zoomBox.activate();
32699 * Method: disableZoomWheel
32702 disableZoomWheel : function() {
32703 this.zoomWheelEnabled = false;
32704 this.handlers.wheel.deactivate();
32708 * Method: enableZoomWheel
32711 enableZoomWheel : function() {
32712 this.zoomWheelEnabled = true;
32714 this.handlers.wheel.activate();
32718 CLASS_NAME: "OpenLayers.Control.Navigation"
32720 /* ======================================================================
32721 OpenLayers/Renderer/SVG.js
32722 ====================================================================== */
32724 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
32725 * full list of contributors). Published under the 2-clause BSD license.
32726 * See license.txt in the OpenLayers distribution or repository for the
32727 * full text of the license. */
32730 * @requires OpenLayers/Renderer/Elements.js
32734 * Class: OpenLayers.Renderer.SVG
32737 * - <OpenLayers.Renderer.Elements>
32739 OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
32745 xmlns: "http://www.w3.org/2000/svg",
32748 * Property: xlinkns
32751 xlinkns: "http://www.w3.org/1999/xlink",
32754 * Constant: MAX_PIXEL
32755 * {Integer} Firefox has a limitation where values larger or smaller than
32756 * about 15000 in an SVG document lock the browser up. This
32762 * Property: translationParameters
32763 * {Object} Hash with "x" and "y" properties
32765 translationParameters: null,
32768 * Property: symbolMetrics
32769 * {Object} Cache for symbol metrics according to their svg coordinate
32770 * space. This is an object keyed by the symbol's id, and values are
32771 * an array of [width, centerX, centerY].
32773 symbolMetrics: null,
32776 * Constructor: OpenLayers.Renderer.SVG
32779 * containerID - {String}
32781 initialize: function(containerID) {
32782 if (!this.supported()) {
32785 OpenLayers.Renderer.Elements.prototype.initialize.apply(this,
32787 this.translationParameters = {x: 0, y: 0};
32789 this.symbolMetrics = {};
32793 * APIMethod: supported
32796 * {Boolean} Whether or not the browser supports the SVG renderer
32798 supported: function() {
32799 var svgFeature = "http://www.w3.org/TR/SVG11/feature#";
32800 return (document.implementation &&
32801 (document.implementation.hasFeature("org.w3c.svg", "1.0") ||
32802 document.implementation.hasFeature(svgFeature + "SVG", "1.1") ||
32803 document.implementation.hasFeature(svgFeature + "BasicStructure", "1.1") ));
32807 * Method: inValidRange
32808 * See #669 for more information
32813 * xyOnly - {Boolean} whether or not to just check for x and y, which means
32814 * to not take the current translation parameters into account if true.
32817 * {Boolean} Whether or not the 'x' and 'y' coordinates are in the
32820 inValidRange: function(x, y, xyOnly) {
32821 var left = x + (xyOnly ? 0 : this.translationParameters.x);
32822 var top = y + (xyOnly ? 0 : this.translationParameters.y);
32823 return (left >= -this.MAX_PIXEL && left <= this.MAX_PIXEL &&
32824 top >= -this.MAX_PIXEL && top <= this.MAX_PIXEL);
32828 * Method: setExtent
32831 * extent - {<OpenLayers.Bounds>}
32832 * resolutionChanged - {Boolean}
32835 * {Boolean} true to notify the layer that the new extent does not exceed
32836 * the coordinate range, and the features will not need to be redrawn.
32839 setExtent: function(extent, resolutionChanged) {
32840 var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments);
32842 var resolution = this.getResolution(),
32843 left = -extent.left / resolution,
32844 top = extent.top / resolution;
32846 // If the resolution has changed, start over changing the corner, because
32847 // the features will redraw.
32848 if (resolutionChanged) {
32852 var extentString = "0 0 " + this.size.w + " " + this.size.h;
32854 this.rendererRoot.setAttributeNS(null, "viewBox", extentString);
32855 this.translate(this.xOffset, 0);
32858 var inRange = this.translate(left - this.left + this.xOffset, top - this.top);
32860 // recenter the coordinate system
32861 this.setExtent(extent, true);
32863 return coordSysUnchanged && inRange;
32868 * Method: translate
32869 * Transforms the SVG coordinate system
32876 * {Boolean} true if the translation parameters are in the valid coordinates
32877 * range, false otherwise.
32879 translate: function(x, y) {
32880 if (!this.inValidRange(x, y, true)) {
32883 var transformString = "";
32885 transformString = "translate(" + x + "," + y + ")";
32887 this.root.setAttributeNS(null, "transform", transformString);
32888 this.translationParameters = {x: x, y: y};
32895 * Sets the size of the drawing surface.
32898 * size - {<OpenLayers.Size>} The size of the drawing surface
32900 setSize: function(size) {
32901 OpenLayers.Renderer.prototype.setSize.apply(this, arguments);
32903 this.rendererRoot.setAttributeNS(null, "width", this.size.w);
32904 this.rendererRoot.setAttributeNS(null, "height", this.size.h);
32908 * Method: getNodeType
32911 * geometry - {<OpenLayers.Geometry>}
32915 * {String} The corresponding node type for the specified geometry
32917 getNodeType: function(geometry, style) {
32918 var nodeType = null;
32919 switch (geometry.CLASS_NAME) {
32920 case "OpenLayers.Geometry.Point":
32921 if (style.externalGraphic) {
32922 nodeType = "image";
32923 } else if (this.isComplexSymbol(style.graphicName)) {
32926 nodeType = "circle";
32929 case "OpenLayers.Geometry.Rectangle":
32932 case "OpenLayers.Geometry.LineString":
32933 nodeType = "polyline";
32935 case "OpenLayers.Geometry.LinearRing":
32936 nodeType = "polygon";
32938 case "OpenLayers.Geometry.Polygon":
32939 case "OpenLayers.Geometry.Curve":
32950 * Use to set all the style attributes to a SVG node.
32952 * Takes care to adjust stroke width and point radius to be
32953 * resolution-relative
32956 * node - {SVGDomElement} An SVG element to decorate
32958 * options - {Object} Currently supported options include
32959 * 'isFilled' {Boolean} and
32960 * 'isStroked' {Boolean}
32962 setStyle: function(node, style, options) {
32963 style = style || node._style;
32964 options = options || node._options;
32966 var title = style.title || style.graphicTitle;
32968 node.setAttributeNS(null, "title", title);
32969 //Standards-conformant SVG
32970 // Prevent duplicate nodes. See issue https://github.com/openlayers/openlayers/issues/92
32971 var titleNode = node.getElementsByTagName("title");
32972 if (titleNode.length > 0) {
32973 titleNode[0].firstChild.textContent = title;
32975 var label = this.nodeFactory(null, "title");
32976 label.textContent = title;
32977 node.appendChild(label);
32981 var r = parseFloat(node.getAttributeNS(null, "r"));
32982 var widthFactor = 1;
32984 if (node._geometryClass == "OpenLayers.Geometry.Point" && r) {
32985 node.style.visibility = "";
32986 if (style.graphic === false) {
32987 node.style.visibility = "hidden";
32988 } else if (style.externalGraphic) {
32989 pos = this.getPosition(node);
32990 if (style.graphicWidth && style.graphicHeight) {
32991 node.setAttributeNS(null, "preserveAspectRatio", "none");
32993 var width = style.graphicWidth || style.graphicHeight;
32994 var height = style.graphicHeight || style.graphicWidth;
32995 width = width ? width : style.pointRadius*2;
32996 height = height ? height : style.pointRadius*2;
32997 var xOffset = (style.graphicXOffset != undefined) ?
32998 style.graphicXOffset : -(0.5 * width);
32999 var yOffset = (style.graphicYOffset != undefined) ?
33000 style.graphicYOffset : -(0.5 * height);
33002 var opacity = style.graphicOpacity || style.fillOpacity;
33004 node.setAttributeNS(null, "x", (pos.x + xOffset).toFixed());
33005 node.setAttributeNS(null, "y", (pos.y + yOffset).toFixed());
33006 node.setAttributeNS(null, "width", width);
33007 node.setAttributeNS(null, "height", height);
33008 node.setAttributeNS(this.xlinkns, "xlink:href", style.externalGraphic);
33009 node.setAttributeNS(null, "style", "opacity: "+opacity);
33010 node.onclick = OpenLayers.Event.preventDefault;
33011 } else if (this.isComplexSymbol(style.graphicName)) {
33012 // the symbol viewBox is three times as large as the symbol
33013 var offset = style.pointRadius * 3;
33014 var size = offset * 2;
33015 var src = this.importSymbol(style.graphicName);
33016 pos = this.getPosition(node);
33017 widthFactor = this.symbolMetrics[src.id][0] * 3 / size;
33019 // remove the node from the dom before we modify it. This
33020 // prevents various rendering issues in Safari and FF
33021 var parent = node.parentNode;
33022 var nextSibling = node.nextSibling;
33024 parent.removeChild(node);
33027 // The more appropriate way to implement this would be use/defs,
33028 // but due to various issues in several browsers, it is safer to
33029 // copy the symbols instead of referencing them.
33030 // See e.g. ticket http://trac.osgeo.org/openlayers/ticket/2985
33031 // and this email thread
33032 // http://osgeo-org.1803224.n2.nabble.com/Select-Control-Ctrl-click-on-Feature-with-a-graphicName-opens-new-browser-window-tc5846039.html
33033 node.firstChild && node.removeChild(node.firstChild);
33034 node.appendChild(src.firstChild.cloneNode(true));
33035 node.setAttributeNS(null, "viewBox", src.getAttributeNS(null, "viewBox"));
33037 node.setAttributeNS(null, "width", size);
33038 node.setAttributeNS(null, "height", size);
33039 node.setAttributeNS(null, "x", pos.x - offset);
33040 node.setAttributeNS(null, "y", pos.y - offset);
33042 // now that the node has all its new properties, insert it
33043 // back into the dom where it was
33045 parent.insertBefore(node, nextSibling);
33046 } else if(parent) {
33047 parent.appendChild(node);
33050 node.setAttributeNS(null, "r", style.pointRadius);
33053 var rotation = style.rotation;
33055 if ((rotation !== undefined || node._rotation !== undefined) && pos) {
33056 node._rotation = rotation;
33058 if (node.nodeName !== "svg") {
33059 node.setAttributeNS(null, "transform",
33060 "rotate(" + rotation + " " + pos.x + " " +
33063 var metrics = this.symbolMetrics[src.id];
33064 node.firstChild.setAttributeNS(null, "transform", "rotate("
33067 + metrics[2] + ")");
33072 if (options.isFilled) {
33073 node.setAttributeNS(null, "fill", style.fillColor);
33074 node.setAttributeNS(null, "fill-opacity", style.fillOpacity);
33076 node.setAttributeNS(null, "fill", "none");
33079 if (options.isStroked) {
33080 node.setAttributeNS(null, "stroke", style.strokeColor);
33081 node.setAttributeNS(null, "stroke-opacity", style.strokeOpacity);
33082 node.setAttributeNS(null, "stroke-width", style.strokeWidth * widthFactor);
33083 node.setAttributeNS(null, "stroke-linecap", style.strokeLinecap || "round");
33084 // Hard-coded linejoin for now, to make it look the same as in VML.
33085 // There is no strokeLinejoin property yet for symbolizers.
33086 node.setAttributeNS(null, "stroke-linejoin", "round");
33087 style.strokeDashstyle && node.setAttributeNS(null,
33088 "stroke-dasharray", this.dashStyle(style, widthFactor));
33090 node.setAttributeNS(null, "stroke", "none");
33093 if (style.pointerEvents) {
33094 node.setAttributeNS(null, "pointer-events", style.pointerEvents);
33097 if (style.cursor != null) {
33098 node.setAttributeNS(null, "cursor", style.cursor);
33105 * Method: dashStyle
33109 * widthFactor - {Number}
33112 * {String} A SVG compliant 'stroke-dasharray' value
33114 dashStyle: function(style, widthFactor) {
33115 var w = style.strokeWidth * widthFactor;
33116 var str = style.strokeDashstyle;
33121 return [1, 4 * w].join();
33123 return [4 * w, 4 * w].join();
33125 return [4 * w, 4 * w, 1, 4 * w].join();
33127 return [8 * w, 4 * w].join();
33128 case 'longdashdot':
33129 return [8 * w, 4 * w, 1, 4 * w].join();
33131 return OpenLayers.String.trim(str).replace(/\s+/g, ",");
33136 * Method: createNode
33139 * type - {String} Kind of node to draw
33140 * id - {String} Id for node
33143 * {DOMElement} A new node of the given type and id
33145 createNode: function(type, id) {
33146 var node = document.createElementNS(this.xmlns, type);
33148 node.setAttributeNS(null, "id", id);
33154 * Method: nodeTypeCompare
33157 * node - {SVGDomElement} An SVG element
33158 * type - {String} Kind of node
33161 * {Boolean} Whether or not the specified node is of the specified type
33163 nodeTypeCompare: function(node, type) {
33164 return (type == node.nodeName);
33168 * Method: createRenderRoot
33171 * {DOMElement} The specific render engine's root element
33173 createRenderRoot: function() {
33174 var svg = this.nodeFactory(this.container.id + "_svgRoot", "svg");
33175 svg.style.display = "block";
33180 * Method: createRoot
33183 * suffix - {String} suffix to append to the id
33188 createRoot: function(suffix) {
33189 return this.nodeFactory(this.container.id + suffix, "g");
33193 * Method: createDefs
33196 * {DOMElement} The element to which we'll add the symbol definitions
33198 createDefs: function() {
33199 var defs = this.nodeFactory(this.container.id + "_defs", "defs");
33200 this.rendererRoot.appendChild(defs);
33204 /**************************************
33206 * GEOMETRY DRAWING FUNCTIONS *
33208 **************************************/
33211 * Method: drawPoint
33212 * This method is only called by the renderer itself.
33215 * node - {DOMElement}
33216 * geometry - {<OpenLayers.Geometry>}
33219 * {DOMElement} or false if the renderer could not draw the point
33221 drawPoint: function(node, geometry) {
33222 return this.drawCircle(node, geometry, 1);
33226 * Method: drawCircle
33227 * This method is only called by the renderer itself.
33230 * node - {DOMElement}
33231 * geometry - {<OpenLayers.Geometry>}
33235 * {DOMElement} or false if the renderer could not draw the circle
33237 drawCircle: function(node, geometry, radius) {
33238 var resolution = this.getResolution();
33239 var x = ((geometry.x - this.featureDx) / resolution + this.left);
33240 var y = (this.top - geometry.y / resolution);
33242 if (this.inValidRange(x, y)) {
33243 node.setAttributeNS(null, "cx", x);
33244 node.setAttributeNS(null, "cy", y);
33245 node.setAttributeNS(null, "r", radius);
33254 * Method: drawLineString
33255 * This method is only called by the renderer itself.
33258 * node - {DOMElement}
33259 * geometry - {<OpenLayers.Geometry>}
33262 * {DOMElement} or null if the renderer could not draw all components of
33263 * the linestring, or false if nothing could be drawn
33265 drawLineString: function(node, geometry) {
33266 var componentsResult = this.getComponentsString(geometry.components);
33267 if (componentsResult.path) {
33268 node.setAttributeNS(null, "points", componentsResult.path);
33269 return (componentsResult.complete ? node : null);
33276 * Method: drawLinearRing
33277 * This method is only called by the renderer itself.
33280 * node - {DOMElement}
33281 * geometry - {<OpenLayers.Geometry>}
33284 * {DOMElement} or null if the renderer could not draw all components
33285 * of the linear ring, or false if nothing could be drawn
33287 drawLinearRing: function(node, geometry) {
33288 var componentsResult = this.getComponentsString(geometry.components);
33289 if (componentsResult.path) {
33290 node.setAttributeNS(null, "points", componentsResult.path);
33291 return (componentsResult.complete ? node : null);
33298 * Method: drawPolygon
33299 * This method is only called by the renderer itself.
33302 * node - {DOMElement}
33303 * geometry - {<OpenLayers.Geometry>}
33306 * {DOMElement} or null if the renderer could not draw all components
33307 * of the polygon, or false if nothing could be drawn
33309 drawPolygon: function(node, geometry) {
33312 var complete = true;
33313 var linearRingResult, path;
33314 for (var j=0, len=geometry.components.length; j<len; j++) {
33316 linearRingResult = this.getComponentsString(
33317 geometry.components[j].components, " ");
33318 path = linearRingResult.path;
33321 complete = linearRingResult.complete && complete;
33328 node.setAttributeNS(null, "d", d);
33329 node.setAttributeNS(null, "fill-rule", "evenodd");
33330 return complete ? node : null;
33337 * Method: drawRectangle
33338 * This method is only called by the renderer itself.
33341 * node - {DOMElement}
33342 * geometry - {<OpenLayers.Geometry>}
33345 * {DOMElement} or false if the renderer could not draw the rectangle
33347 drawRectangle: function(node, geometry) {
33348 var resolution = this.getResolution();
33349 var x = ((geometry.x - this.featureDx) / resolution + this.left);
33350 var y = (this.top - geometry.y / resolution);
33352 if (this.inValidRange(x, y)) {
33353 node.setAttributeNS(null, "x", x);
33354 node.setAttributeNS(null, "y", y);
33355 node.setAttributeNS(null, "width", geometry.width / resolution);
33356 node.setAttributeNS(null, "height", geometry.height / resolution);
33365 * This method is only called by the renderer itself.
33368 * featureId - {String}
33370 * location - {<OpenLayers.Geometry.Point>}
33372 drawText: function(featureId, style, location) {
33373 var drawOutline = (!!style.labelOutlineWidth);
33374 // First draw text in halo color and size and overlay the
33375 // normal text afterwards
33377 var outlineStyle = OpenLayers.Util.extend({}, style);
33378 outlineStyle.fontColor = outlineStyle.labelOutlineColor;
33379 outlineStyle.fontStrokeColor = outlineStyle.labelOutlineColor;
33380 outlineStyle.fontStrokeWidth = style.labelOutlineWidth;
33381 if (style.labelOutlineOpacity) {
33382 outlineStyle.fontOpacity = style.labelOutlineOpacity;
33384 delete outlineStyle.labelOutlineWidth;
33385 this.drawText(featureId, outlineStyle, location);
33388 var resolution = this.getResolution();
33390 var x = ((location.x - this.featureDx) / resolution + this.left);
33391 var y = (location.y / resolution - this.top);
33393 var suffix = (drawOutline)?this.LABEL_OUTLINE_SUFFIX:this.LABEL_ID_SUFFIX;
33394 var label = this.nodeFactory(featureId + suffix, "text");
33396 label.setAttributeNS(null, "x", x);
33397 label.setAttributeNS(null, "y", -y);
33399 if (style.fontColor) {
33400 label.setAttributeNS(null, "fill", style.fontColor);
33402 if (style.fontStrokeColor) {
33403 label.setAttributeNS(null, "stroke", style.fontStrokeColor);
33405 if (style.fontStrokeWidth) {
33406 label.setAttributeNS(null, "stroke-width", style.fontStrokeWidth);
33408 if (style.fontOpacity) {
33409 label.setAttributeNS(null, "opacity", style.fontOpacity);
33411 if (style.fontFamily) {
33412 label.setAttributeNS(null, "font-family", style.fontFamily);
33414 if (style.fontSize) {
33415 label.setAttributeNS(null, "font-size", style.fontSize);
33417 if (style.fontWeight) {
33418 label.setAttributeNS(null, "font-weight", style.fontWeight);
33420 if (style.fontStyle) {
33421 label.setAttributeNS(null, "font-style", style.fontStyle);
33423 if (style.labelSelect === true) {
33424 label.setAttributeNS(null, "pointer-events", "visible");
33425 label._featureId = featureId;
33427 label.setAttributeNS(null, "pointer-events", "none");
33429 var align = style.labelAlign || OpenLayers.Renderer.defaultSymbolizer.labelAlign;
33430 label.setAttributeNS(null, "text-anchor",
33431 OpenLayers.Renderer.SVG.LABEL_ALIGN[align[0]] || "middle");
33433 if (OpenLayers.IS_GECKO === true) {
33434 label.setAttributeNS(null, "dominant-baseline",
33435 OpenLayers.Renderer.SVG.LABEL_ALIGN[align[1]] || "central");
33438 var labelRows = style.label.split('\n');
33439 var numRows = labelRows.length;
33440 while (label.childNodes.length > numRows) {
33441 label.removeChild(label.lastChild);
33443 for (var i = 0; i < numRows; i++) {
33444 var tspan = this.nodeFactory(featureId + suffix + "_tspan_" + i, "tspan");
33445 if (style.labelSelect === true) {
33446 tspan._featureId = featureId;
33447 tspan._geometry = location;
33448 tspan._geometryClass = location.CLASS_NAME;
33450 if (OpenLayers.IS_GECKO === false) {
33451 tspan.setAttributeNS(null, "baseline-shift",
33452 OpenLayers.Renderer.SVG.LABEL_VSHIFT[align[1]] || "-35%");
33454 tspan.setAttribute("x", x);
33456 var vfactor = OpenLayers.Renderer.SVG.LABEL_VFACTOR[align[1]];
33457 if (vfactor == null) {
33460 tspan.setAttribute("dy", (vfactor*(numRows-1)) + "em");
33462 tspan.setAttribute("dy", "1em");
33464 tspan.textContent = (labelRows[i] === '') ? ' ' : labelRows[i];
33465 if (!tspan.parentNode) {
33466 label.appendChild(tspan);
33470 if (!label.parentNode) {
33471 this.textRoot.appendChild(label);
33476 * Method: getComponentString
33479 * components - {Array(<OpenLayers.Geometry.Point>)} Array of points
33480 * separator - {String} character between coordinate pairs. Defaults to ","
33483 * {Object} hash with properties "path" (the string created from the
33484 * components and "complete" (false if the renderer was unable to
33485 * draw all components)
33487 getComponentsString: function(components, separator) {
33488 var renderCmp = [];
33489 var complete = true;
33490 var len = components.length;
33492 var str, component;
33493 for(var i=0; i<len; i++) {
33494 component = components[i];
33495 renderCmp.push(component);
33496 str = this.getShortString(component);
33500 // The current component is outside the valid range. Let's
33501 // see if the previous or next component is inside the range.
33502 // If so, add the coordinate of the intersection with the
33503 // valid range bounds.
33505 if (this.getShortString(components[i - 1])) {
33506 strings.push(this.clipLine(components[i],
33511 if (this.getShortString(components[i + 1])) {
33512 strings.push(this.clipLine(components[i],
33521 path: strings.join(separator || ","),
33528 * Given two points (one inside the valid range, and one outside),
33529 * clips the line betweeen the two points so that the new points are both
33530 * inside the valid range.
33533 * badComponent - {<OpenLayers.Geometry.Point>} original geometry of the
33535 * goodComponent - {<OpenLayers.Geometry.Point>} original geometry of the
33538 * {String} the SVG coordinate pair of the clipped point (like
33539 * getShortString), or an empty string if both passed componets are at
33542 clipLine: function(badComponent, goodComponent) {
33543 if (goodComponent.equals(badComponent)) {
33546 var resolution = this.getResolution();
33547 var maxX = this.MAX_PIXEL - this.translationParameters.x;
33548 var maxY = this.MAX_PIXEL - this.translationParameters.y;
33549 var x1 = (goodComponent.x - this.featureDx) / resolution + this.left;
33550 var y1 = this.top - goodComponent.y / resolution;
33551 var x2 = (badComponent.x - this.featureDx) / resolution + this.left;
33552 var y2 = this.top - badComponent.y / resolution;
33554 if (x2 < -maxX || x2 > maxX) {
33555 k = (y2 - y1) / (x2 - x1);
33556 x2 = x2 < 0 ? -maxX : maxX;
33557 y2 = y1 + (x2 - x1) * k;
33559 if (y2 < -maxY || y2 > maxY) {
33560 k = (x2 - x1) / (y2 - y1);
33561 y2 = y2 < 0 ? -maxY : maxY;
33562 x2 = x1 + (y2 - y1) * k;
33564 return x2 + "," + y2;
33568 * Method: getShortString
33571 * point - {<OpenLayers.Geometry.Point>}
33574 * {String} or false if point is outside the valid range
33576 getShortString: function(point) {
33577 var resolution = this.getResolution();
33578 var x = ((point.x - this.featureDx) / resolution + this.left);
33579 var y = (this.top - point.y / resolution);
33581 if (this.inValidRange(x, y)) {
33582 return x + "," + y;
33589 * Method: getPosition
33590 * Finds the position of an svg node.
33593 * node - {DOMElement}
33596 * {Object} hash with x and y properties, representing the coordinates
33597 * within the svg coordinate system
33599 getPosition: function(node) {
33601 x: parseFloat(node.getAttributeNS(null, "cx")),
33602 y: parseFloat(node.getAttributeNS(null, "cy"))
33607 * Method: importSymbol
33608 * add a new symbol definition from the rendererer's symbol hash
33611 * graphicName - {String} name of the symbol to import
33614 * {DOMElement} - the imported symbol
33616 importSymbol: function (graphicName) {
33618 // create svg defs tag
33619 this.defs = this.createDefs();
33621 var id = this.container.id + "-" + graphicName;
33623 // check if symbol already exists in the defs
33624 var existing = document.getElementById(id);
33625 if (existing != null) {
33629 var symbol = OpenLayers.Renderer.symbol[graphicName];
33631 throw new Error(graphicName + ' is not a valid symbol name');
33634 var symbolNode = this.nodeFactory(id, "symbol");
33635 var node = this.nodeFactory(null, "polygon");
33636 symbolNode.appendChild(node);
33637 var symbolExtent = new OpenLayers.Bounds(
33638 Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
33642 for (var i=0; i<symbol.length; i=i+2) {
33645 symbolExtent.left = Math.min(symbolExtent.left, x);
33646 symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
33647 symbolExtent.right = Math.max(symbolExtent.right, x);
33648 symbolExtent.top = Math.max(symbolExtent.top, y);
33649 points.push(x, ",", y);
33652 node.setAttributeNS(null, "points", points.join(" "));
33654 var width = symbolExtent.getWidth();
33655 var height = symbolExtent.getHeight();
33656 // create a viewBox three times as large as the symbol itself,
33657 // to allow for strokeWidth being displayed correctly at the corners.
33658 var viewBox = [symbolExtent.left - width,
33659 symbolExtent.bottom - height, width * 3, height * 3];
33660 symbolNode.setAttributeNS(null, "viewBox", viewBox.join(" "));
33661 this.symbolMetrics[id] = [
33662 Math.max(width, height),
33663 symbolExtent.getCenterLonLat().lon,
33664 symbolExtent.getCenterLonLat().lat
33667 this.defs.appendChild(symbolNode);
33672 * Method: getFeatureIdFromEvent
33675 * evt - {Object} An <OpenLayers.Event> object
33678 * {String} A feature id or undefined.
33680 getFeatureIdFromEvent: function(evt) {
33681 var featureId = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent.apply(this, arguments);
33683 var target = evt.target;
33684 featureId = target.parentNode && target != this.rendererRoot ?
33685 target.parentNode._featureId : undefined;
33690 CLASS_NAME: "OpenLayers.Renderer.SVG"
33694 * Constant: OpenLayers.Renderer.SVG.LABEL_ALIGN
33697 OpenLayers.Renderer.SVG.LABEL_ALIGN = {
33705 * Constant: OpenLayers.Renderer.SVG.LABEL_VSHIFT
33708 OpenLayers.Renderer.SVG.LABEL_VSHIFT = {
33710 // http://www.w3.org/Graphics/SVG/Test/20061213/htmlObjectHarness/full-text-align-02-b.html
33711 // a baseline-shift of -70% shifts the text exactly from the
33712 // bottom to the top of the baseline, so -35% moves the text to
33713 // the center of the baseline.
33719 * Constant: OpenLayers.Renderer.SVG.LABEL_VFACTOR
33722 OpenLayers.Renderer.SVG.LABEL_VFACTOR = {
33728 * Function: OpenLayers.Renderer.SVG.preventDefault
33729 * *Deprecated*. Use <OpenLayers.Event.preventDefault> method instead.
33730 * Used to prevent default events (especially opening images in a new tab on
33731 * ctrl-click) from being executed for externalGraphic symbols
33733 OpenLayers.Renderer.SVG.preventDefault = function(e) {
33734 OpenLayers.Event.preventDefault(e);
33736 /* ======================================================================
33737 OpenLayers/Control/PanZoom.js
33738 ====================================================================== */
33740 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
33741 * full list of contributors). Published under the 2-clause BSD license.
33742 * See license.txt in the OpenLayers distribution or repository for the
33743 * full text of the license. */
33747 * @requires OpenLayers/Control.js
33748 * @requires OpenLayers/Events/buttonclick.js
33752 * Class: OpenLayers.Control.PanZoom
33753 * The PanZoom is a visible control, composed of a
33754 * <OpenLayers.Control.PanPanel> and a <OpenLayers.Control.ZoomPanel>. By
33755 * default it is drawn in the upper left corner of the map.
33758 * - <OpenLayers.Control>
33760 OpenLayers.Control.PanZoom = OpenLayers.Class(OpenLayers.Control, {
33763 * APIProperty: slideFactor
33764 * {Integer} Number of pixels by which we'll pan the map in any direction
33765 * on clicking the arrow buttons. If you want to pan by some ratio
33766 * of the map dimensions, use <slideRatio> instead.
33771 * APIProperty: slideRatio
33772 * {Number} The fraction of map width/height by which we'll pan the map
33773 * on clicking the arrow buttons. Default is null. If set, will
33774 * override <slideFactor>. E.g. if slideRatio is .5, then the Pan Up
33775 * button will pan up half the map height.
33780 * Property: buttons
33781 * {Array(DOMElement)} Array of Button Divs
33786 * Property: position
33787 * {<OpenLayers.Pixel>}
33792 * Constructor: OpenLayers.Control.PanZoom
33795 * options - {Object}
33797 initialize: function(options) {
33798 this.position = new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X,
33799 OpenLayers.Control.PanZoom.Y);
33800 OpenLayers.Control.prototype.initialize.apply(this, arguments);
33804 * APIMethod: destroy
33806 destroy: function() {
33808 this.map.events.unregister("buttonclick", this, this.onButtonClick);
33810 this.removeButtons();
33811 this.buttons = null;
33812 this.position = null;
33813 OpenLayers.Control.prototype.destroy.apply(this, arguments);
33820 * map - {<OpenLayers.Map>}
33822 setMap: function(map) {
33823 OpenLayers.Control.prototype.setMap.apply(this, arguments);
33824 this.map.events.register("buttonclick", this, this.onButtonClick);
33831 * px - {<OpenLayers.Pixel>}
33834 * {DOMElement} A reference to the container div for the PanZoom control.
33836 draw: function(px) {
33837 // initialize our internal div
33838 OpenLayers.Control.prototype.draw.apply(this, arguments);
33839 px = this.position;
33841 // place the controls
33844 var sz = {w: 18, h: 18};
33845 var centered = new OpenLayers.Pixel(px.x+sz.w/2, px.y);
33847 this._addButton("panup", "north-mini.png", centered, sz);
33848 px.y = centered.y+sz.h;
33849 this._addButton("panleft", "west-mini.png", px, sz);
33850 this._addButton("panright", "east-mini.png", px.add(sz.w, 0), sz);
33851 this._addButton("pandown", "south-mini.png",
33852 centered.add(0, sz.h*2), sz);
33853 this._addButton("zoomin", "zoom-plus-mini.png",
33854 centered.add(0, sz.h*3+5), sz);
33855 this._addButton("zoomworld", "zoom-world-mini.png",
33856 centered.add(0, sz.h*4+5), sz);
33857 this._addButton("zoomout", "zoom-minus-mini.png",
33858 centered.add(0, sz.h*5+5), sz);
33863 * Method: _addButton
33868 * xy - {<OpenLayers.Pixel>}
33869 * sz - {<OpenLayers.Size>}
33872 * {DOMElement} A Div (an alphaImageDiv, to be precise) that contains the
33873 * image of the button, and has all the proper event handlers set.
33875 _addButton:function(id, img, xy, sz) {
33876 var imgLocation = OpenLayers.Util.getImageLocation(img);
33877 var btn = OpenLayers.Util.createAlphaImageDiv(
33878 this.id + "_" + id,
33879 xy, sz, imgLocation, "absolute");
33880 btn.style.cursor = "pointer";
33881 //we want to add the outer div
33882 this.div.appendChild(btn);
33884 btn.className = "olButton";
33886 //we want to remember/reference the outer div
33887 this.buttons.push(btn);
33892 * Method: _removeButton
33897 _removeButton: function(btn) {
33898 this.div.removeChild(btn);
33899 OpenLayers.Util.removeItem(this.buttons, btn);
33903 * Method: removeButtons
33905 removeButtons: function() {
33906 for(var i=this.buttons.length-1; i>=0; --i) {
33907 this._removeButton(this.buttons[i]);
33912 * Method: onButtonClick
33917 onButtonClick: function(evt) {
33918 var btn = evt.buttonElement;
33919 switch (btn.action) {
33921 this.map.pan(0, -this.getSlideFactor("h"));
33924 this.map.pan(0, this.getSlideFactor("h"));
33927 this.map.pan(-this.getSlideFactor("w"), 0);
33930 this.map.pan(this.getSlideFactor("w"), 0);
33936 this.map.zoomOut();
33939 this.map.zoomToMaxExtent();
33945 * Method: getSlideFactor
33948 * dim - {String} "w" or "h" (for width or height).
33951 * {Number} The slide factor for panning in the requested direction.
33953 getSlideFactor: function(dim) {
33954 return this.slideRatio ?
33955 this.map.getSize()[dim] * this.slideRatio :
33959 CLASS_NAME: "OpenLayers.Control.PanZoom"
33966 OpenLayers.Control.PanZoom.X = 4;
33972 OpenLayers.Control.PanZoom.Y = 4;
33973 /* ======================================================================
33975 ====================================================================== */
33977 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
33978 * full list of contributors). Published under the 2-clause BSD license.
33979 * See license.txt in the OpenLayers distribution or repository for the
33980 * full text of the license. */
33983 * @requires OpenLayers/BaseTypes/Class.js
33987 * Class: OpenLayers.Icon
33989 * The icon represents a graphical icon on the screen. Typically used in
33990 * conjunction with a <OpenLayers.Marker> to represent markers on a screen.
33992 * An icon has a url, size and position. It also contains an offset which
33993 * allows the center point to be represented correctly. This can be
33994 * provided either as a fixed offset or a function provided to calculate
33995 * the desired offset.
33998 OpenLayers.Icon = OpenLayers.Class({
34002 * {String} image url
34008 * {<OpenLayers.Size>|Object} An OpenLayers.Size or
34009 * an object with a 'w' and 'h' properties.
34015 * {<OpenLayers.Pixel>|Object} distance in pixels to offset the
34016 * image when being rendered. An OpenLayers.Pixel or an object
34017 * with a 'x' and 'y' properties.
34022 * Property: calculateOffset
34023 * {Function} Function to calculate the offset (based on the size)
34025 calculateOffset: null,
34028 * Property: imageDiv
34035 * {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object
34036 * with a 'x' and 'y' properties.
34041 * Constructor: OpenLayers.Icon
34042 * Creates an icon, which is an image tag in a div.
34045 * size - {<OpenLayers.Size>|Object} An OpenLayers.Size or an
34046 * object with a 'w' and 'h'
34048 * offset - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an
34049 * object with a 'x' and 'y'
34051 * calculateOffset - {Function}
34053 initialize: function(url, size, offset, calculateOffset) {
34055 this.size = size || {w: 20, h: 20};
34056 this.offset = offset || {x: -(this.size.w/2), y: -(this.size.h/2)};
34057 this.calculateOffset = calculateOffset;
34059 var id = OpenLayers.Util.createUniqueID("OL_Icon_");
34060 this.imageDiv = OpenLayers.Util.createAlphaImageDiv(id);
34065 * Nullify references and remove event listeners to prevent circular
34066 * references and memory leaks
34068 destroy: function() {
34069 // erase any drawn elements
34072 OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild);
34073 this.imageDiv.innerHTML = "";
34074 this.imageDiv = null;
34081 * {<OpenLayers.Icon>} A fresh copy of the icon.
34083 clone: function() {
34084 return new OpenLayers.Icon(this.url,
34087 this.calculateOffset);
34094 * size - {<OpenLayers.Size>|Object} An OpenLayers.Size or
34095 * an object with a 'w' and 'h' properties.
34097 setSize: function(size) {
34098 if (size != null) {
34110 setUrl: function(url) {
34119 * Move the div to the given pixel.
34122 * px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an
34123 * object with a 'x' and 'y' properties.
34126 * {DOMElement} A new DOM Image of this icon set at the location passed-in
34128 draw: function(px) {
34129 OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv,
34136 return this.imageDiv;
34141 * Erase the underlying image element.
34143 erase: function() {
34144 if (this.imageDiv != null && this.imageDiv.parentNode != null) {
34145 OpenLayers.Element.remove(this.imageDiv);
34150 * Method: setOpacity
34151 * Change the icon's opacity
34154 * opacity - {float}
34156 setOpacity: function(opacity) {
34157 OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, null,
34158 null, null, null, null, opacity);
34164 * move icon to passed in px.
34167 * px - {<OpenLayers.Pixel>|Object} the pixel position to move to.
34168 * An OpenLayers.Pixel or an object with a 'x' and 'y' properties.
34170 moveTo: function (px) {
34171 //if no px passed in, use stored location
34176 if (this.imageDiv != null) {
34177 if (this.px == null) {
34178 this.display(false);
34180 if (this.calculateOffset) {
34181 this.offset = this.calculateOffset(this.size);
34183 OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, {
34184 x: this.px.x + this.offset.x,
34185 y: this.px.y + this.offset.y
34193 * Hide or show the icon
34196 * display - {Boolean}
34198 display: function(display) {
34199 this.imageDiv.style.display = (display) ? "" : "none";
34204 * APIMethod: isDrawn
34207 * {Boolean} Whether or not the icon is drawn.
34209 isDrawn: function() {
34210 // nodeType 11 for ie, whose nodes *always* have a parentNode
34211 // (of type document fragment)
34212 var isDrawn = (this.imageDiv && this.imageDiv.parentNode &&
34213 (this.imageDiv.parentNode.nodeType != 11));
34218 CLASS_NAME: "OpenLayers.Icon"
34220 /* ======================================================================
34221 OpenLayers/Marker.js
34222 ====================================================================== */
34224 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
34225 * full list of contributors). Published under the 2-clause BSD license.
34226 * See license.txt in the OpenLayers distribution or repository for the
34227 * full text of the license. */
34231 * @requires OpenLayers/BaseTypes/Class.js
34232 * @requires OpenLayers/Events.js
34233 * @requires OpenLayers/Icon.js
34237 * Class: OpenLayers.Marker
34238 * Instances of OpenLayers.Marker are a combination of a
34239 * <OpenLayers.LonLat> and an <OpenLayers.Icon>.
34241 * Markers are generally added to a special layer called
34242 * <OpenLayers.Layer.Markers>.
34246 * var markers = new OpenLayers.Layer.Markers( "Markers" );
34247 * map.addLayer(markers);
34249 * var size = new OpenLayers.Size(21,25);
34250 * var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
34251 * var icon = new OpenLayers.Icon('http://www.openlayers.org/dev/img/marker.png', size, offset);
34252 * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon));
34253 * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon.clone()));
34257 * Note that if you pass an icon into the Marker constructor, it will take
34258 * that icon and use it. This means that you should not share icons between
34259 * markers -- you use them once, but you should clone() for any additional
34260 * markers using that same icon.
34262 OpenLayers.Marker = OpenLayers.Class({
34266 * {<OpenLayers.Icon>} The icon used by this marker.
34272 * {<OpenLayers.LonLat>} location of object
34278 * {<OpenLayers.Events>} the event handler.
34284 * {<OpenLayers.Map>} the map this marker is attached to
34289 * Constructor: OpenLayers.Marker
34292 * lonlat - {<OpenLayers.LonLat>} the position of this marker
34293 * icon - {<OpenLayers.Icon>} the icon for this marker
34295 initialize: function(lonlat, icon) {
34296 this.lonlat = lonlat;
34298 var newIcon = (icon) ? icon : OpenLayers.Marker.defaultIcon();
34299 if (this.icon == null) {
34300 this.icon = newIcon;
34302 this.icon.url = newIcon.url;
34303 this.icon.size = newIcon.size;
34304 this.icon.offset = newIcon.offset;
34305 this.icon.calculateOffset = newIcon.calculateOffset;
34307 this.events = new OpenLayers.Events(this, this.icon.imageDiv);
34311 * APIMethod: destroy
34312 * Destroy the marker. You must first remove the marker from any
34313 * layer which it has been added to, or you will get buggy behavior.
34314 * (This can not be done within the marker since the marker does not
34315 * know which layer it is attached to.)
34317 destroy: function() {
34318 // erase any drawn features
34323 this.events.destroy();
34324 this.events = null;
34326 if (this.icon != null) {
34327 this.icon.destroy();
34334 * Calls draw on the icon, and returns that output.
34337 * px - {<OpenLayers.Pixel>}
34340 * {DOMElement} A new DOM Image with this marker's icon set at the
34341 * location passed-in
34343 draw: function(px) {
34344 return this.icon.draw(px);
34349 * Erases any drawn elements for this marker.
34351 erase: function() {
34352 if (this.icon != null) {
34359 * Move the marker to the new location.
34362 * px - {<OpenLayers.Pixel>|Object} the pixel position to move to.
34363 * An OpenLayers.Pixel or an object with a 'x' and 'y' properties.
34365 moveTo: function (px) {
34366 if ((px != null) && (this.icon != null)) {
34367 this.icon.moveTo(px);
34369 this.lonlat = this.map.getLonLatFromLayerPx(px);
34373 * APIMethod: isDrawn
34376 * {Boolean} Whether or not the marker is drawn.
34378 isDrawn: function() {
34379 var isDrawn = (this.icon && this.icon.isDrawn());
34387 * {Boolean} Whether or not the marker is currently visible on screen.
34389 onScreen:function() {
34391 var onScreen = false;
34393 var screenBounds = this.map.getExtent();
34394 onScreen = screenBounds.containsLonLat(this.lonlat);
34401 * Englarges the markers icon by the specified ratio.
34404 * inflate - {float} the ratio to enlarge the marker by (passing 2
34405 * will double the size).
34407 inflate: function(inflate) {
34409 this.icon.setSize({
34410 w: this.icon.size.w * inflate,
34411 h: this.icon.size.h * inflate
34417 * Method: setOpacity
34418 * Change the opacity of the marker by changin the opacity of
34422 * opacity - {float} Specified as fraction (0.4, etc)
34424 setOpacity: function(opacity) {
34425 this.icon.setOpacity(opacity);
34430 * Change URL of the Icon Image.
34434 setUrl: function(url) {
34435 this.icon.setUrl(url);
34440 * Hide or show the icon
34442 * display - {Boolean}
34444 display: function(display) {
34445 this.icon.display(display);
34448 CLASS_NAME: "OpenLayers.Marker"
34453 * Function: defaultIcon
34454 * Creates a default <OpenLayers.Icon>.
34457 * {<OpenLayers.Icon>} A default OpenLayers.Icon to use for a marker
34459 OpenLayers.Marker.defaultIcon = function() {
34460 return new OpenLayers.Icon(OpenLayers.Util.getImageLocation("marker.png"),
34461 {w: 21, h: 25}, {x: -10.5, y: -25});
34465 /* ======================================================================
34466 OpenLayers/Popup.js
34467 ====================================================================== */
34469 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
34470 * full list of contributors). Published under the 2-clause BSD license.
34471 * See license.txt in the OpenLayers distribution or repository for the
34472 * full text of the license. */
34475 * @requires OpenLayers/BaseTypes/Class.js
34480 * Class: OpenLayers.Popup
34481 * A popup is a small div that can opened and closed on the map.
34482 * Typically opened in response to clicking on a marker.
34483 * See <OpenLayers.Marker>. Popup's don't require their own
34484 * layer and are added the the map using the <OpenLayers.Map.addPopup>
34489 * popup = new OpenLayers.Popup("chicken",
34490 * new OpenLayers.LonLat(5,40),
34491 * new OpenLayers.Size(200,200),
34495 * map.addPopup(popup);
34498 OpenLayers.Popup = OpenLayers.Class({
34502 * {<OpenLayers.Events>} custom event manager
34507 * {String} the unique identifier assigned to this popup.
34513 * {<OpenLayers.LonLat>} the position of this popup on the map
34519 * {DOMElement} the div that contains this popup.
34524 * Property: contentSize
34525 * {<OpenLayers.Size>} the width and height of the content.
34531 * {<OpenLayers.Size>} the width and height of the popup.
34536 * Property: contentHTML
34537 * {String} An HTML string for this popup to display.
34542 * Property: backgroundColor
34543 * {String} the background color used by the popup.
34545 backgroundColor: "",
34548 * Property: opacity
34549 * {float} the opacity of this popup (between 0.0 and 1.0)
34555 * {String} the border size of the popup. (eg 2px)
34560 * Property: contentDiv
34561 * {DOMElement} a reference to the element that holds the content of
34567 * Property: groupDiv
34568 * {DOMElement} First and only child of 'div'. The group Div contains the
34569 * 'contentDiv' and the 'closeDiv'.
34574 * Property: closeDiv
34575 * {DOMElement} the optional closer image
34580 * APIProperty: autoSize
34581 * {Boolean} Resize the popup to auto-fit the contents.
34582 * Default is false.
34587 * APIProperty: minSize
34588 * {<OpenLayers.Size>} Minimum size allowed for the popup's contents.
34593 * APIProperty: maxSize
34594 * {<OpenLayers.Size>} Maximum size allowed for the popup's contents.
34599 * Property: displayClass
34600 * {String} The CSS class of the popup.
34602 displayClass: "olPopup",
34605 * Property: contentDisplayClass
34606 * {String} The CSS class of the popup content div.
34608 contentDisplayClass: "olPopupContent",
34611 * Property: padding
34612 * {int or <OpenLayers.Bounds>} An extra opportunity to specify internal
34613 * padding of the content div inside the popup. This was originally
34614 * confused with the css padding as specified in style.css's
34615 * 'olPopupContent' class. We would like to get rid of this altogether,
34616 * except that it does come in handy for the framed and anchoredbubble
34617 * popups, who need to maintain yet another barrier between their
34618 * content and the outer border of the popup itself.
34620 * Note that in order to not break API, we must continue to support
34621 * this property being set as an integer. Really, though, we'd like to
34622 * have this specified as a Bounds object so that user can specify
34623 * distinct left, top, right, bottom paddings. With the 3.0 release
34624 * we can make this only a bounds.
34629 * Property: disableFirefoxOverflowHack
34630 * {Boolean} The hack for overflow in Firefox causes all elements
34631 * to be re-drawn, which causes Flash elements to be
34632 * re-initialized, which is troublesome.
34633 * With this property the hack can be disabled.
34635 disableFirefoxOverflowHack: false,
34638 * Method: fixPadding
34639 * To be removed in 3.0, this function merely helps us to deal with the
34640 * case where the user may have set an integer value for padding,
34641 * instead of an <OpenLayers.Bounds> object.
34643 fixPadding: function() {
34644 if (typeof this.padding == "number") {
34645 this.padding = new OpenLayers.Bounds(
34646 this.padding, this.padding, this.padding, this.padding
34652 * APIProperty: panMapIfOutOfView
34653 * {Boolean} When drawn, pan map such that the entire popup is visible in
34654 * the current viewport (if necessary).
34655 * Default is false.
34657 panMapIfOutOfView: false,
34660 * APIProperty: keepInMap
34661 * {Boolean} If panMapIfOutOfView is false, and this property is true,
34662 * contrain the popup such that it always fits in the available map
34663 * space. By default, this is not set on the base class. If you are
34664 * creating popups that are near map edges and not allowing pannning,
34665 * and especially if you have a popup which has a
34666 * fixedRelativePosition, setting this to false may be a smart thing to
34667 * do. Subclasses may want to override this setting.
34669 * Default is false.
34674 * APIProperty: closeOnMove
34675 * {Boolean} When map pans, close the popup.
34676 * Default is false.
34678 closeOnMove: false,
34682 * {<OpenLayers.Map>} this gets set in Map.js when the popup is added to the map
34687 * Constructor: OpenLayers.Popup
34691 * id - {String} a unqiue identifier for this popup. If null is passed
34692 * an identifier will be automatically generated.
34693 * lonlat - {<OpenLayers.LonLat>} The position on the map the popup will
34695 * contentSize - {<OpenLayers.Size>} The size of the content.
34696 * contentHTML - {String} An HTML string to display inside the
34698 * closeBox - {Boolean} Whether to display a close box inside
34700 * closeBoxCallback - {Function} Function to be called on closeBox click.
34702 initialize:function(id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback) {
34704 id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
34708 this.lonlat = lonlat;
34710 this.contentSize = (contentSize != null) ? contentSize
34711 : new OpenLayers.Size(
34712 OpenLayers.Popup.WIDTH,
34713 OpenLayers.Popup.HEIGHT);
34714 if (contentHTML != null) {
34715 this.contentHTML = contentHTML;
34717 this.backgroundColor = OpenLayers.Popup.COLOR;
34718 this.opacity = OpenLayers.Popup.OPACITY;
34719 this.border = OpenLayers.Popup.BORDER;
34721 this.div = OpenLayers.Util.createDiv(this.id, null, null,
34722 null, null, null, "hidden");
34723 this.div.className = this.displayClass;
34725 var groupDivId = this.id + "_GroupDiv";
34726 this.groupDiv = OpenLayers.Util.createDiv(groupDivId, null, null,
34727 null, "relative", null,
34730 var id = this.div.id + "_contentDiv";
34731 this.contentDiv = OpenLayers.Util.createDiv(id, null, this.contentSize.clone(),
34733 this.contentDiv.className = this.contentDisplayClass;
34734 this.groupDiv.appendChild(this.contentDiv);
34735 this.div.appendChild(this.groupDiv);
34738 this.addCloseBox(closeBoxCallback);
34741 this.registerEvents();
34746 * nullify references to prevent circular references and memory leaks
34748 destroy: function() {
34751 this.lonlat = null;
34753 this.contentHTML = null;
34755 this.backgroundColor = null;
34756 this.opacity = null;
34757 this.border = null;
34759 if (this.closeOnMove && this.map) {
34760 this.map.events.unregister("movestart", this, this.hide);
34763 this.events.destroy();
34764 this.events = null;
34766 if (this.closeDiv) {
34767 OpenLayers.Event.stopObservingElement(this.closeDiv);
34768 this.groupDiv.removeChild(this.closeDiv);
34770 this.closeDiv = null;
34772 this.div.removeChild(this.groupDiv);
34773 this.groupDiv = null;
34775 if (this.map != null) {
34776 this.map.removePopup(this);
34781 this.autoSize = null;
34782 this.minSize = null;
34783 this.maxSize = null;
34784 this.padding = null;
34785 this.panMapIfOutOfView = null;
34790 * Constructs the elements that make up the popup.
34793 * px - {<OpenLayers.Pixel>} the position the popup in pixels.
34796 * {DOMElement} Reference to a div that contains the drawn popup
34798 draw: function(px) {
34800 if ((this.lonlat != null) && (this.map != null)) {
34801 px = this.map.getLayerPxFromLonLat(this.lonlat);
34805 // this assumes that this.map already exists, which is okay because
34806 // this.draw is only called once the popup has been added to the map.
34807 if (this.closeOnMove) {
34808 this.map.events.register("movestart", this, this.hide);
34811 //listen to movestart, moveend to disable overflow (FF bug)
34812 if (!this.disableFirefoxOverflowHack && OpenLayers.BROWSER_NAME == 'firefox') {
34813 this.map.events.register("movestart", this, function() {
34814 var style = document.defaultView.getComputedStyle(
34815 this.contentDiv, null
34817 var currentOverflow = style.getPropertyValue("overflow");
34818 if (currentOverflow != "hidden") {
34819 this.contentDiv._oldOverflow = currentOverflow;
34820 this.contentDiv.style.overflow = "hidden";
34823 this.map.events.register("moveend", this, function() {
34824 var oldOverflow = this.contentDiv._oldOverflow;
34826 this.contentDiv.style.overflow = oldOverflow;
34827 this.contentDiv._oldOverflow = null;
34833 if (!this.autoSize && !this.size) {
34834 this.setSize(this.contentSize);
34836 this.setBackgroundColor();
34839 this.setContentHTML();
34841 if (this.panMapIfOutOfView) {
34842 this.panIntoView();
34849 * Method: updatePosition
34850 * if the popup has a lonlat and its map members set,
34851 * then have it move itself to its proper position
34853 updatePosition: function() {
34854 if ((this.lonlat) && (this.map)) {
34855 var px = this.map.getLayerPxFromLonLat(this.lonlat);
34866 * px - {<OpenLayers.Pixel>} the top and left position of the popup div.
34868 moveTo: function(px) {
34869 if ((px != null) && (this.div != null)) {
34870 this.div.style.left = px.x + "px";
34871 this.div.style.top = px.y + "px";
34879 * {Boolean} Boolean indicating whether or not the popup is visible
34881 visible: function() {
34882 return OpenLayers.Element.visible(this.div);
34887 * Toggles visibility of the popup.
34889 toggle: function() {
34890 if (this.visible()) {
34899 * Makes the popup visible.
34902 this.div.style.display = '';
34904 if (this.panMapIfOutOfView) {
34905 this.panIntoView();
34911 * Makes the popup invisible.
34914 this.div.style.display = 'none';
34919 * Used to adjust the size of the popup.
34922 * contentSize - {<OpenLayers.Size>} the new size for the popup's
34923 * contents div (in pixels).
34925 setSize:function(contentSize) {
34926 this.size = contentSize.clone();
34928 // if our contentDiv has a css 'padding' set on it by a stylesheet, we
34929 // must add that to the desired "size".
34930 var contentDivPadding = this.getContentDivPadding();
34931 var wPadding = contentDivPadding.left + contentDivPadding.right;
34932 var hPadding = contentDivPadding.top + contentDivPadding.bottom;
34934 // take into account the popup's 'padding' property
34936 wPadding += this.padding.left + this.padding.right;
34937 hPadding += this.padding.top + this.padding.bottom;
34939 // make extra space for the close div
34940 if (this.closeDiv) {
34941 var closeDivWidth = parseInt(this.closeDiv.style.width);
34942 wPadding += closeDivWidth + contentDivPadding.right;
34945 //increase size of the main popup div to take into account the
34946 // users's desired padding and close div.
34947 this.size.w += wPadding;
34948 this.size.h += hPadding;
34950 //now if our browser is IE, we need to actually make the contents
34951 // div itself bigger to take its own padding into effect. this makes
34952 // me want to shoot someone, but so it goes.
34953 if (OpenLayers.BROWSER_NAME == "msie") {
34954 this.contentSize.w +=
34955 contentDivPadding.left + contentDivPadding.right;
34956 this.contentSize.h +=
34957 contentDivPadding.bottom + contentDivPadding.top;
34960 if (this.div != null) {
34961 this.div.style.width = this.size.w + "px";
34962 this.div.style.height = this.size.h + "px";
34964 if (this.contentDiv != null){
34965 this.contentDiv.style.width = contentSize.w + "px";
34966 this.contentDiv.style.height = contentSize.h + "px";
34971 * APIMethod: updateSize
34972 * Auto size the popup so that it precisely fits its contents (as
34973 * determined by this.contentDiv.innerHTML). Popup size will, of
34974 * course, be limited by the available space on the current map
34976 updateSize: function() {
34978 // determine actual render dimensions of the contents by putting its
34979 // contents into a fake contentDiv (for the CSS) and then measuring it
34980 var preparedHTML = "<div class='" + this.contentDisplayClass+ "'>" +
34981 this.contentDiv.innerHTML +
34984 var containerElement = (this.map) ? this.map.div : document.body;
34985 var realSize = OpenLayers.Util.getRenderedDimensions(
34986 preparedHTML, null, {
34987 displayClass: this.displayClass,
34988 containerElement: containerElement
34992 // is the "real" size of the div is safe to display in our map?
34993 var safeSize = this.getSafeContentSize(realSize);
34995 var newSize = null;
34996 if (safeSize.equals(realSize)) {
34997 //real size of content is small enough to fit on the map,
34998 // so we use real size.
34999 newSize = realSize;
35003 // make a new 'size' object with the clipped dimensions
35004 // set or null if not clipped.
35006 w: (safeSize.w < realSize.w) ? safeSize.w : null,
35007 h: (safeSize.h < realSize.h) ? safeSize.h : null
35010 if (fixedSize.w && fixedSize.h) {
35011 //content is too big in both directions, so we will use
35012 // max popup size (safeSize), knowing well that it will
35013 // overflow both ways.
35014 newSize = safeSize;
35016 //content is clipped in only one direction, so we need to
35017 // run getRenderedDimensions() again with a fixed dimension
35018 var clippedSize = OpenLayers.Util.getRenderedDimensions(
35019 preparedHTML, fixedSize, {
35020 displayClass: this.contentDisplayClass,
35021 containerElement: containerElement
35025 //if the clipped size is still the same as the safeSize,
35026 // that means that our content must be fixed in the
35027 // offending direction. If overflow is 'auto', this means
35028 // we are going to have a scrollbar for sure, so we must
35029 // adjust for that.
35031 var currentOverflow = OpenLayers.Element.getStyle(
35032 this.contentDiv, "overflow"
35034 if ( (currentOverflow != "hidden") &&
35035 (clippedSize.equals(safeSize)) ) {
35036 var scrollBar = OpenLayers.Util.getScrollbarWidth();
35038 clippedSize.h += scrollBar;
35040 clippedSize.w += scrollBar;
35044 newSize = this.getSafeContentSize(clippedSize);
35047 this.setSize(newSize);
35051 * Method: setBackgroundColor
35052 * Sets the background color of the popup.
35055 * color - {String} the background color. eg "#FFBBBB"
35057 setBackgroundColor:function(color) {
35058 if (color != undefined) {
35059 this.backgroundColor = color;
35062 if (this.div != null) {
35063 this.div.style.backgroundColor = this.backgroundColor;
35068 * Method: setOpacity
35069 * Sets the opacity of the popup.
35072 * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid).
35074 setOpacity:function(opacity) {
35075 if (opacity != undefined) {
35076 this.opacity = opacity;
35079 if (this.div != null) {
35080 // for Mozilla and Safari
35081 this.div.style.opacity = this.opacity;
35084 this.div.style.filter = 'alpha(opacity=' + this.opacity*100 + ')';
35089 * Method: setBorder
35090 * Sets the border style of the popup.
35093 * border - {String} The border style value. eg 2px
35095 setBorder:function(border) {
35096 if (border != undefined) {
35097 this.border = border;
35100 if (this.div != null) {
35101 this.div.style.border = this.border;
35106 * Method: setContentHTML
35107 * Allows the user to set the HTML content of the popup.
35110 * contentHTML - {String} HTML for the div.
35112 setContentHTML:function(contentHTML) {
35114 if (contentHTML != null) {
35115 this.contentHTML = contentHTML;
35118 if ((this.contentDiv != null) &&
35119 (this.contentHTML != null) &&
35120 (this.contentHTML != this.contentDiv.innerHTML)) {
35122 this.contentDiv.innerHTML = this.contentHTML;
35124 if (this.autoSize) {
35126 //if popup has images, listen for when they finish
35127 // loading and resize accordingly
35128 this.registerImageListeners();
35130 //auto size the popup to its current contents
35138 * Method: registerImageListeners
35139 * Called when an image contained by the popup loaded. this function
35140 * updates the popup size, then unregisters the image load listener.
35142 registerImageListeners: function() {
35144 // As the images load, this function will call updateSize() to
35145 // resize the popup to fit the content div (which presumably is now
35146 // bigger than when the image was not loaded).
35148 // If the 'panMapIfOutOfView' property is set, we will pan the newly
35149 // resized popup back into view.
35151 // Note that this function, when called, will have 'popup' and
35152 // 'img' properties in the context.
35154 var onImgLoad = function() {
35155 if (this.popup.id === null) { // this.popup has been destroyed!
35158 this.popup.updateSize();
35160 if ( this.popup.visible() && this.popup.panMapIfOutOfView ) {
35161 this.popup.panIntoView();
35164 OpenLayers.Event.stopObserving(
35165 this.img, "load", this.img._onImgLoad
35170 //cycle through the images and if their size is 0x0, that means that
35171 // they haven't been loaded yet, so we attach the listener, which
35172 // will fire when the images finish loading and will resize the
35173 // popup accordingly to its new size.
35174 var images = this.contentDiv.getElementsByTagName("img");
35175 for (var i = 0, len = images.length; i < len; i++) {
35176 var img = images[i];
35177 if (img.width == 0 || img.height == 0) {
35184 //expando this function to the image itself before registering
35185 // it. This way we can easily and properly unregister it.
35186 img._onImgLoad = OpenLayers.Function.bind(onImgLoad, context);
35188 OpenLayers.Event.observe(img, 'load', img._onImgLoad);
35194 * APIMethod: getSafeContentSize
35197 * size - {<OpenLayers.Size>} Desired size to make the popup.
35200 * {<OpenLayers.Size>} A size to make the popup which is neither smaller
35201 * than the specified minimum size, nor bigger than the maximum
35202 * size (which is calculated relative to the size of the viewport).
35204 getSafeContentSize: function(size) {
35206 var safeContentSize = size.clone();
35208 // if our contentDiv has a css 'padding' set on it by a stylesheet, we
35209 // must add that to the desired "size".
35210 var contentDivPadding = this.getContentDivPadding();
35211 var wPadding = contentDivPadding.left + contentDivPadding.right;
35212 var hPadding = contentDivPadding.top + contentDivPadding.bottom;
35214 // take into account the popup's 'padding' property
35216 wPadding += this.padding.left + this.padding.right;
35217 hPadding += this.padding.top + this.padding.bottom;
35219 if (this.closeDiv) {
35220 var closeDivWidth = parseInt(this.closeDiv.style.width);
35221 wPadding += closeDivWidth + contentDivPadding.right;
35224 // prevent the popup from being smaller than a specified minimal size
35225 if (this.minSize) {
35226 safeContentSize.w = Math.max(safeContentSize.w,
35227 (this.minSize.w - wPadding));
35228 safeContentSize.h = Math.max(safeContentSize.h,
35229 (this.minSize.h - hPadding));
35232 // prevent the popup from being bigger than a specified maximum size
35233 if (this.maxSize) {
35234 safeContentSize.w = Math.min(safeContentSize.w,
35235 (this.maxSize.w - wPadding));
35236 safeContentSize.h = Math.min(safeContentSize.h,
35237 (this.maxSize.h - hPadding));
35240 //make sure the desired size to set doesn't result in a popup that
35241 // is bigger than the map's viewport.
35243 if (this.map && this.map.size) {
35245 var extraX = 0, extraY = 0;
35246 if (this.keepInMap && !this.panMapIfOutOfView) {
35247 var px = this.map.getPixelFromLonLat(this.lonlat);
35248 switch (this.relativePosition) {
35251 extraY = this.map.size.h - px.y;
35254 extraX = this.map.size.w - px.x;
35255 extraY = this.map.size.h - px.y;
35258 extraX = this.map.size.w - px.x;
35267 extraY = this.map.size.h - px.y;
35272 var maxY = this.map.size.h -
35273 this.map.paddingForPopups.top -
35274 this.map.paddingForPopups.bottom -
35277 var maxX = this.map.size.w -
35278 this.map.paddingForPopups.left -
35279 this.map.paddingForPopups.right -
35282 safeContentSize.w = Math.min(safeContentSize.w, maxX);
35283 safeContentSize.h = Math.min(safeContentSize.h, maxY);
35286 return safeContentSize;
35290 * Method: getContentDivPadding
35291 * Glorious, oh glorious hack in order to determine the css 'padding' of
35292 * the contentDiv. IE/Opera return null here unless we actually add the
35293 * popup's main 'div' element (which contains contentDiv) to the DOM.
35294 * So we make it invisible and then add it to the document temporarily.
35296 * Once we've taken the padding readings we need, we then remove it
35297 * from the DOM (it will actually get added to the DOM in
35298 * Map.js's addPopup)
35301 * {<OpenLayers.Bounds>}
35303 getContentDivPadding: function() {
35305 //use cached value if we have it
35306 var contentDivPadding = this._contentDivPadding;
35307 if (!contentDivPadding) {
35309 if (this.div.parentNode == null) {
35310 //make the div invisible and add it to the page
35311 this.div.style.display = "none";
35312 document.body.appendChild(this.div);
35315 //read the padding settings from css, put them in an OL.Bounds
35316 contentDivPadding = new OpenLayers.Bounds(
35317 OpenLayers.Element.getStyle(this.contentDiv, "padding-left"),
35318 OpenLayers.Element.getStyle(this.contentDiv, "padding-bottom"),
35319 OpenLayers.Element.getStyle(this.contentDiv, "padding-right"),
35320 OpenLayers.Element.getStyle(this.contentDiv, "padding-top")
35324 this._contentDivPadding = contentDivPadding;
35326 if (this.div.parentNode == document.body) {
35327 //remove the div from the page and make it visible again
35328 document.body.removeChild(this.div);
35329 this.div.style.display = "";
35332 return contentDivPadding;
35336 * Method: addCloseBox
35339 * callback - {Function} The callback to be called when the close button
35342 addCloseBox: function(callback) {
35344 this.closeDiv = OpenLayers.Util.createDiv(
35345 this.id + "_close", null, {w: 17, h: 17}
35347 this.closeDiv.className = "olPopupCloseBox";
35349 // use the content div's css padding to determine if we should
35350 // padd the close div
35351 var contentDivPadding = this.getContentDivPadding();
35353 this.closeDiv.style.right = contentDivPadding.right + "px";
35354 this.closeDiv.style.top = contentDivPadding.top + "px";
35355 this.groupDiv.appendChild(this.closeDiv);
35357 var closePopup = callback || function(e) {
35359 OpenLayers.Event.stop(e);
35361 OpenLayers.Event.observe(this.closeDiv, "touchend",
35362 OpenLayers.Function.bindAsEventListener(closePopup, this));
35363 OpenLayers.Event.observe(this.closeDiv, "click",
35364 OpenLayers.Function.bindAsEventListener(closePopup, this));
35368 * Method: panIntoView
35369 * Pans the map such that the popup is totaly viewable (if necessary)
35371 panIntoView: function() {
35373 var mapSize = this.map.getSize();
35375 //start with the top left corner of the popup, in px,
35376 // relative to the viewport
35377 var origTL = this.map.getViewPortPxFromLayerPx( new OpenLayers.Pixel(
35378 parseInt(this.div.style.left),
35379 parseInt(this.div.style.top)
35381 var newTL = origTL.clone();
35383 //new left (compare to margins, using this.size to calculate right)
35384 if (origTL.x < this.map.paddingForPopups.left) {
35385 newTL.x = this.map.paddingForPopups.left;
35387 if ( (origTL.x + this.size.w) > (mapSize.w - this.map.paddingForPopups.right)) {
35388 newTL.x = mapSize.w - this.map.paddingForPopups.right - this.size.w;
35391 //new top (compare to margins, using this.size to calculate bottom)
35392 if (origTL.y < this.map.paddingForPopups.top) {
35393 newTL.y = this.map.paddingForPopups.top;
35395 if ( (origTL.y + this.size.h) > (mapSize.h - this.map.paddingForPopups.bottom)) {
35396 newTL.y = mapSize.h - this.map.paddingForPopups.bottom - this.size.h;
35399 var dx = origTL.x - newTL.x;
35400 var dy = origTL.y - newTL.y;
35402 this.map.pan(dx, dy);
35406 * Method: registerEvents
35407 * Registers events on the popup.
35409 * Do this in a separate function so that subclasses can
35410 * choose to override it if they wish to deal differently
35411 * with mouse events
35413 * Note in the following handler functions that some special
35414 * care is needed to deal correctly with mousing and popups.
35416 * Because the user might select the zoom-rectangle option and
35417 * then drag it over a popup, we need a safe way to allow the
35418 * mousemove and mouseup events to pass through the popup when
35419 * they are initiated from outside. The same procedure is needed for
35420 * touchmove and touchend events.
35422 * Otherwise, we want to essentially kill the event propagation
35423 * for all other events, though we have to do so carefully,
35424 * without disabling basic html functionality, like clicking on
35425 * hyperlinks or drag-selecting text.
35427 registerEvents:function() {
35428 this.events = new OpenLayers.Events(this, this.div, null, true);
35430 function onTouchstart(evt) {
35431 OpenLayers.Event.stop(evt, true);
35434 "mousedown": this.onmousedown,
35435 "mousemove": this.onmousemove,
35436 "mouseup": this.onmouseup,
35437 "click": this.onclick,
35438 "mouseout": this.onmouseout,
35439 "dblclick": this.ondblclick,
35440 "touchstart": onTouchstart,
35447 * Method: onmousedown
35448 * When mouse goes down within the popup, make a note of
35449 * it locally, and then do not propagate the mousedown
35450 * (but do so safely so that user can select text inside)
35455 onmousedown: function (evt) {
35456 this.mousedown = true;
35457 OpenLayers.Event.stop(evt, true);
35461 * Method: onmousemove
35462 * If the drag was started within the popup, then
35463 * do not propagate the mousemove (but do so safely
35464 * so that user can select text inside)
35469 onmousemove: function (evt) {
35470 if (this.mousedown) {
35471 OpenLayers.Event.stop(evt, true);
35476 * Method: onmouseup
35477 * When mouse comes up within the popup, after going down
35478 * in it, reset the flag, and then (once again) do not
35479 * propagate the event, but do so safely so that user can
35480 * select text inside
35485 onmouseup: function (evt) {
35486 if (this.mousedown) {
35487 this.mousedown = false;
35488 OpenLayers.Event.stop(evt, true);
35494 * Ignore clicks, but allowing default browser handling
35499 onclick: function (evt) {
35500 OpenLayers.Event.stop(evt, true);
35504 * Method: onmouseout
35505 * When mouse goes out of the popup set the flag to false so that
35506 * if they let go and then drag back in, we won't be confused.
35511 onmouseout: function (evt) {
35512 this.mousedown = false;
35516 * Method: ondblclick
35517 * Ignore double-clicks, but allowing default browser handling
35522 ondblclick: function (evt) {
35523 OpenLayers.Event.stop(evt, true);
35526 CLASS_NAME: "OpenLayers.Popup"
35529 OpenLayers.Popup.WIDTH = 200;
35530 OpenLayers.Popup.HEIGHT = 200;
35531 OpenLayers.Popup.COLOR = "white";
35532 OpenLayers.Popup.OPACITY = 1;
35533 OpenLayers.Popup.BORDER = "0px";
35534 /* ======================================================================
35535 OpenLayers/Popup/Anchored.js
35536 ====================================================================== */
35538 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
35539 * full list of contributors). Published under the 2-clause BSD license.
35540 * See license.txt in the OpenLayers distribution or repository for the
35541 * full text of the license. */
35545 * @requires OpenLayers/Popup.js
35549 * Class: OpenLayers.Popup.Anchored
35552 * - <OpenLayers.Popup>
35554 OpenLayers.Popup.Anchored =
35555 OpenLayers.Class(OpenLayers.Popup, {
35558 * Property: relativePosition
35559 * {String} Relative position of the popup ("br", "tr", "tl" or "bl").
35561 relativePosition: null,
35564 * APIProperty: keepInMap
35565 * {Boolean} If panMapIfOutOfView is false, and this property is true,
35566 * contrain the popup such that it always fits in the available map
35567 * space. By default, this is set. If you are creating popups that are
35568 * near map edges and not allowing pannning, and especially if you have
35569 * a popup which has a fixedRelativePosition, setting this to false may
35570 * be a smart thing to do.
35572 * For anchored popups, default is true, since subclasses will
35573 * usually want this functionality.
35579 * {Object} Object to which we'll anchor the popup. Must expose a
35580 * 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>).
35585 * Constructor: OpenLayers.Popup.Anchored
35589 * lonlat - {<OpenLayers.LonLat>}
35590 * contentSize - {<OpenLayers.Size>}
35591 * contentHTML - {String}
35592 * anchor - {Object} Object which must expose a 'size' <OpenLayers.Size>
35593 * and 'offset' <OpenLayers.Pixel> (generally an <OpenLayers.Icon>).
35594 * closeBox - {Boolean}
35595 * closeBoxCallback - {Function} Function to be called on closeBox click.
35597 initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
35598 closeBoxCallback) {
35599 var newArguments = [
35600 id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback
35602 OpenLayers.Popup.prototype.initialize.apply(this, newArguments);
35604 this.anchor = (anchor != null) ? anchor
35605 : { size: new OpenLayers.Size(0,0),
35606 offset: new OpenLayers.Pixel(0,0)};
35610 * APIMethod: destroy
35612 destroy: function() {
35613 this.anchor = null;
35614 this.relativePosition = null;
35616 OpenLayers.Popup.prototype.destroy.apply(this, arguments);
35621 * Overridden from Popup since user might hide popup and then show() it
35622 * in a new location (meaning we might want to update the relative
35623 * position on the show)
35626 this.updatePosition();
35627 OpenLayers.Popup.prototype.show.apply(this, arguments);
35632 * Since the popup is moving to a new px, it might need also to be moved
35633 * relative to where the marker is. We first calculate the new
35634 * relativePosition, and then we calculate the new px where we will
35635 * put the popup, based on the new relative position.
35637 * If the relativePosition has changed, we must also call
35638 * updateRelativePosition() to make any visual changes to the popup
35639 * which are associated with putting it in a new relativePosition.
35642 * px - {<OpenLayers.Pixel>}
35644 moveTo: function(px) {
35645 var oldRelativePosition = this.relativePosition;
35646 this.relativePosition = this.calculateRelativePosition(px);
35648 OpenLayers.Popup.prototype.moveTo.call(this, this.calculateNewPx(px));
35650 //if this move has caused the popup to change its relative position,
35651 // we need to make the appropriate cosmetic changes.
35652 if (this.relativePosition != oldRelativePosition) {
35653 this.updateRelativePosition();
35658 * APIMethod: setSize
35661 * contentSize - {<OpenLayers.Size>} the new size for the popup's
35662 * contents div (in pixels).
35664 setSize:function(contentSize) {
35665 OpenLayers.Popup.prototype.setSize.apply(this, arguments);
35667 if ((this.lonlat) && (this.map)) {
35668 var px = this.map.getLayerPxFromLonLat(this.lonlat);
35674 * Method: calculateRelativePosition
35677 * px - {<OpenLayers.Pixel>}
35680 * {String} The relative position ("br" "tr" "tl" "bl") at which the popup
35681 * should be placed.
35683 calculateRelativePosition:function(px) {
35684 var lonlat = this.map.getLonLatFromLayerPx(px);
35686 var extent = this.map.getExtent();
35687 var quadrant = extent.determineQuadrant(lonlat);
35689 return OpenLayers.Bounds.oppositeQuadrant(quadrant);
35693 * Method: updateRelativePosition
35694 * The popup has been moved to a new relative location, so we may want to
35695 * make some cosmetic adjustments to it.
35697 * Note that in the classic Anchored popup, there is nothing to do
35698 * here, since the popup looks exactly the same in all four positions.
35699 * Subclasses such as Framed, however, will want to do something
35702 updateRelativePosition: function() {
35703 //to be overridden by subclasses
35707 * Method: calculateNewPx
35710 * px - {<OpenLayers.Pixel>}
35713 * {<OpenLayers.Pixel>} The the new px position of the popup on the screen
35714 * relative to the passed-in px.
35716 calculateNewPx:function(px) {
35717 var newPx = px.offset(this.anchor.offset);
35719 //use contentSize if size is not already set
35720 var size = this.size || this.contentSize;
35722 var top = (this.relativePosition.charAt(0) == 't');
35723 newPx.y += (top) ? -size.h : this.anchor.size.h;
35725 var left = (this.relativePosition.charAt(1) == 'l');
35726 newPx.x += (left) ? -size.w : this.anchor.size.w;
35731 CLASS_NAME: "OpenLayers.Popup.Anchored"
35733 /* ======================================================================
35734 OpenLayers/Popup/Framed.js
35735 ====================================================================== */
35737 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
35738 * full list of contributors). Published under the 2-clause BSD license.
35739 * See license.txt in the OpenLayers distribution or repository for the
35740 * full text of the license. */
35743 * @requires OpenLayers/Popup/Anchored.js
35747 * Class: OpenLayers.Popup.Framed
35750 * - <OpenLayers.Popup.Anchored>
35752 OpenLayers.Popup.Framed =
35753 OpenLayers.Class(OpenLayers.Popup.Anchored, {
35756 * Property: imageSrc
35757 * {String} location of the image to be used as the popup frame
35762 * Property: imageSize
35763 * {<OpenLayers.Size>} Size (measured in pixels) of the image located
35764 * by the 'imageSrc' property.
35769 * APIProperty: isAlphaImage
35770 * {Boolean} The image has some alpha and thus needs to use the alpha
35771 * image hack. Note that setting this to true will have no noticeable
35772 * effect in FF or IE7 browsers, but will all but crush the ie6
35774 * Default is false.
35776 isAlphaImage: false,
35779 * Property: positionBlocks
35780 * {Object} Hash of different position blocks (Object/Hashs). Each block
35781 * will be keyed by a two-character 'relativePosition'
35782 * code string (ie "tl", "tr", "bl", "br"). Block properties are
35783 * 'offset', 'padding' (self-explanatory), and finally the 'blocks'
35784 * parameter, which is an array of the block objects.
35786 * Each block object must have 'size', 'anchor', and 'position'
35789 * Note that positionBlocks should never be modified at runtime.
35791 positionBlocks: null,
35795 * {Array[Object]} Array of objects, each of which is one "block" of the
35796 * popup. Each block has a 'div' and an 'image' property, both of
35797 * which are DOMElements, and the latter of which is appended to the
35798 * former. These are reused as the popup goes changing positions for
35799 * great economy and elegance.
35804 * APIProperty: fixedRelativePosition
35805 * {Boolean} We want the framed popup to work dynamically placed relative
35806 * to its anchor but also in just one fixed position. A well designed
35807 * framed popup will have the pixels and logic to display itself in
35808 * any of the four relative positions, but (understandably), this will
35809 * not be the case for all of them. By setting this property to 'true',
35810 * framed popup will not recalculate for the best placement each time
35811 * it's open, but will always open the same way.
35812 * Note that if this is set to true, it is generally advisable to also
35813 * set the 'panIntoView' property to true so that the popup can be
35814 * scrolled into view (since it will often be offscreen on open)
35815 * Default is false.
35817 fixedRelativePosition: false,
35820 * Constructor: OpenLayers.Popup.Framed
35824 * lonlat - {<OpenLayers.LonLat>}
35825 * contentSize - {<OpenLayers.Size>}
35826 * contentHTML - {String}
35827 * anchor - {Object} Object to which we'll anchor the popup. Must expose
35828 * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>)
35829 * (Note that this is generally an <OpenLayers.Icon>).
35830 * closeBox - {Boolean}
35831 * closeBoxCallback - {Function} Function to be called on closeBox click.
35833 initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
35834 closeBoxCallback) {
35836 OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments);
35838 if (this.fixedRelativePosition) {
35839 //based on our decided relativePostion, set the current padding
35840 // this keeps us from getting into trouble
35841 this.updateRelativePosition();
35843 //make calculateRelativePosition always return the specified
35845 this.calculateRelativePosition = function(px) {
35846 return this.relativePosition;
35850 this.contentDiv.style.position = "absolute";
35851 this.contentDiv.style.zIndex = 1;
35854 this.closeDiv.style.zIndex = 1;
35857 this.groupDiv.style.position = "absolute";
35858 this.groupDiv.style.top = "0px";
35859 this.groupDiv.style.left = "0px";
35860 this.groupDiv.style.height = "100%";
35861 this.groupDiv.style.width = "100%";
35865 * APIMethod: destroy
35867 destroy: function() {
35868 this.imageSrc = null;
35869 this.imageSize = null;
35870 this.isAlphaImage = null;
35872 this.fixedRelativePosition = false;
35873 this.positionBlocks = null;
35875 //remove our blocks
35876 for(var i = 0; i < this.blocks.length; i++) {
35877 var block = this.blocks[i];
35880 block.div.removeChild(block.image);
35882 block.image = null;
35885 this.groupDiv.removeChild(block.div);
35889 this.blocks = null;
35891 OpenLayers.Popup.Anchored.prototype.destroy.apply(this, arguments);
35895 * APIMethod: setBackgroundColor
35897 setBackgroundColor:function(color) {
35898 //does nothing since the framed popup's entire scheme is based on a
35899 // an image -- changing the background color makes no sense.
35903 * APIMethod: setBorder
35905 setBorder:function() {
35906 //does nothing since the framed popup's entire scheme is based on a
35907 // an image -- changing the popup's border makes no sense.
35911 * Method: setOpacity
35912 * Sets the opacity of the popup.
35915 * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid).
35917 setOpacity:function(opacity) {
35918 //does nothing since we suppose that we'll never apply an opacity
35919 // to a framed popup
35923 * APIMethod: setSize
35924 * Overridden here, because we need to update the blocks whenever the size
35925 * of the popup has changed.
35928 * contentSize - {<OpenLayers.Size>} the new size for the popup's
35929 * contents div (in pixels).
35931 setSize:function(contentSize) {
35932 OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments);
35934 this.updateBlocks();
35938 * Method: updateRelativePosition
35939 * When the relative position changes, we need to set the new padding
35940 * BBOX on the popup, reposition the close div, and update the blocks.
35942 updateRelativePosition: function() {
35944 //update the padding
35945 this.padding = this.positionBlocks[this.relativePosition].padding;
35947 //update the position of our close box to new padding
35948 if (this.closeDiv) {
35949 // use the content div's css padding to determine if we should
35950 // padd the close div
35951 var contentDivPadding = this.getContentDivPadding();
35953 this.closeDiv.style.right = contentDivPadding.right +
35954 this.padding.right + "px";
35955 this.closeDiv.style.top = contentDivPadding.top +
35956 this.padding.top + "px";
35959 this.updateBlocks();
35963 * Method: calculateNewPx
35964 * Besides the standard offset as determined by the Anchored class, our
35965 * Framed popups have a special 'offset' property for each of their
35966 * positions, which is used to offset the popup relative to its anchor.
35969 * px - {<OpenLayers.Pixel>}
35972 * {<OpenLayers.Pixel>} The the new px position of the popup on the screen
35973 * relative to the passed-in px.
35975 calculateNewPx:function(px) {
35976 var newPx = OpenLayers.Popup.Anchored.prototype.calculateNewPx.apply(
35980 newPx = newPx.offset(this.positionBlocks[this.relativePosition].offset);
35986 * Method: createBlocks
35988 createBlocks: function() {
35991 //since all positions contain the same number of blocks, we can
35992 // just pick the first position and use its blocks array to create
35993 // our blocks array
35994 var firstPosition = null;
35995 for(var key in this.positionBlocks) {
35996 firstPosition = key;
36000 var position = this.positionBlocks[firstPosition];
36001 for (var i = 0; i < position.blocks.length; i++) {
36004 this.blocks.push(block);
36006 var divId = this.id + '_FrameDecorationDiv_' + i;
36007 block.div = OpenLayers.Util.createDiv(divId,
36008 null, null, null, "absolute", null, "hidden", null
36011 var imgId = this.id + '_FrameDecorationImg_' + i;
36013 (this.isAlphaImage) ? OpenLayers.Util.createAlphaImageDiv
36014 : OpenLayers.Util.createImage;
36016 block.image = imageCreator(imgId,
36017 null, this.imageSize, this.imageSrc,
36018 "absolute", null, null, null
36021 block.div.appendChild(block.image);
36022 this.groupDiv.appendChild(block.div);
36027 * Method: updateBlocks
36028 * Internal method, called on initialize and when the popup's relative
36029 * position has changed. This function takes care of re-positioning
36030 * the popup's blocks in their appropropriate places.
36032 updateBlocks: function() {
36033 if (!this.blocks) {
36034 this.createBlocks();
36037 if (this.size && this.relativePosition) {
36038 var position = this.positionBlocks[this.relativePosition];
36039 for (var i = 0; i < position.blocks.length; i++) {
36041 var positionBlock = position.blocks[i];
36042 var block = this.blocks[i];
36045 var l = positionBlock.anchor.left;
36046 var b = positionBlock.anchor.bottom;
36047 var r = positionBlock.anchor.right;
36048 var t = positionBlock.anchor.top;
36050 //note that we use the isNaN() test here because if the
36051 // size object is initialized with a "auto" parameter, the
36052 // size constructor calls parseFloat() on the string,
36053 // which will turn it into NaN
36055 var w = (isNaN(positionBlock.size.w)) ? this.size.w - (r + l)
36056 : positionBlock.size.w;
36058 var h = (isNaN(positionBlock.size.h)) ? this.size.h - (b + t)
36059 : positionBlock.size.h;
36061 block.div.style.width = (w < 0 ? 0 : w) + 'px';
36062 block.div.style.height = (h < 0 ? 0 : h) + 'px';
36064 block.div.style.left = (l != null) ? l + 'px' : '';
36065 block.div.style.bottom = (b != null) ? b + 'px' : '';
36066 block.div.style.right = (r != null) ? r + 'px' : '';
36067 block.div.style.top = (t != null) ? t + 'px' : '';
36069 block.image.style.left = positionBlock.position.x + 'px';
36070 block.image.style.top = positionBlock.position.y + 'px';
36073 this.contentDiv.style.left = this.padding.left + "px";
36074 this.contentDiv.style.top = this.padding.top + "px";
36078 CLASS_NAME: "OpenLayers.Popup.Framed"
36080 /* ======================================================================
36081 OpenLayers/Format/JSON.js
36082 ====================================================================== */
36084 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
36085 * full list of contributors). Published under the 2-clause BSD license.
36086 * See license.txt in the OpenLayers distribution or repository for the
36087 * full text of the license. */
36091 * This work draws heavily from the public domain JSON serializer/deserializer
36092 * at http://www.json.org/json.js. Rewritten so that it doesn't modify
36093 * basic data prototypes.
36097 * @requires OpenLayers/Format.js
36101 * Class: OpenLayers.Format.JSON
36102 * A parser to read/write JSON safely. Create a new instance with the
36103 * <OpenLayers.Format.JSON> constructor.
36106 * - <OpenLayers.Format>
36108 OpenLayers.Format.JSON = OpenLayers.Class(OpenLayers.Format, {
36111 * APIProperty: indent
36112 * {String} For "pretty" printing, the indent string will be used once for
36113 * each indentation level.
36118 * APIProperty: space
36119 * {String} For "pretty" printing, the space string will be used after
36120 * the ":" separating a name/value pair.
36125 * APIProperty: newline
36126 * {String} For "pretty" printing, the newline string will be used at the
36127 * end of each name/value pair or array item.
36133 * {Integer} For "pretty" printing, this is incremented/decremented during
36140 * {Boolean} Serialize with extra whitespace for structure. This is set
36141 * by the <write> method.
36146 * Property: nativeJSON
36147 * {Boolean} Does the browser support native json?
36149 nativeJSON: (function() {
36150 return !!(window.JSON && typeof JSON.parse == "function" && typeof JSON.stringify == "function");
36154 * Constructor: OpenLayers.Format.JSON
36155 * Create a new parser for JSON.
36158 * options - {Object} An optional object whose properties will be set on
36164 * Deserialize a json string.
36167 * json - {String} A JSON string
36168 * filter - {Function} A function which will be called for every key and
36169 * value at every level of the final result. Each value will be
36170 * replaced by the result of the filter function. This can be used to
36171 * reform generic objects into instances of classes, or to transform
36172 * date strings into Date objects.
36175 * {Object} An object, array, string, or number .
36177 read: function(json, filter) {
36179 if (this.nativeJSON) {
36180 object = JSON.parse(json, filter);
36183 * Parsing happens in three stages. In the first stage, we run the
36184 * text against a regular expression which looks for non-JSON
36185 * characters. We are especially concerned with '()' and 'new'
36186 * because they can cause invocation, and '=' because it can
36187 * cause mutation. But just to be safe, we will reject all
36188 * unexpected characters.
36190 if (/^[\],:{}\s]*$/.test(json.replace(/\\["\\\/bfnrtu]/g, '@').
36191 replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
36192 replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
36195 * In the second stage we use the eval function to compile the
36196 * text into a JavaScript structure. The '{' operator is
36197 * subject to a syntactic ambiguity in JavaScript - it can
36198 * begin a block or an object literal. We wrap the text in
36199 * parens to eliminate the ambiguity.
36201 object = eval('(' + json + ')');
36204 * In the optional third stage, we recursively walk the new
36205 * structure, passing each name/value pair to a filter
36206 * function for possible transformation.
36208 if(typeof filter === 'function') {
36209 function walk(k, v) {
36210 if(v && typeof v === 'object') {
36212 if(v.hasOwnProperty(i)) {
36213 v[i] = walk(i, v[i]);
36217 return filter(k, v);
36219 object = walk('', object);
36223 // Fall through if the regexp test fails.
36226 if(this.keepData) {
36227 this.data = object;
36235 * Serialize an object into a JSON string.
36238 * value - {String} The object, array, string, number, boolean or date
36239 * to be serialized.
36240 * pretty - {Boolean} Structure the output with newlines and indentation.
36241 * Default is false.
36244 * {String} The JSON string representation of the input value.
36246 write: function(value, pretty) {
36247 this.pretty = !!pretty;
36249 var type = typeof value;
36250 if(this.serialize[type]) {
36252 json = (!this.pretty && this.nativeJSON) ?
36253 JSON.stringify(value) :
36254 this.serialize[type].apply(this, [value]);
36256 OpenLayers.Console.error("Trouble serializing: " + err);
36263 * Method: writeIndent
36264 * Output an indentation string depending on the indentation level.
36267 * {String} An appropriate indentation string.
36269 writeIndent: function() {
36272 for(var i=0; i<this.level; ++i) {
36273 pieces.push(this.indent);
36276 return pieces.join('');
36280 * Method: writeNewline
36281 * Output a string representing a newline if in pretty printing mode.
36284 * {String} A string representing a new line.
36286 writeNewline: function() {
36287 return (this.pretty) ? this.newline : '';
36291 * Method: writeSpace
36292 * Output a string representing a space if in pretty printing mode.
36295 * {String} A space.
36297 writeSpace: function() {
36298 return (this.pretty) ? this.space : '';
36302 * Property: serialize
36303 * Object with properties corresponding to the serializable data types.
36304 * Property values are functions that do the actual serializing.
36308 * Method: serialize.object
36309 * Transform an object into a JSON string.
36312 * object - {Object} The object to be serialized.
36315 * {String} A JSON string representing the object.
36317 'object': function(object) {
36318 // three special objects that we want to treat differently
36319 if(object == null) {
36322 if(object.constructor == Date) {
36323 return this.serialize.date.apply(this, [object]);
36325 if(object.constructor == Array) {
36326 return this.serialize.array.apply(this, [object]);
36328 var pieces = ['{'];
36330 var key, keyJSON, valueJSON;
36332 var addComma = false;
36333 for(key in object) {
36334 if(object.hasOwnProperty(key)) {
36335 // recursive calls need to allow for sub-classing
36336 keyJSON = OpenLayers.Format.JSON.prototype.write.apply(this,
36337 [key, this.pretty]);
36338 valueJSON = OpenLayers.Format.JSON.prototype.write.apply(this,
36339 [object[key], this.pretty]);
36340 if(keyJSON != null && valueJSON != null) {
36344 pieces.push(this.writeNewline(), this.writeIndent(),
36345 keyJSON, ':', this.writeSpace(), valueJSON);
36352 pieces.push(this.writeNewline(), this.writeIndent(), '}');
36353 return pieces.join('');
36357 * Method: serialize.array
36358 * Transform an array into a JSON string.
36361 * array - {Array} The array to be serialized
36364 * {String} A JSON string representing the array.
36366 'array': function(array) {
36368 var pieces = ['['];
36371 for(var i=0, len=array.length; i<len; ++i) {
36372 // recursive calls need to allow for sub-classing
36373 json = OpenLayers.Format.JSON.prototype.write.apply(this,
36374 [array[i], this.pretty]);
36379 pieces.push(this.writeNewline(), this.writeIndent(), json);
36384 pieces.push(this.writeNewline(), this.writeIndent(), ']');
36385 return pieces.join('');
36389 * Method: serialize.string
36390 * Transform a string into a JSON string.
36393 * string - {String} The string to be serialized
36396 * {String} A JSON string representing the string.
36398 'string': function(string) {
36399 // If the string contains no control characters, no quote characters, and no
36400 // backslash characters, then we can simply slap some quotes around it.
36401 // Otherwise we must also replace the offending characters with safe
36412 if(/["\\\x00-\x1f]/.test(string)) {
36413 return '"' + string.replace(/([\x00-\x1f\\"])/g, function(a, b) {
36418 c = b.charCodeAt();
36420 Math.floor(c / 16).toString(16) +
36421 (c % 16).toString(16);
36424 return '"' + string + '"';
36428 * Method: serialize.number
36429 * Transform a number into a JSON string.
36432 * number - {Number} The number to be serialized.
36435 * {String} A JSON string representing the number.
36437 'number': function(number) {
36438 return isFinite(number) ? String(number) : "null";
36442 * Method: serialize.boolean
36443 * Transform a boolean into a JSON string.
36446 * bool - {Boolean} The boolean to be serialized.
36449 * {String} A JSON string representing the boolean.
36451 'boolean': function(bool) {
36452 return String(bool);
36456 * Method: serialize.object
36457 * Transform a date into a JSON string.
36460 * date - {Date} The date to be serialized.
36463 * {String} A JSON string representing the date.
36465 'date': function(date) {
36466 function format(number) {
36467 // Format integers to have at least two digits.
36468 return (number < 10) ? '0' + number : number;
36470 return '"' + date.getFullYear() + '-' +
36471 format(date.getMonth() + 1) + '-' +
36472 format(date.getDate()) + 'T' +
36473 format(date.getHours()) + ':' +
36474 format(date.getMinutes()) + ':' +
36475 format(date.getSeconds()) + '"';
36479 CLASS_NAME: "OpenLayers.Format.JSON"
36482 /* ======================================================================
36483 OpenLayers/Format/GeoJSON.js
36484 ====================================================================== */
36486 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
36487 * full list of contributors). Published under the 2-clause BSD license.
36488 * See license.txt in the OpenLayers distribution or repository for the
36489 * full text of the license. */
36492 * @requires OpenLayers/Format/JSON.js
36493 * @requires OpenLayers/Feature/Vector.js
36494 * @requires OpenLayers/Geometry/Point.js
36495 * @requires OpenLayers/Geometry/MultiPoint.js
36496 * @requires OpenLayers/Geometry/LineString.js
36497 * @requires OpenLayers/Geometry/MultiLineString.js
36498 * @requires OpenLayers/Geometry/Polygon.js
36499 * @requires OpenLayers/Geometry/MultiPolygon.js
36500 * @requires OpenLayers/Console.js
36504 * Class: OpenLayers.Format.GeoJSON
36505 * Read and write GeoJSON. Create a new parser with the
36506 * <OpenLayers.Format.GeoJSON> constructor.
36509 * - <OpenLayers.Format.JSON>
36511 OpenLayers.Format.GeoJSON = OpenLayers.Class(OpenLayers.Format.JSON, {
36514 * APIProperty: ignoreExtraDims
36515 * {Boolean} Ignore dimensions higher than 2 when reading geometry
36518 ignoreExtraDims: false,
36521 * Constructor: OpenLayers.Format.GeoJSON
36522 * Create a new parser for GeoJSON.
36525 * options - {Object} An optional object whose properties will be set on
36531 * Deserialize a GeoJSON string.
36534 * json - {String} A GeoJSON string
36535 * type - {String} Optional string that determines the structure of
36536 * the output. Supported values are "Geometry", "Feature", and
36537 * "FeatureCollection". If absent or null, a default of
36538 * "FeatureCollection" is assumed.
36539 * filter - {Function} A function which will be called for every key and
36540 * value at every level of the final result. Each value will be
36541 * replaced by the result of the filter function. This can be used to
36542 * reform generic objects into instances of classes, or to transform
36543 * date strings into Date objects.
36546 * {Object} The return depends on the value of the type argument. If type
36547 * is "FeatureCollection" (the default), the return will be an array
36548 * of <OpenLayers.Feature.Vector>. If type is "Geometry", the input json
36549 * must represent a single geometry, and the return will be an
36550 * <OpenLayers.Geometry>. If type is "Feature", the input json must
36551 * represent a single feature, and the return will be an
36552 * <OpenLayers.Feature.Vector>.
36554 read: function(json, type, filter) {
36555 type = (type) ? type : "FeatureCollection";
36556 var results = null;
36558 if (typeof json == "string") {
36559 obj = OpenLayers.Format.JSON.prototype.read.apply(this,
36565 OpenLayers.Console.error("Bad JSON: " + json);
36566 } else if(typeof(obj.type) != "string") {
36567 OpenLayers.Console.error("Bad GeoJSON - no type: " + json);
36568 } else if(this.isValidType(obj, type)) {
36572 results = this.parseGeometry(obj);
36574 OpenLayers.Console.error(err);
36579 results = this.parseFeature(obj);
36580 results.type = "Feature";
36582 OpenLayers.Console.error(err);
36585 case "FeatureCollection":
36586 // for type FeatureCollection, we allow input to be any type
36591 results.push(this.parseFeature(obj));
36594 OpenLayers.Console.error(err);
36597 case "FeatureCollection":
36598 for(var i=0, len=obj.features.length; i<len; ++i) {
36600 results.push(this.parseFeature(obj.features[i]));
36603 OpenLayers.Console.error(err);
36609 var geom = this.parseGeometry(obj);
36610 results.push(new OpenLayers.Feature.Vector(geom));
36613 OpenLayers.Console.error(err);
36623 * Method: isValidType
36624 * Check if a GeoJSON object is a valid representative of the given type.
36627 * {Boolean} The object is valid GeoJSON object of the given type.
36629 isValidType: function(obj, type) {
36633 if(OpenLayers.Util.indexOf(
36634 ["Point", "MultiPoint", "LineString", "MultiLineString",
36635 "Polygon", "MultiPolygon", "Box", "GeometryCollection"],
36637 // unsupported geometry type
36638 OpenLayers.Console.error("Unsupported geometry type: " +
36644 case "FeatureCollection":
36645 // allow for any type to be converted to a feature collection
36649 // for Feature types must match
36650 if(obj.type == type) {
36653 OpenLayers.Console.error("Cannot convert types from " +
36654 obj.type + " to " + type);
36661 * Method: parseFeature
36662 * Convert a feature object from GeoJSON into an
36663 * <OpenLayers.Feature.Vector>.
36666 * obj - {Object} An object created from a GeoJSON object
36669 * {<OpenLayers.Feature.Vector>} A feature.
36671 parseFeature: function(obj) {
36672 var feature, geometry, attributes, bbox;
36673 attributes = (obj.properties) ? obj.properties : {};
36674 bbox = (obj.geometry && obj.geometry.bbox) || obj.bbox;
36676 geometry = this.parseGeometry(obj.geometry);
36678 // deal with bad geometries
36681 feature = new OpenLayers.Feature.Vector(geometry, attributes);
36683 feature.bounds = OpenLayers.Bounds.fromArray(bbox);
36686 feature.fid = obj.id;
36692 * Method: parseGeometry
36693 * Convert a geometry object from GeoJSON into an <OpenLayers.Geometry>.
36696 * obj - {Object} An object created from a GeoJSON object
36699 * {<OpenLayers.Geometry>} A geometry.
36701 parseGeometry: function(obj) {
36705 var geometry, collection = false;
36706 if(obj.type == "GeometryCollection") {
36707 if(!(OpenLayers.Util.isArray(obj.geometries))) {
36708 throw "GeometryCollection must have geometries array: " + obj;
36710 var numGeom = obj.geometries.length;
36711 var components = new Array(numGeom);
36712 for(var i=0; i<numGeom; ++i) {
36713 components[i] = this.parseGeometry.apply(
36714 this, [obj.geometries[i]]
36717 geometry = new OpenLayers.Geometry.Collection(components);
36720 if(!(OpenLayers.Util.isArray(obj.coordinates))) {
36721 throw "Geometry must have coordinates array: " + obj;
36723 if(!this.parseCoords[obj.type.toLowerCase()]) {
36724 throw "Unsupported geometry type: " + obj.type;
36727 geometry = this.parseCoords[obj.type.toLowerCase()].apply(
36728 this, [obj.coordinates]
36731 // deal with bad coordinates
36735 // We don't reproject collections because the children are reprojected
36736 // for us when they are created.
36737 if (this.internalProjection && this.externalProjection && !collection) {
36738 geometry.transform(this.externalProjection,
36739 this.internalProjection);
36745 * Property: parseCoords
36746 * Object with properties corresponding to the GeoJSON geometry types.
36747 * Property values are functions that do the actual parsing.
36751 * Method: parseCoords.point
36752 * Convert a coordinate array from GeoJSON into an
36753 * <OpenLayers.Geometry>.
36756 * array - {Object} The coordinates array from the GeoJSON fragment.
36759 * {<OpenLayers.Geometry>} A geometry.
36761 "point": function(array) {
36762 if (this.ignoreExtraDims == false &&
36763 array.length != 2) {
36764 throw "Only 2D points are supported: " + array;
36766 return new OpenLayers.Geometry.Point(array[0], array[1]);
36770 * Method: parseCoords.multipoint
36771 * Convert a coordinate array from GeoJSON into an
36772 * <OpenLayers.Geometry>.
36775 * array - {Object} The coordinates array from the GeoJSON fragment.
36778 * {<OpenLayers.Geometry>} A geometry.
36780 "multipoint": function(array) {
36783 for(var i=0, len=array.length; i<len; ++i) {
36785 p = this.parseCoords["point"].apply(this, [array[i]]);
36791 return new OpenLayers.Geometry.MultiPoint(points);
36795 * Method: parseCoords.linestring
36796 * Convert a coordinate array from GeoJSON into an
36797 * <OpenLayers.Geometry>.
36800 * array - {Object} The coordinates array from the GeoJSON fragment.
36803 * {<OpenLayers.Geometry>} A geometry.
36805 "linestring": function(array) {
36808 for(var i=0, len=array.length; i<len; ++i) {
36810 p = this.parseCoords["point"].apply(this, [array[i]]);
36816 return new OpenLayers.Geometry.LineString(points);
36820 * Method: parseCoords.multilinestring
36821 * Convert a coordinate array from GeoJSON into an
36822 * <OpenLayers.Geometry>.
36825 * array - {Object} The coordinates array from the GeoJSON fragment.
36828 * {<OpenLayers.Geometry>} A geometry.
36830 "multilinestring": function(array) {
36833 for(var i=0, len=array.length; i<len; ++i) {
36835 l = this.parseCoords["linestring"].apply(this, [array[i]]);
36841 return new OpenLayers.Geometry.MultiLineString(lines);
36845 * Method: parseCoords.polygon
36846 * Convert a coordinate array from GeoJSON into an
36847 * <OpenLayers.Geometry>.
36850 * {<OpenLayers.Geometry>} A geometry.
36852 "polygon": function(array) {
36855 for(var i=0, len=array.length; i<len; ++i) {
36857 l = this.parseCoords["linestring"].apply(this, [array[i]]);
36861 r = new OpenLayers.Geometry.LinearRing(l.components);
36864 return new OpenLayers.Geometry.Polygon(rings);
36868 * Method: parseCoords.multipolygon
36869 * Convert a coordinate array from GeoJSON into an
36870 * <OpenLayers.Geometry>.
36873 * array - {Object} The coordinates array from the GeoJSON fragment.
36876 * {<OpenLayers.Geometry>} A geometry.
36878 "multipolygon": function(array) {
36881 for(var i=0, len=array.length; i<len; ++i) {
36883 p = this.parseCoords["polygon"].apply(this, [array[i]]);
36889 return new OpenLayers.Geometry.MultiPolygon(polys);
36893 * Method: parseCoords.box
36894 * Convert a coordinate array from GeoJSON into an
36895 * <OpenLayers.Geometry>.
36898 * array - {Object} The coordinates array from the GeoJSON fragment.
36901 * {<OpenLayers.Geometry>} A geometry.
36903 "box": function(array) {
36904 if(array.length != 2) {
36905 throw "GeoJSON box coordinates must have 2 elements";
36907 return new OpenLayers.Geometry.Polygon([
36908 new OpenLayers.Geometry.LinearRing([
36909 new OpenLayers.Geometry.Point(array[0][0], array[0][1]),
36910 new OpenLayers.Geometry.Point(array[1][0], array[0][1]),
36911 new OpenLayers.Geometry.Point(array[1][0], array[1][1]),
36912 new OpenLayers.Geometry.Point(array[0][0], array[1][1]),
36913 new OpenLayers.Geometry.Point(array[0][0], array[0][1])
36922 * Serialize a feature, geometry, array of features into a GeoJSON string.
36925 * obj - {Object} An <OpenLayers.Feature.Vector>, <OpenLayers.Geometry>,
36926 * or an array of features.
36927 * pretty - {Boolean} Structure the output with newlines and indentation.
36928 * Default is false.
36931 * {String} The GeoJSON string representation of the input geometry,
36932 * features, or array of features.
36934 write: function(obj, pretty) {
36938 if(OpenLayers.Util.isArray(obj)) {
36939 geojson.type = "FeatureCollection";
36940 var numFeatures = obj.length;
36941 geojson.features = new Array(numFeatures);
36942 for(var i=0; i<numFeatures; ++i) {
36943 var element = obj[i];
36944 if(!element instanceof OpenLayers.Feature.Vector) {
36945 var msg = "FeatureCollection only supports collections " +
36946 "of features: " + element;
36949 geojson.features[i] = this.extract.feature.apply(
36953 } else if (obj.CLASS_NAME.indexOf("OpenLayers.Geometry") == 0) {
36954 geojson = this.extract.geometry.apply(this, [obj]);
36955 } else if (obj instanceof OpenLayers.Feature.Vector) {
36956 geojson = this.extract.feature.apply(this, [obj]);
36957 if(obj.layer && obj.layer.projection) {
36958 geojson.crs = this.createCRSObject(obj);
36961 return OpenLayers.Format.JSON.prototype.write.apply(this,
36962 [geojson, pretty]);
36966 * Method: createCRSObject
36967 * Create the CRS object for an object.
36970 * object - {<OpenLayers.Feature.Vector>}
36973 * {Object} An object which can be assigned to the crs property
36974 * of a GeoJSON object.
36976 createCRSObject: function(object) {
36977 var proj = object.layer.projection.toString();
36979 if (proj.match(/epsg:/i)) {
36980 var code = parseInt(proj.substring(proj.indexOf(":") + 1));
36981 if (code == 4326) {
36985 "name": "urn:ogc:def:crs:OGC:1.3:CRS84"
36992 "name": "EPSG:" + code
37001 * Property: extract
37002 * Object with properties corresponding to the GeoJSON types.
37003 * Property values are functions that do the actual value extraction.
37007 * Method: extract.feature
37008 * Return a partial GeoJSON object representing a single feature.
37011 * feature - {<OpenLayers.Feature.Vector>}
37014 * {Object} An object representing the point.
37016 'feature': function(feature) {
37017 var geom = this.extract.geometry.apply(this, [feature.geometry]);
37020 "properties": feature.attributes,
37023 if (feature.fid != null) {
37024 json.id = feature.fid;
37030 * Method: extract.geometry
37031 * Return a GeoJSON object representing a single geometry.
37034 * geometry - {<OpenLayers.Geometry>}
37037 * {Object} An object representing the geometry.
37039 'geometry': function(geometry) {
37040 if (geometry == null) {
37043 if (this.internalProjection && this.externalProjection) {
37044 geometry = geometry.clone();
37045 geometry.transform(this.internalProjection,
37046 this.externalProjection);
37048 var geometryType = geometry.CLASS_NAME.split('.')[2];
37049 var data = this.extract[geometryType.toLowerCase()].apply(this, [geometry]);
37051 if(geometryType == "Collection") {
37053 "type": "GeometryCollection",
37058 "type": geometryType,
37059 "coordinates": data
37067 * Method: extract.point
37068 * Return an array of coordinates from a point.
37071 * point - {<OpenLayers.Geometry.Point>}
37074 * {Array} An array of coordinates representing the point.
37076 'point': function(point) {
37077 return [point.x, point.y];
37081 * Method: extract.multipoint
37082 * Return an array of point coordinates from a multipoint.
37085 * multipoint - {<OpenLayers.Geometry.MultiPoint>}
37088 * {Array} An array of point coordinate arrays representing
37091 'multipoint': function(multipoint) {
37093 for(var i=0, len=multipoint.components.length; i<len; ++i) {
37094 array.push(this.extract.point.apply(this, [multipoint.components[i]]));
37100 * Method: extract.linestring
37101 * Return an array of coordinate arrays from a linestring.
37104 * linestring - {<OpenLayers.Geometry.LineString>}
37107 * {Array} An array of coordinate arrays representing
37110 'linestring': function(linestring) {
37112 for(var i=0, len=linestring.components.length; i<len; ++i) {
37113 array.push(this.extract.point.apply(this, [linestring.components[i]]));
37119 * Method: extract.multilinestring
37120 * Return an array of linestring arrays from a linestring.
37123 * multilinestring - {<OpenLayers.Geometry.MultiLineString>}
37126 * {Array} An array of linestring arrays representing
37127 * the multilinestring.
37129 'multilinestring': function(multilinestring) {
37131 for(var i=0, len=multilinestring.components.length; i<len; ++i) {
37132 array.push(this.extract.linestring.apply(this, [multilinestring.components[i]]));
37138 * Method: extract.polygon
37139 * Return an array of linear ring arrays from a polygon.
37142 * polygon - {<OpenLayers.Geometry.Polygon>}
37145 * {Array} An array of linear ring arrays representing the polygon.
37147 'polygon': function(polygon) {
37149 for(var i=0, len=polygon.components.length; i<len; ++i) {
37150 array.push(this.extract.linestring.apply(this, [polygon.components[i]]));
37156 * Method: extract.multipolygon
37157 * Return an array of polygon arrays from a multipolygon.
37160 * multipolygon - {<OpenLayers.Geometry.MultiPolygon>}
37163 * {Array} An array of polygon arrays representing
37166 'multipolygon': function(multipolygon) {
37168 for(var i=0, len=multipolygon.components.length; i<len; ++i) {
37169 array.push(this.extract.polygon.apply(this, [multipolygon.components[i]]));
37175 * Method: extract.collection
37176 * Return an array of geometries from a geometry collection.
37179 * collection - {<OpenLayers.Geometry.Collection>}
37182 * {Array} An array of geometry objects representing the geometry
37185 'collection': function(collection) {
37186 var len = collection.components.length;
37187 var array = new Array(len);
37188 for(var i=0; i<len; ++i) {
37189 array[i] = this.extract.geometry.apply(
37190 this, [collection.components[i]]
37199 CLASS_NAME: "OpenLayers.Format.GeoJSON"
37202 /* ======================================================================
37203 OpenLayers/Protocol/WFS/v1.js
37204 ====================================================================== */
37206 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
37207 * full list of contributors). Published under the 2-clause BSD license.
37208 * See license.txt in the OpenLayers distribution or repository for the
37209 * full text of the license. */
37212 * @requires OpenLayers/Protocol/WFS.js
37216 * Class: OpenLayers.Protocol.WFS.v1
37217 * Abstract class for for v1.0.0 and v1.1.0 protocol.
37220 * - <OpenLayers.Protocol>
37222 OpenLayers.Protocol.WFS.v1 = OpenLayers.Class(OpenLayers.Protocol, {
37225 * Property: version
37226 * {String} WFS version number.
37231 * Property: srsName
37232 * {String} Name of spatial reference system. Default is "EPSG:4326".
37234 srsName: "EPSG:4326",
37237 * Property: featureType
37238 * {String} Local feature typeName.
37243 * Property: featureNS
37244 * {String} Feature namespace.
37249 * Property: geometryName
37250 * {String} Name of the geometry attribute for features. Default is
37251 * "the_geom" for WFS <version> 1.0, and null for higher versions.
37253 geometryName: "the_geom",
37256 * Property: maxFeatures
37257 * {Integer} Optional maximum number of features to retrieve.
37262 * {String} Optional schema location that will be included in the
37263 * schemaLocation attribute value. Note that the feature type schema
37264 * is required for a strict XML validator (on transactions with an
37265 * insert for example), but is *not* required by the WFS specification
37266 * (since the server is supposed to know about feature type schemas).
37271 * Property: featurePrefix
37272 * {String} Namespace alias for feature type. Default is "feature".
37274 featurePrefix: "feature",
37277 * Property: formatOptions
37278 * {Object} Optional options for the format. If a format is not provided,
37279 * this property can be used to extend the default format options.
37281 formatOptions: null,
37284 * Property: readFormat
37285 * {<OpenLayers.Format>} For WFS requests it is possible to get a
37286 * different output format than GML. In that case, we cannot parse
37287 * the response with the default format (WFST) and we need a different
37288 * format for reading.
37293 * Property: readOptions
37294 * {Object} Optional object to pass to format's read.
37299 * Constructor: OpenLayers.Protocol.WFS
37300 * A class for giving layers WFS protocol.
37303 * options - {Object} Optional object whose properties will be set on the
37306 * Valid options properties:
37307 * url - {String} URL to send requests to (required).
37308 * featureType - {String} Local (without prefix) feature typeName (required).
37309 * featureNS - {String} Feature namespace (required, but can be autodetected
37310 * during the first query if GML is used as readFormat and
37311 * featurePrefix is provided and matches the prefix used by the server
37312 * for this featureType).
37313 * featurePrefix - {String} Feature namespace alias (optional - only used
37314 * for writing if featureNS is provided). Default is 'feature'.
37315 * geometryName - {String} Name of geometry attribute. The default is
37316 * 'the_geom' for WFS <version> 1.0, and null for higher versions. If
37317 * null, it will be set to the name of the first geometry found in the
37318 * first read operation.
37319 * multi - {Boolean} If set to true, geometries will be casted to Multi
37320 * geometries before they are written in a transaction. No casting will
37321 * be done when reading features.
37323 initialize: function(options) {
37324 OpenLayers.Protocol.prototype.initialize.apply(this, [options]);
37325 if(!options.format) {
37326 this.format = OpenLayers.Format.WFST(OpenLayers.Util.extend({
37327 version: this.version,
37328 featureType: this.featureType,
37329 featureNS: this.featureNS,
37330 featurePrefix: this.featurePrefix,
37331 geometryName: this.geometryName,
37332 srsName: this.srsName,
37333 schema: this.schema
37334 }, this.formatOptions));
37336 if (!options.geometryName && parseFloat(this.format.version) > 1.0) {
37337 this.setGeometryName(null);
37342 * APIMethod: destroy
37343 * Clean up the protocol.
37345 destroy: function() {
37346 if(this.options && !this.options.format) {
37347 this.format.destroy();
37349 this.format = null;
37350 OpenLayers.Protocol.prototype.destroy.apply(this);
37355 * Construct a request for reading new features. Since WFS splits the
37356 * basic CRUD operations into GetFeature requests (for read) and
37357 * Transactions (for all others), this method does not make use of the
37358 * format's read method (that is only about reading transaction
37362 * options - {Object} Options for the read operation, in addition to the
37363 * options set on the instance (options set here will take precedence).
37365 * To use a configured protocol to get e.g. a WFS hit count, applications
37366 * could do the following:
37370 * readOptions: {output: "object"},
37371 * resultType: "hits",
37372 * maxFeatures: null,
37373 * callback: function(resp) {
37374 * // process resp.numberOfFeatures here
37379 * To use a configured protocol to use WFS paging (if supported by the
37380 * server), applications could do the following:
37389 * To limit the attributes returned by the GetFeature request, applications
37390 * can use the propertyNames option to specify the properties to include in
37395 * propertyNames: ["DURATION", "INTENSITY"]
37399 read: function(options) {
37400 OpenLayers.Protocol.prototype.read.apply(this, arguments);
37401 options = OpenLayers.Util.extend({}, options);
37402 OpenLayers.Util.applyDefaults(options, this.options || {});
37403 var response = new OpenLayers.Protocol.Response({requestType: "read"});
37405 var data = OpenLayers.Format.XML.prototype.write.apply(
37406 this.format, [this.format.writeNode("wfs:GetFeature", options)]
37409 response.priv = OpenLayers.Request.POST({
37411 callback: this.createCallback(this.handleRead, response, options),
37412 params: options.params,
37413 headers: options.headers,
37421 * APIMethod: setFeatureType
37422 * Change the feature type on the fly.
37425 * featureType - {String} Local (without prefix) feature typeName.
37427 setFeatureType: function(featureType) {
37428 this.featureType = featureType;
37429 this.format.featureType = featureType;
37433 * APIMethod: setGeometryName
37434 * Sets the geometryName option after instantiation.
37437 * geometryName - {String} Name of geometry attribute.
37439 setGeometryName: function(geometryName) {
37440 this.geometryName = geometryName;
37441 this.format.geometryName = geometryName;
37445 * Method: handleRead
37446 * Deal with response from the read request.
37449 * response - {<OpenLayers.Protocol.Response>} The response object to pass
37450 * to the user callback.
37451 * options - {Object} The user options passed to the read call.
37453 handleRead: function(response, options) {
37454 options = OpenLayers.Util.extend({}, options);
37455 OpenLayers.Util.applyDefaults(options, this.options);
37457 if(options.callback) {
37458 var request = response.priv;
37459 if(request.status >= 200 && request.status < 300) {
37461 var result = this.parseResponse(request, options.readOptions);
37462 if (result && result.success !== false) {
37463 if (options.readOptions && options.readOptions.output == "object") {
37464 OpenLayers.Util.extend(response, result);
37466 response.features = result;
37468 response.code = OpenLayers.Protocol.Response.SUCCESS;
37470 // failure (service exception)
37471 response.code = OpenLayers.Protocol.Response.FAILURE;
37472 response.error = result;
37476 response.code = OpenLayers.Protocol.Response.FAILURE;
37478 options.callback.call(options.scope, response);
37483 * Method: parseResponse
37484 * Read HTTP response body and return features
37487 * request - {XMLHttpRequest} The request object
37488 * options - {Object} Optional object to pass to format's read
37491 * {Object} or {Array({<OpenLayers.Feature.Vector>})} or
37492 * {<OpenLayers.Feature.Vector>}
37493 * An object with a features property, an array of features or a single
37496 parseResponse: function(request, options) {
37497 var doc = request.responseXML;
37498 if(!doc || !doc.documentElement) {
37499 doc = request.responseText;
37501 if(!doc || doc.length <= 0) {
37504 var result = (this.readFormat !== null) ? this.readFormat.read(doc) :
37505 this.format.read(doc, options);
37506 if (!this.featureNS) {
37507 var format = this.readFormat || this.format;
37508 this.featureNS = format.featureNS;
37509 // no need to auto-configure again on subsequent reads
37510 format.autoConfig = false;
37511 if (!this.geometryName) {
37512 this.setGeometryName(format.geometryName);
37520 * Given a list of feature, assemble a batch request for update, create,
37521 * and delete transactions. A commit call on the prototype amounts
37522 * to writing a WFS transaction - so the write method on the format
37526 * features - {Array(<OpenLayers.Feature.Vector>)}
37527 * options - {Object}
37529 * Valid options properties:
37530 * nativeElements - {Array({Object})} Array of objects with information for writing
37531 * out <Native> elements, these objects have vendorId, safeToIgnore and
37532 * value properties. The <Native> element is intended to allow access to
37533 * vendor specific capabilities of any particular web feature server or
37537 * {<OpenLayers.Protocol.Response>} A response object with a features
37538 * property containing any insertIds and a priv property referencing
37539 * the XMLHttpRequest object.
37541 commit: function(features, options) {
37543 options = OpenLayers.Util.extend({}, options);
37544 OpenLayers.Util.applyDefaults(options, this.options);
37546 var response = new OpenLayers.Protocol.Response({
37547 requestType: "commit",
37548 reqFeatures: features
37550 response.priv = OpenLayers.Request.POST({
37552 headers: options.headers,
37553 data: this.format.write(features, options),
37554 callback: this.createCallback(this.handleCommit, response, options)
37561 * Method: handleCommit
37562 * Called when the commit request returns.
37565 * response - {<OpenLayers.Protocol.Response>} The response object to pass
37566 * to the user callback.
37567 * options - {Object} The user options passed to the commit call.
37569 handleCommit: function(response, options) {
37570 if(options.callback) {
37571 var request = response.priv;
37573 // ensure that we have an xml doc
37574 var data = request.responseXML;
37575 if(!data || !data.documentElement) {
37576 data = request.responseText;
37579 var obj = this.format.read(data) || {};
37581 response.insertIds = obj.insertIds || [];
37583 response.code = OpenLayers.Protocol.Response.SUCCESS;
37585 response.code = OpenLayers.Protocol.Response.FAILURE;
37586 response.error = obj;
37588 options.callback.call(options.scope, response);
37593 * Method: filterDelete
37594 * Send a request that deletes all features by their filter.
37597 * filter - {<OpenLayers.Filter>} filter
37599 filterDelete: function(filter, options) {
37600 options = OpenLayers.Util.extend({}, options);
37601 OpenLayers.Util.applyDefaults(options, this.options);
37603 var response = new OpenLayers.Protocol.Response({
37604 requestType: "commit"
37607 var root = this.format.createElementNSPlus("wfs:Transaction", {
37610 version: this.version
37614 var deleteNode = this.format.createElementNSPlus("wfs:Delete", {
37616 typeName: (options.featureNS ? this.featurePrefix + ":" : "") +
37617 options.featureType
37621 if(options.featureNS) {
37622 deleteNode.setAttribute("xmlns:" + this.featurePrefix, options.featureNS);
37624 var filterNode = this.format.writeNode("ogc:Filter", filter);
37626 deleteNode.appendChild(filterNode);
37628 root.appendChild(deleteNode);
37630 var data = OpenLayers.Format.XML.prototype.write.apply(
37631 this.format, [root]
37634 return OpenLayers.Request.POST({
37636 callback : options.callback || function(){},
37644 * Abort an ongoing request, the response object passed to
37645 * this method must come from this protocol (as a result
37646 * of a read, or commit operation).
37649 * response - {<OpenLayers.Protocol.Response>}
37651 abort: function(response) {
37653 response.priv.abort();
37657 CLASS_NAME: "OpenLayers.Protocol.WFS.v1"
37659 /* ======================================================================
37660 OpenLayers/Layer/Google/v3.js
37661 ====================================================================== */
37663 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
37664 * full list of contributors). Published under the 2-clause BSD license.
37665 * See license.txt in the OpenLayers distribution or repository for the
37666 * full text of the license. */
37670 * @requires OpenLayers/Layer/Google.js
37674 * Constant: OpenLayers.Layer.Google.v3
37676 * Mixin providing functionality specific to the Google Maps API v3.
37678 * To use this layer, you must include the GMaps v3 API in your html.
37680 * Note that this layer configures the google.maps.map object with the
37681 * "disableDefaultUI" option set to true. Using UI controls that the Google
37682 * Maps API provides is not supported by the OpenLayers API.
37684 OpenLayers.Layer.Google.v3 = {
37687 * Constant: DEFAULTS
37688 * {Object} It is not recommended to change the properties set here. Note
37689 * that Google.v3 layers only work when sphericalMercator is set to true.
37693 * sphericalMercator: true,
37694 * projection: "EPSG:900913"
37699 sphericalMercator: true,
37700 projection: "EPSG:900913"
37704 * APIProperty: animationEnabled
37705 * {Boolean} If set to true, the transition between zoom levels will be
37706 * animated (if supported by the GMaps API for the device used). Set to
37707 * false to match the zooming experience of other layer types. Default
37708 * is true. Note that the GMaps API does not give us control over zoom
37709 * animation, so if set to false, when zooming, this will make the
37710 * layer temporarily invisible, wait until GMaps reports the map being
37711 * idle, and make it visible again. The result will be a blank layer
37712 * for a few moments while zooming.
37714 animationEnabled: true,
37717 * Method: loadMapObject
37718 * Load the GMap and register appropriate event listeners.
37720 loadMapObject: function() {
37722 this.type = google.maps.MapTypeId.ROADMAP;
37725 var cache = OpenLayers.Layer.Google.cache[this.map.id];
37727 // there are already Google layers added to this map
37728 mapObject = cache.mapObject;
37729 // increment the layer count
37732 // this is the first Google layer for this map
37734 var center = this.map.getCenter();
37735 var container = document.createElement('div');
37736 container.className = "olForeignContainer";
37737 container.style.width = '100%';
37738 container.style.height = '100%';
37739 mapObject = new google.maps.Map(container, {
37741 new google.maps.LatLng(center.lat, center.lon) :
37742 new google.maps.LatLng(0, 0),
37743 zoom: this.map.getZoom() || 0,
37744 mapTypeId: this.type,
37745 disableDefaultUI: true,
37746 keyboardShortcuts: false,
37748 disableDoubleClickZoom: true,
37749 scrollwheel: false,
37750 streetViewControl: false
37752 var googleControl = document.createElement('div');
37753 googleControl.style.width = '100%';
37754 googleControl.style.height = '100%';
37755 mapObject.controls[google.maps.ControlPosition.TOP_LEFT].push(googleControl);
37757 // cache elements for use by any other google layers added to
37760 googleControl: googleControl,
37761 mapObject: mapObject,
37764 OpenLayers.Layer.Google.cache[this.map.id] = cache;
37766 this.mapObject = mapObject;
37767 this.setGMapVisibility(this.visibility);
37771 * APIMethod: onMapResize
37773 onMapResize: function() {
37774 if (this.visibility) {
37775 google.maps.event.trigger(this.mapObject, "resize");
37780 * Method: setGMapVisibility
37781 * Display the GMap container and associated elements.
37784 * visible - {Boolean} Display the GMap elements.
37786 setGMapVisibility: function(visible) {
37787 var cache = OpenLayers.Layer.Google.cache[this.map.id];
37788 var map = this.map;
37790 var type = this.type;
37791 var layers = map.layers;
37793 for (var i=layers.length-1; i>=0; --i) {
37795 if (layer instanceof OpenLayers.Layer.Google &&
37796 layer.visibility === true && layer.inRange === true) {
37802 var container = this.mapObject.getDiv();
37803 if (visible === true) {
37804 if (container.parentNode !== map.div) {
37805 if (!cache.rendered) {
37807 google.maps.event.addListenerOnce(this.mapObject, 'tilesloaded', function() {
37808 cache.rendered = true;
37809 me.setGMapVisibility(me.getVisibility());
37810 me.moveTo(me.map.getCenter());
37813 map.div.appendChild(container);
37814 cache.googleControl.appendChild(map.viewPortDiv);
37815 google.maps.event.trigger(this.mapObject, 'resize');
37818 this.mapObject.setMapTypeId(type);
37819 } else if (cache.googleControl.hasChildNodes()) {
37820 map.div.appendChild(map.viewPortDiv);
37821 map.div.removeChild(container);
37827 * Method: getMapContainer
37830 * {DOMElement} the GMap container's div
37832 getMapContainer: function() {
37833 return this.mapObject.getDiv();
37837 // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds
37841 * APIMethod: getMapObjectBoundsFromOLBounds
37844 * olBounds - {<OpenLayers.Bounds>}
37847 * {Object} A MapObject Bounds, translated from olBounds
37848 * Returns null if null value is passed in
37850 getMapObjectBoundsFromOLBounds: function(olBounds) {
37851 var moBounds = null;
37852 if (olBounds != null) {
37853 var sw = this.sphericalMercator ?
37854 this.inverseMercator(olBounds.bottom, olBounds.left) :
37855 new OpenLayers.LonLat(olBounds.bottom, olBounds.left);
37856 var ne = this.sphericalMercator ?
37857 this.inverseMercator(olBounds.top, olBounds.right) :
37858 new OpenLayers.LonLat(olBounds.top, olBounds.right);
37859 moBounds = new google.maps.LatLngBounds(
37860 new google.maps.LatLng(sw.lat, sw.lon),
37861 new google.maps.LatLng(ne.lat, ne.lon)
37868 /************************************
37870 * MapObject Interface Controls *
37872 ************************************/
37875 // LonLat - Pixel Translation
37878 * APIMethod: getMapObjectLonLatFromMapObjectPixel
37881 * moPixel - {Object} MapObject Pixel format
37884 * {Object} MapObject LonLat translated from MapObject Pixel
37886 getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
37887 var size = this.map.getSize();
37888 var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center);
37889 var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center);
37890 var res = this.map.getResolution();
37892 var delta_x = moPixel.x - (size.w / 2);
37893 var delta_y = moPixel.y - (size.h / 2);
37895 var lonlat = new OpenLayers.LonLat(
37896 lon + delta_x * res,
37897 lat - delta_y * res
37900 if (this.wrapDateLine) {
37901 lonlat = lonlat.wrapDateLine(this.maxExtent);
37903 return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat);
37907 * APIMethod: getMapObjectPixelFromMapObjectLonLat
37910 * moLonLat - {Object} MapObject LonLat format
37913 * {Object} MapObject Pixel transtlated from MapObject LonLat
37915 getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
37916 var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);
37917 var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);
37918 var res = this.map.getResolution();
37919 var extent = this.map.getExtent();
37920 return this.getMapObjectPixelFromXY((1/res * (lon - extent.left)),
37921 (1/res * (extent.top - lat)));
37926 * APIMethod: setMapObjectCenter
37927 * Set the mapObject to the specified center and zoom
37930 * center - {Object} MapObject LonLat format
37931 * zoom - {int} MapObject zoom format
37933 setMapObjectCenter: function(center, zoom) {
37934 if (this.animationEnabled === false && zoom != this.mapObject.zoom) {
37935 var mapContainer = this.getMapContainer();
37936 google.maps.event.addListenerOnce(
37940 mapContainer.style.visibility = "";
37943 mapContainer.style.visibility = "hidden";
37945 this.mapObject.setOptions({
37955 * APIMethod: getMapObjectZoomFromMapObjectBounds
37958 * moBounds - {Object} MapObject Bounds format
37961 * {Object} MapObject Zoom for specified MapObject Bounds
37963 getMapObjectZoomFromMapObjectBounds: function(moBounds) {
37964 return this.mapObject.getBoundsZoomLevel(moBounds);
37967 /************************************
37969 * MapObject Primitives *
37971 ************************************/
37977 * APIMethod: getMapObjectLonLatFromLonLat
37984 * {Object} MapObject LonLat built from lon and lat params
37986 getMapObjectLonLatFromLonLat: function(lon, lat) {
37988 if(this.sphericalMercator) {
37989 var lonlat = this.inverseMercator(lon, lat);
37990 gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon);
37992 gLatLng = new google.maps.LatLng(lat, lon);
38000 * APIMethod: getMapObjectPixelFromXY
38007 * {Object} MapObject Pixel from x and y parameters
38009 getMapObjectPixelFromXY: function(x, y) {
38010 return new google.maps.Point(x, y);
38014 /* ======================================================================
38015 OpenLayers/Request.js
38016 ====================================================================== */
38018 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
38019 * full list of contributors). Published under the 2-clause BSD license.
38020 * See license.txt in the OpenLayers distribution or repository for the
38021 * full text of the license. */
38024 * @requires OpenLayers/Events.js
38025 * @requires OpenLayers/Request/XMLHttpRequest.js
38029 * TODO: deprecate me
38030 * Use OpenLayers.Request.proxy instead.
38032 OpenLayers.ProxyHost = "";
38035 * Namespace: OpenLayers.Request
38036 * The OpenLayers.Request namespace contains convenience methods for working
38037 * with XMLHttpRequests. These methods work with a cross-browser
38038 * W3C compliant <OpenLayers.Request.XMLHttpRequest> class.
38040 if (!OpenLayers.Request) {
38042 * This allows for OpenLayers/Request/XMLHttpRequest.js to be included
38043 * before or after this script.
38045 OpenLayers.Request = {};
38047 OpenLayers.Util.extend(OpenLayers.Request, {
38050 * Constant: DEFAULT_CONFIG
38051 * {Object} Default configuration for all requests.
38055 url: window.location.href,
38058 password: undefined,
38060 proxy: OpenLayers.ProxyHost,
38063 callback: function() {},
38070 * Constant: URL_SPLIT_REGEX
38072 URL_SPLIT_REGEX: /([^:]*:)\/\/([^:]*:?[^@]*@)?([^:\/\?]*):?([^\/\?]*)/,
38075 * APIProperty: events
38076 * {<OpenLayers.Events>} An events object that handles all
38077 * events on the {<OpenLayers.Request>} object.
38079 * All event listeners will receive an event object with three properties:
38080 * request - {<OpenLayers.Request.XMLHttpRequest>} The request object.
38081 * config - {Object} The config object sent to the specific request method.
38082 * requestUrl - {String} The request url.
38084 * Supported event types:
38085 * complete - Triggered when we have a response from the request, if a
38086 * listener returns false, no further response processing will take
38088 * success - Triggered when the HTTP response has a success code (200-299).
38089 * failure - Triggered when the HTTP response does not have a success code.
38091 events: new OpenLayers.Events(this),
38094 * Method: makeSameOrigin
38095 * Using the specified proxy, returns a same origin url of the provided url.
38098 * url - {String} An arbitrary url
38099 * proxy {String|Function} The proxy to use to make the provided url a
38103 * {String} the same origin url. If no proxy is provided, the returned url
38104 * will be the same as the provided url.
38106 makeSameOrigin: function(url, proxy) {
38107 var sameOrigin = url.indexOf("http") !== 0;
38108 var urlParts = !sameOrigin && url.match(this.URL_SPLIT_REGEX);
38110 var location = window.location;
38112 urlParts[1] == location.protocol &&
38113 urlParts[3] == location.hostname;
38114 var uPort = urlParts[4], lPort = location.port;
38115 if (uPort != 80 && uPort != "" || lPort != "80" && lPort != "") {
38116 sameOrigin = sameOrigin && uPort == lPort;
38121 if (typeof proxy == "function") {
38124 url = proxy + encodeURIComponent(url);
38133 * Create a new XMLHttpRequest object, open it, set any headers, bind
38134 * a callback to done state, and send any data. It is recommended that
38135 * you use one <GET>, <POST>, <PUT>, <DELETE>, <OPTIONS>, or <HEAD>.
38136 * This method is only documented to provide detail on the configuration
38137 * options available to all request methods.
38140 * config - {Object} Object containing properties for configuring the
38141 * request. Allowed configuration properties are described below.
38142 * This object is modified and should not be reused.
38144 * Allowed config properties:
38145 * method - {String} One of GET, POST, PUT, DELETE, HEAD, or
38146 * OPTIONS. Default is GET.
38147 * url - {String} URL for the request.
38148 * async - {Boolean} Open an asynchronous request. Default is true.
38149 * user - {String} User for relevant authentication scheme. Set
38150 * to null to clear current user.
38151 * password - {String} Password for relevant authentication scheme.
38152 * Set to null to clear current password.
38153 * proxy - {String} Optional proxy. Defaults to
38154 * <OpenLayers.ProxyHost>.
38155 * params - {Object} Any key:value pairs to be appended to the
38156 * url as a query string. Assumes url doesn't already include a query
38157 * string or hash. Typically, this is only appropriate for <GET>
38158 * requests where the query string will be appended to the url.
38159 * Parameter values that are arrays will be
38160 * concatenated with a comma (note that this goes against form-encoding)
38161 * as is done with <OpenLayers.Util.getParameterString>.
38162 * headers - {Object} Object with header:value pairs to be set on
38164 * data - {String | Document} Optional data to send with the request.
38165 * Typically, this is only used with <POST> and <PUT> requests.
38166 * Make sure to provide the appropriate "Content-Type" header for your
38167 * data. For <POST> and <PUT> requests, the content type defaults to
38168 * "application-xml". If your data is a different content type, or
38169 * if you are using a different HTTP method, set the "Content-Type"
38170 * header to match your data type.
38171 * callback - {Function} Function to call when request is done.
38172 * To determine if the request failed, check request.status (200
38173 * indicates success).
38174 * success - {Function} Optional function to call if request status is in
38175 * the 200s. This will be called in addition to callback above and
38176 * would typically only be used as an alternative.
38177 * failure - {Function} Optional function to call if request status is not
38178 * in the 200s. This will be called in addition to callback above and
38179 * would typically only be used as an alternative.
38180 * scope - {Object} If callback is a public method on some object,
38181 * set the scope to that object.
38184 * {XMLHttpRequest} Request object. To abort the request before a response
38185 * is received, call abort() on the request object.
38187 issue: function(config) {
38188 // apply default config - proxy host may have changed
38189 var defaultConfig = OpenLayers.Util.extend(
38190 this.DEFAULT_CONFIG,
38191 {proxy: OpenLayers.ProxyHost}
38193 config = config || {};
38194 config.headers = config.headers || {};
38195 config = OpenLayers.Util.applyDefaults(config, defaultConfig);
38196 config.headers = OpenLayers.Util.applyDefaults(config.headers, defaultConfig.headers);
38197 // Always set the "X-Requested-With" header to signal that this request
38198 // was issued through the XHR-object. Since header keys are case
38199 // insensitive and we want to allow overriding of the "X-Requested-With"
38200 // header through the user we cannot use applyDefaults, but have to
38201 // check manually whether we were called with a "X-Requested-With"
38203 var customRequestedWithHeader = false,
38205 for(headerKey in config.headers) {
38206 if (config.headers.hasOwnProperty( headerKey )) {
38207 if (headerKey.toLowerCase() === 'x-requested-with') {
38208 customRequestedWithHeader = true;
38212 if (customRequestedWithHeader === false) {
38213 // we did not have a custom "X-Requested-With" header
38214 config.headers['X-Requested-With'] = 'XMLHttpRequest';
38217 // create request, open, and set headers
38218 var request = new OpenLayers.Request.XMLHttpRequest();
38219 var url = OpenLayers.Util.urlAppend(config.url,
38220 OpenLayers.Util.getParameterString(config.params || {}));
38221 url = OpenLayers.Request.makeSameOrigin(url, config.proxy);
38223 config.method, url, config.async, config.user, config.password
38225 for(var header in config.headers) {
38226 request.setRequestHeader(header, config.headers[header]);
38229 var events = this.events;
38231 // we want to execute runCallbacks with "this" as the
38235 request.onreadystatechange = function() {
38236 if(request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) {
38237 var proceed = events.triggerEvent(
38239 {request: request, config: config, requestUrl: url}
38241 if(proceed !== false) {
38243 {request: request, config: config, requestUrl: url}
38249 // send request (optionally with data) and return
38250 // call in a timeout for asynchronous requests so the return is
38251 // available before readyState == 4 for cached docs
38252 if(config.async === false) {
38253 request.send(config.data);
38255 window.setTimeout(function(){
38256 if (request.readyState !== 0) { // W3C: 0-UNSENT
38257 request.send(config.data);
38265 * Method: runCallbacks
38266 * Calls the complete, success and failure callbacks. Application
38267 * can listen to the "complete" event, have the listener
38268 * display a confirm window and always return false, and
38269 * execute OpenLayers.Request.runCallbacks if the user
38270 * hits "yes" in the confirm window.
38273 * options - {Object} Hash containing request, config and requestUrl keys
38275 runCallbacks: function(options) {
38276 var request = options.request;
38277 var config = options.config;
38279 // bind callbacks to readyState 4 (done)
38280 var complete = (config.scope) ?
38281 OpenLayers.Function.bind(config.callback, config.scope) :
38284 // optional success callback
38286 if(config.success) {
38287 success = (config.scope) ?
38288 OpenLayers.Function.bind(config.success, config.scope) :
38292 // optional failure callback
38294 if(config.failure) {
38295 failure = (config.scope) ?
38296 OpenLayers.Function.bind(config.failure, config.scope) :
38300 if (OpenLayers.Util.createUrlObject(config.url).protocol == "file:" &&
38301 request.responseText) {
38302 request.status = 200;
38306 if (!request.status || (request.status >= 200 && request.status < 300)) {
38307 this.events.triggerEvent("success", options);
38312 if(request.status && (request.status < 200 || request.status >= 300)) {
38313 this.events.triggerEvent("failure", options);
38322 * Send an HTTP GET request. Additional configuration properties are
38323 * documented in the <issue> method, with the method property set
38327 * config - {Object} Object with properties for configuring the request.
38328 * See the <issue> method for documentation of allowed properties.
38329 * This object is modified and should not be reused.
38332 * {XMLHttpRequest} Request object.
38334 GET: function(config) {
38335 config = OpenLayers.Util.extend(config, {method: "GET"});
38336 return OpenLayers.Request.issue(config);
38341 * Send a POST request. Additional configuration properties are
38342 * documented in the <issue> method, with the method property set
38343 * to POST and "Content-Type" header set to "application/xml".
38346 * config - {Object} Object with properties for configuring the request.
38347 * See the <issue> method for documentation of allowed properties. The
38348 * default "Content-Type" header will be set to "application-xml" if
38349 * none is provided. This object is modified and should not be reused.
38352 * {XMLHttpRequest} Request object.
38354 POST: function(config) {
38355 config = OpenLayers.Util.extend(config, {method: "POST"});
38356 // set content type to application/xml if it isn't already set
38357 config.headers = config.headers ? config.headers : {};
38358 if(!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) {
38359 config.headers["Content-Type"] = "application/xml";
38361 return OpenLayers.Request.issue(config);
38366 * Send an HTTP PUT request. Additional configuration properties are
38367 * documented in the <issue> method, with the method property set
38368 * to PUT and "Content-Type" header set to "application/xml".
38371 * config - {Object} Object with properties for configuring the request.
38372 * See the <issue> method for documentation of allowed properties. The
38373 * default "Content-Type" header will be set to "application-xml" if
38374 * none is provided. This object is modified and should not be reused.
38377 * {XMLHttpRequest} Request object.
38379 PUT: function(config) {
38380 config = OpenLayers.Util.extend(config, {method: "PUT"});
38381 // set content type to application/xml if it isn't already set
38382 config.headers = config.headers ? config.headers : {};
38383 if(!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) {
38384 config.headers["Content-Type"] = "application/xml";
38386 return OpenLayers.Request.issue(config);
38390 * APIMethod: DELETE
38391 * Send an HTTP DELETE request. Additional configuration properties are
38392 * documented in the <issue> method, with the method property set
38396 * config - {Object} Object with properties for configuring the request.
38397 * See the <issue> method for documentation of allowed properties.
38398 * This object is modified and should not be reused.
38401 * {XMLHttpRequest} Request object.
38403 DELETE: function(config) {
38404 config = OpenLayers.Util.extend(config, {method: "DELETE"});
38405 return OpenLayers.Request.issue(config);
38410 * Send an HTTP HEAD request. Additional configuration properties are
38411 * documented in the <issue> method, with the method property set
38415 * config - {Object} Object with properties for configuring the request.
38416 * See the <issue> method for documentation of allowed properties.
38417 * This object is modified and should not be reused.
38420 * {XMLHttpRequest} Request object.
38422 HEAD: function(config) {
38423 config = OpenLayers.Util.extend(config, {method: "HEAD"});
38424 return OpenLayers.Request.issue(config);
38428 * APIMethod: OPTIONS
38429 * Send an HTTP OPTIONS request. Additional configuration properties are
38430 * documented in the <issue> method, with the method property set
38434 * config - {Object} Object with properties for configuring the request.
38435 * See the <issue> method for documentation of allowed properties.
38436 * This object is modified and should not be reused.
38439 * {XMLHttpRequest} Request object.
38441 OPTIONS: function(config) {
38442 config = OpenLayers.Util.extend(config, {method: "OPTIONS"});
38443 return OpenLayers.Request.issue(config);
38447 /* ======================================================================
38448 OpenLayers/Request/XMLHttpRequest.js
38449 ====================================================================== */
38451 // XMLHttpRequest.js Copyright (C) 2010 Sergey Ilinsky (http://www.ilinsky.com)
38453 // Licensed under the Apache License, Version 2.0 (the "License");
38454 // you may not use this file except in compliance with the License.
38455 // You may obtain a copy of the License at
38457 // http://www.apache.org/licenses/LICENSE-2.0
38459 // Unless required by applicable law or agreed to in writing, software
38460 // distributed under the License is distributed on an "AS IS" BASIS,
38461 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
38462 // See the License for the specific language governing permissions and
38463 // limitations under the License.
38466 * @requires OpenLayers/Request.js
38471 // Save reference to earlier defined object implementation (if any)
38472 var oXMLHttpRequest = window.XMLHttpRequest;
38474 // Define on browser type
38475 var bGecko = !!window.controllers,
38476 bIE = window.document.all && !window.opera,
38477 bIE7 = bIE && window.navigator.userAgent.match(/MSIE 7.0/);
38479 // Enables "XMLHttpRequest()" call next to "new XMLHttpReques()"
38480 function fXMLHttpRequest() {
38481 this._object = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject("Microsoft.XMLHTTP");
38482 this._listeners = [];
38486 function cXMLHttpRequest() {
38487 return new fXMLHttpRequest;
38489 cXMLHttpRequest.prototype = fXMLHttpRequest.prototype;
38491 // BUGFIX: Firefox with Firebug installed would break pages if not executed
38492 if (bGecko && oXMLHttpRequest.wrapped)
38493 cXMLHttpRequest.wrapped = oXMLHttpRequest.wrapped;
38496 cXMLHttpRequest.UNSENT = 0;
38497 cXMLHttpRequest.OPENED = 1;
38498 cXMLHttpRequest.HEADERS_RECEIVED = 2;
38499 cXMLHttpRequest.LOADING = 3;
38500 cXMLHttpRequest.DONE = 4;
38502 // Public Properties
38503 cXMLHttpRequest.prototype.readyState = cXMLHttpRequest.UNSENT;
38504 cXMLHttpRequest.prototype.responseText = '';
38505 cXMLHttpRequest.prototype.responseXML = null;
38506 cXMLHttpRequest.prototype.status = 0;
38507 cXMLHttpRequest.prototype.statusText = '';
38509 // Priority proposal
38510 cXMLHttpRequest.prototype.priority = "NORMAL";
38512 // Instance-level Events Handlers
38513 cXMLHttpRequest.prototype.onreadystatechange = null;
38515 // Class-level Events Handlers
38516 cXMLHttpRequest.onreadystatechange = null;
38517 cXMLHttpRequest.onopen = null;
38518 cXMLHttpRequest.onsend = null;
38519 cXMLHttpRequest.onabort = null;
38522 cXMLHttpRequest.prototype.open = function(sMethod, sUrl, bAsync, sUser, sPassword) {
38523 // Delete headers, required when object is reused
38524 delete this._headers;
38526 // When bAsync parameter value is omitted, use true as default
38527 if (arguments.length < 3)
38530 // Save async parameter for fixing Gecko bug with missing readystatechange in synchronous requests
38531 this._async = bAsync;
38533 // Set the onreadystatechange handler
38534 var oRequest = this,
38535 nState = this.readyState,
38538 // BUGFIX: IE - memory leak on page unload (inter-page leak)
38539 if (bIE && bAsync) {
38540 fOnUnload = function() {
38541 if (nState != cXMLHttpRequest.DONE) {
38542 fCleanTransport(oRequest);
38543 // Safe to abort here since onreadystatechange handler removed
38547 window.attachEvent("onunload", fOnUnload);
38550 // Add method sniffer
38551 if (cXMLHttpRequest.onopen)
38552 cXMLHttpRequest.onopen.apply(this, arguments);
38554 if (arguments.length > 4)
38555 this._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
38557 if (arguments.length > 3)
38558 this._object.open(sMethod, sUrl, bAsync, sUser);
38560 this._object.open(sMethod, sUrl, bAsync);
38562 this.readyState = cXMLHttpRequest.OPENED;
38563 fReadyStateChange(this);
38565 this._object.onreadystatechange = function() {
38566 if (bGecko && !bAsync)
38569 // Synchronize state
38570 oRequest.readyState = oRequest._object.readyState;
38573 fSynchronizeValues(oRequest);
38575 // BUGFIX: Firefox fires unnecessary DONE when aborting
38576 if (oRequest._aborted) {
38577 // Reset readyState to UNSENT
38578 oRequest.readyState = cXMLHttpRequest.UNSENT;
38584 if (oRequest.readyState == cXMLHttpRequest.DONE) {
38586 delete oRequest._data;
38588 fQueue_remove(oRequest);*/
38590 fCleanTransport(oRequest);
38591 // Uncomment this block if you need a fix for IE cache
38593 // BUGFIX: IE - cache issue
38594 if (!oRequest._object.getResponseHeader("Date")) {
38595 // Save object to cache
38596 oRequest._cached = oRequest._object;
38598 // Instantiate a new transport object
38599 cXMLHttpRequest.call(oRequest);
38604 oRequest._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
38606 oRequest._object.open(sMethod, sUrl, bAsync, sUser);
38609 oRequest._object.open(sMethod, sUrl, bAsync);
38610 oRequest._object.setRequestHeader("If-Modified-Since", oRequest._cached.getResponseHeader("Last-Modified") || new window.Date(0));
38611 // Copy headers set
38612 if (oRequest._headers)
38613 for (var sHeader in oRequest._headers)
38614 if (typeof oRequest._headers[sHeader] == "string") // Some frameworks prototype objects with functions
38615 oRequest._object.setRequestHeader(sHeader, oRequest._headers[sHeader]);
38617 oRequest._object.onreadystatechange = function() {
38618 // Synchronize state
38619 oRequest.readyState = oRequest._object.readyState;
38621 if (oRequest._aborted) {
38623 oRequest.readyState = cXMLHttpRequest.UNSENT;
38629 if (oRequest.readyState == cXMLHttpRequest.DONE) {
38631 fCleanTransport(oRequest);
38633 // get cached request
38634 if (oRequest.status == 304)
38635 oRequest._object = oRequest._cached;
38638 delete oRequest._cached;
38641 fSynchronizeValues(oRequest);
38644 fReadyStateChange(oRequest);
38646 // BUGFIX: IE - memory leak in interrupted
38648 window.detachEvent("onunload", fOnUnload);
38651 oRequest._object.send(null);
38653 // Return now - wait until re-sent request is finished
38657 // BUGFIX: IE - memory leak in interrupted
38659 window.detachEvent("onunload", fOnUnload);
38662 // BUGFIX: Some browsers (Internet Explorer, Gecko) fire OPEN readystate twice
38663 if (nState != oRequest.readyState)
38664 fReadyStateChange(oRequest);
38666 nState = oRequest.readyState;
38669 function fXMLHttpRequest_send(oRequest) {
38670 oRequest._object.send(oRequest._data);
38672 // BUGFIX: Gecko - missing readystatechange calls in synchronous requests
38673 if (bGecko && !oRequest._async) {
38674 oRequest.readyState = cXMLHttpRequest.OPENED;
38676 // Synchronize state
38677 fSynchronizeValues(oRequest);
38679 // Simulate missing states
38680 while (oRequest.readyState < cXMLHttpRequest.DONE) {
38681 oRequest.readyState++;
38682 fReadyStateChange(oRequest);
38683 // Check if we are aborted
38684 if (oRequest._aborted)
38689 cXMLHttpRequest.prototype.send = function(vData) {
38690 // Add method sniffer
38691 if (cXMLHttpRequest.onsend)
38692 cXMLHttpRequest.onsend.apply(this, arguments);
38694 if (!arguments.length)
38697 // BUGFIX: Safari - fails sending documents created/modified dynamically, so an explicit serialization required
38698 // BUGFIX: IE - rewrites any custom mime-type to "text/xml" in case an XMLNode is sent
38699 // BUGFIX: Gecko - fails sending Element (this is up to the implementation either to standard)
38700 if (vData && vData.nodeType) {
38701 vData = window.XMLSerializer ? new window.XMLSerializer().serializeToString(vData) : vData.xml;
38702 if (!this._headers["Content-Type"])
38703 this._object.setRequestHeader("Content-Type", "application/xml");
38706 this._data = vData;
38712 fXMLHttpRequest_send(this);
38714 cXMLHttpRequest.prototype.abort = function() {
38715 // Add method sniffer
38716 if (cXMLHttpRequest.onabort)
38717 cXMLHttpRequest.onabort.apply(this, arguments);
38719 // BUGFIX: Gecko - unnecessary DONE when aborting
38720 if (this.readyState > cXMLHttpRequest.UNSENT)
38721 this._aborted = true;
38723 this._object.abort();
38725 // BUGFIX: IE - memory leak
38726 fCleanTransport(this);
38728 this.readyState = cXMLHttpRequest.UNSENT;
38731 /* if (this._async)
38732 fQueue_remove(this);*/
38734 cXMLHttpRequest.prototype.getAllResponseHeaders = function() {
38735 return this._object.getAllResponseHeaders();
38737 cXMLHttpRequest.prototype.getResponseHeader = function(sName) {
38738 return this._object.getResponseHeader(sName);
38740 cXMLHttpRequest.prototype.setRequestHeader = function(sName, sValue) {
38741 // BUGFIX: IE - cache issue
38742 if (!this._headers)
38743 this._headers = {};
38744 this._headers[sName] = sValue;
38746 return this._object.setRequestHeader(sName, sValue);
38749 // EventTarget interface implementation
38750 cXMLHttpRequest.prototype.addEventListener = function(sName, fHandler, bUseCapture) {
38751 for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
38752 if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)
38755 this._listeners.push([sName, fHandler, bUseCapture]);
38758 cXMLHttpRequest.prototype.removeEventListener = function(sName, fHandler, bUseCapture) {
38759 for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
38760 if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)
38764 this._listeners.splice(nIndex, 1);
38767 cXMLHttpRequest.prototype.dispatchEvent = function(oEvent) {
38768 var oEventPseudo = {
38769 'type': oEvent.type,
38771 'currentTarget':this,
38773 'bubbles': oEvent.bubbles,
38774 'cancelable': oEvent.cancelable,
38775 'timeStamp': oEvent.timeStamp,
38776 'stopPropagation': function() {}, // There is no flow
38777 'preventDefault': function() {}, // There is no default action
38778 'initEvent': function() {} // Original event object should be initialized
38781 // Execute onreadystatechange
38782 if (oEventPseudo.type == "readystatechange" && this.onreadystatechange)
38783 (this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEventPseudo]);
38785 // Execute listeners
38786 for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
38787 if (oListener[0] == oEventPseudo.type && !oListener[2])
38788 (oListener[1].handleEvent || oListener[1]).apply(this, [oEventPseudo]);
38792 cXMLHttpRequest.prototype.toString = function() {
38793 return '[' + "object" + ' ' + "XMLHttpRequest" + ']';
38796 cXMLHttpRequest.toString = function() {
38797 return '[' + "XMLHttpRequest" + ']';
38801 function fReadyStateChange(oRequest) {
38803 if (cXMLHttpRequest.onreadystatechange)
38804 cXMLHttpRequest.onreadystatechange.apply(oRequest);
38807 oRequest.dispatchEvent({
38808 'type': "readystatechange",
38810 'cancelable': false,
38811 'timeStamp': new Date + 0
38815 function fGetDocument(oRequest) {
38816 var oDocument = oRequest.responseXML,
38817 sResponse = oRequest.responseText;
38818 // Try parsing responseText
38819 if (bIE && sResponse && oDocument && !oDocument.documentElement && oRequest.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)) {
38820 oDocument = new window.ActiveXObject("Microsoft.XMLDOM");
38821 oDocument.async = false;
38822 oDocument.validateOnParse = false;
38823 oDocument.loadXML(sResponse);
38825 // Check if there is no error in document
38827 if ((bIE && oDocument.parseError != 0) || !oDocument.documentElement || (oDocument.documentElement && oDocument.documentElement.tagName == "parsererror"))
38832 function fSynchronizeValues(oRequest) {
38833 try { oRequest.responseText = oRequest._object.responseText; } catch (e) {}
38834 try { oRequest.responseXML = fGetDocument(oRequest._object); } catch (e) {}
38835 try { oRequest.status = oRequest._object.status; } catch (e) {}
38836 try { oRequest.statusText = oRequest._object.statusText; } catch (e) {}
38839 function fCleanTransport(oRequest) {
38840 // BUGFIX: IE - memory leak (on-page leak)
38841 oRequest._object.onreadystatechange = new window.Function;
38845 var oQueuePending = {"CRITICAL":[],"HIGH":[],"NORMAL":[],"LOW":[],"LOWEST":[]},
38846 aQueueRunning = [];
38847 function fQueue_add(oRequest) {
38848 oQueuePending[oRequest.priority in oQueuePending ? oRequest.priority : "NORMAL"].push(oRequest);
38850 setTimeout(fQueue_process);
38853 function fQueue_remove(oRequest) {
38854 for (var nIndex = 0, bFound = false; nIndex < aQueueRunning.length; nIndex++)
38856 aQueueRunning[nIndex - 1] = aQueueRunning[nIndex];
38858 if (aQueueRunning[nIndex] == oRequest)
38861 aQueueRunning.length--;
38863 setTimeout(fQueue_process);
38866 function fQueue_process() {
38867 if (aQueueRunning.length < 6) {
38868 for (var sPriority in oQueuePending) {
38869 if (oQueuePending[sPriority].length) {
38870 var oRequest = oQueuePending[sPriority][0];
38871 oQueuePending[sPriority] = oQueuePending[sPriority].slice(1);
38873 aQueueRunning.push(oRequest);
38875 fXMLHttpRequest_send(oRequest);
38882 // Internet Explorer 5.0 (missing apply)
38883 if (!window.Function.prototype.apply) {
38884 window.Function.prototype.apply = function(oRequest, oArguments) {
38887 oRequest.__func = this;
38888 oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]);
38889 delete oRequest.__func;
38893 // Register new object with window
38895 * Class: OpenLayers.Request.XMLHttpRequest
38896 * Standard-compliant (W3C) cross-browser implementation of the
38897 * XMLHttpRequest object. From
38898 * http://code.google.com/p/xmlhttprequest/.
38900 if (!OpenLayers.Request) {
38902 * This allows for OpenLayers/Request.js to be included
38903 * before or after this script.
38905 OpenLayers.Request = {};
38907 OpenLayers.Request.XMLHttpRequest = cXMLHttpRequest;
38909 /* ======================================================================
38910 OpenLayers/Lang/de.js
38911 ====================================================================== */
38913 /* Translators (2009 onwards):
38920 * @requires OpenLayers/Lang.js
38924 * Namespace: OpenLayers.Lang["de"]
38925 * Dictionary for Deutsch. Keys for entries are used in calls to
38926 * <OpenLayers.Lang.translate>. Entry bodies are normal strings or
38927 * strings formatted for use with <OpenLayers.String.format> calls.
38929 OpenLayers.Lang["de"] = OpenLayers.Util.applyDefaults({
38931 'unhandledRequest': "Unbehandelte Anfragerückmeldung ${statusText}",
38933 'Permalink': "Permalink",
38935 'Overlays': "Overlays",
38937 'Base Layer': "Grundkarte",
38939 'noFID': "Ein Feature, für das keine FID existiert, kann nicht aktualisiert werden.",
38941 'browserNotSupported': "Ihr Browser unterstützt keine Vektordarstellung. Aktuell unterstützte Renderer:\n${renderers}",
38943 '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.",
38945 'commitSuccess': "WFS-Transaktion: Erfolgreich ${response}",
38947 'commitFailed': "WFS-Transaktion: Fehlgeschlagen ${response}",
38949 '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",
38951 '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",
38953 'Scale = 1 : ${scaleDenom}': "Maßstab = 1 : ${scaleDenom}",
38963 '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.",
38965 'methodDeprecated': "Die Methode ist veraltet und wird in 3.0 entfernt. Bitte verwende stattdessen ${newMethod}."
38968 /* ======================================================================
38969 OpenLayers/Popup/FramedCloud.js
38970 ====================================================================== */
38972 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
38973 * full list of contributors). Published under the 2-clause BSD license.
38974 * See license.txt in the OpenLayers distribution or repository for the
38975 * full text of the license. */
38978 * @requires OpenLayers/Popup/Framed.js
38979 * @requires OpenLayers/Util.js
38980 * @requires OpenLayers/BaseTypes/Bounds.js
38981 * @requires OpenLayers/BaseTypes/Pixel.js
38982 * @requires OpenLayers/BaseTypes/Size.js
38986 * Class: OpenLayers.Popup.FramedCloud
38989 * - <OpenLayers.Popup.Framed>
38991 OpenLayers.Popup.FramedCloud =
38992 OpenLayers.Class(OpenLayers.Popup.Framed, {
38995 * Property: contentDisplayClass
38996 * {String} The CSS class of the popup content div.
38998 contentDisplayClass: "olFramedCloudPopupContent",
39001 * APIProperty: autoSize
39002 * {Boolean} Framed Cloud is autosizing by default.
39007 * APIProperty: panMapIfOutOfView
39008 * {Boolean} Framed Cloud does pan into view by default.
39010 panMapIfOutOfView: true,
39013 * APIProperty: imageSize
39014 * {<OpenLayers.Size>}
39016 imageSize: new OpenLayers.Size(1276, 736),
39019 * APIProperty: isAlphaImage
39020 * {Boolean} The FramedCloud does not use an alpha image (in honor of the
39021 * good ie6 folk out there)
39023 isAlphaImage: false,
39026 * APIProperty: fixedRelativePosition
39027 * {Boolean} The Framed Cloud popup works in just one fixed position.
39029 fixedRelativePosition: false,
39032 * Property: positionBlocks
39033 * {Object} Hash of differen position blocks, keyed by relativePosition
39034 * two-character code string (ie "tl", "tr", "bl", "br")
39038 'offset': new OpenLayers.Pixel(44, 0),
39039 'padding': new OpenLayers.Bounds(8, 40, 8, 9),
39042 size: new OpenLayers.Size('auto', 'auto'),
39043 anchor: new OpenLayers.Bounds(0, 51, 22, 0),
39044 position: new OpenLayers.Pixel(0, 0)
39047 size: new OpenLayers.Size(22, 'auto'),
39048 anchor: new OpenLayers.Bounds(null, 50, 0, 0),
39049 position: new OpenLayers.Pixel(-1238, 0)
39052 size: new OpenLayers.Size('auto', 19),
39053 anchor: new OpenLayers.Bounds(0, 32, 22, null),
39054 position: new OpenLayers.Pixel(0, -631)
39057 size: new OpenLayers.Size(22, 18),
39058 anchor: new OpenLayers.Bounds(null, 32, 0, null),
39059 position: new OpenLayers.Pixel(-1238, -632)
39062 size: new OpenLayers.Size(81, 35),
39063 anchor: new OpenLayers.Bounds(null, 0, 0, null),
39064 position: new OpenLayers.Pixel(0, -688)
39069 'offset': new OpenLayers.Pixel(-45, 0),
39070 'padding': new OpenLayers.Bounds(8, 40, 8, 9),
39073 size: new OpenLayers.Size('auto', 'auto'),
39074 anchor: new OpenLayers.Bounds(0, 51, 22, 0),
39075 position: new OpenLayers.Pixel(0, 0)
39078 size: new OpenLayers.Size(22, 'auto'),
39079 anchor: new OpenLayers.Bounds(null, 50, 0, 0),
39080 position: new OpenLayers.Pixel(-1238, 0)
39083 size: new OpenLayers.Size('auto', 19),
39084 anchor: new OpenLayers.Bounds(0, 32, 22, null),
39085 position: new OpenLayers.Pixel(0, -631)
39088 size: new OpenLayers.Size(22, 19),
39089 anchor: new OpenLayers.Bounds(null, 32, 0, null),
39090 position: new OpenLayers.Pixel(-1238, -631)
39093 size: new OpenLayers.Size(81, 35),
39094 anchor: new OpenLayers.Bounds(0, 0, null, null),
39095 position: new OpenLayers.Pixel(-215, -687)
39100 'offset': new OpenLayers.Pixel(45, 0),
39101 'padding': new OpenLayers.Bounds(8, 9, 8, 40),
39104 size: new OpenLayers.Size('auto', 'auto'),
39105 anchor: new OpenLayers.Bounds(0, 21, 22, 32),
39106 position: new OpenLayers.Pixel(0, 0)
39109 size: new OpenLayers.Size(22, 'auto'),
39110 anchor: new OpenLayers.Bounds(null, 21, 0, 32),
39111 position: new OpenLayers.Pixel(-1238, 0)
39114 size: new OpenLayers.Size('auto', 21),
39115 anchor: new OpenLayers.Bounds(0, 0, 22, null),
39116 position: new OpenLayers.Pixel(0, -629)
39119 size: new OpenLayers.Size(22, 21),
39120 anchor: new OpenLayers.Bounds(null, 0, 0, null),
39121 position: new OpenLayers.Pixel(-1238, -629)
39124 size: new OpenLayers.Size(81, 33),
39125 anchor: new OpenLayers.Bounds(null, null, 0, 0),
39126 position: new OpenLayers.Pixel(-101, -674)
39131 'offset': new OpenLayers.Pixel(-44, 0),
39132 'padding': new OpenLayers.Bounds(8, 9, 8, 40),
39135 size: new OpenLayers.Size('auto', 'auto'),
39136 anchor: new OpenLayers.Bounds(0, 21, 22, 32),
39137 position: new OpenLayers.Pixel(0, 0)
39140 size: new OpenLayers.Size(22, 'auto'),
39141 anchor: new OpenLayers.Bounds(null, 21, 0, 32),
39142 position: new OpenLayers.Pixel(-1238, 0)
39145 size: new OpenLayers.Size('auto', 21),
39146 anchor: new OpenLayers.Bounds(0, 0, 22, null),
39147 position: new OpenLayers.Pixel(0, -629)
39150 size: new OpenLayers.Size(22, 21),
39151 anchor: new OpenLayers.Bounds(null, 0, 0, null),
39152 position: new OpenLayers.Pixel(-1238, -629)
39155 size: new OpenLayers.Size(81, 33),
39156 anchor: new OpenLayers.Bounds(0, null, null, 0),
39157 position: new OpenLayers.Pixel(-311, -674)
39164 * APIProperty: minSize
39165 * {<OpenLayers.Size>}
39167 minSize: new OpenLayers.Size(105, 10),
39170 * APIProperty: maxSize
39171 * {<OpenLayers.Size>}
39173 maxSize: new OpenLayers.Size(1200, 660),
39176 * Constructor: OpenLayers.Popup.FramedCloud
39180 * lonlat - {<OpenLayers.LonLat>}
39181 * contentSize - {<OpenLayers.Size>}
39182 * contentHTML - {String}
39183 * anchor - {Object} Object to which we'll anchor the popup. Must expose
39184 * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>)
39185 * (Note that this is generally an <OpenLayers.Icon>).
39186 * closeBox - {Boolean}
39187 * closeBoxCallback - {Function} Function to be called on closeBox click.
39189 initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
39190 closeBoxCallback) {
39192 this.imageSrc = OpenLayers.Util.getImageLocation('cloud-popup-relative.png');
39193 OpenLayers.Popup.Framed.prototype.initialize.apply(this, arguments);
39194 this.contentDiv.className = this.contentDisplayClass;
39197 CLASS_NAME: "OpenLayers.Popup.FramedCloud"
39199 /* ======================================================================
39201 ====================================================================== */
39203 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
39204 * full list of contributors). Published under the 2-clause BSD license.
39205 * See license.txt in the OpenLayers distribution or repository for the
39206 * full text of the license. */
39210 * @requires OpenLayers/BaseTypes/Class.js
39211 * @requires OpenLayers/Util.js
39212 * @requires OpenLayers/Style.js
39216 * Class: OpenLayers.Rule
39217 * This class represents an SLD Rule, as being used for rule-based SLD styling.
39219 OpenLayers.Rule = OpenLayers.Class({
39223 * {String} A unique id for this session.
39228 * APIProperty: name
39229 * {String} name of this rule
39235 * {String} Title of this rule (set if included in SLD)
39240 * Property: description
39241 * {String} Description of this rule (set if abstract is included in SLD)
39246 * Property: context
39247 * {Object} An optional object with properties that the rule should be
39248 * evaluated against. If no context is specified, feature.attributes will
39255 * {<OpenLayers.Filter>} Optional filter for the rule.
39260 * Property: elseFilter
39261 * {Boolean} Determines whether this rule is only to be applied only if
39262 * no other rules match (ElseFilter according to the SLD specification).
39263 * Default is false. For instances of OpenLayers.Rule, if elseFilter is
39264 * false, the rule will always apply. For subclasses, the else property is
39270 * Property: symbolizer
39271 * {Object} Symbolizer or hash of symbolizers for this rule. If hash of
39272 * symbolizers, keys are one or more of ["Point", "Line", "Polygon"]. The
39273 * latter if useful if it is required to style e.g. vertices of a line
39274 * with a point symbolizer. Note, however, that this is not implemented
39275 * yet in OpenLayers, but it is the way how symbolizers are defined in
39281 * Property: symbolizers
39282 * {Array} Collection of symbolizers associated with this rule. If
39283 * provided at construction, the symbolizers array has precedence
39284 * over the deprecated symbolizer property. Note that multiple
39285 * symbolizers are not currently supported by the vector renderers.
39286 * Rules with multiple symbolizers are currently only useful for
39287 * maintaining elements in an SLD document.
39292 * APIProperty: minScaleDenominator
39293 * {Number} or {String} minimum scale at which to draw the feature.
39294 * In the case of a String, this can be a combination of text and
39295 * propertyNames in the form "literal ${propertyName}"
39297 minScaleDenominator: null,
39300 * APIProperty: maxScaleDenominator
39301 * {Number} or {String} maximum scale at which to draw the feature.
39302 * In the case of a String, this can be a combination of text and
39303 * propertyNames in the form "literal ${propertyName}"
39305 maxScaleDenominator: null,
39308 * Constructor: OpenLayers.Rule
39312 * options - {Object} An optional object with properties to set on the
39316 * {<OpenLayers.Rule>}
39318 initialize: function(options) {
39319 this.symbolizer = {};
39320 OpenLayers.Util.extend(this, options);
39321 if (this.symbolizers) {
39322 delete this.symbolizer;
39324 this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
39328 * APIMethod: destroy
39329 * nullify references to prevent circular references and memory leaks
39331 destroy: function() {
39332 for (var i in this.symbolizer) {
39333 this.symbolizer[i] = null;
39335 this.symbolizer = null;
39336 delete this.symbolizers;
39340 * APIMethod: evaluate
39341 * evaluates this rule for a specific feature
39344 * feature - {<OpenLayers.Feature>} feature to apply the rule to.
39347 * {Boolean} true if the rule applies, false if it does not.
39348 * This rule is the default rule and always returns true.
39350 evaluate: function(feature) {
39351 var context = this.getContext(feature);
39352 var applies = true;
39354 if (this.minScaleDenominator || this.maxScaleDenominator) {
39355 var scale = feature.layer.map.getScale();
39358 // check if within minScale/maxScale bounds
39359 if (this.minScaleDenominator) {
39360 applies = scale >= OpenLayers.Style.createLiteral(
39361 this.minScaleDenominator, context);
39363 if (applies && this.maxScaleDenominator) {
39364 applies = scale < OpenLayers.Style.createLiteral(
39365 this.maxScaleDenominator, context);
39368 // check if optional filter applies
39369 if(applies && this.filter) {
39370 // feature id filters get the feature, others get the context
39371 if(this.filter.CLASS_NAME == "OpenLayers.Filter.FeatureId") {
39372 applies = this.filter.evaluate(feature);
39374 applies = this.filter.evaluate(context);
39382 * Method: getContext
39383 * Gets the context for evaluating this rule
39386 * feature - {<OpenLayers.Feature>} feature to take the context from if
39387 * none is specified.
39389 getContext: function(feature) {
39390 var context = this.context;
39392 context = feature.attributes || feature.data;
39394 if (typeof this.context == "function") {
39395 context = this.context(feature);
39402 * Clones this rule.
39405 * {<OpenLayers.Rule>} Clone of this rule.
39407 clone: function() {
39408 var options = OpenLayers.Util.extend({}, this);
39409 if (this.symbolizers) {
39410 // clone symbolizers
39411 var len = this.symbolizers.length;
39412 options.symbolizers = new Array(len);
39413 for (var i=0; i<len; ++i) {
39414 options.symbolizers[i] = this.symbolizers[i].clone();
39417 // clone symbolizer
39418 options.symbolizer = {};
39420 for(var key in this.symbolizer) {
39421 value = this.symbolizer[key];
39422 type = typeof value;
39423 if(type === "object") {
39424 options.symbolizer[key] = OpenLayers.Util.extend({}, value);
39425 } else if(type === "string") {
39426 options.symbolizer[key] = value;
39431 options.filter = this.filter && this.filter.clone();
39433 options.context = this.context && OpenLayers.Util.extend({}, this.context);
39434 return new OpenLayers.Rule(options);
39437 CLASS_NAME: "OpenLayers.Rule"
39439 /* ======================================================================
39440 OpenLayers/Handler/Pinch.js
39441 ====================================================================== */
39443 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
39444 * full list of contributors). Published under the 2-clause BSD license.
39445 * See license.txt in the OpenLayers distribution or repository for the
39446 * full text of the license. */
39449 * @requires OpenLayers/Handler.js
39453 * Class: OpenLayers.Handler.Pinch
39454 * The pinch handler is used to deal with sequences of browser events related
39455 * to pinch gestures. The handler is used by controls that want to know
39456 * when a pinch sequence begins, when a pinch is happening, and when it has
39459 * Controls that use the pinch handler typically construct it with callbacks
39460 * for 'start', 'move', and 'done'. Callbacks for these keys are
39461 * called when the pinch begins, with each change, and when the pinch is
39464 * Create a new pinch handler with the <OpenLayers.Handler.Pinch> constructor.
39467 * - <OpenLayers.Handler>
39469 OpenLayers.Handler.Pinch = OpenLayers.Class(OpenLayers.Handler, {
39472 * Property: started
39473 * {Boolean} When a touchstart event is received, we want to record it,
39474 * but not set 'pinching' until the touchmove get started after
39480 * Property: stopDown
39481 * {Boolean} Stop propagation of touchstart events from getting to
39482 * listeners on the same element. Default is false.
39487 * Property: pinching
39494 * {Object} Object that store informations related to pinch last touch.
39500 * {Object} Object that store informations related to pinch touchstart.
39505 * Constructor: OpenLayers.Handler.Pinch
39506 * Returns OpenLayers.Handler.Pinch
39509 * control - {<OpenLayers.Control>} The control that is making use of
39510 * this handler. If a handler is being used without a control, the
39511 * handlers setMap method must be overridden to deal properly with
39513 * callbacks - {Object} An object containing functions to be called when
39514 * the pinch operation start, change, or is finished. The callbacks
39515 * should expect to receive an object argument, which contains
39516 * information about scale, distance, and position of touch points.
39517 * options - {Object}
39521 * Method: touchstart
39522 * Handle touchstart events
39528 * {Boolean} Let the event propagate.
39530 touchstart: function(evt) {
39531 var propagate = true;
39532 this.pinching = false;
39533 if (OpenLayers.Event.isMultiTouch(evt)) {
39534 this.started = true;
39535 this.last = this.start = {
39536 distance: this.getDistance(evt.touches),
39540 this.callback("start", [evt, this.start]);
39541 propagate = !this.stopDown;
39542 } else if (this.started) {
39543 // Some webkit versions send fake single-touch events during
39544 // multitouch, which cause the drag handler to trigger
39547 this.started = false;
39551 // prevent document dragging
39552 OpenLayers.Event.preventDefault(evt);
39557 * Method: touchmove
39558 * Handle touchmove events
39564 * {Boolean} Let the event propagate.
39566 touchmove: function(evt) {
39567 if (this.started && OpenLayers.Event.isMultiTouch(evt)) {
39568 this.pinching = true;
39569 var current = this.getPinchData(evt);
39570 this.callback("move", [evt, current]);
39571 this.last = current;
39572 // prevent document dragging
39573 OpenLayers.Event.stop(evt);
39574 } else if (this.started) {
39575 // Some webkit versions send fake single-touch events during
39576 // multitouch, which cause the drag handler to trigger
39584 * Handle touchend events
39590 * {Boolean} Let the event propagate.
39592 touchend: function(evt) {
39593 if (this.started && !OpenLayers.Event.isMultiTouch(evt)) {
39594 this.started = false;
39595 this.pinching = false;
39596 this.callback("done", [evt, this.start, this.last]);
39606 * Activate the handler.
39609 * {Boolean} The handler was successfully activated.
39611 activate: function() {
39612 var activated = false;
39613 if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
39614 this.pinching = false;
39621 * Method: deactivate
39622 * Deactivate the handler.
39625 * {Boolean} The handler was successfully deactivated.
39627 deactivate: function() {
39628 var deactivated = false;
39629 if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
39630 this.started = false;
39631 this.pinching = false;
39634 deactivated = true;
39636 return deactivated;
39640 * Method: getDistance
39641 * Get the distance in pixels between two touches.
39644 * touches - {Array(Object)}
39647 * {Number} The distance in pixels.
39649 getDistance: function(touches) {
39650 var t0 = touches[0];
39651 var t1 = touches[1];
39653 Math.pow(t0.olClientX - t1.olClientX, 2) +
39654 Math.pow(t0.olClientY - t1.olClientY, 2)
39660 * Method: getPinchData
39661 * Get informations about the pinch event.
39667 * {Object} Object that contains data about the current pinch.
39669 getPinchData: function(evt) {
39670 var distance = this.getDistance(evt.touches);
39671 var scale = distance / this.start.distance;
39673 distance: distance,
39674 delta: this.last.distance - distance,
39679 CLASS_NAME: "OpenLayers.Handler.Pinch"
39682 /* ======================================================================
39683 OpenLayers/Lang/en.js
39684 ====================================================================== */
39687 * @requires OpenLayers/Lang.js
39691 * Namespace: OpenLayers.Lang["en"]
39692 * Dictionary for English. Keys for entries are used in calls to
39693 * <OpenLayers.Lang.translate>. Entry bodies are normal strings or
39694 * strings formatted for use with <OpenLayers.String.format> calls.
39696 OpenLayers.Lang.en = {
39698 'unhandledRequest': "Unhandled request return ${statusText}",
39700 'Permalink': "Permalink",
39702 'Overlays': "Overlays",
39704 'Base Layer': "Base Layer",
39706 'noFID': "Can't update a feature for which there is no FID.",
39708 'browserNotSupported':
39709 "Your browser does not support vector rendering. Currently supported renderers are:\n${renderers}",
39712 'minZoomLevelError':
39713 "The minZoomLevel property is only intended for use " +
39714 "with the FixedZoomLevels-descendent layers. That this " +
39715 "wfs layer checks for minZoomLevel is a relic of the" +
39716 "past. We cannot, however, remove it without possibly " +
39717 "breaking OL based applications that may depend on it." +
39718 " Therefore we are deprecating it -- the minZoomLevel " +
39719 "check below will be removed at 3.0. Please instead " +
39720 "use min/max resolution setting as described here: " +
39721 "http://trac.openlayers.org/wiki/SettingZoomLevels",
39723 'commitSuccess': "WFS Transaction: SUCCESS ${response}",
39725 'commitFailed': "WFS Transaction: FAILED ${response}",
39728 "The Google Layer was unable to load correctly.<br><br>" +
39729 "To get rid of this message, select a new BaseLayer " +
39730 "in the layer switcher in the upper-right corner.<br><br>" +
39731 "Most likely, this is because the Google Maps library " +
39732 "script was either not included, or does not contain the " +
39733 "correct API key for your site.<br><br>" +
39734 "Developers: For help getting this working correctly, " +
39735 "<a href='http://trac.openlayers.org/wiki/Google' " +
39736 "target='_blank'>click here</a>",
39739 "The ${layerType} Layer was unable to load correctly.<br><br>" +
39740 "To get rid of this message, select a new BaseLayer " +
39741 "in the layer switcher in the upper-right corner.<br><br>" +
39742 "Most likely, this is because the ${layerLib} library " +
39743 "script was not correctly included.<br><br>" +
39744 "Developers: For help getting this working correctly, " +
39745 "<a href='http://trac.openlayers.org/wiki/${layerLib}' " +
39746 "target='_blank'>click here</a>",
39748 'Scale = 1 : ${scaleDenom}': "Scale = 1 : ${scaleDenom}",
39750 //labels for the graticule control
39755 'Graticule': 'Graticule',
39758 'reprojectDeprecated':
39759 "You are using the 'reproject' option " +
39760 "on the ${layerName} layer. This option is deprecated: " +
39761 "its use was designed to support displaying data over commercial " +
39762 "basemaps, but that functionality should now be achieved by using " +
39763 "Spherical Mercator support. More information is available from " +
39764 "http://trac.openlayers.org/wiki/SphericalMercator.",
39767 'methodDeprecated':
39768 "This method has been deprecated and will be removed in 3.0. " +
39769 "Please use ${newMethod} instead.",
39775 /* ======================================================================
39776 OpenLayers/Control/PinchZoom.js
39777 ====================================================================== */
39779 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
39780 * full list of contributors). Published under the 2-clause BSD license.
39781 * See license.txt in the OpenLayers distribution or repository for the
39782 * full text of the license. */
39785 * @requires OpenLayers/Handler/Pinch.js
39789 * Class: OpenLayers.Control.PinchZoom
39792 * - <OpenLayers.Control>
39794 OpenLayers.Control.PinchZoom = OpenLayers.Class(OpenLayers.Control, {
39798 * {OpenLayers.Control.TYPES}
39800 type: OpenLayers.Control.TYPE_TOOL,
39803 * Property: pinchOrigin
39804 * {Object} Cached object representing the pinch start (in pixels).
39809 * Property: currentCenter
39810 * {Object} Cached object representing the latest pinch center (in pixels).
39812 currentCenter: null,
39815 * APIProperty: autoActivate
39816 * {Boolean} Activate the control when it is added to a map. Default is
39819 autoActivate: true,
39822 * APIProperty: preserveCenter
39823 * {Boolean} Set this to true if you don't want the map center to change
39824 * while pinching. For example you may want to set preserveCenter to
39825 * true when the user location is being watched and you want to preserve
39826 * the user location at the center of the map even if he zooms in or
39827 * out using pinch. This property's value can be changed any time on an
39828 * existing instance. Default is false.
39830 preserveCenter: false,
39833 * APIProperty: handlerOptions
39834 * {Object} Used to set non-default properties on the pinch handler
39838 * Constructor: OpenLayers.Control.PinchZoom
39839 * Create a control for zooming with pinch gestures. This works on devices
39840 * with multi-touch support.
39843 * options - {Object} An optional object whose properties will be set on
39846 initialize: function(options) {
39847 OpenLayers.Control.prototype.initialize.apply(this, arguments);
39848 this.handler = new OpenLayers.Handler.Pinch(this, {
39849 start: this.pinchStart,
39850 move: this.pinchMove,
39851 done: this.pinchDone
39852 }, this.handlerOptions);
39856 * Method: pinchStart
39860 * pinchData - {Object} pinch data object related to the current touchmove
39861 * of the pinch gesture. This give us the current scale of the pinch.
39863 pinchStart: function(evt, pinchData) {
39864 var xy = (this.preserveCenter) ?
39865 this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy;
39866 this.pinchOrigin = xy;
39867 this.currentCenter = xy;
39871 * Method: pinchMove
39875 * pinchData - {Object} pinch data object related to the current touchmove
39876 * of the pinch gesture. This give us the current scale of the pinch.
39878 pinchMove: function(evt, pinchData) {
39879 var scale = pinchData.scale;
39880 var containerOrigin = this.map.layerContainerOriginPx;
39881 var pinchOrigin = this.pinchOrigin;
39882 var current = (this.preserveCenter) ?
39883 this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy;
39885 var dx = Math.round((containerOrigin.x + current.x - pinchOrigin.x) + (scale - 1) * (containerOrigin.x - pinchOrigin.x));
39886 var dy = Math.round((containerOrigin.y + current.y - pinchOrigin.y) + (scale - 1) * (containerOrigin.y - pinchOrigin.y));
39888 this.map.applyTransform(dx, dy, scale);
39889 this.currentCenter = current;
39893 * Method: pinchDone
39897 * start - {Object} pinch data object related to the touchstart event that
39898 * started the pinch gesture.
39899 * last - {Object} pinch data object related to the last touchmove event
39900 * of the pinch gesture. This give us the final scale of the pinch.
39902 pinchDone: function(evt, start, last) {
39903 this.map.applyTransform();
39904 var zoom = this.map.getZoomForResolution(this.map.getResolution() / last.scale, true);
39905 if (zoom !== this.map.getZoom() || !this.currentCenter.equals(this.pinchOrigin)) {
39906 var resolution = this.map.getResolutionForZoom(zoom);
39908 var location = this.map.getLonLatFromPixel(this.pinchOrigin);
39909 var zoomPixel = this.currentCenter;
39910 var size = this.map.getSize();
39912 location.lon += resolution * ((size.w / 2) - zoomPixel.x);
39913 location.lat -= resolution * ((size.h / 2) - zoomPixel.y);
39915 // Force a reflow before calling setCenter. This is to work
39916 // around an issue occuring in iOS.
39918 // See https://github.com/openlayers/openlayers/pull/351.
39920 // Without a reflow setting the layer container div's top left
39921 // style properties to "0px" - as done in Map.moveTo when zoom
39922 // is changed - won't actually correctly reposition the layer
39925 // Also, we need to use a statement that the Google Closure
39926 // compiler won't optimize away.
39927 this.map.div.clientWidth = this.map.div.clientWidth;
39929 this.map.setCenter(location, zoom);
39933 CLASS_NAME: "OpenLayers.Control.PinchZoom"
39936 /* ======================================================================
39937 OpenLayers/Control/TouchNavigation.js
39938 ====================================================================== */
39940 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
39941 * full list of contributors). Published under the 2-clause BSD license.
39942 * See license.txt in the OpenLayers distribution or repository for the
39943 * full text of the license. */
39946 * @requires OpenLayers/Control/DragPan.js
39947 * @requires OpenLayers/Control/PinchZoom.js
39948 * @requires OpenLayers/Handler/Click.js
39952 * Class: OpenLayers.Control.TouchNavigation
39953 * The navigation control handles map browsing with touch events (dragging,
39954 * double-tapping, tap with two fingers, and pinch zoom). Create a new
39955 * control with the <OpenLayers.Control.TouchNavigation> constructor.
39957 * If you’re only targeting touch enabled devices with your mapping application,
39958 * you can create a map with only a TouchNavigation control. The
39959 * <OpenLayers.Control.Navigation> control is mobile ready by default, but
39960 * you can generate a smaller build of the library by only including this
39961 * touch navigation control if you aren't concerned about mouse interaction.
39964 * - <OpenLayers.Control>
39966 OpenLayers.Control.TouchNavigation = OpenLayers.Class(OpenLayers.Control, {
39969 * Property: dragPan
39970 * {<OpenLayers.Control.DragPan>}
39975 * APIProperty: dragPanOptions
39976 * {Object} Options passed to the DragPan control.
39978 dragPanOptions: null,
39981 * Property: pinchZoom
39982 * {<OpenLayers.Control.PinchZoom>}
39987 * APIProperty: pinchZoomOptions
39988 * {Object} Options passed to the PinchZoom control.
39990 pinchZoomOptions: null,
39993 * APIProperty: clickHandlerOptions
39994 * {Object} Options passed to the Click handler.
39996 clickHandlerOptions: null,
39999 * APIProperty: documentDrag
40000 * {Boolean} Allow panning of the map by dragging outside map viewport.
40001 * Default is false.
40003 documentDrag: false,
40006 * APIProperty: autoActivate
40007 * {Boolean} Activate the control when it is added to a map. Default is
40010 autoActivate: true,
40013 * Constructor: OpenLayers.Control.TouchNavigation
40014 * Create a new navigation control
40017 * options - {Object} An optional object whose properties will be set on
40020 initialize: function(options) {
40021 this.handlers = {};
40022 OpenLayers.Control.prototype.initialize.apply(this, arguments);
40027 * The destroy method is used to perform any clean up before the control
40028 * is dereferenced. Typically this is where event listeners are removed
40029 * to prevent memory leaks.
40031 destroy: function() {
40034 this.dragPan.destroy();
40036 this.dragPan = null;
40037 if (this.pinchZoom) {
40038 this.pinchZoom.destroy();
40039 delete this.pinchZoom;
40041 OpenLayers.Control.prototype.destroy.apply(this,arguments);
40047 activate: function() {
40048 if(OpenLayers.Control.prototype.activate.apply(this,arguments)) {
40049 this.dragPan.activate();
40050 this.handlers.click.activate();
40051 this.pinchZoom.activate();
40058 * Method: deactivate
40060 deactivate: function() {
40061 if(OpenLayers.Control.prototype.deactivate.apply(this,arguments)) {
40062 this.dragPan.deactivate();
40063 this.handlers.click.deactivate();
40064 this.pinchZoom.deactivate();
40074 var clickCallbacks = {
40075 click: this.defaultClick,
40076 dblclick: this.defaultDblClick
40078 var clickOptions = OpenLayers.Util.extend({
40082 }, this.clickHandlerOptions);
40083 this.handlers.click = new OpenLayers.Handler.Click(
40084 this, clickCallbacks, clickOptions
40086 this.dragPan = new OpenLayers.Control.DragPan(
40087 OpenLayers.Util.extend({
40089 documentDrag: this.documentDrag
40090 }, this.dragPanOptions)
40092 this.dragPan.draw();
40093 this.pinchZoom = new OpenLayers.Control.PinchZoom(
40094 OpenLayers.Util.extend({map: this.map}, this.pinchZoomOptions)
40099 * Method: defaultClick
40104 defaultClick: function (evt) {
40105 if(evt.lastTouches && evt.lastTouches.length == 2) {
40106 this.map.zoomOut();
40111 * Method: defaultDblClick
40116 defaultDblClick: function (evt) {
40117 this.map.zoomTo(this.map.zoom + 1, evt.xy);
40120 CLASS_NAME: "OpenLayers.Control.TouchNavigation"
40122 /* ======================================================================
40123 OpenLayers/Renderer/VML.js
40124 ====================================================================== */
40126 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
40127 * full list of contributors). Published under the 2-clause BSD license.
40128 * See license.txt in the OpenLayers distribution or repository for the
40129 * full text of the license. */
40132 * @requires OpenLayers/Renderer/Elements.js
40136 * Class: OpenLayers.Renderer.VML
40137 * Render vector features in browsers with VML capability. Construct a new
40138 * VML renderer with the <OpenLayers.Renderer.VML> constructor.
40140 * Note that for all calculations in this class, we use (num | 0) to truncate a
40141 * float value to an integer. This is done because it seems that VML doesn't
40142 * support float values.
40145 * - <OpenLayers.Renderer.Elements>
40147 OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
40151 * {String} XML Namespace URN
40153 xmlns: "urn:schemas-microsoft-com:vml",
40156 * Property: symbolCache
40157 * {DOMElement} node holding symbols. This hash is keyed by symbol name,
40158 * and each value is a hash with a "path" and an "extent" property.
40164 * {Object} Hash with "x" and "y" properties
40169 * Constructor: OpenLayers.Renderer.VML
40170 * Create a new VML renderer.
40173 * containerID - {String} The id for the element that contains the renderer
40175 initialize: function(containerID) {
40176 if (!this.supported()) {
40179 if (!document.namespaces.olv) {
40180 document.namespaces.add("olv", this.xmlns);
40181 var style = document.createStyleSheet();
40182 var shapes = ['shape','rect', 'oval', 'fill', 'stroke', 'imagedata', 'group','textbox'];
40183 for (var i = 0, len = shapes.length; i < len; i++) {
40185 style.addRule('olv\\:' + shapes[i], "behavior: url(#default#VML); " +
40186 "position: absolute; display: inline-block;");
40190 OpenLayers.Renderer.Elements.prototype.initialize.apply(this,
40195 * APIMethod: supported
40196 * Determine whether a browser supports this renderer.
40199 * {Boolean} The browser supports the VML renderer
40201 supported: function() {
40202 return !!(document.namespaces);
40206 * Method: setExtent
40207 * Set the renderer's extent
40210 * extent - {<OpenLayers.Bounds>}
40211 * resolutionChanged - {Boolean}
40214 * {Boolean} true to notify the layer that the new extent does not exceed
40215 * the coordinate range, and the features will not need to be redrawn.
40217 setExtent: function(extent, resolutionChanged) {
40218 var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments);
40219 var resolution = this.getResolution();
40221 var left = (extent.left/resolution) | 0;
40222 var top = (extent.top/resolution - this.size.h) | 0;
40223 if (resolutionChanged || !this.offset) {
40224 this.offset = {x: left, y: top};
40228 left = left - this.offset.x;
40229 top = top - this.offset.y;
40233 var org = (left - this.xOffset) + " " + top;
40234 this.root.coordorigin = org;
40235 var roots = [this.root, this.vectorRoot, this.textRoot];
40237 for(var i=0, len=roots.length; i<len; ++i) {
40240 var size = this.size.w + " " + this.size.h;
40241 root.coordsize = size;
40244 // flip the VML display Y axis upside down so it
40245 // matches the display Y axis of the map
40246 this.root.style.flip = "y";
40248 return coordSysUnchanged;
40254 * Set the size of the drawing surface
40257 * size - {<OpenLayers.Size>} the size of the drawing surface
40259 setSize: function(size) {
40260 OpenLayers.Renderer.prototype.setSize.apply(this, arguments);
40262 // setting width and height on all roots to avoid flicker which we
40263 // would get with 100% width and height on child roots
40270 var w = this.size.w + "px";
40271 var h = this.size.h + "px";
40273 for(var i=0, len=roots.length; i<len; ++i) {
40275 root.style.width = w;
40276 root.style.height = h;
40281 * Method: getNodeType
40282 * Get the node type for a geometry and style
40285 * geometry - {<OpenLayers.Geometry>}
40289 * {String} The corresponding node type for the specified geometry
40291 getNodeType: function(geometry, style) {
40292 var nodeType = null;
40293 switch (geometry.CLASS_NAME) {
40294 case "OpenLayers.Geometry.Point":
40295 if (style.externalGraphic) {
40296 nodeType = "olv:rect";
40297 } else if (this.isComplexSymbol(style.graphicName)) {
40298 nodeType = "olv:shape";
40300 nodeType = "olv:oval";
40303 case "OpenLayers.Geometry.Rectangle":
40304 nodeType = "olv:rect";
40306 case "OpenLayers.Geometry.LineString":
40307 case "OpenLayers.Geometry.LinearRing":
40308 case "OpenLayers.Geometry.Polygon":
40309 case "OpenLayers.Geometry.Curve":
40310 nodeType = "olv:shape";
40320 * Use to set all the style attributes to a VML node.
40323 * node - {DOMElement} An VML element to decorate
40325 * options - {Object} Currently supported options include
40326 * 'isFilled' {Boolean} and
40327 * 'isStroked' {Boolean}
40328 * geometry - {<OpenLayers.Geometry>}
40330 setStyle: function(node, style, options, geometry) {
40331 style = style || node._style;
40332 options = options || node._options;
40333 var fillColor = style.fillColor;
40335 var title = style.title || style.graphicTitle;
40337 node.title = title;
40340 if (node._geometryClass === "OpenLayers.Geometry.Point") {
40341 if (style.externalGraphic) {
40342 options.isFilled = true;
40343 var width = style.graphicWidth || style.graphicHeight;
40344 var height = style.graphicHeight || style.graphicWidth;
40345 width = width ? width : style.pointRadius*2;
40346 height = height ? height : style.pointRadius*2;
40348 var resolution = this.getResolution();
40349 var xOffset = (style.graphicXOffset != undefined) ?
40350 style.graphicXOffset : -(0.5 * width);
40351 var yOffset = (style.graphicYOffset != undefined) ?
40352 style.graphicYOffset : -(0.5 * height);
40354 node.style.left = ((((geometry.x - this.featureDx)/resolution - this.offset.x)+xOffset) | 0) + "px";
40355 node.style.top = (((geometry.y/resolution - this.offset.y)-(yOffset+height)) | 0) + "px";
40356 node.style.width = width + "px";
40357 node.style.height = height + "px";
40358 node.style.flip = "y";
40360 // modify fillColor and options for stroke styling below
40361 fillColor = "none";
40362 options.isStroked = false;
40363 } else if (this.isComplexSymbol(style.graphicName)) {
40364 var cache = this.importSymbol(style.graphicName);
40365 node.path = cache.path;
40366 node.coordorigin = cache.left + "," + cache.bottom;
40367 var size = cache.size;
40368 node.coordsize = size + "," + size;
40369 this.drawCircle(node, geometry, style.pointRadius);
40370 node.style.flip = "y";
40372 this.drawCircle(node, geometry, style.pointRadius);
40377 if (options.isFilled) {
40378 node.fillcolor = fillColor;
40380 node.filled = "false";
40382 var fills = node.getElementsByTagName("fill");
40383 var fill = (fills.length == 0) ? null : fills[0];
40384 if (!options.isFilled) {
40386 node.removeChild(fill);
40390 fill = this.createNode('olv:fill', node.id + "_fill");
40392 fill.opacity = style.fillOpacity;
40394 if (node._geometryClass === "OpenLayers.Geometry.Point" &&
40395 style.externalGraphic) {
40397 // override fillOpacity
40398 if (style.graphicOpacity) {
40399 fill.opacity = style.graphicOpacity;
40402 fill.src = style.externalGraphic;
40403 fill.type = "frame";
40405 if (!(style.graphicWidth && style.graphicHeight)) {
40406 fill.aspect = "atmost";
40409 if (fill.parentNode != node) {
40410 node.appendChild(fill);
40414 // additional rendering for rotated graphics or symbols
40415 var rotation = style.rotation;
40416 if ((rotation !== undefined || node._rotation !== undefined)) {
40417 node._rotation = rotation;
40418 if (style.externalGraphic) {
40419 this.graphicRotate(node, xOffset, yOffset, style);
40420 // make the fill fully transparent, because we now have
40421 // the graphic as imagedata element. We cannot just remove
40422 // the fill, because this is part of the hack described
40423 // in graphicRotate
40425 } else if(node._geometryClass === "OpenLayers.Geometry.Point") {
40426 node.style.rotation = rotation || 0;
40431 var strokes = node.getElementsByTagName("stroke");
40432 var stroke = (strokes.length == 0) ? null : strokes[0];
40433 if (!options.isStroked) {
40434 node.stroked = false;
40440 stroke = this.createNode('olv:stroke', node.id + "_stroke");
40441 node.appendChild(stroke);
40444 stroke.color = style.strokeColor;
40445 stroke.weight = style.strokeWidth + "px";
40446 stroke.opacity = style.strokeOpacity;
40447 stroke.endcap = style.strokeLinecap == 'butt' ? 'flat' :
40448 (style.strokeLinecap || 'round');
40449 if (style.strokeDashstyle) {
40450 stroke.dashstyle = this.dashStyle(style);
40454 if (style.cursor != "inherit" && style.cursor != null) {
40455 node.style.cursor = style.cursor;
40461 * Method: graphicRotate
40462 * If a point is to be styled with externalGraphic and rotation, VML fills
40463 * cannot be used to display the graphic, because rotation of graphic
40464 * fills is not supported by the VML implementation of Internet Explorer.
40465 * This method creates a olv:imagedata element inside the VML node,
40466 * DXImageTransform.Matrix and BasicImage filters for rotation and
40467 * opacity, and a 3-step hack to remove rendering artefacts from the
40468 * graphic and preserve the ability of graphics to trigger events.
40469 * Finally, OpenLayers methods are used to determine the correct
40470 * insertion point of the rotated image, because DXImageTransform.Matrix
40471 * does the rotation without the ability to specify a rotation center
40475 * node - {DOMElement}
40476 * xOffset - {Number} rotation center relative to image, x coordinate
40477 * yOffset - {Number} rotation center relative to image, y coordinate
40480 graphicRotate: function(node, xOffset, yOffset, style) {
40481 var style = style || node._style;
40482 var rotation = style.rotation || 0;
40484 var aspectRatio, size;
40485 if (!(style.graphicWidth && style.graphicHeight)) {
40486 // load the image to determine its size
40487 var img = new Image();
40488 img.onreadystatechange = OpenLayers.Function.bind(function() {
40489 if(img.readyState == "complete" ||
40490 img.readyState == "interactive") {
40491 aspectRatio = img.width / img.height;
40492 size = Math.max(style.pointRadius * 2,
40493 style.graphicWidth || 0,
40494 style.graphicHeight || 0);
40495 xOffset = xOffset * aspectRatio;
40496 style.graphicWidth = size * aspectRatio;
40497 style.graphicHeight = size;
40498 this.graphicRotate(node, xOffset, yOffset, style);
40501 img.src = style.externalGraphic;
40503 // will be called again by the onreadystate handler
40506 size = Math.max(style.graphicWidth, style.graphicHeight);
40507 aspectRatio = style.graphicWidth / style.graphicHeight;
40510 var width = Math.round(style.graphicWidth || size * aspectRatio);
40511 var height = Math.round(style.graphicHeight || size);
40512 node.style.width = width + "px";
40513 node.style.height = height + "px";
40515 // Three steps are required to remove artefacts for images with
40516 // transparent backgrounds (resulting from using DXImageTransform
40517 // filters on svg objects), while preserving awareness for browser
40518 // events on images:
40519 // - Use the fill as usual (like for unrotated images) to handle
40521 // - specify an imagedata element with the same src as the fill
40522 // - style the imagedata element with an AlphaImageLoader filter
40524 var image = document.getElementById(node.id + "_image");
40526 image = this.createNode("olv:imagedata", node.id + "_image");
40527 node.appendChild(image);
40529 image.style.width = width + "px";
40530 image.style.height = height + "px";
40531 image.src = style.externalGraphic;
40532 image.style.filter =
40533 "progid:DXImageTransform.Microsoft.AlphaImageLoader(" +
40534 "src='', sizingMethod='scale')";
40536 var rot = rotation * Math.PI / 180;
40537 var sintheta = Math.sin(rot);
40538 var costheta = Math.cos(rot);
40540 // do the rotation on the image
40542 "progid:DXImageTransform.Microsoft.Matrix(M11=" + costheta +
40543 ",M12=" + (-sintheta) + ",M21=" + sintheta + ",M22=" + costheta +
40544 ",SizingMethod='auto expand')\n";
40546 // set the opacity (needed for the imagedata)
40547 var opacity = style.graphicOpacity || style.fillOpacity;
40548 if (opacity && opacity != 1) {
40550 "progid:DXImageTransform.Microsoft.BasicImage(opacity=" +
40553 node.style.filter = filter;
40555 // do the rotation again on a box, so we know the insertion point
40556 var centerPoint = new OpenLayers.Geometry.Point(-xOffset, -yOffset);
40557 var imgBox = new OpenLayers.Bounds(0, 0, width, height).toGeometry();
40558 imgBox.rotate(style.rotation, centerPoint);
40559 var imgBounds = imgBox.getBounds();
40561 node.style.left = Math.round(
40562 parseInt(node.style.left) + imgBounds.left) + "px";
40563 node.style.top = Math.round(
40564 parseInt(node.style.top) - imgBounds.bottom) + "px";
40569 * Does some node postprocessing to work around browser issues:
40570 * - Some versions of Internet Explorer seem to be unable to set fillcolor
40571 * and strokecolor to "none" correctly before the fill node is appended
40572 * to a visible vml node. This method takes care of that and sets
40573 * fillcolor and strokecolor again if needed.
40574 * - In some cases, a node won't become visible after being drawn. Setting
40575 * style.visibility to "visible" works around that.
40578 * node - {DOMElement}
40580 postDraw: function(node) {
40581 node.style.visibility = "visible";
40582 var fillColor = node._style.fillColor;
40583 var strokeColor = node._style.strokeColor;
40584 if (fillColor == "none" &&
40585 node.fillcolor != fillColor) {
40586 node.fillcolor = fillColor;
40588 if (strokeColor == "none" &&
40589 node.strokecolor != strokeColor) {
40590 node.strokecolor = strokeColor;
40596 * Method: setNodeDimension
40597 * Get the geometry's bounds, convert it to our vml coordinate system,
40598 * then set the node's position, size, and local coordinate system.
40601 * node - {DOMElement}
40602 * geometry - {<OpenLayers.Geometry>}
40604 setNodeDimension: function(node, geometry) {
40606 var bbox = geometry.getBounds();
40608 var resolution = this.getResolution();
40611 new OpenLayers.Bounds(((bbox.left - this.featureDx)/resolution - this.offset.x) | 0,
40612 (bbox.bottom/resolution - this.offset.y) | 0,
40613 ((bbox.right - this.featureDx)/resolution - this.offset.x) | 0,
40614 (bbox.top/resolution - this.offset.y) | 0);
40616 // Set the internal coordinate system to draw the path
40617 node.style.left = scaledBox.left + "px";
40618 node.style.top = scaledBox.top + "px";
40619 node.style.width = scaledBox.getWidth() + "px";
40620 node.style.height = scaledBox.getHeight() + "px";
40622 node.coordorigin = scaledBox.left + " " + scaledBox.top;
40623 node.coordsize = scaledBox.getWidth()+ " " + scaledBox.getHeight();
40628 * Method: dashStyle
40634 * {String} A VML compliant 'stroke-dasharray' value
40636 dashStyle: function(style) {
40637 var dash = style.strokeDashstyle;
40644 case 'longdashdot':
40647 // very basic guessing of dash style patterns
40648 var parts = dash.split(/[ ,]/);
40649 if (parts.length == 2) {
40650 if (1*parts[0] >= 2*parts[1]) {
40653 return (parts[0] == 1 || parts[1] == 1) ? "dot" : "dash";
40654 } else if (parts.length == 4) {
40655 return (1*parts[0] >= 2*parts[1]) ? "longdashdot" :
40663 * Method: createNode
40664 * Create a new node
40667 * type - {String} Kind of node to draw
40668 * id - {String} Id for node
40671 * {DOMElement} A new node of the given type and id
40673 createNode: function(type, id) {
40674 var node = document.createElement(type);
40679 // IE hack to make elements unselectable, to prevent 'blue flash'
40680 // while dragging vectors; #1410
40681 node.unselectable = 'on';
40682 node.onselectstart = OpenLayers.Function.False;
40688 * Method: nodeTypeCompare
40689 * Determine whether a node is of a given type
40692 * node - {DOMElement} An VML element
40693 * type - {String} Kind of node
40696 * {Boolean} Whether or not the specified node is of the specified type
40698 nodeTypeCompare: function(node, type) {
40701 var subType = type;
40702 var splitIndex = subType.indexOf(":");
40703 if (splitIndex != -1) {
40704 subType = subType.substr(splitIndex+1);
40708 var nodeName = node.nodeName;
40709 splitIndex = nodeName.indexOf(":");
40710 if (splitIndex != -1) {
40711 nodeName = nodeName.substr(splitIndex+1);
40714 return (subType == nodeName);
40718 * Method: createRenderRoot
40719 * Create the renderer root
40722 * {DOMElement} The specific render engine's root element
40724 createRenderRoot: function() {
40725 return this.nodeFactory(this.container.id + "_vmlRoot", "div");
40729 * Method: createRoot
40730 * Create the main root element
40733 * suffix - {String} suffix to append to the id
40738 createRoot: function(suffix) {
40739 return this.nodeFactory(this.container.id + suffix, "olv:group");
40742 /**************************************
40744 * GEOMETRY DRAWING FUNCTIONS *
40746 **************************************/
40749 * Method: drawPoint
40753 * node - {DOMElement}
40754 * geometry - {<OpenLayers.Geometry>}
40757 * {DOMElement} or false if the point could not be drawn
40759 drawPoint: function(node, geometry) {
40760 return this.drawCircle(node, geometry, 1);
40764 * Method: drawCircle
40766 * Size and Center a circle given geometry (x,y center) and radius
40769 * node - {DOMElement}
40770 * geometry - {<OpenLayers.Geometry>}
40774 * {DOMElement} or false if the circle could not ne drawn
40776 drawCircle: function(node, geometry, radius) {
40777 if(!isNaN(geometry.x)&& !isNaN(geometry.y)) {
40778 var resolution = this.getResolution();
40780 node.style.left = ((((geometry.x - this.featureDx) /resolution - this.offset.x) | 0) - radius) + "px";
40781 node.style.top = (((geometry.y /resolution - this.offset.y) | 0) - radius) + "px";
40783 var diameter = radius * 2;
40785 node.style.width = diameter + "px";
40786 node.style.height = diameter + "px";
40794 * Method: drawLineString
40795 * Render a linestring.
40798 * node - {DOMElement}
40799 * geometry - {<OpenLayers.Geometry>}
40804 drawLineString: function(node, geometry) {
40805 return this.drawLine(node, geometry, false);
40809 * Method: drawLinearRing
40810 * Render a linearring
40813 * node - {DOMElement}
40814 * geometry - {<OpenLayers.Geometry>}
40819 drawLinearRing: function(node, geometry) {
40820 return this.drawLine(node, geometry, true);
40828 * node - {DOMElement}
40829 * geometry - {<OpenLayers.Geometry>}
40830 * closeLine - {Boolean} Close the line? (make it a ring?)
40835 drawLine: function(node, geometry, closeLine) {
40837 this.setNodeDimension(node, geometry);
40839 var resolution = this.getResolution();
40840 var numComponents = geometry.components.length;
40841 var parts = new Array(numComponents);
40844 for (var i = 0; i < numComponents; i++) {
40845 comp = geometry.components[i];
40846 x = ((comp.x - this.featureDx)/resolution - this.offset.x) | 0;
40847 y = (comp.y/resolution - this.offset.y) | 0;
40848 parts[i] = " " + x + "," + y + " l ";
40850 var end = (closeLine) ? " x e" : " e";
40851 node.path = "m" + parts.join("") + end;
40856 * Method: drawPolygon
40860 * node - {DOMElement}
40861 * geometry - {<OpenLayers.Geometry>}
40866 drawPolygon: function(node, geometry) {
40867 this.setNodeDimension(node, geometry);
40869 var resolution = this.getResolution();
40872 var j, jj, points, area, first, second, i, ii, comp, pathComp, x, y;
40873 for (j=0, jj=geometry.components.length; j<jj; j++) {
40875 points = geometry.components[j].components;
40876 // we only close paths of interior rings with area
40880 for (i=0, ii=points.length; i<ii; i++) {
40882 x = ((comp.x - this.featureDx) / resolution - this.offset.x) | 0;
40883 y = (comp.y / resolution - this.offset.y) | 0;
40884 pathComp = " " + x + "," + y;
40885 path.push(pathComp);
40890 // IE improperly renders sub-paths that have no area.
40891 // Instead of checking the area of every ring, we confirm
40892 // the ring has at least three distinct points. This does
40893 // not catch all non-zero area cases, but it greatly improves
40894 // interior ring digitizing and is a minor performance hit
40895 // when rendering rings with many points.
40898 } else if (first != pathComp) {
40901 } else if (second != pathComp) {
40908 path.push(area ? " x " : " ");
40911 node.path = path.join("");
40916 * Method: drawRectangle
40917 * Render a rectangle
40920 * node - {DOMElement}
40921 * geometry - {<OpenLayers.Geometry>}
40926 drawRectangle: function(node, geometry) {
40927 var resolution = this.getResolution();
40929 node.style.left = (((geometry.x - this.featureDx)/resolution - this.offset.x) | 0) + "px";
40930 node.style.top = ((geometry.y/resolution - this.offset.y) | 0) + "px";
40931 node.style.width = ((geometry.width/resolution) | 0) + "px";
40932 node.style.height = ((geometry.height/resolution) | 0) + "px";
40939 * This method is only called by the renderer itself.
40942 * featureId - {String}
40944 * location - {<OpenLayers.Geometry.Point>}
40946 drawText: function(featureId, style, location) {
40947 var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "olv:rect");
40948 var textbox = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_textbox", "olv:textbox");
40950 var resolution = this.getResolution();
40951 label.style.left = (((location.x - this.featureDx)/resolution - this.offset.x) | 0) + "px";
40952 label.style.top = ((location.y/resolution - this.offset.y) | 0) + "px";
40953 label.style.flip = "y";
40955 textbox.innerText = style.label;
40957 if (style.cursor != "inherit" && style.cursor != null) {
40958 textbox.style.cursor = style.cursor;
40960 if (style.fontColor) {
40961 textbox.style.color = style.fontColor;
40963 if (style.fontOpacity) {
40964 textbox.style.filter = 'alpha(opacity=' + (style.fontOpacity * 100) + ')';
40966 if (style.fontFamily) {
40967 textbox.style.fontFamily = style.fontFamily;
40969 if (style.fontSize) {
40970 textbox.style.fontSize = style.fontSize;
40972 if (style.fontWeight) {
40973 textbox.style.fontWeight = style.fontWeight;
40975 if (style.fontStyle) {
40976 textbox.style.fontStyle = style.fontStyle;
40978 if(style.labelSelect === true) {
40979 label._featureId = featureId;
40980 textbox._featureId = featureId;
40981 textbox._geometry = location;
40982 textbox._geometryClass = location.CLASS_NAME;
40984 textbox.style.whiteSpace = "nowrap";
40985 // fun with IE: IE7 in standards compliant mode does not display any
40986 // text with a left inset of 0. So we set this to 1px and subtract one
40987 // pixel later when we set label.style.left
40988 textbox.inset = "1px,0px,0px,0px";
40990 if(!label.parentNode) {
40991 label.appendChild(textbox);
40992 this.textRoot.appendChild(label);
40995 var align = style.labelAlign || "cm";
40996 if (align.length == 1) {
40999 var xshift = textbox.clientWidth *
41000 (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(0,1)]);
41001 var yshift = textbox.clientHeight *
41002 (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(1,1)]);
41003 label.style.left = parseInt(label.style.left)-xshift-1+"px";
41004 label.style.top = parseInt(label.style.top)+yshift+"px";
41010 * moves this renderer's root to a different renderer.
41013 * renderer - {<OpenLayers.Renderer>} target renderer for the moved root
41014 * root - {DOMElement} optional root node. To be used when this renderer
41015 * holds roots from multiple layers to tell this method which one to
41019 * {Boolean} true if successful, false otherwise
41021 moveRoot: function(renderer) {
41022 var layer = this.map.getLayer(renderer.container.id);
41023 if(layer instanceof OpenLayers.Layer.Vector.RootContainer) {
41024 layer = this.map.getLayer(this.container.id);
41026 layer && layer.renderer.clear();
41027 OpenLayers.Renderer.Elements.prototype.moveRoot.apply(this, arguments);
41028 layer && layer.redraw();
41032 * Method: importSymbol
41033 * add a new symbol definition from the rendererer's symbol hash
41036 * graphicName - {String} name of the symbol to import
41039 * {Object} - hash of {DOMElement} "symbol" and {Number} "size"
41041 importSymbol: function (graphicName) {
41042 var id = this.container.id + "-" + graphicName;
41044 // check if symbol already exists in the cache
41045 var cache = this.symbolCache[id];
41050 var symbol = OpenLayers.Renderer.symbol[graphicName];
41052 throw new Error(graphicName + ' is not a valid symbol name');
41055 var symbolExtent = new OpenLayers.Bounds(
41056 Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
41058 var pathitems = ["m"];
41059 for (var i=0; i<symbol.length; i=i+2) {
41061 var y = symbol[i+1];
41062 symbolExtent.left = Math.min(symbolExtent.left, x);
41063 symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
41064 symbolExtent.right = Math.max(symbolExtent.right, x);
41065 symbolExtent.top = Math.max(symbolExtent.top, y);
41070 pathitems.push("l");
41073 pathitems.push("x e");
41074 var path = pathitems.join(" ");
41076 var diff = (symbolExtent.getWidth() - symbolExtent.getHeight()) / 2;
41078 symbolExtent.bottom = symbolExtent.bottom - diff;
41079 symbolExtent.top = symbolExtent.top + diff;
41081 symbolExtent.left = symbolExtent.left + diff;
41082 symbolExtent.right = symbolExtent.right - diff;
41087 size: symbolExtent.getWidth(), // equals getHeight() now
41088 left: symbolExtent.left,
41089 bottom: symbolExtent.bottom
41091 this.symbolCache[id] = cache;
41096 CLASS_NAME: "OpenLayers.Renderer.VML"
41100 * Constant: OpenLayers.Renderer.VML.LABEL_SHIFT
41103 OpenLayers.Renderer.VML.LABEL_SHIFT = {
41111 /* ======================================================================
41112 OpenLayers/Protocol/WFS/v1_0_0.js
41113 ====================================================================== */
41115 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
41116 * full list of contributors). Published under the 2-clause BSD license.
41117 * See license.txt in the OpenLayers distribution or repository for the
41118 * full text of the license. */
41121 * @requires OpenLayers/Protocol/WFS/v1.js
41122 * @requires OpenLayers/Format/WFST/v1_0_0.js
41126 * Class: OpenLayers.Protocol.WFS.v1_0_0
41127 * A WFS v1.0.0 protocol for vector layers. Create a new instance with the
41128 * <OpenLayers.Protocol.WFS.v1_0_0> constructor.
41131 * - <OpenLayers.Protocol.WFS.v1>
41133 OpenLayers.Protocol.WFS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, {
41136 * Property: version
41137 * {String} WFS version number.
41142 * Constructor: OpenLayers.Protocol.WFS.v1_0_0
41143 * A class for giving layers WFS v1.0.0 protocol.
41146 * options - {Object} Optional object whose properties will be set on the
41149 * Valid options properties:
41150 * featureType - {String} Local (without prefix) feature typeName (required).
41151 * featureNS - {String} Feature namespace (optional).
41152 * featurePrefix - {String} Feature namespace alias (optional - only used
41153 * if featureNS is provided). Default is 'feature'.
41154 * geometryName - {String} Name of geometry attribute. Default is 'the_geom'.
41157 CLASS_NAME: "OpenLayers.Protocol.WFS.v1_0_0"