3 OpenLayers.js -- OpenLayers Map Viewer Library
5 Copyright (c) 2006-2015 by OpenLayers Contributors
6 Published under the 2-clause BSD license.
7 See https://raw.githubusercontent.com/openlayers/ol2/master/license.txt for the full text of the license, and https://raw.githubusercontent.com/openlayers/ol2/master/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/ol2>)
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-2015 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.14 dev",
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/Class.js
142 ====================================================================== */
144 /* Copyright (c) 2006-2015 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 * Constructor: OpenLayers.Class
155 * Base class used to construct all other classes. Includes support for
156 * multiple inheritance.
158 * This constructor is new in OpenLayers 2.5. At OpenLayers 3.0, the old
159 * syntax for creating classes and dealing with inheritance
162 * To create a new OpenLayers-style class, use the following syntax:
164 * var MyClass = OpenLayers.Class(prototype);
167 * To create a new OpenLayers-style class with multiple inheritance, use the
170 * var MyClass = OpenLayers.Class(Class1, Class2, prototype);
173 * Note that instanceof reflection will only reveal Class1 as superclass.
176 OpenLayers.Class = function() {
177 var len = arguments.length;
178 var P = arguments[0];
179 var F = arguments[len-1];
181 var C = typeof F.initialize == "function" ?
183 function(){ P.prototype.initialize.apply(this, arguments); };
186 var newArgs = [C, P].concat(
187 Array.prototype.slice.call(arguments).slice(1, len-1), F);
188 OpenLayers.inherit.apply(null, newArgs);
196 * Function: OpenLayers.inherit
199 * C - {Object} the class that inherits
200 * P - {Object} the superclass to inherit from
202 * In addition to the mandatory C and P parameters, an arbitrary number of
203 * objects can be passed, which will extend C.
205 OpenLayers.inherit = function(C, P) {
206 var F = function() {};
207 F.prototype = P.prototype;
210 for(i=2, l=arguments.length; i<l; i++) {
212 if(typeof o === "function") {
215 OpenLayers.Util.extend(C.prototype, o);
220 * APIFunction: extend
221 * Copy all properties of a source object to a destination object. Modifies
222 * the passed in destination object. Any properties on the source object
223 * that are set to undefined will not be (re)set on the destination object.
226 * destination - {Object} The object that will be modified
227 * source - {Object} The object with properties to be set on the destination
230 * {Object} The destination object.
232 OpenLayers.Util = OpenLayers.Util || {};
233 OpenLayers.Util.extend = function(destination, source) {
234 destination = destination || {};
236 for (var property in source) {
237 var value = source[property];
238 if (value !== undefined) {
239 destination[property] = value;
244 * IE doesn't include the toString property when iterating over an object's
245 * properties with the for(property in object) syntax. Explicitly check if
246 * the source has its own toString property.
250 * FF/Windows < 2.0.0.13 reports "Illegal operation on WrappedNative
251 * prototype object" when calling hawOwnProperty if the source object
252 * is an instance of window.Event.
255 var sourceIsEvt = typeof window.Event == "function"
256 && source instanceof window.Event;
259 && source.hasOwnProperty && source.hasOwnProperty("toString")) {
260 destination.toString = source.toString;
265 /* ======================================================================
266 OpenLayers/BaseTypes.js
267 ====================================================================== */
269 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
270 * full list of contributors). Published under the 2-clause BSD license.
271 * See license.txt in the OpenLayers distribution or repository for the
272 * full text of the license. */
275 * @requires OpenLayers/SingleFile.js
279 * Header: OpenLayers Base Types
280 * OpenLayers custom string, number and function functions are described here.
284 * Namespace: OpenLayers.String
285 * Contains convenience functions for string manipulation.
287 OpenLayers.String = {
290 * APIFunction: startsWith
291 * Test whether a string starts with another string.
294 * str - {String} The string to test.
295 * sub - {String} The substring to look for.
298 * {Boolean} The first string starts with the second.
300 startsWith: function(str, sub) {
301 return (str.indexOf(sub) == 0);
305 * APIFunction: contains
306 * Test whether a string contains another string.
309 * str - {String} The string to test.
310 * sub - {String} The substring to look for.
313 * {Boolean} The first string contains the second.
315 contains: function(str, sub) {
316 return (str.indexOf(sub) != -1);
321 * Removes leading and trailing whitespace characters from a string.
324 * str - {String} The (potentially) space padded string. This string is not
328 * {String} A trimmed version of the string with all leading and
329 * trailing spaces removed.
331 trim: function(str) {
332 return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
336 * APIFunction: camelize
337 * Camel-case a hyphenated string.
338 * Ex. "chicken-head" becomes "chickenHead", and
339 * "-chicken-head" becomes "ChickenHead".
342 * str - {String} The string to be camelized. The original is not modified.
345 * {String} The string, camelized
347 camelize: function(str) {
348 var oStringList = str.split('-');
349 var camelizedString = oStringList[0];
350 for (var i=1, len=oStringList.length; i<len; i++) {
351 var s = oStringList[i];
352 camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
354 return camelizedString;
358 * APIFunction: format
359 * Given a string with tokens in the form ${token}, return a string
360 * with tokens replaced with properties from the given context
361 * object. Represent a literal "${" by doubling it, e.g. "${${".
364 * template - {String} A string with tokens to be replaced. A template
365 * has the form "literal ${token}" where the token will be replaced
366 * by the value of context["token"].
367 * context - {Object} An optional object with properties corresponding
368 * to the tokens in the format string. If no context is sent, the
369 * window object will be used.
370 * args - {Array} Optional arguments to pass to any functions found in
371 * the context. If a context property is a function, the token
372 * will be replaced by the return from the function called with
376 * {String} A string with tokens replaced from the context object.
378 format: function(template, context, args) {
386 var replacer = function(str, match) {
389 // Loop through all subs. Example: ${a.b.c}
390 // 0 -> replacement = context[a];
391 // 1 -> replacement = context[a][b];
392 // 2 -> replacement = context[a][b][c];
393 var subs = match.split(/\.+/);
394 for (var i=0; i< subs.length; i++) {
396 replacement = context;
398 if (replacement === undefined) {
401 replacement = replacement[subs[i]];
404 if(typeof replacement == "function") {
406 replacement.apply(null, args) :
410 // If replacement is undefined, return the string 'undefined'.
411 // This is a workaround for a bugs in browsers not properly
412 // dealing with non-participating groups in regular expressions:
413 // http://blog.stevenlevithan.com/archives/npcg-javascript
414 if (typeof replacement == 'undefined') {
421 return template.replace(OpenLayers.String.tokenRegEx, replacer);
425 * Property: tokenRegEx
426 * Used to find tokens in a string.
427 * Examples: ${a}, ${a.b.c}, ${a-b}, ${5}
429 tokenRegEx: /\$\{([\w.]+?)\}/g,
432 * Property: numberRegEx
433 * Used to test strings as numbers.
435 numberRegEx: /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/,
438 * APIFunction: isNumeric
439 * Determine whether a string contains only a numeric value.
443 * OpenLayers.String.isNumeric("6.02e23") // true
444 * OpenLayers.String.isNumeric("12 dozen") // false
445 * OpenLayers.String.isNumeric("4") // true
446 * OpenLayers.String.isNumeric(" 4 ") // false
450 * {Boolean} String contains only a number.
452 isNumeric: function(value) {
453 return OpenLayers.String.numberRegEx.test(value);
457 * APIFunction: numericIf
458 * Converts a string that appears to be a numeric value into a number.
462 * trimWhitespace - {Boolean}
465 * {Number|String} a Number if the passed value is a number, a String
468 numericIf: function(value, trimWhitespace) {
469 var originalValue = value;
470 if (trimWhitespace === true && value != null && value.replace) {
471 value = value.replace(/^\s*|\s*$/g, "");
473 return OpenLayers.String.isNumeric(value) ? parseFloat(value) : originalValue;
479 * Namespace: OpenLayers.Number
480 * Contains convenience functions for manipulating numbers.
482 OpenLayers.Number = {
485 * Property: decimalSeparator
486 * Decimal separator to use when formatting numbers.
488 decimalSeparator: ".",
491 * Property: thousandsSeparator
492 * Thousands separator to use when formatting numbers.
494 thousandsSeparator: ",",
497 * APIFunction: limitSigDigs
498 * Limit the number of significant digits on a float.
505 * {Float} The number, rounded to the specified number of significant
508 limitSigDigs: function(num, sig) {
511 fig = parseFloat(num.toPrecision(sig));
517 * APIFunction: format
518 * Formats a number for output.
522 * dec - {Integer} Number of decimal places to round to.
523 * Defaults to 0. Set to null to leave decimal places unchanged.
524 * tsep - {String} Thousands separator.
526 * dsep - {String} Decimal separator.
530 * {String} A string representing the formatted number.
532 format: function(num, dec, tsep, dsep) {
533 dec = (typeof dec != "undefined") ? dec : 0;
534 tsep = (typeof tsep != "undefined") ? tsep :
535 OpenLayers.Number.thousandsSeparator;
536 dsep = (typeof dsep != "undefined") ? dsep :
537 OpenLayers.Number.decimalSeparator;
540 num = parseFloat(num.toFixed(dec));
543 var parts = num.toString().split(".");
544 if (parts.length == 1 && dec == null) {
545 // integer where we do not want to touch the decimals
549 var integer = parts[0];
551 var thousands = /(-?[0-9]+)([0-9]{3})/;
552 while(thousands.test(integer)) {
553 integer = integer.replace(thousands, "$1" + tsep + "$2");
561 var rem = parts.length > 1 ? parts[1] : "0";
563 rem = rem + new Array(dec - rem.length + 1).join("0");
565 str = integer + dsep + rem;
572 * Create a zero padded string optionally with a radix for casting numbers.
575 * num - {Number} The number to be zero padded.
576 * len - {Number} The length of the string to be returned.
577 * radix - {Number} An integer between 2 and 36 specifying the base to use
578 * for representing numeric values.
580 zeroPad: function(num, len, radix) {
581 var str = num.toString(radix || 10);
582 while (str.length < len) {
590 * Namespace: OpenLayers.Function
591 * Contains convenience functions for function manipulation.
593 OpenLayers.Function = {
596 * Bind a function to an object. Method to easily create closures with
600 * func - {Function} Input function.
601 * object - {Object} The object to bind to the input function (as this).
604 * {Function} A closure with 'this' set to the passed in object.
606 bind: function(func, object) {
607 // create a reference to all arguments past the second one
608 var args = Array.prototype.slice.call(arguments, 2);
610 // Push on any additional arguments from the actual function call.
611 // These will come after those sent to the bind call.
612 var newArgs = args.concat(
613 Array.prototype.slice.call(arguments, 0)
615 return func.apply(object, newArgs);
620 * APIFunction: bindAsEventListener
621 * Bind a function to an object, and configure it to receive the event
622 * object as first parameter when called.
625 * func - {Function} Input function to serve as an event listener.
626 * object - {Object} A reference to this.
631 bindAsEventListener: function(func, object) {
632 return function(event) {
633 return func.call(object, event || window.event);
639 * A simple function to that just does "return false". We use this to
640 * avoid attaching anonymous functions to DOM event handlers, which
641 * causes "issues" on IE<8.
644 * document.onclick = OpenLayers.Function.False;
655 * A simple function to that just does "return true". We use this to
656 * avoid attaching anonymous functions to DOM event handlers, which
657 * causes "issues" on IE<8.
660 * document.onclick = OpenLayers.Function.True;
671 * A reusable function that returns ``undefined``.
681 * Namespace: OpenLayers.Array
682 * Contains convenience functions for array manipulation.
688 * Filter an array. Provides the functionality of the
689 * Array.prototype.filter extension to the ECMA-262 standard. Where
690 * available, Array.prototype.filter will be used.
692 * Based on well known example from http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/filter
695 * array - {Array} The array to be filtered. This array is not mutated.
696 * Elements added to this array by the callback will not be visited.
697 * callback - {Function} A function that is called for each element in
698 * the array. If this function returns true, the element will be
699 * included in the return. The function will be called with three
700 * arguments: the element in the array, the index of that element, and
701 * the array itself. If the optional caller parameter is specified
702 * the callback will be called with this set to caller.
703 * caller - {Object} Optional object to be set as this when the callback
707 * {Array} An array of elements from the passed in array for which the
708 * callback returns true.
710 filter: function(array, callback, caller) {
712 if (Array.prototype.filter) {
713 selected = array.filter(callback, caller);
715 var len = array.length;
716 if (typeof callback != "function") {
717 throw new TypeError();
719 for(var i=0; i<len; i++) {
722 if (callback.call(caller, val, i, array)) {
732 /* ======================================================================
733 OpenLayers/BaseTypes/Bounds.js
734 ====================================================================== */
736 /* Copyright (c) 2006-2015 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 decimal places 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-2015 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-2015 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-2015 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-2015 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-2015 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 extension 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-2015 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-2015 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");
4395 * Function: getConstructor
4396 * Take an OpenLayers style CLASS_NAME and return a constructor.
4399 * className - {String} The dot delimited class name (e.g. 'OpenLayers.Foo').
4402 * {Function} The constructor.
4404 OpenLayers.Util.getConstructor = function(className) {
4406 var parts = className.split('.');
4407 if (parts[0] === "OpenLayers") {
4408 Constructor = OpenLayers;
4410 // someone extended our base class and used their own namespace
4411 // this will not work when the library is evaluated in a closure
4412 // but it is the best we can do (until we ourselves provide a global)
4413 Constructor = window[parts[0]];
4415 for (var i = 1, ii = parts.length; i < ii; ++i) {
4416 Constructor = Constructor[parts[i]];
4420 /* ======================================================================
4421 OpenLayers/Events.js
4422 ====================================================================== */
4424 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
4425 * full list of contributors). Published under the 2-clause BSD license.
4426 * See license.txt in the OpenLayers distribution or repository for the
4427 * full text of the license. */
4431 * @requires OpenLayers/Util.js
4435 * Namespace: OpenLayers.Event
4436 * Utility functions for event handling.
4438 OpenLayers.Event = {
4441 * Property: observers
4442 * {Object} A hashtable cache of the event observers. Keyed by
4443 * element._eventCacheID
4448 * Constant: KEY_SPACE
4454 * Constant: KEY_BACKSPACE
4466 * Constant: KEY_RETURN
4478 * Constant: KEY_LEFT
4490 * Constant: KEY_RIGHT
4496 * Constant: KEY_DOWN
4502 * Constant: KEY_DELETE
4510 * Cross browser event element detection.
4516 * {DOMElement} The element that caused the event
4518 element: function(event) {
4519 return event.target || event.srcElement;
4523 * Method: isSingleTouch
4524 * Determine whether event was caused by a single touch
4532 isSingleTouch: function(event) {
4533 return event.touches && event.touches.length == 1;
4537 * Method: isMultiTouch
4538 * Determine whether event was caused by a multi touch
4546 isMultiTouch: function(event) {
4547 return event.touches && event.touches.length > 1;
4551 * Method: isTouchEvent
4552 * Determine whether the event was triggered by a touch
4560 isTouchEvent: function(evt) {
4561 return ("" + evt.type).indexOf("touch") === 0 || (
4562 "pointerType" in evt && (
4563 evt.pointerType === evt.MSPOINTER_TYPE_TOUCH /*IE10 pointer*/ ||
4564 evt.pointerType === "touch" /*W3C pointer*/));
4568 * Method: isLeftClick
4569 * Determine whether event was caused by a left click.
4577 isLeftClick: function(event) {
4578 return (((event.which) && (event.which == 1)) ||
4579 ((event.button) && (event.button == 1)));
4583 * Method: isRightClick
4584 * Determine whether event was caused by a right mouse click.
4592 isRightClick: function(event) {
4593 return (((event.which) && (event.which == 3)) ||
4594 ((event.button) && (event.button == 2)));
4599 * Stops an event from propagating.
4603 * allowDefault - {Boolean} If true, we stop the event chain but
4604 * still allow the default browser behaviour (text selection,
4605 * radio-button clicking, etc). Default is false.
4607 stop: function(event, allowDefault) {
4609 if (!allowDefault) {
4610 OpenLayers.Event.preventDefault(event);
4613 if (event.stopPropagation) {
4614 event.stopPropagation();
4616 event.cancelBubble = true;
4621 * Method: preventDefault
4622 * Cancels the event if it is cancelable, without stopping further
4623 * propagation of the event.
4628 preventDefault: function(event) {
4629 if (event.preventDefault) {
4630 event.preventDefault();
4632 event.returnValue = false;
4637 * Method: findElement
4641 * tagName - {String}
4644 * {DOMElement} The first node with the given tagName, starting from the
4645 * node the event was triggered on and traversing the DOM upwards
4647 findElement: function(event, tagName) {
4648 var element = OpenLayers.Event.element(event);
4649 while (element.parentNode && (!element.tagName ||
4650 (element.tagName.toUpperCase() != tagName.toUpperCase()))){
4651 element = element.parentNode;
4660 * elementParam - {DOMElement || String}
4662 * observer - {function}
4663 * useCapture - {Boolean}
4665 observe: function(elementParam, name, observer, useCapture) {
4666 var element = OpenLayers.Util.getElement(elementParam);
4667 useCapture = useCapture || false;
4669 if (name == 'keypress' &&
4670 (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
4671 || element.attachEvent)) {
4675 //if observers cache has not yet been created, create it
4676 if (!this.observers) {
4677 this.observers = {};
4680 //if not already assigned, make a new unique cache ID
4681 if (!element._eventCacheID) {
4682 var idPrefix = "eventCacheID_";
4684 idPrefix = element.id + "_" + idPrefix;
4686 element._eventCacheID = OpenLayers.Util.createUniqueID(idPrefix);
4689 var cacheID = element._eventCacheID;
4691 //if there is not yet a hash entry for this element, add one
4692 if (!this.observers[cacheID]) {
4693 this.observers[cacheID] = [];
4696 //add a new observer to this element's list
4697 this.observers[cacheID].push({
4700 'observer': observer,
4701 'useCapture': useCapture
4704 //add the actual browser event listener
4705 if (element.addEventListener) {
4706 element.addEventListener(name, observer, useCapture);
4707 } else if (element.attachEvent) {
4708 element.attachEvent('on' + name, observer);
4713 * Method: stopObservingElement
4714 * Given the id of an element to stop observing, cycle through the
4715 * element's cached observers, calling stopObserving on each one,
4716 * skipping those entries which can no longer be removed.
4719 * elementParam - {DOMElement || String}
4721 stopObservingElement: function(elementParam) {
4722 var element = OpenLayers.Util.getElement(elementParam);
4723 var cacheID = element._eventCacheID;
4725 this._removeElementObservers(OpenLayers.Event.observers[cacheID]);
4729 * Method: _removeElementObservers
4732 * elementObservers - {Array(Object)} Array of (element, name,
4733 * observer, usecapture) objects,
4734 * taken directly from hashtable
4736 _removeElementObservers: function(elementObservers) {
4737 if (elementObservers) {
4738 for(var i = elementObservers.length-1; i >= 0; i--) {
4739 var entry = elementObservers[i];
4740 OpenLayers.Event.stopObserving.apply(this, [
4741 entry.element, entry.name, entry.observer, entry.useCapture
4748 * Method: stopObserving
4751 * elementParam - {DOMElement || String}
4753 * observer - {function}
4754 * useCapture - {Boolean}
4757 * {Boolean} Whether or not the event observer was removed
4759 stopObserving: function(elementParam, name, observer, useCapture) {
4760 useCapture = useCapture || false;
4762 var element = OpenLayers.Util.getElement(elementParam);
4763 var cacheID = element._eventCacheID;
4765 if (name == 'keypress') {
4766 if ( navigator.appVersion.match(/Konqueror|Safari|KHTML/) ||
4767 element.detachEvent) {
4772 // find element's entry in this.observers cache and remove it
4773 var foundEntry = false;
4774 var elementObservers = OpenLayers.Event.observers[cacheID];
4775 if (elementObservers) {
4777 // find the specific event type in the element's list
4779 while(!foundEntry && i < elementObservers.length) {
4780 var cacheEntry = elementObservers[i];
4782 if ((cacheEntry.name == name) &&
4783 (cacheEntry.observer == observer) &&
4784 (cacheEntry.useCapture == useCapture)) {
4786 elementObservers.splice(i, 1);
4787 if (elementObservers.length == 0) {
4788 delete OpenLayers.Event.observers[cacheID];
4797 //actually remove the event listener from browser
4799 if (element.removeEventListener) {
4800 element.removeEventListener(name, observer, useCapture);
4801 } else if (element && element.detachEvent) {
4802 element.detachEvent('on' + name, observer);
4809 * Method: unloadCache
4810 * Cycle through all the element entries in the events cache and call
4811 * stopObservingElement on each.
4813 unloadCache: function() {
4814 // check for OpenLayers.Event before checking for observers, because
4815 // OpenLayers.Event may be undefined in IE if no map instance was
4817 if (OpenLayers.Event && OpenLayers.Event.observers) {
4818 for (var cacheID in OpenLayers.Event.observers) {
4819 var elementObservers = OpenLayers.Event.observers[cacheID];
4820 OpenLayers.Event._removeElementObservers.apply(this,
4821 [elementObservers]);
4823 OpenLayers.Event.observers = false;
4827 CLASS_NAME: "OpenLayers.Event"
4830 /* prevent memory leaks in IE */
4831 OpenLayers.Event.observe(window, 'unload', OpenLayers.Event.unloadCache, false);
4834 * Class: OpenLayers.Events
4836 OpenLayers.Events = OpenLayers.Class({
4839 * Constant: BROWSER_EVENTS
4840 * {Array(String)} supported events
4843 "mouseover", "mouseout",
4844 "mousedown", "mouseup", "mousemove",
4845 "click", "dblclick", "rightclick", "dblrightclick",
4846 "resize", "focus", "blur",
4847 "touchstart", "touchmove", "touchend",
4852 * Constant: standard pointer model
4855 TOUCH_MODEL_POINTER: "pointer",
4858 * Constant: prefixed pointer model (IE10)
4861 TOUCH_MODEL_MSPOINTER: "MSPointer",
4864 * Constant: legacy touch model
4867 TOUCH_MODEL_TOUCH: "touch",
4870 * Property: listeners
4871 * {Object} Hashtable of Array(Function): events listener functions
4877 * {Object} the code object issuing application events
4883 * {DOMElement} the DOM element receiving browser events
4888 * Property: eventHandler
4889 * {Function} bound event handler attached to elements
4894 * APIProperty: fallThrough
4900 * APIProperty: includeXY
4901 * {Boolean} Should the .xy property automatically be created for browser
4902 * mouse events? In general, this should be false. If it is true, then
4903 * mouse events will automatically generate a '.xy' property on the
4904 * event object that is passed. (Prior to OpenLayers 2.7, this was true
4905 * by default.) Otherwise, you can call the getMousePosition on the
4906 * relevant events handler on the object available via the 'evt.object'
4907 * property of the evt object. So, for most events, you can call:
4908 * function named(evt) {
4909 * this.xy = this.object.events.getMousePosition(evt)
4912 * This option typically defaults to false for performance reasons:
4913 * when creating an events object whose primary purpose is to manage
4914 * relatively positioned mouse events within a div, it may make
4915 * sense to set it to true.
4917 * This option is also used to control whether the events object caches
4918 * offsets. If this is false, it will not: the reason for this is that
4919 * it is only expected to be called many times if the includeXY property
4920 * is set to true. If you set this to true, you are expected to clear
4921 * the offset cache manually (using this.clearMouseCache()) if:
4922 * the border of the element changes
4923 * the location of the element in the page changes
4928 * APIProperty: extensions
4929 * {Object} Event extensions registered with this instance. Keys are
4930 * event types, values are {OpenLayers.Events.*} extension instances or
4931 * {Boolean} for events that an instantiated extension provides in
4932 * addition to the one it was created for.
4934 * Extensions create an event in addition to browser events, which usually
4935 * fires when a sequence of browser events is completed. Extensions are
4936 * automatically instantiated when a listener is registered for an event
4937 * provided by an extension.
4939 * Extensions are created in the <OpenLayers.Events> namespace using
4940 * <OpenLayers.Class>, and named after the event they provide.
4941 * The constructor receives the target <OpenLayers.Events> instance as
4942 * argument. Extensions that need to capture browser events before they
4943 * propagate can register their listeners events using <register>, with
4944 * {extension: true} as 4th argument.
4946 * If an extension creates more than one event, an alias for each event
4947 * type should be created and reference the same class. The constructor
4948 * should set a reference in the target's extensions registry to itself.
4950 * Below is a minimal extension that provides the "foostart" and "fooend"
4951 * event types, which replace the native "click" event type if clicked on
4952 * an element with the css class "foo":
4955 * OpenLayers.Events.foostart = OpenLayers.Class({
4956 * initialize: function(target) {
4957 * this.target = target;
4958 * this.target.register("click", this, this.doStuff, {extension: true});
4959 * // only required if extension provides more than one event type
4960 * this.target.extensions["foostart"] = true;
4961 * this.target.extensions["fooend"] = true;
4963 * destroy: function() {
4964 * var target = this.target;
4965 * target.unregister("click", this, this.doStuff);
4966 * delete this.target;
4967 * // only required if extension provides more than one event type
4968 * delete target.extensions["foostart"];
4969 * delete target.extensions["fooend"];
4971 * doStuff: function(evt) {
4972 * var propagate = true;
4973 * if (OpenLayers.Event.element(evt).className === "foo") {
4974 * propagate = false;
4975 * var target = this.target;
4976 * target.triggerEvent("foostart");
4977 * window.setTimeout(function() {
4978 * target.triggerEvent("fooend");
4984 * // only required if extension provides more than one event type
4985 * OpenLayers.Events.fooend = OpenLayers.Events.foostart;
4992 * Property: extensionCount
4993 * {Object} Keys are event types (like in <listeners>), values are the
4994 * number of extension listeners for each event type.
4996 extensionCount: null,
4999 * Method: clearMouseListener
5000 * A version of <clearMouseCache> that is bound to this instance so that
5001 * it can be used with <OpenLayers.Event.observe> and
5002 * <OpenLayers.Event.stopObserving>.
5004 clearMouseListener: null,
5007 * Constructor: OpenLayers.Events
5008 * Construct an OpenLayers.Events object.
5011 * object - {Object} The js object to which this Events object is being added
5012 * element - {DOMElement} A dom element to respond to browser events
5013 * eventTypes - {Array(String)} Deprecated. Array of custom application
5014 * events. A listener may be registered for any named event, regardless
5015 * of the values provided here.
5016 * fallThrough - {Boolean} Allow events to fall through after these have
5018 * options - {Object} Options for the events object.
5020 initialize: function (object, element, eventTypes, fallThrough, options) {
5021 OpenLayers.Util.extend(this, options);
5022 this.object = object;
5023 this.fallThrough = fallThrough;
5024 this.listeners = {};
5025 this.extensions = {};
5026 this.extensionCount = {};
5027 this._pointerTouches = [];
5029 // if a dom element is specified, add a listeners list
5030 // for browser events on the element and register them
5031 if (element != null) {
5032 this.attachToElement(element);
5037 * APIMethod: destroy
5039 destroy: function () {
5040 for (var e in this.extensions) {
5041 if (typeof this.extensions[e] !== "boolean") {
5042 this.extensions[e].destroy();
5045 this.extensions = null;
5047 OpenLayers.Event.stopObservingElement(this.element);
5048 if(this.element.hasScrollEvent) {
5049 OpenLayers.Event.stopObserving(
5050 window, "scroll", this.clearMouseListener
5054 this.element = null;
5056 this.listeners = null;
5058 this.fallThrough = null;
5059 this.eventHandler = null;
5063 * APIMethod: addEventType
5064 * Deprecated. Any event can be triggered without adding it first.
5067 * eventName - {String}
5069 addEventType: function(eventName) {
5073 * Method: attachToElement
5076 * element - {HTMLDOMElement} a DOM element to attach browser events to
5078 attachToElement: function (element) {
5080 OpenLayers.Event.stopObservingElement(this.element);
5082 // keep a bound copy of handleBrowserEvent() so that we can
5083 // pass the same function to both Event.observe() and .stopObserving()
5084 this.eventHandler = OpenLayers.Function.bindAsEventListener(
5085 this.handleBrowserEvent, this
5088 // to be used with observe and stopObserving
5089 this.clearMouseListener = OpenLayers.Function.bind(
5090 this.clearMouseCache, this
5093 this.element = element;
5094 var touchModel = this.getTouchModel();
5096 for (var i = 0, len = this.BROWSER_EVENTS.length; i < len; i++) {
5097 type = this.BROWSER_EVENTS[i];
5098 // register the event cross-browser
5099 OpenLayers.Event.observe(element, type, this.eventHandler
5101 if ((touchModel === this.TOUCH_MODEL_POINTER ||
5102 touchModel === this.TOUCH_MODEL_MSPOINTER) &&
5103 type.indexOf('touch') === 0) {
5104 this.addPointerTouchListener(element, type, this.eventHandler);
5107 // disable dragstart in IE so that mousedown/move/up works normally
5108 OpenLayers.Event.observe(element, "dragstart", OpenLayers.Event.stop);
5113 * Convenience method for registering listeners with a common scope.
5114 * Internally, this method calls <register> as shown in the examples
5119 * // register a single listener for the "loadstart" event
5120 * events.on({"loadstart": loadStartListener});
5122 * // this is equivalent to the following
5123 * events.register("loadstart", undefined, loadStartListener);
5125 * // register multiple listeners to be called with the same `this` object
5127 * "loadstart": loadStartListener,
5128 * "loadend": loadEndListener,
5132 * // this is equivalent to the following
5133 * events.register("loadstart", object, loadStartListener);
5134 * events.register("loadend", object, loadEndListener);
5140 on: function(object) {
5141 for(var type in object) {
5142 if(type != "scope" && object.hasOwnProperty(type)) {
5143 this.register(type, object.scope, object[type]);
5149 * APIMethod: register
5150 * Register an event on the events object.
5152 * When the event is triggered, the 'func' function will be called, in the
5153 * context of 'obj'. Imagine we were to register an event, specifying an
5154 * OpenLayers.Bounds Object as 'obj'. When the event is triggered, the
5155 * context in the callback function will be our Bounds object. This means
5156 * that within our callback function, we can access the properties and
5157 * methods of the Bounds object through the "this" variable. So our
5158 * callback could execute something like:
5159 * : leftStr = "Left: " + this.left;
5163 * : centerStr = "Center: " + this.getCenterLonLat();
5166 * type - {String} Name of the event to register
5167 * obj - {Object} The object to bind the context to for the callback#.
5168 * If no object is specified, default is the Events's 'object' property.
5169 * func - {Function} The callback function. If no callback is
5170 * specified, this function does nothing.
5171 * priority - {Boolean|Object} If true, adds the new listener to the
5172 * *front* of the events queue instead of to the end.
5174 * Valid options for priority:
5175 * extension - {Boolean} If true, then the event will be registered as
5176 * extension event. Extension events are handled before all other
5179 register: function (type, obj, func, priority) {
5180 if (type in OpenLayers.Events && !this.extensions[type]) {
5181 this.extensions[type] = new OpenLayers.Events[type](this);
5187 var listeners = this.listeners[type];
5190 this.listeners[type] = listeners;
5191 this.extensionCount[type] = 0;
5193 var listener = {obj: obj, func: func};
5195 listeners.splice(this.extensionCount[type], 0, listener);
5196 if (typeof priority === "object" && priority.extension) {
5197 this.extensionCount[type]++;
5200 listeners.push(listener);
5206 * APIMethod: registerPriority
5207 * Same as register() but adds the new listener to the *front* of the
5208 * events queue instead of to the end.
5210 * TODO: get rid of this in 3.0 - Decide whether listeners should be
5211 * called in the order they were registered or in reverse order.
5215 * type - {String} Name of the event to register
5216 * obj - {Object} The object to bind the context to for the callback#.
5217 * If no object is specified, default is the Events's
5218 * 'object' property.
5219 * func - {Function} The callback function. If no callback is
5220 * specified, this function does nothing.
5222 registerPriority: function (type, obj, func) {
5223 this.register(type, obj, func, true);
5228 * Convenience method for unregistering listeners with a common scope.
5229 * Internally, this method calls <unregister> as shown in the examples
5234 * // unregister a single listener for the "loadstart" event
5235 * events.un({"loadstart": loadStartListener});
5237 * // this is equivalent to the following
5238 * events.unregister("loadstart", undefined, loadStartListener);
5240 * // unregister multiple listeners with the same `this` object
5242 * "loadstart": loadStartListener,
5243 * "loadend": loadEndListener,
5247 * // this is equivalent to the following
5248 * events.unregister("loadstart", object, loadStartListener);
5249 * events.unregister("loadend", object, loadEndListener);
5252 un: function(object) {
5253 for(var type in object) {
5254 if(type != "scope" && object.hasOwnProperty(type)) {
5255 this.unregister(type, object.scope, object[type]);
5261 * APIMethod: unregister
5265 * obj - {Object} If none specified, defaults to this.object
5268 unregister: function (type, obj, func) {
5272 var listeners = this.listeners[type];
5273 if (listeners != null) {
5274 for (var i=0, len=listeners.length; i<len; i++) {
5275 if (listeners[i].obj == obj && listeners[i].func == func) {
5276 listeners.splice(i, 1);
5285 * Remove all listeners for a given event type. If type is not registered,
5291 remove: function(type) {
5292 if (this.listeners[type] != null) {
5293 this.listeners[type] = [];
5298 * APIMethod: triggerEvent
5299 * Trigger a specified registered event.
5303 * evt - {Event || Object} will be passed to the listeners.
5306 * {Boolean} The last listener return. If a listener returns false, the
5307 * chain of listeners will stop getting called.
5309 triggerEvent: function (type, evt) {
5310 var listeners = this.listeners[type];
5313 if(!listeners || listeners.length == 0) {
5317 // prep evt object with object & div references
5321 evt.object = this.object;
5322 evt.element = this.element;
5327 // execute all callbacks registered for specified type
5328 // get a clone of the listeners array to
5329 // allow for splicing during callbacks
5330 listeners = listeners.slice();
5332 for (var i=0, len=listeners.length; i<len; i++) {
5333 var callback = listeners[i];
5334 // bind the context to callback.obj
5335 continueChain = callback.func.apply(callback.obj, [evt]);
5337 if ((continueChain != undefined) && (continueChain == false)) {
5338 // if callback returns false, execute no more callbacks.
5342 // don't fall through to other DOM elements
5343 if (!this.fallThrough) {
5344 OpenLayers.Event.stop(evt, true);
5346 return continueChain;
5350 * Method: handleBrowserEvent
5351 * Basically just a wrapper to the triggerEvent() function, but takes
5352 * care to set a property 'xy' on the event with the current mouse
5358 handleBrowserEvent: function (evt) {
5359 var type = evt.type, listeners = this.listeners[type];
5360 if(!listeners || listeners.length == 0) {
5361 // noone's listening, bail out
5364 // add clientX & clientY to all events - corresponds to average x, y
5365 var touches = evt.touches;
5366 if (touches && touches[0]) {
5369 var num = touches.length;
5371 for (var i=0; i<num; ++i) {
5372 touch = this.getTouchClientXY(touches[i]);
5376 evt.clientX = x / num;
5377 evt.clientY = y / num;
5379 if (this.includeXY) {
5380 evt.xy = this.getMousePosition(evt);
5382 this.triggerEvent(type, evt);
5386 * Method: getTouchClientXY
5387 * WebKit has a few bugs for clientX/clientY. This method detects them
5388 * and calculate the correct values.
5391 * evt - {Touch} a Touch object from a TouchEvent
5394 * {Object} An object with only clientX and clientY properties with the
5395 * calculated values.
5397 getTouchClientXY: function (evt) {
5398 // olMochWin is to override window, used for testing
5399 var win = window.olMockWin || window,
5400 winPageX = win.pageXOffset,
5401 winPageY = win.pageYOffset,
5405 if (evt.pageY === 0 && Math.floor(y) > Math.floor(evt.pageY) ||
5406 evt.pageX === 0 && Math.floor(x) > Math.floor(evt.pageX)) {
5407 // iOS4 include scroll offset in clientX/Y
5410 } else if (y < (evt.pageY - winPageY) || x < (evt.pageX - winPageX) ) {
5411 // Some Android browsers have totally bogus values for clientX/Y
5412 // when scrolling/zooming a page
5413 x = evt.pageX - winPageX;
5414 y = evt.pageY - winPageY;
5427 * APIMethod: clearMouseCache
5428 * Clear cached data about the mouse position. This should be called any
5429 * time the element that events are registered on changes position
5432 clearMouseCache: function() {
5433 this.element.scrolls = null;
5434 this.element.lefttop = null;
5435 this.element.offsets = null;
5439 * Method: getMousePosition
5445 * {<OpenLayers.Pixel>} The current xy coordinate of the mouse, adjusted
5448 getMousePosition: function (evt) {
5449 if (!this.includeXY) {
5450 this.clearMouseCache();
5451 } else if (!this.element.hasScrollEvent) {
5452 OpenLayers.Event.observe(window, "scroll", this.clearMouseListener);
5453 this.element.hasScrollEvent = true;
5456 if (!this.element.scrolls) {
5457 var viewportElement = OpenLayers.Util.getViewportElement();
5458 this.element.scrolls = [
5459 window.pageXOffset || viewportElement.scrollLeft,
5460 window.pageYOffset || viewportElement.scrollTop
5464 if (!this.element.lefttop) {
5465 this.element.lefttop = [
5466 (document.documentElement.clientLeft || 0),
5467 (document.documentElement.clientTop || 0)
5471 if (!this.element.offsets) {
5472 this.element.offsets = OpenLayers.Util.pagePosition(this.element);
5475 return new OpenLayers.Pixel(
5476 (evt.clientX + this.element.scrolls[0]) - this.element.offsets[0]
5477 - this.element.lefttop[0],
5478 (evt.clientY + this.element.scrolls[1]) - this.element.offsets[1]
5479 - this.element.lefttop[1]
5484 * Method: getTouchModel
5485 * Get the touch model currently in use.
5487 * This is cached on OpenLayers.Events as _TOUCH_MODEL
5490 * {string} The current touch model (TOUCH_MODEL_xxx), null if none
5492 getTouchModel: function() {
5493 if (!("_TOUCH_MODEL" in OpenLayers.Events)) {
5494 OpenLayers.Events._TOUCH_MODEL =
5495 (window.PointerEvent && "pointer") ||
5496 (window.MSPointerEvent && "MSPointer") ||
5497 (("ontouchdown" in document) && "touch") ||
5500 return OpenLayers.Events._TOUCH_MODEL;
5504 * Method: addPointerTouchListener
5507 * element - {DOMElement} The DOM element to register the listener on
5508 * type - {String} The event type
5509 * handler - {Function} the handler
5511 addPointerTouchListener: function (element, type, handler) {
5512 var eventHandler = this.eventHandler;
5513 var touches = this._pointerTouches;
5515 function pointerHandler(evt) {
5516 handler(OpenLayers.Util.applyDefaults({
5517 stopPropagation: function() {
5518 for (var i=touches.length-1; i>=0; --i) {
5519 touches[i].stopPropagation();
5522 preventDefault: function() {
5523 for (var i=touches.length-1; i>=0; --i) {
5524 touches[i].preventDefault();
5533 return this.addPointerTouchListenerStart(element, type, pointerHandler);
5535 return this.addPointerTouchListenerEnd(element, type, pointerHandler);
5537 return this.addPointerTouchListenerMove(element, type, pointerHandler);
5539 throw 'Unknown touch event type';
5544 * Method: addPointerTouchListenerStart
5547 * element - {DOMElement} The DOM element to register the listener on
5548 * type - {String} The event type
5549 * handler - {Function} the handler
5551 addPointerTouchListenerStart: function(element, type, handler) {
5552 var touches = this._pointerTouches;
5554 var cb = function(e) {
5556 // pointer could be mouse or pen
5557 if (!OpenLayers.Event.isTouchEvent(e)) {
5561 var alreadyInArray = false;
5562 for (var i=0, ii=touches.length; i<ii; ++i) {
5563 if (touches[i].pointerId == e.pointerId) {
5564 alreadyInArray = true;
5568 if (!alreadyInArray) {
5572 e.touches = touches.slice();
5576 OpenLayers.Event.observe(element,
5577 this.getTouchModel() === this.TOUCH_MODEL_MSPOINTER ?
5578 'MSPointerDown' : 'pointerdown',
5581 // the pointerId only needs to be removed from the _pointerTouches array
5582 // when the pointer has left its element
5583 var internalCb = function (e) {
5585 // pointer could be mouse or pen
5586 if (!OpenLayers.Event.isTouchEvent(e)) {
5591 for (var i = 0, ii = touches.length; i < ii; ++i) {
5592 if (touches[i].pointerId == e.pointerId) {
5593 if (this.clientWidth != 0 && this.clientHeight != 0) {
5594 if ((Math.ceil(e.clientX) >= this.clientWidth || Math.ceil(e.clientY) >= this.clientHeight)) {
5595 touches.splice(i, 1);
5602 OpenLayers.Event.observe(element,
5603 this.getTouchModel() === this.TOUCH_MODEL_MSPOINTER ?
5604 'MSPointerOut' : 'pointerout',
5609 * Method: addPointerTouchListenerMove
5612 * element - {DOMElement} The DOM element to register the listener on
5613 * type - {String} The event type
5614 * handler - {Function} the handler
5616 addPointerTouchListenerMove: function (element, type, handler) {
5617 var touches = this._pointerTouches;
5618 var cb = function(e) {
5620 // pointer could be mouse or pen
5621 if (!OpenLayers.Event.isTouchEvent(e)) {
5625 if (touches.length == 1 && touches[0].pageX == e.pageX &&
5626 touches[0].pageY == e.pageY) {
5627 // don't trigger event when pointer has not moved
5630 for (var i=0, ii=touches.length; i<ii; ++i) {
5631 if (touches[i].pointerId == e.pointerId) {
5637 e.touches = touches.slice();
5641 OpenLayers.Event.observe(element,
5642 this.getTouchModel() === this.TOUCH_MODEL_MSPOINTER ?
5643 'MSPointerMove' : 'pointermove',
5648 * Method: addPointerTouchListenerEnd
5651 * element - {DOMElement} The DOM element to register the listener on
5652 * type - {String} The event type
5653 * handler - {Function} the handler
5655 addPointerTouchListenerEnd: function (element, type, handler) {
5656 var touches = this._pointerTouches;
5658 var cb = function(e) {
5660 // pointer could be mouse or pen
5661 if (!OpenLayers.Event.isTouchEvent(e)) {
5665 for (var i=0, ii=touches.length; i<ii; ++i) {
5666 if (touches[i].pointerId == e.pointerId) {
5667 touches.splice(i, 1);
5672 e.touches = touches.slice();
5676 OpenLayers.Event.observe(element,
5677 this.getTouchModel() === this.TOUCH_MODEL_MSPOINTER ?
5678 'MSPointerUp' : 'pointerup',
5682 CLASS_NAME: "OpenLayers.Events"
5684 /* ======================================================================
5686 ====================================================================== */
5688 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
5689 * full list of contributors). Published under the 2-clause BSD license.
5690 * See license.txt in the OpenLayers distribution or repository for the
5691 * full text of the license. */
5694 * @requires OpenLayers/BaseTypes/Class.js
5698 * Class: OpenLayers.Icon
5700 * The icon represents a graphical icon on the screen. Typically used in
5701 * conjunction with a <OpenLayers.Marker> to represent markers on a screen.
5703 * An icon has a url, size and position. It also contains an offset which
5704 * allows the center point to be represented correctly. This can be
5705 * provided either as a fixed offset or a function provided to calculate
5706 * the desired offset.
5709 OpenLayers.Icon = OpenLayers.Class({
5713 * {String} image url
5719 * {<OpenLayers.Size>|Object} An OpenLayers.Size or
5720 * an object with a 'w' and 'h' properties.
5726 * {<OpenLayers.Pixel>|Object} distance in pixels to offset the
5727 * image when being rendered. An OpenLayers.Pixel or an object
5728 * with a 'x' and 'y' properties.
5733 * Property: calculateOffset
5734 * {Function} Function to calculate the offset (based on the size)
5736 calculateOffset: null,
5739 * Property: imageDiv
5746 * {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object
5747 * with a 'x' and 'y' properties.
5752 * Constructor: OpenLayers.Icon
5753 * Creates an icon, which is an image tag in a div.
5756 * size - {<OpenLayers.Size>|Object} An OpenLayers.Size or an
5757 * object with a 'w' and 'h'
5759 * offset - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an
5760 * object with a 'x' and 'y'
5762 * calculateOffset - {Function}
5764 initialize: function(url, size, offset, calculateOffset) {
5766 this.size = size || {w: 20, h: 20};
5767 this.offset = offset || {x: -(this.size.w/2), y: -(this.size.h/2)};
5768 this.calculateOffset = calculateOffset;
5770 var id = OpenLayers.Util.createUniqueID("OL_Icon_");
5771 this.imageDiv = OpenLayers.Util.createAlphaImageDiv(id);
5776 * Nullify references and remove event listeners to prevent circular
5777 * references and memory leaks
5779 destroy: function() {
5780 // erase any drawn elements
5783 OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild);
5784 this.imageDiv.innerHTML = "";
5785 this.imageDiv = null;
5792 * {<OpenLayers.Icon>} A fresh copy of the icon.
5795 return new OpenLayers.Icon(this.url,
5798 this.calculateOffset);
5805 * size - {<OpenLayers.Size>|Object} An OpenLayers.Size or
5806 * an object with a 'w' and 'h' properties.
5808 setSize: function(size) {
5821 setUrl: function(url) {
5830 * Move the div to the given pixel.
5833 * px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an
5834 * object with a 'x' and 'y' properties.
5837 * {DOMElement} A new DOM Image of this icon set at the location passed-in
5839 draw: function(px) {
5840 OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv,
5847 return this.imageDiv;
5852 * Erase the underlying image element.
5855 if (this.imageDiv != null && this.imageDiv.parentNode != null) {
5856 OpenLayers.Element.remove(this.imageDiv);
5861 * Method: setOpacity
5862 * Change the icon's opacity
5867 setOpacity: function(opacity) {
5868 OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, null,
5869 null, null, null, null, opacity);
5875 * move icon to passed in px.
5878 * px - {<OpenLayers.Pixel>|Object} the pixel position to move to.
5879 * An OpenLayers.Pixel or an object with a 'x' and 'y' properties.
5881 moveTo: function (px) {
5882 //if no px passed in, use stored location
5887 if (this.imageDiv != null) {
5888 if (this.px == null) {
5889 this.display(false);
5891 if (this.calculateOffset) {
5892 this.offset = this.calculateOffset(this.size);
5894 OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, {
5895 x: this.px.x + this.offset.x,
5896 y: this.px.y + this.offset.y
5904 * Hide or show the icon
5907 * display - {Boolean}
5909 display: function(display) {
5910 this.imageDiv.style.display = (display) ? "" : "none";
5915 * APIMethod: isDrawn
5918 * {Boolean} Whether or not the icon is drawn.
5920 isDrawn: function() {
5921 // nodeType 11 for ie, whose nodes *always* have a parentNode
5922 // (of type document fragment)
5923 var isDrawn = (this.imageDiv && this.imageDiv.parentNode &&
5924 (this.imageDiv.parentNode.nodeType != 11));
5929 CLASS_NAME: "OpenLayers.Icon"
5931 /* ======================================================================
5932 OpenLayers/Marker.js
5933 ====================================================================== */
5935 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
5936 * full list of contributors). Published under the 2-clause BSD license.
5937 * See license.txt in the OpenLayers distribution or repository for the
5938 * full text of the license. */
5942 * @requires OpenLayers/BaseTypes/Class.js
5943 * @requires OpenLayers/Events.js
5944 * @requires OpenLayers/Icon.js
5948 * Class: OpenLayers.Marker
5949 * Instances of OpenLayers.Marker are a combination of a
5950 * <OpenLayers.LonLat> and an <OpenLayers.Icon>.
5952 * Markers are generally added to a special layer called
5953 * <OpenLayers.Layer.Markers>.
5957 * var markers = new OpenLayers.Layer.Markers( "Markers" );
5958 * map.addLayer(markers);
5960 * var size = new OpenLayers.Size(21,25);
5961 * var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
5962 * var icon = new OpenLayers.Icon('http://www.openlayers.org/dev/img/marker.png', size, offset);
5963 * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon));
5964 * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon.clone()));
5968 * Note that if you pass an icon into the Marker constructor, it will take
5969 * that icon and use it. This means that you should not share icons between
5970 * markers -- you use them once, but you should clone() for any additional
5971 * markers using that same icon.
5973 OpenLayers.Marker = OpenLayers.Class({
5977 * {<OpenLayers.Icon>} The icon used by this marker.
5983 * {<OpenLayers.LonLat>} location of object
5989 * {<OpenLayers.Events>} the event handler.
5995 * {<OpenLayers.Map>} the map this marker is attached to
6000 * Constructor: OpenLayers.Marker
6003 * lonlat - {<OpenLayers.LonLat>} the position of this marker
6004 * icon - {<OpenLayers.Icon>} the icon for this marker
6006 initialize: function(lonlat, icon) {
6007 this.lonlat = lonlat;
6009 var newIcon = (icon) ? icon : OpenLayers.Marker.defaultIcon();
6010 if (this.icon == null) {
6011 this.icon = newIcon;
6013 this.icon.url = newIcon.url;
6014 this.icon.size = newIcon.size;
6015 this.icon.offset = newIcon.offset;
6016 this.icon.calculateOffset = newIcon.calculateOffset;
6018 this.events = new OpenLayers.Events(this, this.icon.imageDiv);
6022 * APIMethod: destroy
6023 * Destroy the marker. You must first remove the marker from any
6024 * layer which it has been added to, or you will get buggy behavior.
6025 * (This can not be done within the marker since the marker does not
6026 * know which layer it is attached to.)
6028 destroy: function() {
6029 // erase any drawn features
6034 this.events.destroy();
6037 if (this.icon != null) {
6038 this.icon.destroy();
6045 * Calls draw on the icon, and returns that output.
6048 * px - {<OpenLayers.Pixel>}
6051 * {DOMElement} A new DOM Image with this marker's icon set at the
6052 * location passed-in
6054 draw: function(px) {
6055 return this.icon.draw(px);
6060 * Erases any drawn elements for this marker.
6063 if (this.icon != null) {
6070 * Move the marker to the new location.
6073 * px - {<OpenLayers.Pixel>|Object} the pixel position to move to.
6074 * An OpenLayers.Pixel or an object with a 'x' and 'y' properties.
6076 moveTo: function (px) {
6077 if ((px != null) && (this.icon != null)) {
6078 this.icon.moveTo(px);
6080 this.lonlat = this.map.getLonLatFromLayerPx(px);
6084 * APIMethod: isDrawn
6087 * {Boolean} Whether or not the marker is drawn.
6089 isDrawn: function() {
6090 var isDrawn = (this.icon && this.icon.isDrawn());
6098 * {Boolean} Whether or not the marker is currently visible on screen.
6100 onScreen:function() {
6102 var onScreen = false;
6104 var screenBounds = this.map.getExtent();
6105 onScreen = screenBounds.containsLonLat(this.lonlat);
6112 * Englarges the markers icon by the specified ratio.
6115 * inflate - {float} the ratio to enlarge the marker by (passing 2
6116 * will double the size).
6118 inflate: function(inflate) {
6121 w: this.icon.size.w * inflate,
6122 h: this.icon.size.h * inflate
6128 * Method: setOpacity
6129 * Change the opacity of the marker by changin the opacity of
6133 * opacity - {float} Specified as fraction (0.4, etc)
6135 setOpacity: function(opacity) {
6136 this.icon.setOpacity(opacity);
6141 * Change URL of the Icon Image.
6145 setUrl: function(url) {
6146 this.icon.setUrl(url);
6151 * Hide or show the icon
6153 * display - {Boolean}
6155 display: function(display) {
6156 this.icon.display(display);
6159 CLASS_NAME: "OpenLayers.Marker"
6164 * Function: defaultIcon
6165 * Creates a default <OpenLayers.Icon>.
6168 * {<OpenLayers.Icon>} A default OpenLayers.Icon to use for a marker
6170 OpenLayers.Marker.defaultIcon = function() {
6171 return new OpenLayers.Icon(OpenLayers.Util.getImageLocation("marker.png"),
6172 {w: 21, h: 25}, {x: -10.5, y: -25});
6176 /* ======================================================================
6177 OpenLayers/Util/vendorPrefix.js
6178 ====================================================================== */
6180 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
6181 * full list of contributors). Published under the 2-clause BSD license.
6182 * See license.txt in the OpenLayers distribution or repository for the
6183 * full text of the license. */
6186 * @requires OpenLayers/SingleFile.js
6189 OpenLayers.Util = OpenLayers.Util || {};
6191 * Namespace: OpenLayers.Util.vendorPrefix
6192 * A collection of utility functions to detect vendor prefixed features
6194 OpenLayers.Util.vendorPrefix = (function() {
6197 var VENDOR_PREFIXES = ["", "O", "ms", "Moz", "Webkit"],
6198 divStyle = document.createElement("div").style,
6204 * Function: domToCss
6205 * Converts a upper camel case DOM style property name to a CSS property
6206 * i.e. transformOrigin -> transform-origin
6207 * or WebkitTransformOrigin -> -webkit-transform-origin
6210 * prefixedDom - {String} The property to convert
6213 * {String} The CSS property
6215 function domToCss(prefixedDom) {
6216 if (!prefixedDom) { return null; }
6218 replace(/([A-Z])/g, function(c) { return "-" + c.toLowerCase(); }).
6219 replace(/^ms-/, "-ms-");
6224 * Detect which property is used for a CSS property
6227 * property - {String} The standard (unprefixed) CSS property name
6230 * {String} The standard CSS property, prefixed property or null if not
6233 function css(property) {
6234 if (cssCache[property] === undefined) {
6235 var domProperty = property.
6236 replace(/(-[\s\S])/g, function(c) { return c.charAt(1).toUpperCase(); });
6237 var prefixedDom = style(domProperty);
6238 cssCache[property] = domToCss(prefixedDom);
6240 return cssCache[property];
6245 * Detect which property is used for a JS property/method
6248 * obj - {Object} The object to test on
6249 * property - {String} The standard (unprefixed) JS property name
6252 * {String} The standard JS property, prefixed property or null if not
6255 function js(obj, property) {
6256 if (jsCache[property] === undefined) {
6259 l = VENDOR_PREFIXES.length,
6261 isStyleObj = (typeof obj.cssText !== "undefined");
6263 jsCache[property] = null;
6265 prefix = VENDOR_PREFIXES[i];
6268 // js prefix should be lower-case, while style
6269 // properties have upper case on first character
6270 prefix = prefix.toLowerCase();
6272 tmpProp = prefix + property.charAt(0).toUpperCase() + property.slice(1);
6277 if(obj[tmpProp] !== undefined) {
6278 jsCache[property] = tmpProp;
6283 return jsCache[property];
6288 * Detect which property is used for a DOM style property
6291 * property - {String} The standard (unprefixed) style property name
6294 * {String} The standard style property, prefixed property or null if not
6297 function style(property) {
6298 return js(divStyle, property);
6311 /* ======================================================================
6312 OpenLayers/Animation.js
6313 ====================================================================== */
6315 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
6316 * full list of contributors). Published under the 2-clause BSD license.
6317 * See license.txt in the OpenLayers distribution or repository for the
6318 * full text of the license. */
6321 * @requires OpenLayers/SingleFile.js
6322 * @requires OpenLayers/Util/vendorPrefix.js
6326 * Namespace: OpenLayers.Animation
6327 * A collection of utility functions for executing methods that repaint a
6328 * portion of the browser window. These methods take advantage of the
6329 * browser's scheduled repaints where requestAnimationFrame is available.
6331 OpenLayers.Animation = (function(window) {
6334 * Property: isNative
6335 * {Boolean} true if a native requestAnimationFrame function is available
6337 var requestAnimationFrame = OpenLayers.Util.vendorPrefix.js(window, "requestAnimationFrame");
6338 var isNative = !!(requestAnimationFrame);
6341 * Function: requestFrame
6342 * Schedule a function to be called at the next available animation frame.
6343 * Uses the native method where available. Where requestAnimationFrame is
6344 * not available, setTimeout will be called with a 16ms delay.
6347 * callback - {Function} The function to be called at the next animation frame.
6348 * element - {DOMElement} Optional element that visually bounds the animation.
6350 var requestFrame = (function() {
6351 var request = window[requestAnimationFrame] ||
6352 function(callback, element) {
6353 window.setTimeout(callback, 16);
6355 // bind to window to avoid illegal invocation of native function
6356 return function(callback, element) {
6357 request.apply(window, [callback, element]);
6361 // private variables for animation loops
6367 * Executes a method with <requestFrame> in series for some
6371 * callback - {Function} The function to be called at the next animation frame.
6372 * duration - {Number} Optional duration for the loop. If not provided, the
6373 * animation loop will execute indefinitely.
6374 * element - {DOMElement} Optional element that visually bounds the animation.
6377 * {Number} Identifier for the animation loop. Used to stop animations with
6380 function start(callback, duration, element) {
6381 duration = duration > 0 ? duration : Number.POSITIVE_INFINITY;
6383 var start = +new Date;
6384 loops[id] = function() {
6385 if (loops[id] && +new Date - start <= duration) {
6388 requestFrame(loops[id], element);
6394 requestFrame(loops[id], element);
6400 * Terminates an animation loop started with <start>.
6403 * id - {Number} Identifier returned from <start>.
6411 requestFrame: requestFrame,
6417 /* ======================================================================
6419 ====================================================================== */
6421 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
6422 * full list of contributors). Published under the 2-clause BSD license.
6423 * See license.txt in the OpenLayers distribution or repository for the
6424 * full text of the license. */
6427 * @requires OpenLayers/BaseTypes/Class.js
6428 * @requires OpenLayers/Animation.js
6432 * Namespace: OpenLayers.Tween
6434 OpenLayers.Tween = OpenLayers.Class({
6437 * APIProperty: easing
6438 * {<OpenLayers.Easing>(Function)} Easing equation used for the animation
6439 * Defaultly set to OpenLayers.Easing.Expo.easeOut
6444 * APIProperty: begin
6445 * {Object} Values to start the animation with
6450 * APIProperty: finish
6451 * {Object} Values to finish the animation with
6456 * APIProperty: duration
6457 * {int} duration of the tween (number of steps)
6462 * APIProperty: callbacks
6463 * {Object} An object with start, eachStep and done properties whose values
6464 * are functions to be call during the animation. They are passed the
6465 * current computed value as argument.
6471 * {int} Step counter
6476 * APIProperty: minFrameRate
6477 * {Number} The minimum framerate for animations in frames per second. After
6478 * each step, the time spent in the animation is compared to the calculated
6479 * time at this frame rate. If the animation runs longer than the calculated
6480 * time, the next step is skipped. Default is 30.
6485 * Property: startTime
6486 * {Number} The timestamp of the first execution step. Used for skipping
6492 * Property: animationId
6493 * {int} Loop id returned by OpenLayers.Animation.start
6499 * {Boolean} Tells if the easing is currently playing
6504 * Constructor: OpenLayers.Tween
6508 * easing - {<OpenLayers.Easing>(Function)} easing function method to use
6510 initialize: function(easing) {
6511 this.easing = (easing) ? easing : OpenLayers.Easing.Expo.easeOut;
6516 * Plays the Tween, and calls the callback method on each step
6519 * begin - {Object} values to start the animation with
6520 * finish - {Object} values to finish the animation with
6521 * duration - {int} duration of the tween (number of steps)
6522 * options - {Object} hash of options (callbacks (start, eachStep, done),
6525 start: function(begin, finish, duration, options) {
6526 this.playing = true;
6528 this.finish = finish;
6529 this.duration = duration;
6530 this.callbacks = options.callbacks;
6531 this.minFrameRate = options.minFrameRate || 30;
6533 this.startTime = new Date().getTime();
6534 OpenLayers.Animation.stop(this.animationId);
6535 this.animationId = null;
6536 if (this.callbacks && this.callbacks.start) {
6537 this.callbacks.start.call(this, this.begin);
6539 this.animationId = OpenLayers.Animation.start(
6540 OpenLayers.Function.bind(this.play, this)
6546 * Stops the Tween, and calls the done callback
6547 * Doesn't do anything if animation is already finished
6550 if (!this.playing) {
6554 if (this.callbacks && this.callbacks.done) {
6555 this.callbacks.done.call(this, this.finish);
6557 OpenLayers.Animation.stop(this.animationId);
6558 this.animationId = null;
6559 this.playing = false;
6564 * Calls the appropriate easing method
6568 for (var i in this.begin) {
6569 var b = this.begin[i];
6570 var f = this.finish[i];
6571 if (b == null || f == null || isNaN(b) || isNaN(f)) {
6572 throw new TypeError('invalid value for Tween');
6576 value[i] = this.easing.apply(this, [this.time, b, c, this.duration]);
6580 if (this.callbacks && this.callbacks.eachStep) {
6581 // skip frames if frame rate drops below threshold
6582 if ((new Date().getTime() - this.startTime) / this.time <= 1000 / this.minFrameRate) {
6583 this.callbacks.eachStep.call(this, value);
6587 if (this.time > this.duration) {
6593 * Create empty functions for all easing methods.
6595 CLASS_NAME: "OpenLayers.Tween"
6599 * Namespace: OpenLayers.Easing
6602 * Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>
6604 OpenLayers.Easing = {
6606 * Create empty functions for all easing methods.
6608 CLASS_NAME: "OpenLayers.Easing"
6612 * Namespace: OpenLayers.Easing.Linear
6614 OpenLayers.Easing.Linear = {
6621 * b - {Float} beginning position
6622 * c - {Float} total change
6623 * d - {Float} duration of the transition
6628 easeIn: function(t, b, c, d) {
6637 * b - {Float} beginning position
6638 * c - {Float} total change
6639 * d - {Float} duration of the transition
6644 easeOut: function(t, b, c, d) {
6649 * Function: easeInOut
6653 * b - {Float} beginning position
6654 * c - {Float} total change
6655 * d - {Float} duration of the transition
6660 easeInOut: function(t, b, c, d) {
6664 CLASS_NAME: "OpenLayers.Easing.Linear"
6668 * Namespace: OpenLayers.Easing.Expo
6670 OpenLayers.Easing.Expo = {
6677 * b - {Float} beginning position
6678 * c - {Float} total change
6679 * d - {Float} duration of the transition
6684 easeIn: function(t, b, c, d) {
6685 return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
6693 * b - {Float} beginning position
6694 * c - {Float} total change
6695 * d - {Float} duration of the transition
6700 easeOut: function(t, b, c, d) {
6701 return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
6705 * Function: easeInOut
6709 * b - {Float} beginning position
6710 * c - {Float} total change
6711 * d - {Float} duration of the transition
6716 easeInOut: function(t, b, c, d) {
6718 if (t==d) return b+c;
6719 if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
6720 return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
6723 CLASS_NAME: "OpenLayers.Easing.Expo"
6727 * Namespace: OpenLayers.Easing.Quad
6729 OpenLayers.Easing.Quad = {
6736 * b - {Float} beginning position
6737 * c - {Float} total change
6738 * d - {Float} duration of the transition
6743 easeIn: function(t, b, c, d) {
6744 return c*(t/=d)*t + b;
6752 * b - {Float} beginning position
6753 * c - {Float} total change
6754 * d - {Float} duration of the transition
6759 easeOut: function(t, b, c, d) {
6760 return -c *(t/=d)*(t-2) + b;
6764 * Function: easeInOut
6768 * b - {Float} beginning position
6769 * c - {Float} total change
6770 * d - {Float} duration of the transition
6775 easeInOut: function(t, b, c, d) {
6776 if ((t/=d/2) < 1) return c/2*t*t + b;
6777 return -c/2 * ((--t)*(t-2) - 1) + b;
6780 CLASS_NAME: "OpenLayers.Easing.Quad"
6782 /* ======================================================================
6783 OpenLayers/Projection.js
6784 ====================================================================== */
6786 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
6787 * full list of contributors). Published under the 2-clause BSD license.
6788 * See license.txt in the OpenLayers distribution or repository for the
6789 * full text of the license. */
6792 * @requires OpenLayers/BaseTypes/Class.js
6793 * @requires OpenLayers/Util.js
6797 * Namespace: OpenLayers.Projection
6798 * Methods for coordinate transforms between coordinate systems. By default,
6799 * OpenLayers ships with the ability to transform coordinates between
6800 * geographic (EPSG:4326) and web or spherical mercator (EPSG:900913 et al.)
6801 * coordinate reference systems. See the <transform> method for details
6804 * Additional transforms may be added by using the <proj4js at http://proj4js.org/>
6805 * library. If the proj4js library is included, the <transform> method
6806 * will work between any two coordinate reference systems with proj4js
6809 * If the proj4js library is not included, or if you wish to allow transforms
6810 * between arbitrary coordinate reference systems, use the <addTransform>
6811 * method to register a custom transform method.
6813 OpenLayers.Projection = OpenLayers.Class({
6817 * {Object} Proj4js.Proj instance.
6822 * Property: projCode
6828 * Property: titleRegEx
6829 * {RegExp} regular expression to strip the title from a proj4js definition
6831 titleRegEx: /\+title=[^\+]*/,
6834 * Constructor: OpenLayers.Projection
6835 * This class offers several methods for interacting with a wrapped
6836 * pro4js projection object.
6839 * projCode - {String} A string identifying the Well Known Identifier for
6841 * options - {Object} An optional object to set additional properties
6842 * on the projection.
6845 * {<OpenLayers.Projection>} A projection object.
6847 initialize: function(projCode, options) {
6848 OpenLayers.Util.extend(this, options);
6849 this.projCode = projCode;
6850 if (typeof Proj4js == "object") {
6851 this.proj = new Proj4js.Proj(projCode);
6856 * APIMethod: getCode
6857 * Get the string SRS code.
6860 * {String} The SRS code.
6862 getCode: function() {
6863 return this.proj ? this.proj.srsCode : this.projCode;
6867 * APIMethod: getUnits
6868 * Get the units string for the projection -- returns null if
6869 * proj4js is not available.
6872 * {String} The units abbreviation.
6874 getUnits: function() {
6875 return this.proj ? this.proj.units : null;
6880 * Convert projection to string (getCode wrapper).
6883 * {String} The projection code.
6885 toString: function() {
6886 return this.getCode();
6891 * Test equality of two projection instances. Determines equality based
6892 * solely on the projection code.
6895 * {Boolean} The two projections are equivalent.
6897 equals: function(projection) {
6898 var p = projection, equals = false;
6900 if (!(p instanceof OpenLayers.Projection)) {
6901 p = new OpenLayers.Projection(p);
6903 if ((typeof Proj4js == "object") && this.proj.defData && p.proj.defData) {
6904 equals = this.proj.defData.replace(this.titleRegEx, "") ==
6905 p.proj.defData.replace(this.titleRegEx, "");
6906 } else if (p.getCode) {
6907 var source = this.getCode(), target = p.getCode();
6908 equals = source == target ||
6909 !!OpenLayers.Projection.transforms[source] &&
6910 OpenLayers.Projection.transforms[source][target] ===
6911 OpenLayers.Projection.nullTransform;
6918 * Destroy projection object.
6920 destroy: function() {
6922 delete this.projCode;
6925 CLASS_NAME: "OpenLayers.Projection"
6929 * Property: transforms
6930 * {Object} Transforms is an object, with from properties, each of which may
6931 * have a to property. This allows you to define projections without
6932 * requiring support for proj4js to be included.
6934 * This object has keys which correspond to a 'source' projection object. The
6935 * keys should be strings, corresponding to the projection.getCode() value.
6936 * Each source projection object should have a set of destination projection
6937 * keys included in the object.
6939 * Each value in the destination object should be a transformation function,
6940 * where the function is expected to be passed an object with a .x and a .y
6941 * property. The function should return the object, with the .x and .y
6942 * transformed according to the transformation function.
6944 * Note - Properties on this object should not be set directly. To add a
6945 * transform method to this object, use the <addTransform> method. For an
6946 * example of usage, see the OpenLayers.Layer.SphericalMercator file.
6948 OpenLayers.Projection.transforms = {};
6951 * APIProperty: defaults
6952 * {Object} Defaults for the SRS codes known to OpenLayers (currently
6953 * EPSG:4326, CRS:84, urn:ogc:def:crs:EPSG:6.6:4326, EPSG:900913, EPSG:3857,
6954 * EPSG:102113, EPSG:102100 and OSGEO:41001). Keys are the SRS code, values are
6955 * units, maxExtent (the validity extent for the SRS in projected coordinates),
6956 * worldExtent (the world's extent in EPSG:4326) and yx (true if this SRS
6957 * is known to have a reverse axis order).
6959 OpenLayers.Projection.defaults = {
6962 maxExtent: [-180, -90, 180, 90],
6963 worldExtent: [-180, -90, 180, 90],
6968 maxExtent: [-180, -90, 180, 90],
6969 worldExtent: [-180, -90, 180, 90]
6973 maxExtent: [-20037508.34, -20037508.34, 20037508.34, 20037508.34],
6974 worldExtent: [-180, -89, 180, 89]
6979 * APIMethod: addTransform
6980 * Set a custom transform method between two projections. Use this method in
6981 * cases where the proj4js lib is not available or where custom projections
6982 * need to be handled.
6985 * from - {String} The code for the source projection
6986 * to - {String} the code for the destination projection
6987 * method - {Function} A function that takes a point as an argument and
6988 * transforms that point from the source to the destination projection
6989 * in place. The original point should be modified.
6991 OpenLayers.Projection.addTransform = function(from, to, method) {
6992 if (method === OpenLayers.Projection.nullTransform) {
6993 var defaults = OpenLayers.Projection.defaults[from];
6994 if (defaults && !OpenLayers.Projection.defaults[to]) {
6995 OpenLayers.Projection.defaults[to] = defaults;
6998 if(!OpenLayers.Projection.transforms[from]) {
6999 OpenLayers.Projection.transforms[from] = {};
7001 OpenLayers.Projection.transforms[from][to] = method;
7005 * APIMethod: transform
7006 * Transform a point coordinate from one projection to another. Note that
7007 * the input point is transformed in place.
7010 * point - {<OpenLayers.Geometry.Point> | Object} An object with x and y
7011 * properties representing coordinates in those dimensions.
7012 * source - {OpenLayers.Projection} Source map coordinate system
7013 * dest - {OpenLayers.Projection} Destination map coordinate system
7016 * point - {object} A transformed coordinate. The original point is modified.
7018 OpenLayers.Projection.transform = function(point, source, dest) {
7019 if (source && dest) {
7020 if (!(source instanceof OpenLayers.Projection)) {
7021 source = new OpenLayers.Projection(source);
7023 if (!(dest instanceof OpenLayers.Projection)) {
7024 dest = new OpenLayers.Projection(dest);
7026 if (source.proj && dest.proj) {
7027 point = Proj4js.transform(source.proj, dest.proj, point);
7029 var sourceCode = source.getCode();
7030 var destCode = dest.getCode();
7031 var transforms = OpenLayers.Projection.transforms;
7032 if (transforms[sourceCode] && transforms[sourceCode][destCode]) {
7033 transforms[sourceCode][destCode](point);
7041 * APIFunction: nullTransform
7042 * A null transformation - useful for defining projection aliases when
7043 * proj4js is not available:
7046 * OpenLayers.Projection.addTransform("EPSG:3857", "EPSG:900913",
7047 * OpenLayers.Projection.nullTransform);
7048 * OpenLayers.Projection.addTransform("EPSG:900913", "EPSG:3857",
7049 * OpenLayers.Projection.nullTransform);
7052 OpenLayers.Projection.nullTransform = function(point) {
7057 * Note: Transforms for web mercator <-> geographic
7058 * OpenLayers recognizes EPSG:3857, EPSG:900913, EPSG:102113, EPSG:102100 and
7059 * OSGEO:41001. OpenLayers originally started referring to EPSG:900913 as web
7060 * mercator. The EPSG has declared EPSG:3857 to be web mercator.
7061 * ArcGIS 10 recognizes the EPSG:3857, EPSG:102113, and EPSG:102100 as
7062 * 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.
7063 * For geographic, OpenLayers recognizes EPSG:4326, CRS:84 and
7064 * urn:ogc:def:crs:EPSG:6.6:4326. OpenLayers also knows about the reverse axis
7065 * order for EPSG:4326.
7069 var pole = 20037508.34;
7071 function inverseMercator(xy) {
7072 xy.x = 180 * xy.x / pole;
7073 xy.y = 180 / Math.PI * (2 * Math.atan(Math.exp((xy.y / pole) * Math.PI)) - Math.PI / 2);
7077 function forwardMercator(xy) {
7078 xy.x = xy.x * pole / 180;
7079 var y = Math.log(Math.tan((90 + xy.y) * Math.PI / 360)) / Math.PI * pole;
7080 xy.y = Math.max(-20037508.34, Math.min(y, 20037508.34));
7084 function map(base, codes) {
7085 var add = OpenLayers.Projection.addTransform;
7086 var same = OpenLayers.Projection.nullTransform;
7087 var i, len, code, other, j;
7088 for (i=0, len=codes.length; i<len; ++i) {
7090 add(base, code, forwardMercator);
7091 add(code, base, inverseMercator);
7092 for (j=i+1; j<len; ++j) {
7094 add(code, other, same);
7095 add(other, code, same);
7100 // list of equivalent codes for web mercator
7101 var mercator = ["EPSG:900913", "EPSG:3857", "EPSG:102113", "EPSG:102100", "OSGEO:41001"],
7102 geographic = ["CRS:84", "urn:ogc:def:crs:EPSG:6.6:4326", "EPSG:4326"],
7104 for (i=mercator.length-1; i>=0; --i) {
7105 map(mercator[i], geographic);
7107 for (i=geographic.length-1; i>=0; --i) {
7108 map(geographic[i], mercator);
7112 /* ======================================================================
7114 ====================================================================== */
7116 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
7117 * full list of contributors). Published under the 2-clause BSD license.
7118 * See license.txt in the OpenLayers distribution or repository for the
7119 * full text of the license. */
7122 * @requires OpenLayers/BaseTypes/Class.js
7123 * @requires OpenLayers/Util.js
7124 * @requires OpenLayers/Util/vendorPrefix.js
7125 * @requires OpenLayers/Events.js
7126 * @requires OpenLayers/Tween.js
7127 * @requires OpenLayers/Projection.js
7131 * Class: OpenLayers.Map
7132 * Instances of OpenLayers.Map are interactive maps embedded in a web page.
7133 * Create a new map with the <OpenLayers.Map> constructor.
7135 * On their own maps do not provide much functionality. To extend a map
7136 * it's necessary to add controls (<OpenLayers.Control>) and
7137 * layers (<OpenLayers.Layer>) to the map.
7139 OpenLayers.Map = OpenLayers.Class({
7142 * Constant: Z_INDEX_BASE
7143 * {Object} Base z-indexes for different classes of thing
7154 * APIProperty: events
7155 * {<OpenLayers.Events>}
7157 * Register a listener for a particular event with the following syntax:
7159 * map.events.register(type, obj, listener);
7162 * Listeners will be called with a reference to an event object. The
7163 * properties of this event depends on exactly what happened.
7165 * All event objects have at least the following properties:
7166 * object - {Object} A reference to map.events.object.
7167 * element - {DOMElement} A reference to map.events.element.
7169 * Browser events have the following additional properties:
7170 * xy - {<OpenLayers.Pixel>} The pixel location of the event (relative
7171 * to the the map viewport).
7173 * Supported map event types:
7174 * preaddlayer - triggered before a layer has been added. The event
7175 * object will include a *layer* property that references the layer
7176 * to be added. When a listener returns "false" the adding will be
7178 * addlayer - triggered after a layer has been added. The event object
7179 * will include a *layer* property that references the added layer.
7180 * preremovelayer - triggered before a layer has been removed. The event
7181 * object will include a *layer* property that references the layer
7182 * to be removed. When a listener returns "false" the removal will be
7184 * removelayer - triggered after a layer has been removed. The event
7185 * object will include a *layer* property that references the removed
7187 * changelayer - triggered after a layer name change, order change,
7188 * opacity change, params change, visibility change (actual visibility,
7189 * not the layer's visibility property) or attribution change (due to
7190 * extent change). Listeners will receive an event object with *layer*
7191 * and *property* properties. The *layer* property will be a reference
7192 * to the changed layer. The *property* property will be a key to the
7193 * changed property (name, order, opacity, params, visibility or
7195 * movestart - triggered after the start of a drag, pan, or zoom. The event
7196 * object may include a *zoomChanged* property that tells whether the
7198 * move - triggered after each drag, pan, or zoom
7199 * moveend - triggered after a drag, pan, or zoom completes
7200 * zoomstart - triggered when a zoom starts. Listeners receive an object
7201 * with *center* and *zoom* properties, for the target center and zoom
7203 * zoomend - triggered after a zoom completes
7204 * mouseover - triggered after mouseover the map
7205 * mouseout - triggered after mouseout the map
7206 * mousemove - triggered after mousemove the map
7207 * changebaselayer - triggered after the base layer changes
7208 * updatesize - triggered after the <updateSize> method was executed
7213 * {String} Unique identifier for the map
7218 * Property: fractionalZoom
7219 * {Boolean} For a base layer that supports it, allow the map resolution
7220 * to be set to a value between one of the values in the resolutions
7221 * array. Default is false.
7223 * When fractionalZoom is set to true, it is possible to zoom to
7224 * an arbitrary extent. This requires a base layer from a source
7225 * that supports requests for arbitrary extents (i.e. not cached
7226 * tiles on a regular lattice). This means that fractionalZoom
7227 * will not work with commercial layers (Google, Yahoo, VE), layers
7228 * using TileCache, or any other pre-cached data sources.
7230 * If you are using fractionalZoom, then you should also use
7231 * <getResolutionForZoom> instead of layer.resolutions[zoom] as the
7232 * former works for non-integer zoom levels.
7234 fractionalZoom: false,
7237 * APIProperty: events
7238 * {<OpenLayers.Events>} An events object that handles all
7244 * APIProperty: allOverlays
7245 * {Boolean} Allow the map to function with "overlays" only. Defaults to
7246 * false. If true, the lowest layer in the draw order will act as
7247 * the base layer. In addition, if set to true, all layers will
7248 * have isBaseLayer set to false when they are added to the map.
7251 * If you set map.allOverlays to true, then you *cannot* use
7252 * map.setBaseLayer or layer.setIsBaseLayer. With allOverlays true,
7253 * the lowest layer in the draw layer is the base layer. So, to change
7254 * the base layer, use <setLayerIndex> or <raiseLayer> to set the layer
7261 * {DOMElement|String} The element that contains the map (or an id for
7262 * that element). If the <OpenLayers.Map> constructor is called
7263 * with two arguments, this should be provided as the first argument.
7264 * Alternatively, the map constructor can be called with the options
7265 * object as the only argument. In this case (one argument), a
7266 * div property may or may not be provided. If the div property
7267 * is not provided, the map can be rendered to a container later
7268 * using the <render> method.
7271 * If you are calling <render> after map construction, do not use
7272 * <maxResolution> auto. Instead, divide your <maxExtent> by your
7273 * maximum expected dimension.
7278 * Property: dragging
7279 * {Boolean} The map is currently being dragged.
7285 * {<OpenLayers.Size>} Size of the main div (this.div)
7290 * Property: viewPortDiv
7291 * {HTMLDivElement} The element that represents the map viewport
7296 * Property: layerContainerOrigin
7297 * {<OpenLayers.LonLat>} The lonlat at which the later container was
7298 * re-initialized (on-zoom)
7300 layerContainerOrigin: null,
7303 * Property: layerContainerDiv
7304 * {HTMLDivElement} The element that contains the layers.
7306 layerContainerDiv: null,
7309 * APIProperty: layers
7310 * {Array(<OpenLayers.Layer>)} Ordered list of layers in the map
7315 * APIProperty: controls
7316 * {Array(<OpenLayers.Control>)} List of controls associated with the map.
7318 * If not provided in the map options at construction, the map will
7319 * by default be given the following controls if present in the build:
7320 * - <OpenLayers.Control.Navigation> or <OpenLayers.Control.TouchNavigation>
7321 * - <OpenLayers.Control.Zoom> or <OpenLayers.Control.PanZoom>
7322 * - <OpenLayers.Control.ArgParser>
7323 * - <OpenLayers.Control.Attribution>
7329 * {Array(<OpenLayers.Popup>)} List of popups associated with the map
7334 * APIProperty: baseLayer
7335 * {<OpenLayers.Layer>} The currently selected base layer. This determines
7336 * min/max zoom level, projection, etc.
7342 * {<OpenLayers.LonLat>} The current center of the map
7347 * Property: resolution
7348 * {Float} The resolution of the map.
7354 * {Integer} The current zoom level of the map
7359 * Property: panRatio
7360 * {Float} The ratio of the current extent within
7361 * which panning will tween.
7366 * APIProperty: options
7367 * {Object} The options object passed to the class constructor. Read-only.
7374 * APIProperty: tileSize
7375 * {<OpenLayers.Size>} Set in the map options to override the default tile
7376 * size for this map.
7381 * APIProperty: projection
7382 * {String} Set in the map options to specify the default projection
7383 * for layers added to this map. When using a projection other than EPSG:4326
7384 * (CRS:84, Geographic) or EPSG:3857 (EPSG:900913, Web Mercator),
7385 * also set maxExtent, maxResolution or resolutions. Default is "EPSG:4326".
7386 * Note that the projection of the map is usually determined
7387 * by that of the current baseLayer (see <baseLayer> and <getProjectionObject>).
7389 projection: "EPSG:4326",
7392 * APIProperty: units
7393 * {String} The map units. Possible values are 'degrees' (or 'dd'), 'm',
7394 * 'ft', 'km', 'mi', 'inches'. Normally taken from the projection.
7395 * Only required if both map and layers do not define a projection,
7396 * or if they define a projection which does not define units
7401 * APIProperty: resolutions
7402 * {Array(Float)} A list of map resolutions (map units per pixel) in
7403 * descending order. If this is not set in the layer constructor, it
7404 * will be set based on other resolution related properties
7405 * (maxExtent, maxResolution, maxScale, etc.).
7410 * APIProperty: maxResolution
7411 * {Float} Required if you are not displaying the whole world on a tile
7412 * with the size specified in <tileSize>.
7414 maxResolution: null,
7417 * APIProperty: minResolution
7420 minResolution: null,
7423 * APIProperty: maxScale
7429 * APIProperty: minScale
7435 * APIProperty: maxExtent
7436 * {<OpenLayers.Bounds>|Array} If provided as an array, the array
7437 * should consist of four values (left, bottom, right, top).
7438 * The maximum extent for the map.
7439 * Default depends on projection; if this is one of those defined in OpenLayers.Projection.defaults
7440 * (EPSG:4326 or web mercator), maxExtent will be set to the value defined there;
7441 * else, defaults to null.
7442 * To restrict user panning and zooming of the map, use <restrictedExtent> instead.
7443 * The value for <maxExtent> will change calculations for tile URLs.
7448 * APIProperty: minExtent
7449 * {<OpenLayers.Bounds>|Array} If provided as an array, the array
7450 * should consist of four values (left, bottom, right, top).
7451 * The minimum extent for the map. Defaults to null.
7456 * APIProperty: restrictedExtent
7457 * {<OpenLayers.Bounds>|Array} If provided as an array, the array
7458 * should consist of four values (left, bottom, right, top).
7459 * Limit map navigation to this extent where possible.
7460 * If a non-null restrictedExtent is set, panning will be restricted
7461 * to the given bounds. In addition, zooming to a resolution that
7462 * displays more than the restricted extent will center the map
7463 * on the restricted extent. If you wish to limit the zoom level
7464 * or resolution, use maxResolution.
7466 restrictedExtent: null,
7469 * APIProperty: numZoomLevels
7470 * {Integer} Number of zoom levels for the map. Defaults to 16. Set a
7471 * different value in the map options if needed.
7476 * APIProperty: theme
7477 * {String} Relative path to a CSS file from which to load theme styles.
7478 * Specify null in the map options (e.g. {theme: null}) if you
7479 * want to get cascading style declarations - by putting links to
7480 * stylesheets or style declarations directly in your page.
7485 * APIProperty: displayProjection
7486 * {<OpenLayers.Projection>} Requires proj4js support for projections other
7487 * than EPSG:4326 or EPSG:900913/EPSG:3857. Projection used by
7488 * several controls to display data to user. If this property is set,
7489 * it will be set on any control which has a null displayProjection
7490 * property at the time the control is added to the map.
7492 displayProjection: null,
7495 * APIProperty: tileManager
7496 * {<OpenLayers.TileManager>|Object} By default, and if the build contains
7497 * TileManager.js, the map will use the TileManager to queue image requests
7498 * and to cache tile image elements. To create a map without a TileManager
7499 * configure the map with tileManager: null. To create a TileManager with
7500 * non-default options, supply the options instead or alternatively supply
7501 * an instance of {<OpenLayers.TileManager>}.
7505 * APIProperty: fallThrough
7506 * {Boolean} Should OpenLayers allow events on the map to fall through to
7507 * other elements on the page, or should it swallow them? (#457)
7508 * Default is to swallow.
7513 * APIProperty: autoUpdateSize
7514 * {Boolean} Should OpenLayers automatically update the size of the map
7515 * when the resize event is fired. Default is true.
7517 autoUpdateSize: true,
7520 * APIProperty: eventListeners
7521 * {Object} If set as an option at construction, the eventListeners
7522 * object will be registered with <OpenLayers.Events.on>. Object
7523 * structure must be a listeners object as shown in the example for
7524 * the events.on method.
7526 eventListeners: null,
7529 * Property: panTween
7530 * {<OpenLayers.Tween>} Animated panning tween object, see panTo()
7535 * APIProperty: panMethod
7536 * {Function} The Easing function to be used for tweening. Default is
7537 * OpenLayers.Easing.Expo.easeOut. Setting this to 'null' turns off
7540 panMethod: OpenLayers.Easing.Expo.easeOut,
7543 * Property: panDuration
7544 * {Integer} The number of steps to be passed to the
7545 * OpenLayers.Tween.start() method when the map is
7552 * Property: zoomTween
7553 * {<OpenLayers.Tween>} Animated zooming tween object, see zoomTo()
7558 * APIProperty: zoomMethod
7559 * {Function} The Easing function to be used for tweening. Default is
7560 * OpenLayers.Easing.Quad.easeOut. Setting this to 'null' turns off
7563 zoomMethod: OpenLayers.Easing.Quad.easeOut,
7566 * Property: zoomDuration
7567 * {Integer} The number of steps to be passed to the
7568 * OpenLayers.Tween.start() method when the map is zoomed.
7574 * Property: paddingForPopups
7575 * {<OpenLayers.Bounds>} Outside margin of the popup. Used to prevent
7576 * the popup from getting too close to the map border.
7578 paddingForPopups : null,
7581 * Property: layerContainerOriginPx
7582 * {Object} Cached object representing the layer container origin (in pixels).
7584 layerContainerOriginPx: null,
7588 * {Object} An object with a 'x' and 'y' values that is the lower
7589 * left of maxExtent in viewport pixel space.
7590 * Used to verify in moveByPx that the new location we're moving to
7591 * is valid. It is also used in the getLonLatFromViewPortPx function
7598 * {Object} An object with a 'x' and 'y' values that is the top
7599 * right of maxExtent in viewport pixel space.
7600 * Used to verify in moveByPx that the new location we're moving to
7606 * Constructor: OpenLayers.Map
7607 * Constructor for a new OpenLayers.Map instance. There are two possible
7608 * ways to call the map constructor. See the examples below.
7611 * div - {DOMElement|String} The element or id of an element in your page
7612 * that will contain the map. May be omitted if the <div> option is
7613 * provided or if you intend to call the <render> method later.
7614 * options - {Object} Optional object with properties to tag onto the map.
7616 * Valid options (in addition to the listed API properties):
7617 * center - {<OpenLayers.LonLat>|Array} The default initial center of the map.
7618 * If provided as array, the first value is the x coordinate,
7619 * and the 2nd value is the y coordinate.
7620 * Only specify if <layers> is provided.
7621 * Note that if an ArgParser/Permalink control is present,
7622 * and the querystring contains coordinates, center will be set
7623 * by that, and this option will be ignored.
7624 * zoom - {Number} The initial zoom level for the map. Only specify if
7625 * <layers> is provided.
7626 * Note that if an ArgParser/Permalink control is present,
7627 * and the querystring contains a zoom level, zoom will be set
7628 * by that, and this option will be ignored.
7632 * // create a map with default options in an element with the id "map1"
7633 * var map = new OpenLayers.Map("map1");
7635 * // create a map with non-default options in an element with id "map2"
7637 * projection: "EPSG:3857",
7638 * maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000),
7639 * center: new OpenLayers.LonLat(-12356463.476333, 5621521.4854095)
7641 * var map = new OpenLayers.Map("map2", options);
7643 * // map with non-default options - same as above but with a single argument,
7644 * // a restricted extent, and using arrays for bounds and center
7645 * var map = new OpenLayers.Map({
7647 * projection: "EPSG:3857",
7648 * maxExtent: [-18924313.432222, -15538711.094146, 18924313.432222, 15538711.094146],
7649 * restrictedExtent: [-13358338.893333, -9608371.5085962, 13358338.893333, 9608371.5085962],
7650 * center: [-12356463.476333, 5621521.4854095]
7653 * // create a map without a reference to a container - call render later
7654 * var map = new OpenLayers.Map({
7655 * projection: "EPSG:3857",
7656 * maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000)
7660 initialize: function (div, options) {
7662 // If only one argument is provided, check if it is an object.
7663 var isDOMElement = OpenLayers.Util.isElement(div);
7664 if(arguments.length === 1 && typeof div === "object" && !isDOMElement) {
7666 div = options && options.div;
7669 // Simple-type defaults are set in class definition.
7670 // Now set complex-type defaults
7671 this.tileSize = new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH,
7672 OpenLayers.Map.TILE_HEIGHT);
7674 this.paddingForPopups = new OpenLayers.Bounds(15, 15, 15, 15);
7676 this.theme = OpenLayers._getScriptLocation() +
7677 'theme/default/style.css';
7679 // backup original options
7680 this.options = OpenLayers.Util.extend({}, options);
7682 // now override default options
7683 OpenLayers.Util.extend(this, options);
7685 var projCode = this.projection instanceof OpenLayers.Projection ?
7686 this.projection.projCode : this.projection;
7687 OpenLayers.Util.applyDefaults(this, OpenLayers.Projection.defaults[projCode]);
7689 // allow extents and center to be arrays
7690 if (this.maxExtent && !(this.maxExtent instanceof OpenLayers.Bounds)) {
7691 this.maxExtent = new OpenLayers.Bounds(this.maxExtent);
7693 if (this.minExtent && !(this.minExtent instanceof OpenLayers.Bounds)) {
7694 this.minExtent = new OpenLayers.Bounds(this.minExtent);
7696 if (this.restrictedExtent && !(this.restrictedExtent instanceof OpenLayers.Bounds)) {
7697 this.restrictedExtent = new OpenLayers.Bounds(this.restrictedExtent);
7699 if (this.center && !(this.center instanceof OpenLayers.LonLat)) {
7700 this.center = new OpenLayers.LonLat(this.center);
7703 // initialize layers array
7706 this.id = OpenLayers.Util.createUniqueID("OpenLayers.Map_");
7708 this.div = OpenLayers.Util.getElement(div);
7710 this.div = document.createElement("div");
7711 this.div.style.height = "1px";
7712 this.div.style.width = "1px";
7715 OpenLayers.Element.addClass(this.div, 'olMap');
7717 // the viewPortDiv is the outermost div we modify
7718 var id = this.id + "_OpenLayers_ViewPort";
7719 this.viewPortDiv = OpenLayers.Util.createDiv(id, null, null, null,
7722 this.viewPortDiv.style.width = "100%";
7723 this.viewPortDiv.style.height = "100%";
7724 this.viewPortDiv.className = "olMapViewport";
7725 this.div.appendChild(this.viewPortDiv);
7727 this.events = new OpenLayers.Events(
7728 this, this.viewPortDiv, null, this.fallThrough,
7732 if (OpenLayers.TileManager && this.tileManager !== null) {
7733 if (!(this.tileManager instanceof OpenLayers.TileManager)) {
7734 this.tileManager = new OpenLayers.TileManager(this.tileManager);
7736 this.tileManager.addMap(this);
7739 // the layerContainerDiv is the one that holds all the layers
7740 id = this.id + "_OpenLayers_Container";
7741 this.layerContainerDiv = OpenLayers.Util.createDiv(id);
7742 this.layerContainerDiv.style.zIndex=this.Z_INDEX_BASE['Popup']-1;
7743 this.layerContainerOriginPx = {x: 0, y: 0};
7744 this.applyTransform();
7746 this.viewPortDiv.appendChild(this.layerContainerDiv);
7749 if(this.eventListeners instanceof Object) {
7750 this.events.on(this.eventListeners);
7753 if (this.autoUpdateSize === true) {
7754 // updateSize on catching the window's resize
7755 // Note that this is ok, as updateSize() does nothing if the
7756 // map's size has not actually changed.
7757 this.updateSizeDestroy = OpenLayers.Function.bind(this.updateSize,
7759 OpenLayers.Event.observe(window, 'resize',
7760 this.updateSizeDestroy);
7763 // only append link stylesheet if the theme property is set
7765 // check existing links for equivalent url
7767 var nodes = document.getElementsByTagName('link');
7768 for(var i=0, len=nodes.length; i<len; ++i) {
7769 if(OpenLayers.Util.isEquivalentUrl(nodes.item(i).href,
7775 // only add a new node if one with an equivalent url hasn't already
7778 var cssNode = document.createElement('link');
7779 cssNode.setAttribute('rel', 'stylesheet');
7780 cssNode.setAttribute('type', 'text/css');
7781 cssNode.setAttribute('href', this.theme);
7782 document.getElementsByTagName('head')[0].appendChild(cssNode);
7786 if (this.controls == null) { // default controls
7788 if (OpenLayers.Control != null) { // running full or lite?
7789 // Navigation or TouchNavigation depending on what is in build
7790 if (OpenLayers.Control.Navigation) {
7791 this.controls.push(new OpenLayers.Control.Navigation());
7792 } else if (OpenLayers.Control.TouchNavigation) {
7793 this.controls.push(new OpenLayers.Control.TouchNavigation());
7795 if (OpenLayers.Control.Zoom) {
7796 this.controls.push(new OpenLayers.Control.Zoom());
7797 } else if (OpenLayers.Control.PanZoom) {
7798 this.controls.push(new OpenLayers.Control.PanZoom());
7801 if (OpenLayers.Control.ArgParser) {
7802 this.controls.push(new OpenLayers.Control.ArgParser());
7804 if (OpenLayers.Control.Attribution) {
7805 this.controls.push(new OpenLayers.Control.Attribution());
7810 for(var i=0, len=this.controls.length; i<len; i++) {
7811 this.addControlToMap(this.controls[i]);
7816 this.unloadDestroy = OpenLayers.Function.bind(this.destroy, this);
7819 // always call map.destroy()
7820 OpenLayers.Event.observe(window, 'unload', this.unloadDestroy);
7822 // add any initial layers
7823 if (options && options.layers) {
7825 * If you have set options.center, the map center property will be
7826 * set at this point. However, since setCenter has not been called,
7827 * addLayers gets confused. So we delete the map center in this
7828 * case. Because the check below uses options.center, it will
7829 * be properly set below.
7833 this.addLayers(options.layers);
7834 // set center (and optionally zoom)
7835 if (options.center && !this.getCenter()) {
7836 // zoom can be undefined here
7837 this.setCenter(options.center, options.zoom);
7841 if (this.panMethod) {
7842 this.panTween = new OpenLayers.Tween(this.panMethod);
7844 if (this.zoomMethod && this.applyTransform.transform) {
7845 this.zoomTween = new OpenLayers.Tween(this.zoomMethod);
7850 * APIMethod: getViewport
7851 * Get the DOMElement representing the view port.
7856 getViewport: function() {
7857 return this.viewPortDiv;
7862 * Render the map to a specified container.
7865 * div - {String|DOMElement} The container that the map should be rendered
7866 * to. If different than the current container, the map viewport
7867 * will be moved from the current to the new container.
7869 render: function(div) {
7870 this.div = OpenLayers.Util.getElement(div);
7871 OpenLayers.Element.addClass(this.div, 'olMap');
7872 this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);
7873 this.div.appendChild(this.viewPortDiv);
7878 * Method: unloadDestroy
7879 * Function that is called to destroy the map on page unload. stored here
7880 * so that if map is manually destroyed, we can unregister this.
7882 unloadDestroy: null,
7885 * Method: updateSizeDestroy
7886 * When the map is destroyed, we need to stop listening to updateSize
7887 * events: this method stores the function we need to unregister in
7890 updateSizeDestroy: null,
7893 * APIMethod: destroy
7895 * Note that if you are using an application which removes a container
7896 * of the map from the DOM, you need to ensure that you destroy the
7897 * map *before* this happens; otherwise, the page unload handler
7898 * will fail because the DOM elements that map.destroy() wants
7899 * to clean up will be gone. (See
7900 * http://trac.osgeo.org/openlayers/ticket/2277 for more information).
7901 * This will apply to GeoExt and also to other applications which
7902 * modify the DOM of the container of the OpenLayers Map.
7904 destroy:function() {
7905 // if unloadDestroy is null, we've already been destroyed
7906 if (!this.unloadDestroy) {
7910 // make sure panning doesn't continue after destruction
7912 this.panTween.stop();
7913 this.panTween = null;
7915 // make sure zooming doesn't continue after destruction
7916 if(this.zoomTween) {
7917 this.zoomTween.stop();
7918 this.zoomTween = null;
7921 // map has been destroyed. dont do it again!
7922 OpenLayers.Event.stopObserving(window, 'unload', this.unloadDestroy);
7923 this.unloadDestroy = null;
7925 if (this.updateSizeDestroy) {
7926 OpenLayers.Event.stopObserving(window, 'resize',
7927 this.updateSizeDestroy);
7930 this.paddingForPopups = null;
7932 if (this.controls != null) {
7933 for (var i = this.controls.length - 1; i>=0; --i) {
7934 this.controls[i].destroy();
7936 this.controls = null;
7938 if (this.layers != null) {
7939 for (var i = this.layers.length - 1; i>=0; --i) {
7940 //pass 'false' to destroy so that map wont try to set a new
7941 // baselayer after each baselayer is removed
7942 this.layers[i].destroy(false);
7946 if (this.viewPortDiv && this.viewPortDiv.parentNode) {
7947 this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);
7949 this.viewPortDiv = null;
7951 if (this.tileManager) {
7952 this.tileManager.removeMap(this);
7953 this.tileManager = null;
7956 if(this.eventListeners) {
7957 this.events.un(this.eventListeners);
7958 this.eventListeners = null;
7960 this.events.destroy();
7963 this.options = null;
7967 * APIMethod: setOptions
7968 * Change the map options
7971 * options - {Object} Hashtable of options to tag to the map
7973 setOptions: function(options) {
7974 var updatePxExtent = this.minPx &&
7975 options.restrictedExtent != this.restrictedExtent;
7976 OpenLayers.Util.extend(this, options);
7977 // force recalculation of minPx and maxPx
7978 updatePxExtent && this.moveTo(this.getCachedCenter(), this.zoom, {
7979 forceZoomChange: true
7984 * APIMethod: getTileSize
7985 * Get the tile size for the map
7988 * {<OpenLayers.Size>}
7990 getTileSize: function() {
7991 return this.tileSize;
7997 * Get a list of objects given a property and a match item.
8000 * array - {String} A property on the map whose value is an array.
8001 * property - {String} A property on each item of the given array.
8002 * match - {String | Object} A string to match. Can also be a regular
8003 * expression literal or object. In addition, it can be any object
8004 * with a method named test. For reqular expressions or other, if
8005 * match.test(map[array][i][property]) evaluates to true, the item will
8006 * be included in the array returned. If no items are found, an empty
8007 * array is returned.
8010 * {Array} An array of items where the given property matches the given
8013 getBy: function(array, property, match) {
8014 var test = (typeof match.test == "function");
8015 var found = OpenLayers.Array.filter(this[array], function(item) {
8016 return item[property] == match || (test && match.test(item[property]));
8022 * APIMethod: getLayersBy
8023 * Get a list of layers with properties matching the given criteria.
8026 * property - {String} A layer property to be matched.
8027 * match - {String | Object} A string to match. Can also be a regular
8028 * expression literal or object. In addition, it can be any object
8029 * with a method named test. For reqular expressions or other, if
8030 * match.test(layer[property]) evaluates to true, the layer will be
8031 * included in the array returned. If no layers are found, an empty
8032 * array is returned.
8035 * {Array(<OpenLayers.Layer>)} A list of layers matching the given criteria.
8036 * An empty array is returned if no matches are found.
8038 getLayersBy: function(property, match) {
8039 return this.getBy("layers", property, match);
8043 * APIMethod: getLayersByName
8044 * Get a list of layers with names matching the given name.
8047 * match - {String | Object} A layer name. The name can also be a regular
8048 * expression literal or object. In addition, it can be any object
8049 * with a method named test. For reqular expressions or other, if
8050 * name.test(layer.name) evaluates to true, the layer will be included
8051 * in the list of layers returned. If no layers are found, an empty
8052 * array is returned.
8055 * {Array(<OpenLayers.Layer>)} A list of layers matching the given name.
8056 * An empty array is returned if no matches are found.
8058 getLayersByName: function(match) {
8059 return this.getLayersBy("name", match);
8063 * APIMethod: getLayersByClass
8064 * Get a list of layers of a given class (CLASS_NAME).
8067 * match - {String | Object} A layer class name. The match can also be a
8068 * regular expression literal or object. In addition, it can be any
8069 * object with a method named test. For reqular expressions or other,
8070 * if type.test(layer.CLASS_NAME) evaluates to true, the layer will
8071 * be included in the list of layers returned. If no layers are
8072 * found, an empty array is returned.
8075 * {Array(<OpenLayers.Layer>)} A list of layers matching the given class.
8076 * An empty array is returned if no matches are found.
8078 getLayersByClass: function(match) {
8079 return this.getLayersBy("CLASS_NAME", match);
8083 * APIMethod: getControlsBy
8084 * Get a list of controls with properties matching the given criteria.
8087 * property - {String} A control property to be matched.
8088 * match - {String | Object} A string to match. Can also be a regular
8089 * expression literal or object. In addition, it can be any object
8090 * with a method named test. For reqular expressions or other, if
8091 * match.test(layer[property]) evaluates to true, the layer will be
8092 * included in the array returned. If no layers are found, an empty
8093 * array is returned.
8096 * {Array(<OpenLayers.Control>)} A list of controls matching the given
8097 * criteria. An empty array is returned if no matches are found.
8099 getControlsBy: function(property, match) {
8100 return this.getBy("controls", property, match);
8104 * APIMethod: getControlsByClass
8105 * Get a list of controls of a given class (CLASS_NAME).
8108 * match - {String | Object} A control class name. The match can also be a
8109 * regular expression literal or object. In addition, it can be any
8110 * object with a method named test. For reqular expressions or other,
8111 * if type.test(control.CLASS_NAME) evaluates to true, the control will
8112 * be included in the list of controls returned. If no controls are
8113 * found, an empty array is returned.
8116 * {Array(<OpenLayers.Control>)} A list of controls matching the given class.
8117 * An empty array is returned if no matches are found.
8119 getControlsByClass: function(match) {
8120 return this.getControlsBy("CLASS_NAME", match);
8123 /********************************************************/
8125 /* Layer Functions */
8127 /* The following functions deal with adding and */
8128 /* removing Layers to and from the Map */
8130 /********************************************************/
8133 * APIMethod: getLayer
8134 * Get a layer based on its id
8137 * id - {String} A layer id
8140 * {<OpenLayers.Layer>} The Layer with the corresponding id from the map's
8141 * layer collection, or null if not found.
8143 getLayer: function(id) {
8144 var foundLayer = null;
8145 for (var i=0, len=this.layers.length; i<len; i++) {
8146 var layer = this.layers[i];
8147 if (layer.id == id) {
8156 * Method: setLayerZIndex
8159 * layer - {<OpenLayers.Layer>}
8162 setLayerZIndex: function (layer, zIdx) {
8164 this.Z_INDEX_BASE[layer.isBaseLayer ? 'BaseLayer' : 'Overlay']
8169 * Method: resetLayersZIndex
8170 * Reset each layer's z-index based on layer's array index
8172 resetLayersZIndex: function() {
8173 for (var i=0, len=this.layers.length; i<len; i++) {
8174 var layer = this.layers[i];
8175 this.setLayerZIndex(layer, i);
8180 * APIMethod: addLayer
8183 * layer - {<OpenLayers.Layer>}
8186 * {Boolean} True if the layer has been added to the map.
8188 addLayer: function (layer) {
8189 for(var i = 0, len = this.layers.length; i < len; i++) {
8190 if (this.layers[i] == layer) {
8194 if (this.events.triggerEvent("preaddlayer", {layer: layer}) === false) {
8197 if(this.allOverlays) {
8198 layer.isBaseLayer = false;
8201 layer.div.className = "olLayerDiv";
8202 layer.div.style.overflow = "";
8203 this.setLayerZIndex(layer, this.layers.length);
8205 if (layer.isFixed) {
8206 this.viewPortDiv.appendChild(layer.div);
8208 this.layerContainerDiv.appendChild(layer.div);
8210 this.layers.push(layer);
8213 if (layer.isBaseLayer || (this.allOverlays && !this.baseLayer)) {
8214 if (this.baseLayer == null) {
8215 // set the first baselaye we add as the baselayer
8216 this.setBaseLayer(layer);
8218 layer.setVisibility(false);
8224 this.events.triggerEvent("addlayer", {layer: layer});
8225 layer.events.triggerEvent("added", {map: this, layer: layer});
8232 * APIMethod: addLayers
8235 * layers - {Array(<OpenLayers.Layer>)}
8237 addLayers: function (layers) {
8238 for (var i=0, len=layers.length; i<len; i++) {
8239 this.addLayer(layers[i]);
8244 * APIMethod: removeLayer
8245 * Removes a layer from the map by removing its visual element (the
8246 * layer.div property), then removing it from the map's internal list
8247 * of layers, setting the layer's map property to null.
8249 * a "removelayer" event is triggered.
8251 * very worthy of mention is that simply removing a layer from a map
8252 * will not cause the removal of any popups which may have been created
8253 * by the layer. this is due to the fact that it was decided at some
8254 * point that popups would not belong to layers. thus there is no way
8255 * for us to know here to which layer the popup belongs.
8257 * A simple solution to this is simply to call destroy() on the layer.
8258 * the default OpenLayers.Layer class's destroy() function
8259 * automatically takes care to remove itself from whatever map it has
8262 * The correct solution is for the layer itself to register an
8263 * event-handler on "removelayer" and when it is called, if it
8264 * recognizes itself as the layer being removed, then it cycles through
8265 * its own personal list of popups, removing them from the map.
8268 * layer - {<OpenLayers.Layer>}
8269 * setNewBaseLayer - {Boolean} Default is true
8271 removeLayer: function(layer, setNewBaseLayer) {
8272 if (this.events.triggerEvent("preremovelayer", {layer: layer}) === false) {
8275 if (setNewBaseLayer == null) {
8276 setNewBaseLayer = true;
8279 if (layer.isFixed) {
8280 this.viewPortDiv.removeChild(layer.div);
8282 this.layerContainerDiv.removeChild(layer.div);
8284 OpenLayers.Util.removeItem(this.layers, layer);
8285 layer.removeMap(this);
8288 // if we removed the base layer, need to set a new one
8289 if(this.baseLayer == layer) {
8290 this.baseLayer = null;
8291 if(setNewBaseLayer) {
8292 for(var i=0, len=this.layers.length; i<len; i++) {
8293 var iLayer = this.layers[i];
8294 if (iLayer.isBaseLayer || this.allOverlays) {
8295 this.setBaseLayer(iLayer);
8302 this.resetLayersZIndex();
8304 this.events.triggerEvent("removelayer", {layer: layer});
8305 layer.events.triggerEvent("removed", {map: this, layer: layer});
8309 * APIMethod: getNumLayers
8312 * {Int} The number of layers attached to the map.
8314 getNumLayers: function () {
8315 return this.layers.length;
8319 * APIMethod: getLayerIndex
8322 * layer - {<OpenLayers.Layer>}
8325 * {Integer} The current (zero-based) index of the given layer in the map's
8326 * layer stack. Returns -1 if the layer isn't on the map.
8328 getLayerIndex: function (layer) {
8329 return OpenLayers.Util.indexOf(this.layers, layer);
8333 * APIMethod: setLayerIndex
8334 * Move the given layer to the specified (zero-based) index in the layer
8335 * list, changing its z-index in the map display. Use
8336 * map.getLayerIndex() to find out the current index of a layer. Note
8337 * that this cannot (or at least should not) be effectively used to
8338 * raise base layers above overlays.
8341 * layer - {<OpenLayers.Layer>}
8344 setLayerIndex: function (layer, idx) {
8345 var base = this.getLayerIndex(layer);
8348 } else if (idx > this.layers.length) {
8349 idx = this.layers.length;
8352 this.layers.splice(base, 1);
8353 this.layers.splice(idx, 0, layer);
8354 for (var i=0, len=this.layers.length; i<len; i++) {
8355 this.setLayerZIndex(this.layers[i], i);
8357 this.events.triggerEvent("changelayer", {
8358 layer: layer, property: "order"
8360 if(this.allOverlays) {
8362 this.setBaseLayer(layer);
8363 } else if(this.baseLayer !== this.layers[0]) {
8364 this.setBaseLayer(this.layers[0]);
8371 * APIMethod: raiseLayer
8372 * Change the index of the given layer by delta. If delta is positive,
8373 * the layer is moved up the map's layer stack; if delta is negative,
8374 * the layer is moved down. Again, note that this cannot (or at least
8375 * should not) be effectively used to raise base layers above overlays.
8378 * layer - {<OpenLayers.Layer>}
8381 raiseLayer: function (layer, delta) {
8382 var idx = this.getLayerIndex(layer) + delta;
8383 this.setLayerIndex(layer, idx);
8387 * APIMethod: setBaseLayer
8388 * Allows user to specify one of the currently-loaded layers as the Map's
8392 * newBaseLayer - {<OpenLayers.Layer>}
8394 setBaseLayer: function(newBaseLayer) {
8396 if (newBaseLayer != this.baseLayer) {
8398 // ensure newBaseLayer is already loaded
8399 if (OpenLayers.Util.indexOf(this.layers, newBaseLayer) != -1) {
8401 // preserve center and scale when changing base layers
8402 var center = this.getCachedCenter();
8403 var oldResolution = this.getResolution();
8404 var newResolution = OpenLayers.Util.getResolutionFromScale(
8405 this.getScale(), newBaseLayer.units
8408 // make the old base layer invisible
8409 if (this.baseLayer != null && !this.allOverlays) {
8410 this.baseLayer.setVisibility(false);
8413 // set new baselayer
8414 this.baseLayer = newBaseLayer;
8416 if(!this.allOverlays || this.baseLayer.visibility) {
8417 this.baseLayer.setVisibility(true);
8418 // Layer may previously have been visible but not in range.
8419 // In this case we need to redraw it to make it visible.
8420 if (this.baseLayer.inRange === false) {
8421 this.baseLayer.redraw();
8426 if (center != null) {
8427 // new zoom level derived from old scale
8428 var newZoom = this.getZoomForResolution(
8429 newResolution || this.resolution, true
8431 // zoom and force zoom change
8432 this.setCenter(center, newZoom, false, oldResolution != newResolution);
8435 this.events.triggerEvent("changebaselayer", {
8436 layer: this.baseLayer
8443 /********************************************************/
8445 /* Control Functions */
8447 /* The following functions deal with adding and */
8448 /* removing Controls to and from the Map */
8450 /********************************************************/
8453 * APIMethod: addControl
8454 * Add the passed over control to the map. Optionally
8455 * position the control at the given pixel.
8458 * control - {<OpenLayers.Control>}
8459 * px - {<OpenLayers.Pixel>}
8461 addControl: function (control, px) {
8462 this.controls.push(control);
8463 this.addControlToMap(control, px);
8467 * APIMethod: addControls
8468 * Add all of the passed over controls to the map.
8469 * You can pass over an optional second array
8470 * with pixel-objects to position the controls.
8471 * The indices of the two arrays should match and
8472 * you can add null as pixel for those controls
8473 * you want to be autopositioned.
8476 * controls - {Array(<OpenLayers.Control>)}
8477 * pixels - {Array(<OpenLayers.Pixel>)}
8479 addControls: function (controls, pixels) {
8480 var pxs = (arguments.length === 1) ? [] : pixels;
8481 for (var i=0, len=controls.length; i<len; i++) {
8482 var ctrl = controls[i];
8483 var px = (pxs[i]) ? pxs[i] : null;
8484 this.addControl( ctrl, px );
8489 * Method: addControlToMap
8493 * control - {<OpenLayers.Control>}
8494 * px - {<OpenLayers.Pixel>}
8496 addControlToMap: function (control, px) {
8497 // If a control doesn't have a div at this point, it belongs in the
8499 control.outsideViewport = (control.div != null);
8501 // If the map has a displayProjection, and the control doesn't, set
8502 // the display projection.
8503 if (this.displayProjection && !control.displayProjection) {
8504 control.displayProjection = this.displayProjection;
8507 control.setMap(this);
8508 var div = control.draw(px);
8510 if(!control.outsideViewport) {
8511 div.style.zIndex = this.Z_INDEX_BASE['Control'] +
8512 this.controls.length;
8513 this.viewPortDiv.appendChild( div );
8516 if(control.autoActivate) {
8522 * APIMethod: getControl
8525 * id - {String} ID of the control to return.
8528 * {<OpenLayers.Control>} The control from the map's list of controls
8529 * which has a matching 'id'. If none found,
8532 getControl: function (id) {
8533 var returnControl = null;
8534 for(var i=0, len=this.controls.length; i<len; i++) {
8535 var control = this.controls[i];
8536 if (control.id == id) {
8537 returnControl = control;
8541 return returnControl;
8545 * APIMethod: removeControl
8546 * Remove a control from the map. Removes the control both from the map
8547 * object's internal array of controls, as well as from the map's
8548 * viewPort (assuming the control was not added outsideViewport)
8551 * control - {<OpenLayers.Control>} The control to remove.
8553 removeControl: function (control) {
8554 //make sure control is non-null and actually part of our map
8555 if ( (control) && (control == this.getControl(control.id)) ) {
8556 if (control.div && (control.div.parentNode == this.viewPortDiv)) {
8557 this.viewPortDiv.removeChild(control.div);
8559 OpenLayers.Util.removeItem(this.controls, control);
8563 /********************************************************/
8565 /* Popup Functions */
8567 /* The following functions deal with adding and */
8568 /* removing Popups to and from the Map */
8570 /********************************************************/
8573 * APIMethod: addPopup
8576 * popup - {<OpenLayers.Popup>}
8577 * exclusive - {Boolean} If true, closes all other popups first
8579 addPopup: function(popup, exclusive) {
8582 //remove all other popups from screen
8583 for (var i = this.popups.length - 1; i >= 0; --i) {
8584 this.removePopup(this.popups[i]);
8589 this.popups.push(popup);
8590 var popupDiv = popup.draw();
8592 popupDiv.style.zIndex = this.Z_INDEX_BASE['Popup'] +
8594 this.layerContainerDiv.appendChild(popupDiv);
8599 * APIMethod: removePopup
8602 * popup - {<OpenLayers.Popup>}
8604 removePopup: function(popup) {
8605 OpenLayers.Util.removeItem(this.popups, popup);
8607 try { this.layerContainerDiv.removeChild(popup.div); }
8608 catch (e) { } // Popups sometimes apparently get disconnected
8609 // from the layerContainerDiv, and cause complaints.
8614 /********************************************************/
8616 /* Container Div Functions */
8618 /* The following functions deal with the access to */
8619 /* and maintenance of the size of the container div */
8621 /********************************************************/
8624 * APIMethod: getSize
8627 * {<OpenLayers.Size>} An <OpenLayers.Size> object that represents the
8628 * size, in pixels, of the div into which OpenLayers
8630 * Note - A clone() of this locally cached variable is
8631 * returned, so as not to allow users to modify it.
8633 getSize: function () {
8635 if (this.size != null) {
8636 size = this.size.clone();
8642 * APIMethod: updateSize
8643 * This function should be called by any external code which dynamically
8644 * changes the size of the map div (because mozilla wont let us catch
8645 * the "onresize" for an element)
8647 updateSize: function() {
8648 // the div might have moved on the page, also
8649 var newSize = this.getCurrentSize();
8650 if (newSize && !isNaN(newSize.h) && !isNaN(newSize.w)) {
8651 this.events.clearMouseCache();
8652 var oldSize = this.getSize();
8653 if (oldSize == null) {
8654 this.size = oldSize = newSize;
8656 if (!newSize.equals(oldSize)) {
8658 // store the new size
8659 this.size = newSize;
8661 //notify layers of mapresize
8662 for(var i=0, len=this.layers.length; i<len; i++) {
8663 this.layers[i].onMapResize();
8666 var center = this.getCachedCenter();
8668 if (this.baseLayer != null && center != null) {
8669 var zoom = this.getZoom();
8671 this.setCenter(center, zoom);
8676 this.events.triggerEvent("updatesize");
8680 * Method: getCurrentSize
8683 * {<OpenLayers.Size>} A new <OpenLayers.Size> object with the dimensions
8686 getCurrentSize: function() {
8688 var size = new OpenLayers.Size(this.div.clientWidth,
8689 this.div.clientHeight);
8691 if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {
8692 size.w = this.div.offsetWidth;
8693 size.h = this.div.offsetHeight;
8695 if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {
8696 size.w = parseInt(this.div.style.width);
8697 size.h = parseInt(this.div.style.height);
8703 * Method: calculateBounds
8706 * center - {<OpenLayers.LonLat>} Default is this.getCenter()
8707 * resolution - {float} Default is this.getResolution()
8710 * {<OpenLayers.Bounds>} A bounds based on resolution, center, and
8713 calculateBounds: function(center, resolution) {
8717 if (center == null) {
8718 center = this.getCachedCenter();
8720 if (resolution == null) {
8721 resolution = this.getResolution();
8724 if ((center != null) && (resolution != null)) {
8725 var halfWDeg = (this.size.w * resolution) / 2;
8726 var halfHDeg = (this.size.h * resolution) / 2;
8728 extent = new OpenLayers.Bounds(center.lon - halfWDeg,
8729 center.lat - halfHDeg,
8730 center.lon + halfWDeg,
8731 center.lat + halfHDeg);
8738 /********************************************************/
8740 /* Zoom, Center, Pan Functions */
8742 /* The following functions handle the validation, */
8743 /* getting and setting of the Zoom Level and Center */
8744 /* as well as the panning of the Map */
8746 /********************************************************/
8748 * APIMethod: getCenter
8751 * {<OpenLayers.LonLat>}
8753 getCenter: function () {
8755 var cachedCenter = this.getCachedCenter();
8757 center = cachedCenter.clone();
8763 * Method: getCachedCenter
8766 * {<OpenLayers.LonLat>}
8768 getCachedCenter: function() {
8769 if (!this.center && this.size) {
8770 this.center = this.getLonLatFromViewPortPx({
8779 * APIMethod: getZoom
8784 getZoom: function () {
8790 * Allows user to pan by a value of screen pixels
8795 * options - {Object} Options to configure panning:
8796 * - *animate* {Boolean} Use panTo instead of setCenter. Default is true.
8797 * - *dragging* {Boolean} Call setCenter with dragging true. Default is
8800 pan: function(dx, dy, options) {
8801 options = OpenLayers.Util.applyDefaults(options, {
8805 if (options.dragging) {
8806 if (dx != 0 || dy != 0) {
8807 this.moveByPx(dx, dy);
8811 var centerPx = this.getViewPortPxFromLonLat(this.getCachedCenter());
8814 var newCenterPx = centerPx.add(dx, dy);
8816 if (this.dragging || !newCenterPx.equals(centerPx)) {
8817 var newCenterLonLat = this.getLonLatFromViewPortPx(newCenterPx);
8818 if (options.animate) {
8819 this.panTo(newCenterLonLat);
8821 this.moveTo(newCenterLonLat);
8823 this.dragging = false;
8824 this.events.triggerEvent("moveend");
8834 * Allows user to pan to a new lonlat.
8835 * If the new lonlat is in the current extent the map will slide smoothly
8838 * lonlat - {<OpenLayers.LonLat>}
8840 panTo: function(lonlat) {
8841 if (this.panTween && this.getExtent().scale(this.panRatio).containsLonLat(lonlat)) {
8842 var center = this.getCachedCenter();
8844 // center will not change, don't do nothing
8845 if (lonlat.equals(center)) {
8849 var from = this.getPixelFromLonLat(center);
8850 var to = this.getPixelFromLonLat(lonlat);
8851 var vector = { x: to.x - from.x, y: to.y - from.y };
8852 var last = { x: 0, y: 0 };
8854 this.panTween.start( { x: 0, y: 0 }, vector, this.panDuration, {
8856 eachStep: OpenLayers.Function.bind(function(px) {
8857 var x = px.x - last.x,
8859 this.moveByPx(x, y);
8860 last.x = Math.round(px.x);
8861 last.y = Math.round(px.y);
8863 done: OpenLayers.Function.bind(function(px) {
8864 this.moveTo(lonlat);
8865 this.dragging = false;
8866 this.events.triggerEvent("moveend");
8871 this.setCenter(lonlat);
8876 * APIMethod: setCenter
8877 * Set the map center (and optionally, the zoom level).
8880 * lonlat - {<OpenLayers.LonLat>|Array} The new center location.
8881 * If provided as array, the first value is the x coordinate,
8882 * and the 2nd value is the y coordinate.
8883 * zoom - {Integer} Optional zoom level.
8884 * dragging - {Boolean} Specifies whether or not to trigger
8885 * movestart/end events
8886 * forceZoomChange - {Boolean} Specifies whether or not to trigger zoom
8887 * change events (needed on baseLayer change)
8889 * TBD: reconsider forceZoomChange in 3.0
8891 setCenter: function(lonlat, zoom, dragging, forceZoomChange) {
8892 if (this.panTween) {
8893 this.panTween.stop();
8895 if (this.zoomTween) {
8896 this.zoomTween.stop();
8898 this.moveTo(lonlat, zoom, {
8899 'dragging': dragging,
8900 'forceZoomChange': forceZoomChange
8906 * Drag the map by pixels.
8912 moveByPx: function(dx, dy) {
8913 var hw = this.size.w / 2;
8914 var hh = this.size.h / 2;
8917 var wrapDateLine = this.baseLayer.wrapDateLine;
8918 var xRestriction = 0;
8919 var yRestriction = 0;
8920 if (this.restrictedExtent) {
8923 // wrapping the date line makes no sense for restricted extents
8924 wrapDateLine = false;
8926 dx = wrapDateLine ||
8927 x <= this.maxPx.x - xRestriction &&
8928 x >= this.minPx.x + xRestriction ? Math.round(dx) : 0;
8929 dy = y <= this.maxPx.y - yRestriction &&
8930 y >= this.minPx.y + yRestriction ? Math.round(dy) : 0;
8932 if (!this.dragging) {
8933 this.dragging = true;
8934 this.events.triggerEvent("movestart");
8938 this.layerContainerOriginPx.x -= dx;
8943 this.layerContainerOriginPx.y -= dy;
8947 this.applyTransform();
8949 for (i=0, len=this.layers.length; i<len; ++i) {
8950 layer = this.layers[i];
8951 if (layer.visibility &&
8952 (layer === this.baseLayer || layer.inRange)) {
8953 layer.moveByPx(dx, dy);
8954 layer.events.triggerEvent("move");
8957 this.events.triggerEvent("move");
8962 * Method: adjustZoom
8965 * zoom - {Number} The zoom level to adjust
8968 * {Integer} Adjusted zoom level that shows a map not wider than its
8969 * <baseLayer>'s maxExtent.
8971 adjustZoom: function(zoom) {
8972 if (this.baseLayer && this.baseLayer.wrapDateLine) {
8973 var resolution, resolutions = this.baseLayer.resolutions,
8974 maxResolution = this.getMaxExtent().getWidth() / this.size.w;
8975 if (this.getResolutionForZoom(zoom) > maxResolution) {
8976 if (this.fractionalZoom) {
8977 zoom = this.getZoomForResolution(maxResolution);
8979 for (var i=zoom|0, ii=resolutions.length; i<ii; ++i) {
8980 if (resolutions[i] <= maxResolution) {
8992 * APIMethod: getMinZoom
8993 * Returns the minimum zoom level for the current map view. If the base
8994 * layer is configured with <wrapDateLine> set to true, this will be the
8995 * first zoom level that shows no more than one world width in the current
8996 * map viewport. Components that rely on this value (e.g. zoom sliders)
8997 * should also listen to the map's "updatesize" event and call this method
8998 * in the "updatesize" listener.
9001 * {Number} Minimum zoom level that shows a map not wider than its
9002 * <baseLayer>'s maxExtent. This is an Integer value, unless the map is
9003 * configured with <fractionalZoom> set to true.
9005 getMinZoom: function() {
9006 return this.adjustZoom(0);
9013 * lonlat - {<OpenLayers.LonLat>}
9015 * options - {Object}
9017 moveTo: function(lonlat, zoom, options) {
9018 if (lonlat != null && !(lonlat instanceof OpenLayers.LonLat)) {
9019 lonlat = new OpenLayers.LonLat(lonlat);
9025 zoom = parseFloat(zoom);
9026 if (!this.fractionalZoom) {
9027 zoom = Math.round(zoom);
9030 var requestedZoom = zoom;
9031 zoom = this.adjustZoom(zoom);
9032 if (zoom !== requestedZoom) {
9033 // zoom was adjusted, so keep old lonlat to avoid panning
9034 lonlat = this.getCenter();
9036 // dragging is false by default
9037 var dragging = options.dragging || this.dragging;
9038 // forceZoomChange is false by default
9039 var forceZoomChange = options.forceZoomChange;
9041 if (!this.getCachedCenter() && !this.isValidLonLat(lonlat)) {
9042 lonlat = this.maxExtent.getCenterLonLat();
9043 this.center = lonlat.clone();
9046 if(this.restrictedExtent != null) {
9047 // In 3.0, decide if we want to change interpretation of maxExtent.
9048 if(lonlat == null) {
9049 lonlat = this.center;
9052 zoom = this.getZoom();
9054 var resolution = this.getResolutionForZoom(zoom);
9055 var extent = this.calculateBounds(lonlat, resolution);
9056 if(!this.restrictedExtent.containsBounds(extent)) {
9057 var maxCenter = this.restrictedExtent.getCenterLonLat();
9058 if(extent.getWidth() > this.restrictedExtent.getWidth()) {
9059 lonlat = new OpenLayers.LonLat(maxCenter.lon, lonlat.lat);
9060 } else if(extent.left < this.restrictedExtent.left) {
9061 lonlat = lonlat.add(this.restrictedExtent.left -
9063 } else if(extent.right > this.restrictedExtent.right) {
9064 lonlat = lonlat.add(this.restrictedExtent.right -
9067 if(extent.getHeight() > this.restrictedExtent.getHeight()) {
9068 lonlat = new OpenLayers.LonLat(lonlat.lon, maxCenter.lat);
9069 } else if(extent.bottom < this.restrictedExtent.bottom) {
9070 lonlat = lonlat.add(0, this.restrictedExtent.bottom -
9073 else if(extent.top > this.restrictedExtent.top) {
9074 lonlat = lonlat.add(0, this.restrictedExtent.top -
9080 var zoomChanged = forceZoomChange || (
9081 (this.isValidZoomLevel(zoom)) &&
9082 (zoom != this.getZoom()) );
9084 var centerChanged = (this.isValidLonLat(lonlat)) &&
9085 (!lonlat.equals(this.center));
9087 // if neither center nor zoom will change, no need to do anything
9088 if (zoomChanged || centerChanged || dragging) {
9089 dragging || this.events.triggerEvent("movestart", {
9090 zoomChanged: zoomChanged
9093 if (centerChanged) {
9094 if (!zoomChanged && this.center) {
9095 // if zoom hasn't changed, just slide layerContainer
9096 // (must be done before setting this.center to new value)
9097 this.centerLayerContainer(lonlat);
9099 this.center = lonlat.clone();
9102 var res = zoomChanged ?
9103 this.getResolutionForZoom(zoom) : this.getResolution();
9104 // (re)set the layerContainerDiv's location
9105 if (zoomChanged || this.layerContainerOrigin == null) {
9106 this.layerContainerOrigin = this.getCachedCenter();
9107 this.layerContainerOriginPx.x = 0;
9108 this.layerContainerOriginPx.y = 0;
9109 this.applyTransform();
9110 var maxExtent = this.getMaxExtent({restricted: true});
9111 var maxExtentCenter = maxExtent.getCenterLonLat();
9112 var lonDelta = this.center.lon - maxExtentCenter.lon;
9113 var latDelta = maxExtentCenter.lat - this.center.lat;
9114 var extentWidth = Math.round(maxExtent.getWidth() / res);
9115 var extentHeight = Math.round(maxExtent.getHeight() / res);
9117 x: (this.size.w - extentWidth) / 2 - lonDelta / res,
9118 y: (this.size.h - extentHeight) / 2 - latDelta / res
9121 x: this.minPx.x + Math.round(maxExtent.getWidth() / res),
9122 y: this.minPx.y + Math.round(maxExtent.getHeight() / res)
9128 this.resolution = res;
9131 var bounds = this.getExtent();
9133 //send the move call to the baselayer and all the overlays
9135 if(this.baseLayer.visibility) {
9136 this.baseLayer.moveTo(bounds, zoomChanged, options.dragging);
9137 options.dragging || this.baseLayer.events.triggerEvent(
9138 "moveend", {zoomChanged: zoomChanged}
9142 bounds = this.baseLayer.getExtent();
9144 for (var i=this.layers.length-1; i>=0; --i) {
9145 var layer = this.layers[i];
9146 if (layer !== this.baseLayer && !layer.isBaseLayer) {
9147 var inRange = layer.calculateInRange();
9148 if (layer.inRange != inRange) {
9149 // the inRange property has changed. If the layer is
9150 // no longer in range, we turn it off right away. If
9151 // the layer is no longer out of range, the moveTo
9152 // call below will turn on the layer.
9153 layer.inRange = inRange;
9155 layer.display(false);
9157 this.events.triggerEvent("changelayer", {
9158 layer: layer, property: "visibility"
9161 if (inRange && layer.visibility) {
9162 layer.moveTo(bounds, zoomChanged, options.dragging);
9163 options.dragging || layer.events.triggerEvent(
9164 "moveend", {zoomChanged: zoomChanged}
9170 this.events.triggerEvent("move");
9171 dragging || this.events.triggerEvent("moveend");
9175 for (var i=0, len=this.popups.length; i<len; i++) {
9176 this.popups[i].updatePosition();
9178 this.events.triggerEvent("zoomend");
9184 * Method: centerLayerContainer
9185 * This function takes care to recenter the layerContainerDiv.
9188 * lonlat - {<OpenLayers.LonLat>}
9190 centerLayerContainer: function (lonlat) {
9191 var originPx = this.getViewPortPxFromLonLat(this.layerContainerOrigin);
9192 var newPx = this.getViewPortPxFromLonLat(lonlat);
9194 if ((originPx != null) && (newPx != null)) {
9195 var oldLeft = this.layerContainerOriginPx.x;
9196 var oldTop = this.layerContainerOriginPx.y;
9197 var newLeft = Math.round(originPx.x - newPx.x);
9198 var newTop = Math.round(originPx.y - newPx.y);
9199 this.applyTransform(
9200 (this.layerContainerOriginPx.x = newLeft),
9201 (this.layerContainerOriginPx.y = newTop));
9202 var dx = oldLeft - newLeft;
9203 var dy = oldTop - newTop;
9212 * Method: isValidZoomLevel
9215 * zoomLevel - {Integer}
9218 * {Boolean} Whether or not the zoom level passed in is non-null and
9219 * within the min/max range of zoom levels.
9221 isValidZoomLevel: function(zoomLevel) {
9222 return ( (zoomLevel != null) &&
9224 (zoomLevel < this.getNumZoomLevels()) );
9228 * Method: isValidLonLat
9231 * lonlat - {<OpenLayers.LonLat>}
9234 * {Boolean} Whether or not the lonlat passed in is non-null and within
9235 * the maxExtent bounds
9237 isValidLonLat: function(lonlat) {
9239 if (lonlat != null) {
9240 var maxExtent = this.getMaxExtent();
9241 var worldBounds = this.baseLayer.wrapDateLine && maxExtent;
9242 valid = maxExtent.containsLonLat(lonlat, {worldBounds: worldBounds});
9247 /********************************************************/
9251 /* Accessor functions to Layer Options parameters */
9253 /********************************************************/
9256 * APIMethod: getProjection
9257 * This method returns a string representing the projection. In
9258 * the case of projection support, this will be the srsCode which
9259 * is loaded -- otherwise it will simply be the string value that
9260 * was passed to the projection at startup.
9262 * FIXME: In 3.0, we will remove getProjectionObject, and instead
9263 * return a Projection object from this function.
9266 * {String} The Projection string from the base layer or null.
9268 getProjection: function() {
9269 var projection = this.getProjectionObject();
9270 return projection ? projection.getCode() : null;
9274 * APIMethod: getProjectionObject
9275 * Returns the projection obect from the baselayer.
9278 * {<OpenLayers.Projection>} The Projection of the base layer.
9280 getProjectionObject: function() {
9281 var projection = null;
9282 if (this.baseLayer != null) {
9283 projection = this.baseLayer.projection;
9289 * APIMethod: getMaxResolution
9292 * {String} The Map's Maximum Resolution
9294 getMaxResolution: function() {
9295 var maxResolution = null;
9296 if (this.baseLayer != null) {
9297 maxResolution = this.baseLayer.maxResolution;
9299 return maxResolution;
9303 * APIMethod: getMaxExtent
9306 * options - {Object}
9309 * restricted - {Boolean} If true, returns restricted extent (if it is
9313 * {<OpenLayers.Bounds>} The maxExtent property as set on the current
9314 * baselayer, unless the 'restricted' option is set, in which case
9315 * the 'restrictedExtent' option from the map is returned (if it
9318 getMaxExtent: function (options) {
9319 var maxExtent = null;
9320 if(options && options.restricted && this.restrictedExtent){
9321 maxExtent = this.restrictedExtent;
9322 } else if (this.baseLayer != null) {
9323 maxExtent = this.baseLayer.maxExtent;
9329 * APIMethod: getNumZoomLevels
9332 * {Integer} The total number of zoom levels that can be displayed by the
9333 * current baseLayer.
9335 getNumZoomLevels: function() {
9336 var numZoomLevels = null;
9337 if (this.baseLayer != null) {
9338 numZoomLevels = this.baseLayer.numZoomLevels;
9340 return numZoomLevels;
9343 /********************************************************/
9345 /* Baselayer Functions */
9347 /* The following functions, all publicly exposed */
9348 /* in the API?, are all merely wrappers to the */
9349 /* the same calls on whatever layer is set as */
9350 /* the current base layer */
9352 /********************************************************/
9355 * APIMethod: getExtent
9358 * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat
9359 * bounds of the current viewPort.
9360 * If no baselayer is set, returns null.
9362 getExtent: function () {
9364 if (this.baseLayer != null) {
9365 extent = this.baseLayer.getExtent();
9371 * APIMethod: getResolution
9374 * {Float} The current resolution of the map.
9375 * If no baselayer is set, returns null.
9377 getResolution: function () {
9378 var resolution = null;
9379 if (this.baseLayer != null) {
9380 resolution = this.baseLayer.getResolution();
9381 } else if(this.allOverlays === true && this.layers.length > 0) {
9382 // while adding the 1st layer to the map in allOverlays mode,
9383 // this.baseLayer is not set yet when we need the resolution
9384 // for calculateInRange.
9385 resolution = this.layers[0].getResolution();
9391 * APIMethod: getUnits
9394 * {Float} The current units of the map.
9395 * If no baselayer is set, returns null.
9397 getUnits: function () {
9399 if (this.baseLayer != null) {
9400 units = this.baseLayer.units;
9406 * APIMethod: getScale
9409 * {Float} The current scale denominator of the map.
9410 * If no baselayer is set, returns null.
9412 getScale: function () {
9414 if (this.baseLayer != null) {
9415 var res = this.getResolution();
9416 var units = this.baseLayer.units;
9417 scale = OpenLayers.Util.getScaleFromResolution(res, units);
9424 * APIMethod: getZoomForExtent
9427 * bounds - {<OpenLayers.Bounds>}
9428 * closest - {Boolean} Find the zoom level that most closely fits the
9429 * specified bounds. Note that this may result in a zoom that does
9430 * not exactly contain the entire extent.
9434 * {Integer} A suitable zoom level for the specified bounds.
9435 * If no baselayer is set, returns null.
9437 getZoomForExtent: function (bounds, closest) {
9439 if (this.baseLayer != null) {
9440 zoom = this.baseLayer.getZoomForExtent(bounds, closest);
9446 * APIMethod: getResolutionForZoom
9452 * {Float} A suitable resolution for the specified zoom. If no baselayer
9453 * is set, returns null.
9455 getResolutionForZoom: function(zoom) {
9456 var resolution = null;
9457 if(this.baseLayer) {
9458 resolution = this.baseLayer.getResolutionForZoom(zoom);
9464 * APIMethod: getZoomForResolution
9467 * resolution - {Float}
9468 * closest - {Boolean} Find the zoom level that corresponds to the absolute
9469 * closest resolution, which may result in a zoom whose corresponding
9470 * resolution is actually smaller than we would have desired (if this
9471 * is being called from a getZoomForExtent() call, then this means that
9472 * the returned zoom index might not actually contain the entire
9473 * extent specified... but it'll be close).
9477 * {Integer} A suitable zoom level for the specified resolution.
9478 * If no baselayer is set, returns null.
9480 getZoomForResolution: function(resolution, closest) {
9482 if (this.baseLayer != null) {
9483 zoom = this.baseLayer.getZoomForResolution(resolution, closest);
9488 /********************************************************/
9490 /* Zooming Functions */
9492 /* The following functions, all publicly exposed */
9493 /* in the API, are all merely wrappers to the */
9494 /* the setCenter() function */
9496 /********************************************************/
9500 * Zoom to a specific zoom level. Zooming will be animated unless the map
9501 * is configured with {zoomMethod: null}. To zoom without animation, use
9502 * <setCenter> without a lonlat argument.
9507 zoomTo: function(zoom, xy) {
9508 // non-API arguments:
9509 // xy - {<OpenLayers.Pixel>} optional zoom origin
9512 if (map.isValidZoomLevel(zoom)) {
9513 if (map.baseLayer.wrapDateLine) {
9514 zoom = map.adjustZoom(zoom);
9517 map.getZoomTargetCenter(xy, map.getResolutionForZoom(zoom)) :
9520 map.events.triggerEvent('zoomstart', {
9525 if (map.zoomTween) {
9526 map.zoomTween.stop();
9527 var currentRes = map.getResolution(),
9528 targetRes = map.getResolutionForZoom(zoom),
9530 end = {scale: currentRes / targetRes};
9532 var size = map.getSize();
9533 xy = {x: size.w / 2, y: size.h / 2};
9535 map.zoomTween.start(start, end, map.zoomDuration, {
9536 minFrameRate: 50, // don't spend much time zooming
9538 eachStep: function(data) {
9539 var containerOrigin = map.layerContainerOriginPx,
9541 dx = ((scale - 1) * (containerOrigin.x - xy.x)) | 0,
9542 dy = ((scale - 1) * (containerOrigin.y - xy.y)) | 0;
9543 map.applyTransform(containerOrigin.x + dx, containerOrigin.y + dy, scale);
9545 done: function(data) {
9546 map.applyTransform();
9547 var resolution = map.getResolution() / data.scale,
9548 newZoom = map.getZoomForResolution(resolution, true),
9549 newCenter = data.scale === 1 ? center :
9550 map.getZoomTargetCenter(xy, resolution);
9551 map.moveTo(newCenter, newZoom);
9556 map.setCenter(center, zoom);
9565 zoomIn: function() {
9566 if (this.zoomTween) {
9567 this.zoomTween.stop();
9569 this.zoomTo(this.getZoom() + 1);
9573 * APIMethod: zoomOut
9576 zoomOut: function() {
9577 if (this.zoomTween) {
9578 this.zoomTween.stop();
9580 this.zoomTo(this.getZoom() - 1);
9584 * APIMethod: zoomToExtent
9585 * Zoom to the passed in bounds, recenter
9588 * bounds - {<OpenLayers.Bounds>|Array} If provided as an array, the array
9589 * should consist of four values (left, bottom, right, top).
9590 * closest - {Boolean} Find the zoom level that most closely fits the
9591 * specified bounds. Note that this may result in a zoom that does
9592 * not exactly contain the entire extent.
9596 zoomToExtent: function(bounds, closest) {
9597 if (!(bounds instanceof OpenLayers.Bounds)) {
9598 bounds = new OpenLayers.Bounds(bounds);
9600 var center = bounds.getCenterLonLat();
9601 if (this.baseLayer.wrapDateLine) {
9602 var maxExtent = this.getMaxExtent();
9604 //fix straddling bounds (in the case of a bbox that straddles the
9605 // dateline, it's left and right boundaries will appear backwards.
9606 // we fix this by allowing a right value that is greater than the
9607 // max value at the dateline -- this allows us to pass a valid
9608 // bounds to calculate zoom)
9610 bounds = bounds.clone();
9611 while (bounds.right < bounds.left) {
9612 bounds.right += maxExtent.getWidth();
9614 //if the bounds was straddling (see above), then the center point
9615 // we got from it was wrong. So we take our new bounds and ask it
9618 center = bounds.getCenterLonLat().wrapDateLine(maxExtent);
9620 this.setCenter(center, this.getZoomForExtent(bounds, closest));
9624 * APIMethod: zoomToMaxExtent
9625 * Zoom to the full extent and recenter.
9628 * options - {Object}
9631 * restricted - {Boolean} True to zoom to restricted extent if it is
9632 * set. Defaults to true.
9634 zoomToMaxExtent: function(options) {
9635 //restricted is true by default
9636 var restricted = (options) ? options.restricted : true;
9638 var maxExtent = this.getMaxExtent({
9639 'restricted': restricted
9641 this.zoomToExtent(maxExtent);
9645 * APIMethod: zoomToScale
9646 * Zoom to a specified scale
9650 * closest - {Boolean} Find the zoom level that most closely fits the
9651 * specified scale. Note that this may result in a zoom that does
9652 * not exactly contain the entire extent.
9656 zoomToScale: function(scale, closest) {
9657 var res = OpenLayers.Util.getResolutionFromScale(scale,
9658 this.baseLayer.units);
9660 var halfWDeg = (this.size.w * res) / 2;
9661 var halfHDeg = (this.size.h * res) / 2;
9662 var center = this.getCachedCenter();
9664 var extent = new OpenLayers.Bounds(center.lon - halfWDeg,
9665 center.lat - halfHDeg,
9666 center.lon + halfWDeg,
9667 center.lat + halfHDeg);
9668 this.zoomToExtent(extent, closest);
9671 /********************************************************/
9673 /* Translation Functions */
9675 /* The following functions translate between */
9676 /* LonLat, LayerPx, and ViewPortPx */
9678 /********************************************************/
9681 // TRANSLATION: LonLat <-> ViewPortPx
9685 * Method: getLonLatFromViewPortPx
9688 * viewPortPx - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or
9689 * an object with a 'x'
9690 * and 'y' properties.
9693 * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view
9694 * port <OpenLayers.Pixel>, translated into lon/lat
9695 * by the current base layer.
9697 getLonLatFromViewPortPx: function (viewPortPx) {
9699 if (this.baseLayer != null) {
9700 lonlat = this.baseLayer.getLonLatFromViewPortPx(viewPortPx);
9706 * APIMethod: getViewPortPxFromLonLat
9709 * lonlat - {<OpenLayers.LonLat>}
9712 * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in
9713 * <OpenLayers.LonLat>, translated into view port
9714 * pixels by the current base layer.
9716 getViewPortPxFromLonLat: function (lonlat) {
9718 if (this.baseLayer != null) {
9719 px = this.baseLayer.getViewPortPxFromLonLat(lonlat);
9725 * Method: getZoomTargetCenter
9728 * xy - {<OpenLayers.Pixel>} The zoom origin pixel location on the screen
9729 * resolution - {Float} The resolution we want to get the center for
9732 * {<OpenLayers.LonLat>} The location of the map center after the
9733 * transformation described by the origin xy and the target resolution.
9735 getZoomTargetCenter: function (xy, resolution) {
9737 size = this.getSize(),
9738 deltaX = size.w/2 - xy.x,
9739 deltaY = xy.y - size.h/2,
9740 zoomPoint = this.getLonLatFromPixel(xy);
9742 lonlat = new OpenLayers.LonLat(
9743 zoomPoint.lon + deltaX * resolution,
9744 zoomPoint.lat + deltaY * resolution
9751 // CONVENIENCE TRANSLATION FUNCTIONS FOR API
9755 * APIMethod: getLonLatFromPixel
9758 * px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object with
9759 * a 'x' and 'y' properties.
9762 * {<OpenLayers.LonLat>} An OpenLayers.LonLat corresponding to the given
9763 * OpenLayers.Pixel, translated into lon/lat by the
9764 * current base layer
9766 getLonLatFromPixel: function (px) {
9767 return this.getLonLatFromViewPortPx(px);
9771 * APIMethod: getPixelFromLonLat
9772 * Returns a pixel location given a map location. The map location is
9773 * translated to an integer pixel location (in viewport pixel
9774 * coordinates) by the current base layer.
9777 * lonlat - {<OpenLayers.LonLat>} A map location.
9780 * {<OpenLayers.Pixel>} An OpenLayers.Pixel corresponding to the
9781 * <OpenLayers.LonLat> translated into view port pixels by the current
9784 getPixelFromLonLat: function (lonlat) {
9785 var px = this.getViewPortPxFromLonLat(lonlat);
9786 px.x = Math.round(px.x);
9787 px.y = Math.round(px.y);
9792 * Method: getGeodesicPixelSize
9795 * px - {<OpenLayers.Pixel>} The pixel to get the geodesic length for. If
9796 * not provided, the center pixel of the map viewport will be used.
9799 * {<OpenLayers.Size>} The geodesic size of the pixel in kilometers.
9801 getGeodesicPixelSize: function(px) {
9802 var lonlat = px ? this.getLonLatFromPixel(px) : (
9803 this.getCachedCenter() || new OpenLayers.LonLat(0, 0));
9804 var res = this.getResolution();
9805 var left = lonlat.add(-res / 2, 0);
9806 var right = lonlat.add(res / 2, 0);
9807 var bottom = lonlat.add(0, -res / 2);
9808 var top = lonlat.add(0, res / 2);
9809 var dest = new OpenLayers.Projection("EPSG:4326");
9810 var source = this.getProjectionObject() || dest;
9811 if(!source.equals(dest)) {
9812 left.transform(source, dest);
9813 right.transform(source, dest);
9814 bottom.transform(source, dest);
9815 top.transform(source, dest);
9818 return new OpenLayers.Size(
9819 OpenLayers.Util.distVincenty(left, right),
9820 OpenLayers.Util.distVincenty(bottom, top)
9827 // TRANSLATION: ViewPortPx <-> LayerPx
9831 * APIMethod: getViewPortPxFromLayerPx
9834 * layerPx - {<OpenLayers.Pixel>}
9837 * {<OpenLayers.Pixel>} Layer Pixel translated into ViewPort Pixel
9840 getViewPortPxFromLayerPx:function(layerPx) {
9841 var viewPortPx = null;
9842 if (layerPx != null) {
9843 var dX = this.layerContainerOriginPx.x;
9844 var dY = this.layerContainerOriginPx.y;
9845 viewPortPx = layerPx.add(dX, dY);
9851 * APIMethod: getLayerPxFromViewPortPx
9854 * viewPortPx - {<OpenLayers.Pixel>}
9857 * {<OpenLayers.Pixel>} ViewPort Pixel translated into Layer Pixel
9860 getLayerPxFromViewPortPx:function(viewPortPx) {
9862 if (viewPortPx != null) {
9863 var dX = -this.layerContainerOriginPx.x;
9864 var dY = -this.layerContainerOriginPx.y;
9865 layerPx = viewPortPx.add(dX, dY);
9866 if (isNaN(layerPx.x) || isNaN(layerPx.y)) {
9874 // TRANSLATION: LonLat <-> LayerPx
9878 * Method: getLonLatFromLayerPx
9881 * px - {<OpenLayers.Pixel>}
9884 * {<OpenLayers.LonLat>}
9886 getLonLatFromLayerPx: function (px) {
9887 //adjust for displacement of layerContainerDiv
9888 px = this.getViewPortPxFromLayerPx(px);
9889 return this.getLonLatFromViewPortPx(px);
9893 * APIMethod: getLayerPxFromLonLat
9896 * lonlat - {<OpenLayers.LonLat>} lonlat
9899 * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in
9900 * <OpenLayers.LonLat>, translated into layer pixels
9901 * by the current base layer
9903 getLayerPxFromLonLat: function (lonlat) {
9904 //adjust for displacement of layerContainerDiv
9905 var px = this.getPixelFromLonLat(lonlat);
9906 return this.getLayerPxFromViewPortPx(px);
9910 * Method: applyTransform
9911 * Applies the given transform to the <layerContainerDiv>. This method has
9912 * a 2-stage fallback from translate3d/scale3d via translate/scale to plain
9913 * style.left/style.top, in which case no scaling is supported.
9916 * x - {Number} x parameter for the translation. Defaults to the x value of
9917 * the map's <layerContainerOriginPx>
9918 * y - {Number} y parameter for the translation. Defaults to the y value of
9919 * the map's <layerContainerOriginPx>
9920 * scale - {Number} scale. Defaults to 1 if not provided.
9922 applyTransform: function(x, y, scale) {
9924 var origin = this.layerContainerOriginPx,
9925 needTransform = scale !== 1;
9929 var style = this.layerContainerDiv.style,
9930 transform = this.applyTransform.transform,
9931 template = this.applyTransform.template;
9933 if (transform === undefined) {
9934 transform = OpenLayers.Util.vendorPrefix.style('transform');
9935 this.applyTransform.transform = transform;
9937 // Try translate3d, but only if the viewPortDiv has a transform
9938 // defined in a stylesheet
9939 var computedStyle = OpenLayers.Element.getStyle(this.viewPortDiv,
9940 OpenLayers.Util.vendorPrefix.css('transform'));
9941 if (!computedStyle || computedStyle !== 'none') {
9942 template = ['translate3d(', ',0) ', 'scale3d(', ',1)'];
9943 style[transform] = [template[0], '0,0', template[1]].join('');
9945 // If no transform is defined in the stylesheet or translate3d
9946 // does not stick, use translate and scale
9947 if (!template || !~style[transform].indexOf(template[0])) {
9948 template = ['translate(', ') ', 'scale(', ')'];
9950 this.applyTransform.template = template;
9954 // If we do 3d transforms, we always want to use them. If we do 2d
9955 // transforms, we only use them when we need to.
9956 if (transform !== null && (template[0] === 'translate3d(' || needTransform === true)) {
9957 // Our 2d transforms are combined with style.left and style.top, so
9958 // adjust x and y values and set the origin as left and top
9959 if (needTransform === true && template[0] === 'translate(') {
9962 style.left = origin.x + 'px';
9963 style.top = origin.y + 'px';
9965 style[transform] = [
9966 template[0], x, 'px,', y, 'px', template[1],
9967 template[2], scale, ',', scale, template[3]
9970 style.left = x + 'px';
9971 style.top = y + 'px';
9972 // We previously might have had needTransform, so remove transform
9973 if (transform !== null) {
9974 style[transform] = '';
9979 CLASS_NAME: "OpenLayers.Map"
9983 * Constant: TILE_WIDTH
9984 * {Integer} 256 Default tile width (unless otherwise specified)
9986 OpenLayers.Map.TILE_WIDTH = 256;
9988 * Constant: TILE_HEIGHT
9989 * {Integer} 256 Default tile height (unless otherwise specified)
9991 OpenLayers.Map.TILE_HEIGHT = 256;
9992 /* ======================================================================
9993 OpenLayers/Feature.js
9994 ====================================================================== */
9996 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
9997 * full list of contributors). Published under the 2-clause BSD license.
9998 * See license.txt in the OpenLayers distribution or repository for the
9999 * full text of the license. */
10003 * @requires OpenLayers/BaseTypes/Class.js
10004 * @requires OpenLayers/Util.js
10008 * Class: OpenLayers.Feature
10009 * Features are combinations of geography and attributes. The OpenLayers.Feature
10010 * class specifically combines a marker and a lonlat.
10012 OpenLayers.Feature = OpenLayers.Class({
10016 * {<OpenLayers.Layer>}
10028 * {<OpenLayers.LonLat>}
10040 * {<OpenLayers.Marker>}
10045 * APIProperty: popupClass
10046 * {<OpenLayers.Class>} The class which will be used to instantiate
10047 * a new Popup. Default is <OpenLayers.Popup.Anchored>.
10053 * {<OpenLayers.Popup>}
10058 * Constructor: OpenLayers.Feature
10059 * Constructor for features.
10062 * layer - {<OpenLayers.Layer>}
10063 * lonlat - {<OpenLayers.LonLat>}
10067 * {<OpenLayers.Feature>}
10069 initialize: function(layer, lonlat, data) {
10070 this.layer = layer;
10071 this.lonlat = lonlat;
10072 this.data = (data != null) ? data : {};
10073 this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
10078 * nullify references to prevent circular references and memory leaks
10080 destroy: function() {
10082 //remove the popup from the map
10083 if ((this.layer != null) && (this.layer.map != null)) {
10084 if (this.popup != null) {
10085 this.layer.map.removePopup(this.popup);
10088 // remove the marker from the layer
10089 if (this.layer != null && this.marker != null) {
10090 this.layer.removeMarker(this.marker);
10095 this.lonlat = null;
10097 if (this.marker != null) {
10098 this.destroyMarker(this.marker);
10099 this.marker = null;
10101 if (this.popup != null) {
10102 this.destroyPopup(this.popup);
10111 * {Boolean} Whether or not the feature is currently visible on screen
10112 * (based on its 'lonlat' property)
10114 onScreen:function() {
10116 var onScreen = false;
10117 if ((this.layer != null) && (this.layer.map != null)) {
10118 var screenBounds = this.layer.map.getExtent();
10119 onScreen = screenBounds.containsLonLat(this.lonlat);
10126 * Method: createMarker
10127 * Based on the data associated with the Feature, create and return a marker object.
10130 * {<OpenLayers.Marker>} A Marker Object created from the 'lonlat' and 'icon' properties
10131 * set in this.data. If no 'lonlat' is set, returns null. If no
10132 * 'icon' is set, OpenLayers.Marker() will load the default image.
10134 * Note - this.marker is set to return value
10137 createMarker: function() {
10139 if (this.lonlat != null) {
10140 this.marker = new OpenLayers.Marker(this.lonlat, this.data.icon);
10142 return this.marker;
10146 * Method: destroyMarker
10148 * If user overrides the createMarker() function, s/he should be able
10149 * to also specify an alternative function for destroying it
10151 destroyMarker: function() {
10152 this.marker.destroy();
10156 * Method: createPopup
10157 * Creates a popup object created from the 'lonlat', 'popupSize',
10158 * and 'popupContentHTML' properties set in this.data. It uses
10159 * this.marker.icon as default anchor.
10161 * If no 'lonlat' is set, returns null.
10162 * If no this.marker has been created, no anchor is sent.
10164 * Note - the returned popup object is 'owned' by the feature, so you
10165 * cannot use the popup's destroy method to discard the popup.
10166 * Instead, you must use the feature's destroyPopup
10168 * Note - this.popup is set to return value
10171 * closeBox - {Boolean} create popup with closebox or not
10174 * {<OpenLayers.Popup>} Returns the created popup, which is also set
10175 * as 'popup' property of this feature. Will be of whatever type
10176 * specified by this feature's 'popupClass' property, but must be
10177 * of type <OpenLayers.Popup>.
10180 createPopup: function(closeBox) {
10182 if (this.lonlat != null) {
10184 var anchor = (this.marker) ? this.marker.icon : null;
10185 var popupClass = this.popupClass ?
10186 this.popupClass : OpenLayers.Popup.Anchored;
10187 this.popup = new popupClass(this.id + "_popup",
10189 this.data.popupSize,
10190 this.data.popupContentHTML,
10194 if (this.data.overflow != null) {
10195 this.popup.contentDiv.style.overflow = this.data.overflow;
10198 this.popup.feature = this;
10205 * Method: destroyPopup
10206 * Destroys the popup created via createPopup.
10208 * As with the marker, if user overrides the createPopup() function, s/he
10209 * should also be able to override the destruction
10211 destroyPopup: function() {
10213 this.popup.feature = null;
10214 this.popup.destroy();
10219 CLASS_NAME: "OpenLayers.Feature"
10221 /* ======================================================================
10222 OpenLayers/Feature/Vector.js
10223 ====================================================================== */
10225 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
10226 * full list of contributors). Published under the 2-clause BSD license.
10227 * See license.txt in the OpenLayers distribution or repository for the
10228 * full text of the license. */
10231 OpenLayers.State = {
10233 UNKNOWN: 'Unknown',
10240 * @requires OpenLayers/Feature.js
10241 * @requires OpenLayers/Util.js
10245 * Class: OpenLayers.Feature.Vector
10246 * Vector features use the OpenLayers.Geometry classes as geometry description.
10247 * They have an 'attributes' property, which is the data object, and a 'style'
10248 * property, the default values of which are defined in the
10249 * <OpenLayers.Feature.Vector.style> objects.
10252 * - <OpenLayers.Feature>
10254 OpenLayers.Feature.Vector = OpenLayers.Class(OpenLayers.Feature, {
10263 * APIProperty: geometry
10264 * {<OpenLayers.Geometry>}
10269 * APIProperty: attributes
10270 * {Object} This object holds arbitrary, serializable properties that
10271 * describe the feature.
10277 * {<OpenLayers.Bounds>} The box bounding that feature's geometry, that
10278 * property can be set by an <OpenLayers.Format> object when
10279 * deserializing the feature, so in most cases it represents an
10280 * information set by the server.
10291 * APIProperty: style
10298 * {String} If this property is set it will be taken into account by
10299 * {<OpenLayers.HTTP>} when updating or deleting the feature.
10304 * Property: renderIntent
10305 * {String} rendering intent currently being used
10307 renderIntent: "default",
10310 * APIProperty: modified
10311 * {Object} An object with the originals of the geometry and attributes of
10312 * the feature, if they were changed. Currently this property is only read
10313 * by <OpenLayers.Format.WFST.v1>, and written by
10314 * <OpenLayers.Control.ModifyFeature>, which sets the geometry property.
10315 * Applications can set the originals of modified attributes in the
10316 * attributes property. Note that applications have to check if this
10317 * object and the attributes property is already created before using it.
10318 * After a change made with ModifyFeature, this object could look like
10322 * geometry: >Object
10326 * When an application has made changes to feature attributes, it could
10327 * have set the attributes to something like this:
10332 * myAttribute: "original"
10337 * Note that <OpenLayers.Format.WFST.v1> only checks for truthy values in
10338 * *modified.geometry* and the attribute names in *modified.attributes*,
10339 * but it is recommended to set the original values (and not just true) as
10340 * attribute value, so applications could use this information to undo
10346 * Constructor: OpenLayers.Feature.Vector
10347 * Create a vector feature.
10350 * geometry - {<OpenLayers.Geometry>} The geometry that this feature
10352 * attributes - {Object} An optional object that will be mapped to the
10353 * <attributes> property.
10354 * style - {Object} An optional style object.
10356 initialize: function(geometry, attributes, style) {
10357 OpenLayers.Feature.prototype.initialize.apply(this,
10358 [null, null, attributes]);
10359 this.lonlat = null;
10360 this.geometry = geometry ? geometry : null;
10362 this.attributes = {};
10364 this.attributes = OpenLayers.Util.extend(this.attributes,
10367 this.style = style ? style : null;
10372 * nullify references to prevent circular references and memory leaks
10374 destroy: function() {
10376 this.layer.removeFeatures(this);
10380 this.geometry = null;
10381 this.modified = null;
10382 OpenLayers.Feature.prototype.destroy.apply(this, arguments);
10387 * Create a clone of this vector feature. Does not set any non-standard
10391 * {<OpenLayers.Feature.Vector>} An exact clone of this vector feature.
10393 clone: function () {
10394 return new OpenLayers.Feature.Vector(
10395 this.geometry ? this.geometry.clone() : null,
10402 * Determine whether the feature is within the map viewport. This method
10403 * tests for an intersection between the geometry and the viewport
10404 * bounds. If a more efficient but less precise geometry bounds
10405 * intersection is desired, call the method with the boundsOnly
10409 * boundsOnly - {Boolean} Only test whether a feature's bounds intersects
10410 * the viewport bounds. Default is false. If false, the feature's
10411 * geometry must intersect the viewport for onScreen to return true.
10414 * {Boolean} The feature is currently visible on screen (optionally
10415 * based on its bounds if boundsOnly is true).
10417 onScreen:function(boundsOnly) {
10418 var onScreen = false;
10419 if(this.layer && this.layer.map) {
10420 var screenBounds = this.layer.map.getExtent();
10422 var featureBounds = this.geometry.getBounds();
10423 onScreen = screenBounds.intersectsBounds(featureBounds);
10425 var screenPoly = screenBounds.toGeometry();
10426 onScreen = screenPoly.intersects(this.geometry);
10433 * Method: getVisibility
10434 * Determine whether the feature is displayed or not. It may not displayed
10436 * - its style display property is set to 'none',
10437 * - it doesn't belong to any layer,
10438 * - the styleMap creates a symbolizer with display property set to 'none'
10440 * - the layer which it belongs to is not visible.
10443 * {Boolean} The feature is currently displayed.
10445 getVisibility: function() {
10446 return !(this.style && this.style.display == 'none' ||
10448 this.layer && this.layer.styleMap &&
10449 this.layer.styleMap.createSymbolizer(this, this.renderIntent).display == 'none' ||
10450 this.layer && !this.layer.getVisibility());
10454 * Method: createMarker
10455 * HACK - we need to decide if all vector features should be able to
10459 * {<OpenLayers.Marker>} For now just returns null
10461 createMarker: function() {
10466 * Method: destroyMarker
10467 * HACK - we need to decide if all vector features should be able to
10470 * If user overrides the createMarker() function, s/he should be able
10471 * to also specify an alternative function for destroying it
10473 destroyMarker: function() {
10478 * Method: createPopup
10479 * HACK - we need to decide if all vector features should be able to
10483 * {<OpenLayers.Popup>} For now just returns null
10485 createPopup: function() {
10491 * Determins whether the feature intersects with the specified location.
10494 * lonlat - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an
10495 * object with a 'lon' and 'lat' properties.
10496 * toleranceLon - {float} Optional tolerance in Geometric Coords
10497 * toleranceLat - {float} Optional tolerance in Geographic Coords
10500 * {Boolean} Whether or not the feature is at the specified location
10502 atPoint: function(lonlat, toleranceLon, toleranceLat) {
10503 var atPoint = false;
10504 if(this.geometry) {
10505 atPoint = this.geometry.atPoint(lonlat, toleranceLon,
10512 * Method: destroyPopup
10513 * HACK - we need to decide if all vector features should be able to
10516 destroyPopup: function() {
10522 * Moves the feature and redraws it at its new location
10525 * location - {<OpenLayers.LonLat> or <OpenLayers.Pixel>} the
10526 * location to which to move the feature.
10528 move: function(location) {
10530 if(!this.layer || !this.geometry.move){
10531 //do nothing if no layer or immoveable geometry
10536 if (location.CLASS_NAME == "OpenLayers.LonLat") {
10537 pixel = this.layer.getViewPortPxFromLonLat(location);
10542 var lastPixel = this.layer.getViewPortPxFromLonLat(this.geometry.getBounds().getCenterLonLat());
10543 var res = this.layer.map.getResolution();
10544 this.geometry.move(res * (pixel.x - lastPixel.x),
10545 res * (lastPixel.y - pixel.y));
10546 this.layer.drawFeature(this);
10552 * Sets the new state
10557 toState: function(state) {
10558 if (state == OpenLayers.State.UPDATE) {
10559 switch (this.state) {
10560 case OpenLayers.State.UNKNOWN:
10561 case OpenLayers.State.DELETE:
10562 this.state = state;
10564 case OpenLayers.State.UPDATE:
10565 case OpenLayers.State.INSERT:
10568 } else if (state == OpenLayers.State.INSERT) {
10569 switch (this.state) {
10570 case OpenLayers.State.UNKNOWN:
10573 this.state = state;
10576 } else if (state == OpenLayers.State.DELETE) {
10577 switch (this.state) {
10578 case OpenLayers.State.INSERT:
10579 // the feature should be destroyed
10581 case OpenLayers.State.DELETE:
10583 case OpenLayers.State.UNKNOWN:
10584 case OpenLayers.State.UPDATE:
10585 this.state = state;
10588 } else if (state == OpenLayers.State.UNKNOWN) {
10589 this.state = state;
10593 CLASS_NAME: "OpenLayers.Feature.Vector"
10598 * Constant: OpenLayers.Feature.Vector.style
10599 * OpenLayers features can have a number of style attributes. The 'default'
10600 * style will typically be used if no other style is specified. These
10601 * styles correspond for the most part, to the styling properties defined
10602 * by the SVG standard.
10603 * Information on fill properties: http://www.w3.org/TR/SVG/painting.html#FillProperties
10604 * Information on stroke properties: http://www.w3.org/TR/SVG/painting.html#StrokeProperties
10606 * Symbolizer properties:
10607 * fill - {Boolean} Set to false if no fill is desired.
10608 * fillColor - {String} Hex fill color. Default is "#ee9900".
10609 * fillOpacity - {Number} Fill opacity (0-1). Default is 0.4
10610 * stroke - {Boolean} Set to false if no stroke is desired.
10611 * strokeColor - {String} Hex stroke color. Default is "#ee9900".
10612 * strokeOpacity - {Number} Stroke opacity (0-1). Default is 1.
10613 * strokeWidth - {Number} Pixel stroke width. Default is 1.
10614 * strokeLinecap - {String} Stroke cap type. Default is "round". [butt | round | square]
10615 * strokeDashstyle - {String} Stroke dash style. Default is "solid". [dot | dash | dashdot | longdash | longdashdot | solid]
10616 * graphic - {Boolean} Set to false if no graphic is desired.
10617 * pointRadius - {Number} Pixel point radius. Default is 6.
10618 * pointerEvents - {String} Default is "visiblePainted".
10619 * cursor - {String} Default is "".
10620 * externalGraphic - {String} Url to an external graphic that will be used for rendering points.
10621 * graphicWidth - {Number} Pixel width for sizing an external graphic.
10622 * graphicHeight - {Number} Pixel height for sizing an external graphic.
10623 * graphicOpacity - {Number} Opacity (0-1) for an external graphic.
10624 * graphicXOffset - {Number} Pixel offset along the positive x axis for displacing an external graphic.
10625 * graphicYOffset - {Number} Pixel offset along the positive y axis for displacing an external graphic.
10626 * 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).
10627 * graphicZIndex - {Number} The integer z-index value to use in rendering.
10628 * graphicName - {String} Named graphic to use when rendering points. Supported values include "circle" (default),
10629 * "square", "star", "x", "cross", "triangle".
10630 * graphicTitle - {String} Tooltip when hovering over a feature. *deprecated*, use title instead
10631 * title - {String} Tooltip when hovering over a feature. Not supported by the canvas renderer.
10632 * backgroundGraphic - {String} Url to a graphic to be used as the background under an externalGraphic.
10633 * backgroundGraphicZIndex - {Number} The integer z-index value to use in rendering the background graphic.
10634 * backgroundXOffset - {Number} The x offset (in pixels) for the background graphic.
10635 * backgroundYOffset - {Number} The y offset (in pixels) for the background graphic.
10636 * backgroundHeight - {Number} The height of the background graphic. If not provided, the graphicHeight will be used.
10637 * backgroundWidth - {Number} The width of the background width. If not provided, the graphicWidth will be used.
10638 * label - {String} The text for an optional label. For browsers that use the canvas renderer, this requires either
10639 * fillText or mozDrawText to be available.
10640 * labelAlign - {String} Label alignment. This specifies the insertion point relative to the text. It is a string
10641 * composed of two characters. The first character is for the horizontal alignment, the second for the vertical
10642 * alignment. Valid values for horizontal alignment: "l"=left, "c"=center, "r"=right. Valid values for vertical
10643 * alignment: "t"=top, "m"=middle, "b"=bottom. Example values: "lt", "cm", "rb". Default is "cm".
10644 * labelXOffset - {Number} Pixel offset along the positive x axis for displacing the label. Not supported by the canvas renderer.
10645 * labelYOffset - {Number} Pixel offset along the positive y axis for displacing the label. Not supported by the canvas renderer.
10646 * labelSelect - {Boolean} If set to true, labels will be selectable using SelectFeature or similar controls.
10647 * Default is false.
10648 * labelOutlineColor - {String} The color of the label outline. Default is 'white'. Only supported by the canvas & SVG renderers.
10649 * labelOutlineWidth - {Number} The width of the label outline. Default is 3, set to 0 or null to disable. Only supported by the SVG renderers.
10650 * labelOutlineOpacity - {Number} The opacity (0-1) of the label outline. Default is fontOpacity. Only supported by the canvas & SVG renderers.
10651 * fontColor - {String} The font color for the label, to be provided like CSS.
10652 * fontOpacity - {Number} Opacity (0-1) for the label
10653 * fontFamily - {String} The font family for the label, to be provided like in CSS.
10654 * fontSize - {String} The font size for the label, to be provided like in CSS.
10655 * fontStyle - {String} The font style for the label, to be provided like in CSS.
10656 * fontWeight - {String} The font weight for the label, to be provided like in CSS.
10657 * display - {String} Symbolizers will have no effect if display is set to "none". All other values have no effect.
10659 OpenLayers.Feature.Vector.style = {
10661 fillColor: "#ee9900",
10663 hoverFillColor: "white",
10664 hoverFillOpacity: 0.8,
10665 strokeColor: "#ee9900",
10668 strokeLinecap: "round",
10669 strokeDashstyle: "solid",
10670 hoverStrokeColor: "red",
10671 hoverStrokeOpacity: 1,
10672 hoverStrokeWidth: 0.2,
10674 hoverPointRadius: 1,
10675 hoverPointUnit: "%",
10676 pointerEvents: "visiblePainted",
10678 fontColor: "#000000",
10680 labelOutlineColor: "white",
10681 labelOutlineWidth: 3
10686 hoverFillColor: "white",
10687 hoverFillOpacity: 0.8,
10688 strokeColor: "blue",
10691 strokeLinecap: "round",
10692 strokeDashstyle: "solid",
10693 hoverStrokeColor: "red",
10694 hoverStrokeOpacity: 1,
10695 hoverStrokeWidth: 0.2,
10697 hoverPointRadius: 1,
10698 hoverPointUnit: "%",
10699 pointerEvents: "visiblePainted",
10701 fontColor: "#000000",
10703 labelOutlineColor: "white",
10704 labelOutlineWidth: 3
10708 fillColor: "#66cccc",
10710 hoverFillColor: "white",
10711 hoverFillOpacity: 0.8,
10712 strokeColor: "#66cccc",
10714 strokeLinecap: "round",
10716 strokeDashstyle: "solid",
10717 hoverStrokeColor: "red",
10718 hoverStrokeOpacity: 1,
10719 hoverStrokeWidth: 0.2,
10721 hoverPointRadius: 1,
10722 hoverPointUnit: "%",
10723 pointerEvents: "visiblePainted",
10725 fontColor: "#000000",
10727 labelOutlineColor: "white",
10728 labelOutlineWidth: 3
10735 /* ======================================================================
10736 OpenLayers/Style.js
10737 ====================================================================== */
10739 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
10740 * full list of contributors). Published under the 2-clause BSD license.
10741 * See license.txt in the OpenLayers distribution or repository for the
10742 * full text of the license. */
10746 * @requires OpenLayers/BaseTypes/Class.js
10747 * @requires OpenLayers/Util.js
10748 * @requires OpenLayers/Feature/Vector.js
10752 * Class: OpenLayers.Style
10753 * This class represents a UserStyle obtained
10754 * from a SLD, containing styling rules.
10756 OpenLayers.Style = OpenLayers.Class({
10760 * {String} A unique id for this session.
10765 * APIProperty: name
10772 * {String} Title of this style (set if included in SLD)
10777 * Property: description
10778 * {String} Description of this style (set if abstract is included in SLD)
10783 * APIProperty: layerName
10784 * {<String>} name of the layer that this style belongs to, usually
10785 * according to the NamedLayer attribute of an SLD document.
10790 * APIProperty: isDefault
10797 * {Array(<OpenLayers.Rule>)}
10802 * APIProperty: context
10803 * {Object} An optional object with properties that symbolizers' property
10804 * values should be evaluated against. If no context is specified,
10805 * feature.attributes will be used
10810 * Property: defaultStyle
10811 * {Object} hash of style properties to use as default for merging
10812 * rule-based style symbolizers onto. If no rules are defined,
10813 * createSymbolizer will return this style. If <defaultsPerSymbolizer> is set to
10814 * true, the defaultStyle will only be taken into account if there are
10817 defaultStyle: null,
10820 * Property: defaultsPerSymbolizer
10821 * {Boolean} If set to true, the <defaultStyle> will extend the symbolizer
10822 * of every rule. Properties of the <defaultStyle> will also be used to set
10823 * missing symbolizer properties if the symbolizer has stroke, fill or
10824 * graphic set to true. Default is false.
10826 defaultsPerSymbolizer: false,
10829 * Property: propertyStyles
10830 * {Hash of Boolean} cache of style properties that need to be parsed for
10831 * propertyNames. Property names are keys, values won't be used.
10833 propertyStyles: null,
10837 * Constructor: OpenLayers.Style
10838 * Creates a UserStyle.
10841 * style - {Object} Optional hash of style properties that will be
10842 * used as default style for this style object. This style
10843 * applies if no rules are specified. Symbolizers defined in
10844 * rules will extend this default style.
10845 * options - {Object} An optional object with properties to set on the
10849 * rules - {Array(<OpenLayers.Rule>)} List of rules to be added to the
10853 * {<OpenLayers.Style>}
10855 initialize: function(style, options) {
10857 OpenLayers.Util.extend(this, options);
10859 if(options && options.rules) {
10860 this.addRules(options.rules);
10863 // use the default style from OpenLayers.Feature.Vector if no style
10864 // was given in the constructor
10865 this.setDefaultStyle(style ||
10866 OpenLayers.Feature.Vector.style["default"]);
10868 this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
10872 * APIMethod: destroy
10873 * nullify references to prevent circular references and memory leaks
10875 destroy: function() {
10876 for (var i=0, len=this.rules.length; i<len; i++) {
10877 this.rules[i].destroy();
10878 this.rules[i] = null;
10881 this.defaultStyle = null;
10885 * Method: createSymbolizer
10886 * creates a style by applying all feature-dependent rules to the base
10890 * feature - {<OpenLayers.Feature>} feature to evaluate rules for
10893 * {Object} symbolizer hash
10895 createSymbolizer: function(feature) {
10896 var style = this.defaultsPerSymbolizer ? {} : this.createLiterals(
10897 OpenLayers.Util.extend({}, this.defaultStyle), feature);
10899 var rules = this.rules;
10902 var elseRules = [];
10903 var appliedRules = false;
10904 for(var i=0, len=rules.length; i<len; i++) {
10906 // does the rule apply?
10907 var applies = rule.evaluate(feature);
10910 if(rule instanceof OpenLayers.Rule && rule.elseFilter) {
10911 elseRules.push(rule);
10913 appliedRules = true;
10914 this.applySymbolizer(rule, style, feature);
10919 // if no other rules apply, apply the rules with else filters
10920 if(appliedRules == false && elseRules.length > 0) {
10921 appliedRules = true;
10922 for(var i=0, len=elseRules.length; i<len; i++) {
10923 this.applySymbolizer(elseRules[i], style, feature);
10927 // don't display if there were rules but none applied
10928 if(rules.length > 0 && appliedRules == false) {
10929 style.display = "none";
10932 if (style.label != null && typeof style.label !== "string") {
10933 style.label = String(style.label);
10940 * Method: applySymbolizer
10943 * rule - {<OpenLayers.Rule>}
10945 * feature - {<OpenLayer.Feature.Vector>}
10948 * {Object} A style with new symbolizer applied.
10950 applySymbolizer: function(rule, style, feature) {
10951 var symbolizerPrefix = feature.geometry ?
10952 this.getSymbolizerPrefix(feature.geometry) :
10953 OpenLayers.Style.SYMBOLIZER_PREFIXES[0];
10955 var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer;
10957 if(this.defaultsPerSymbolizer === true) {
10958 var defaults = this.defaultStyle;
10959 OpenLayers.Util.applyDefaults(symbolizer, {
10960 pointRadius: defaults.pointRadius
10962 if(symbolizer.stroke === true || symbolizer.graphic === true) {
10963 OpenLayers.Util.applyDefaults(symbolizer, {
10964 strokeWidth: defaults.strokeWidth,
10965 strokeColor: defaults.strokeColor,
10966 strokeOpacity: defaults.strokeOpacity,
10967 strokeDashstyle: defaults.strokeDashstyle,
10968 strokeLinecap: defaults.strokeLinecap
10971 if(symbolizer.fill === true || symbolizer.graphic === true) {
10972 OpenLayers.Util.applyDefaults(symbolizer, {
10973 fillColor: defaults.fillColor,
10974 fillOpacity: defaults.fillOpacity
10977 if(symbolizer.graphic === true) {
10978 OpenLayers.Util.applyDefaults(symbolizer, {
10979 pointRadius: this.defaultStyle.pointRadius,
10980 externalGraphic: this.defaultStyle.externalGraphic,
10981 graphicName: this.defaultStyle.graphicName,
10982 graphicOpacity: this.defaultStyle.graphicOpacity,
10983 graphicWidth: this.defaultStyle.graphicWidth,
10984 graphicHeight: this.defaultStyle.graphicHeight,
10985 graphicXOffset: this.defaultStyle.graphicXOffset,
10986 graphicYOffset: this.defaultStyle.graphicYOffset
10991 // merge the style with the current style
10992 return this.createLiterals(
10993 OpenLayers.Util.extend(style, symbolizer), feature);
10997 * Method: createLiterals
10998 * creates literals for all style properties that have an entry in
10999 * <this.propertyStyles>.
11002 * style - {Object} style to create literals for. Will be modified
11004 * feature - {Object}
11007 * {Object} the modified style
11009 createLiterals: function(style, feature) {
11010 var context = OpenLayers.Util.extend({}, feature.attributes || feature.data);
11011 OpenLayers.Util.extend(context, this.context);
11013 for (var i in this.propertyStyles) {
11014 style[i] = OpenLayers.Style.createLiteral(style[i], context, feature, i);
11020 * Method: findPropertyStyles
11021 * Looks into all rules for this style and the defaultStyle to collect
11022 * all the style hash property names containing ${...} strings that have
11023 * to be replaced using the createLiteral method before returning them.
11026 * {Object} hash of property names that need createLiteral parsing. The
11027 * name of the property is the key, and the value is true;
11029 findPropertyStyles: function() {
11030 var propertyStyles = {};
11032 // check the default style
11033 var style = this.defaultStyle;
11034 this.addPropertyStyles(propertyStyles, style);
11036 // walk through all rules to check for properties in their symbolizer
11037 var rules = this.rules;
11038 var symbolizer, value;
11039 for (var i=0, len=rules.length; i<len; i++) {
11040 symbolizer = rules[i].symbolizer;
11041 for (var key in symbolizer) {
11042 value = symbolizer[key];
11043 if (typeof value == "object") {
11044 // symbolizer key is "Point", "Line" or "Polygon"
11045 this.addPropertyStyles(propertyStyles, value);
11047 // symbolizer is a hash of style properties
11048 this.addPropertyStyles(propertyStyles, symbolizer);
11053 return propertyStyles;
11057 * Method: addPropertyStyles
11060 * propertyStyles - {Object} hash to add new property styles to. Will be
11062 * symbolizer - {Object} search this symbolizer for property styles
11065 * {Object} propertyStyles hash
11067 addPropertyStyles: function(propertyStyles, symbolizer) {
11069 for (var key in symbolizer) {
11070 property = symbolizer[key];
11071 if (typeof property == "string" &&
11072 property.match(/\$\{\w+\}/)) {
11073 propertyStyles[key] = true;
11076 return propertyStyles;
11080 * APIMethod: addRules
11081 * Adds rules to this style.
11084 * rules - {Array(<OpenLayers.Rule>)}
11086 addRules: function(rules) {
11087 Array.prototype.push.apply(this.rules, rules);
11088 this.propertyStyles = this.findPropertyStyles();
11092 * APIMethod: setDefaultStyle
11093 * Sets the default style for this style object.
11096 * style - {Object} Hash of style properties
11098 setDefaultStyle: function(style) {
11099 this.defaultStyle = style;
11100 this.propertyStyles = this.findPropertyStyles();
11104 * Method: getSymbolizerPrefix
11105 * Returns the correct symbolizer prefix according to the
11106 * geometry type of the passed geometry
11109 * geometry - {<OpenLayers.Geometry>}
11112 * {String} key of the according symbolizer
11114 getSymbolizerPrefix: function(geometry) {
11115 var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES;
11116 for (var i=0, len=prefixes.length; i<len; i++) {
11117 if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) {
11118 return prefixes[i];
11125 * Clones this style.
11128 * {<OpenLayers.Style>} Clone of this style.
11130 clone: function() {
11131 var options = OpenLayers.Util.extend({}, this);
11134 options.rules = [];
11135 for(var i=0, len=this.rules.length; i<len; ++i) {
11136 options.rules.push(this.rules[i].clone());
11140 options.context = this.context && OpenLayers.Util.extend({}, this.context);
11141 //clone default style
11142 var defaultStyle = OpenLayers.Util.extend({}, this.defaultStyle);
11143 return new OpenLayers.Style(defaultStyle, options);
11146 CLASS_NAME: "OpenLayers.Style"
11151 * Function: createLiteral
11152 * converts a style value holding a combination of PropertyName and Literal
11153 * into a Literal, taking the property values from the passed features.
11156 * value - {String} value to parse. If this string contains a construct like
11157 * "foo ${bar}", then "foo " will be taken as literal, and "${bar}"
11158 * will be replaced by the value of the "bar" attribute of the passed
11160 * context - {Object} context to take attribute values from
11161 * feature - {<OpenLayers.Feature.Vector>} optional feature to pass to
11162 * <OpenLayers.String.format> for evaluating functions in the
11164 * property - {String} optional, name of the property for which the literal is
11165 * being created for evaluating functions in the context.
11168 * {String} the parsed value. In the example of the value parameter above, the
11169 * result would be "foo valueOfBar", assuming that the passed feature has an
11170 * attribute named "bar" with the value "valueOfBar".
11172 OpenLayers.Style.createLiteral = function(value, context, feature, property) {
11173 if (typeof value == "string" && value.indexOf("${") != -1) {
11174 value = OpenLayers.String.format(value, context, [feature, property]);
11175 value = (isNaN(value) || !value) ? value : parseFloat(value);
11181 * Constant: OpenLayers.Style.SYMBOLIZER_PREFIXES
11182 * {Array} prefixes of the sld symbolizers. These are the
11183 * same as the main geometry types
11185 OpenLayers.Style.SYMBOLIZER_PREFIXES = ['Point', 'Line', 'Polygon', 'Text',
11187 /* ======================================================================
11189 ====================================================================== */
11191 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
11192 * full list of contributors). Published under the 2-clause BSD license.
11193 * See license.txt in the OpenLayers distribution or repository for the
11194 * full text of the license. */
11198 * @requires OpenLayers/BaseTypes/Class.js
11199 * @requires OpenLayers/Util.js
11200 * @requires OpenLayers/Style.js
11204 * Class: OpenLayers.Rule
11205 * This class represents an SLD Rule, as being used for rule-based SLD styling.
11207 OpenLayers.Rule = OpenLayers.Class({
11211 * {String} A unique id for this session.
11216 * APIProperty: name
11217 * {String} name of this rule
11223 * {String} Title of this rule (set if included in SLD)
11228 * Property: description
11229 * {String} Description of this rule (set if abstract is included in SLD)
11234 * Property: context
11235 * {Object} An optional object with properties that the rule should be
11236 * evaluated against. If no context is specified, feature.attributes will
11243 * {<OpenLayers.Filter>} Optional filter for the rule.
11248 * Property: elseFilter
11249 * {Boolean} Determines whether this rule is only to be applied only if
11250 * no other rules match (ElseFilter according to the SLD specification).
11251 * Default is false. For instances of OpenLayers.Rule, if elseFilter is
11252 * false, the rule will always apply. For subclasses, the else property is
11258 * Property: symbolizer
11259 * {Object} Symbolizer or hash of symbolizers for this rule. If hash of
11260 * symbolizers, keys are one or more of ["Point", "Line", "Polygon"]. The
11261 * latter if useful if it is required to style e.g. vertices of a line
11262 * with a point symbolizer. Note, however, that this is not implemented
11263 * yet in OpenLayers, but it is the way how symbolizers are defined in
11269 * Property: symbolizers
11270 * {Array} Collection of symbolizers associated with this rule. If
11271 * provided at construction, the symbolizers array has precedence
11272 * over the deprecated symbolizer property. Note that multiple
11273 * symbolizers are not currently supported by the vector renderers.
11274 * Rules with multiple symbolizers are currently only useful for
11275 * maintaining elements in an SLD document.
11280 * APIProperty: minScaleDenominator
11281 * {Number} or {String} minimum scale at which to draw the feature.
11282 * In the case of a String, this can be a combination of text and
11283 * propertyNames in the form "literal ${propertyName}"
11285 minScaleDenominator: null,
11288 * APIProperty: maxScaleDenominator
11289 * {Number} or {String} maximum scale at which to draw the feature.
11290 * In the case of a String, this can be a combination of text and
11291 * propertyNames in the form "literal ${propertyName}"
11293 maxScaleDenominator: null,
11296 * Constructor: OpenLayers.Rule
11300 * options - {Object} An optional object with properties to set on the
11304 * {<OpenLayers.Rule>}
11306 initialize: function(options) {
11307 this.symbolizer = {};
11308 OpenLayers.Util.extend(this, options);
11309 if (this.symbolizers) {
11310 delete this.symbolizer;
11312 this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
11316 * APIMethod: destroy
11317 * nullify references to prevent circular references and memory leaks
11319 destroy: function() {
11320 for (var i in this.symbolizer) {
11321 this.symbolizer[i] = null;
11323 this.symbolizer = null;
11324 delete this.symbolizers;
11328 * APIMethod: evaluate
11329 * evaluates this rule for a specific feature
11332 * feature - {<OpenLayers.Feature>} feature to apply the rule to.
11335 * {Boolean} true if the rule applies, false if it does not.
11336 * This rule is the default rule and always returns true.
11338 evaluate: function(feature) {
11339 var context = this.getContext(feature);
11340 var applies = true;
11342 if (this.minScaleDenominator || this.maxScaleDenominator) {
11343 var scale = feature.layer.map.getScale();
11346 // check if within minScale/maxScale bounds
11347 if (this.minScaleDenominator) {
11348 applies = scale >= OpenLayers.Style.createLiteral(
11349 this.minScaleDenominator, context);
11351 if (applies && this.maxScaleDenominator) {
11352 applies = scale < OpenLayers.Style.createLiteral(
11353 this.maxScaleDenominator, context);
11356 // check if optional filter applies
11357 if(applies && this.filter) {
11358 // feature id filters get the feature, others get the context
11359 if(this.filter.CLASS_NAME == "OpenLayers.Filter.FeatureId") {
11360 applies = this.filter.evaluate(feature);
11362 applies = this.filter.evaluate(context);
11370 * Method: getContext
11371 * Gets the context for evaluating this rule
11374 * feature - {<OpenLayers.Feature>} feature to take the context from if
11375 * none is specified.
11377 getContext: function(feature) {
11378 var context = this.context;
11380 context = feature.attributes || feature.data;
11382 if (typeof this.context == "function") {
11383 context = this.context(feature);
11390 * Clones this rule.
11393 * {<OpenLayers.Rule>} Clone of this rule.
11395 clone: function() {
11396 var options = OpenLayers.Util.extend({}, this);
11397 if (this.symbolizers) {
11398 // clone symbolizers
11399 var len = this.symbolizers.length;
11400 options.symbolizers = new Array(len);
11401 for (var i=0; i<len; ++i) {
11402 options.symbolizers[i] = this.symbolizers[i].clone();
11405 // clone symbolizer
11406 options.symbolizer = {};
11408 for(var key in this.symbolizer) {
11409 value = this.symbolizer[key];
11410 type = typeof value;
11411 if(type === "object") {
11412 options.symbolizer[key] = OpenLayers.Util.extend({}, value);
11413 } else if(type === "string") {
11414 options.symbolizer[key] = value;
11419 options.filter = this.filter && this.filter.clone();
11421 options.context = this.context && OpenLayers.Util.extend({}, this.context);
11422 return new OpenLayers.Rule(options);
11425 CLASS_NAME: "OpenLayers.Rule"
11427 /* ======================================================================
11428 OpenLayers/StyleMap.js
11429 ====================================================================== */
11431 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
11432 * full list of contributors). Published under the 2-clause BSD license.
11433 * See license.txt in the OpenLayers distribution or repository for the
11434 * full text of the license. */
11437 * @requires OpenLayers/BaseTypes/Class.js
11438 * @requires OpenLayers/Style.js
11439 * @requires OpenLayers/Feature/Vector.js
11443 * Class: OpenLayers.StyleMap
11445 OpenLayers.StyleMap = OpenLayers.Class({
11449 * {Object} Hash of {<OpenLayers.Style>}, keyed by names of well known
11450 * rendering intents (e.g. "default", "temporary", "select", "delete").
11455 * Property: extendDefault
11456 * {Boolean} if true, every render intent will extend the symbolizers
11457 * specified for the "default" intent at rendering time. Otherwise, every
11458 * rendering intent will be treated as a completely independent style.
11460 extendDefault: true,
11463 * Constructor: OpenLayers.StyleMap
11466 * style - {Object} Optional. Either a style hash, or a style object, or
11467 * a hash of style objects (style hashes) keyed by rendering
11468 * intent. If just one style hash or style object is passed,
11469 * this will be used for all known render intents (default,
11470 * select, temporary)
11471 * options - {Object} optional hash of additional options for this
11474 initialize: function (style, options) {
11476 "default": new OpenLayers.Style(
11477 OpenLayers.Feature.Vector.style["default"]),
11478 "select": new OpenLayers.Style(
11479 OpenLayers.Feature.Vector.style["select"]),
11480 "temporary": new OpenLayers.Style(
11481 OpenLayers.Feature.Vector.style["temporary"]),
11482 "delete": new OpenLayers.Style(
11483 OpenLayers.Feature.Vector.style["delete"])
11486 // take whatever the user passed as style parameter and convert it
11487 // into parts of stylemap.
11488 if(style instanceof OpenLayers.Style) {
11489 // user passed a style object
11490 this.styles["default"] = style;
11491 this.styles["select"] = style;
11492 this.styles["temporary"] = style;
11493 this.styles["delete"] = style;
11494 } else if(typeof style == "object") {
11495 for(var key in style) {
11496 if(style[key] instanceof OpenLayers.Style) {
11497 // user passed a hash of style objects
11498 this.styles[key] = style[key];
11499 } else if(typeof style[key] == "object") {
11500 // user passsed a hash of style hashes
11501 this.styles[key] = new OpenLayers.Style(style[key]);
11503 // user passed a style hash (i.e. symbolizer)
11504 this.styles["default"] = new OpenLayers.Style(style);
11505 this.styles["select"] = new OpenLayers.Style(style);
11506 this.styles["temporary"] = new OpenLayers.Style(style);
11507 this.styles["delete"] = new OpenLayers.Style(style);
11512 OpenLayers.Util.extend(this, options);
11518 destroy: function() {
11519 for(var key in this.styles) {
11520 this.styles[key].destroy();
11522 this.styles = null;
11526 * Method: createSymbolizer
11527 * Creates the symbolizer for a feature for a render intent.
11530 * feature - {<OpenLayers.Feature>} The feature to evaluate the rules
11531 * of the intended style against.
11532 * intent - {String} The intent determines the symbolizer that will be
11533 * used to draw the feature. Well known intents are "default"
11534 * (for just drawing the features), "select" (for selected
11535 * features) and "temporary" (for drawing features).
11538 * {Object} symbolizer hash
11540 createSymbolizer: function(feature, intent) {
11542 feature = new OpenLayers.Feature.Vector();
11544 if(!this.styles[intent]) {
11545 intent = "default";
11547 feature.renderIntent = intent;
11548 var defaultSymbolizer = {};
11549 if(this.extendDefault && intent != "default") {
11550 defaultSymbolizer = this.styles["default"].createSymbolizer(feature);
11552 return OpenLayers.Util.extend(defaultSymbolizer,
11553 this.styles[intent].createSymbolizer(feature));
11557 * Method: addUniqueValueRules
11558 * Convenience method to create comparison rules for unique values of a
11559 * property. The rules will be added to the style object for a specified
11560 * rendering intent. This method is a shortcut for creating something like
11561 * the "unique value legends" familiar from well known desktop GIS systems
11564 * renderIntent - {String} rendering intent to add the rules to
11565 * property - {String} values of feature attributes to create the
11567 * symbolizers - {Object} Hash of symbolizers, keyed by the desired
11569 * context - {Object} An optional object with properties that
11570 * symbolizers' property values should be evaluated
11571 * against. If no context is specified, feature.attributes
11574 addUniqueValueRules: function(renderIntent, property, symbolizers, context) {
11576 for (var value in symbolizers) {
11577 rules.push(new OpenLayers.Rule({
11578 symbolizer: symbolizers[value],
11580 filter: new OpenLayers.Filter.Comparison({
11581 type: OpenLayers.Filter.Comparison.EQUAL_TO,
11582 property: property,
11587 this.styles[renderIntent].addRules(rules);
11590 CLASS_NAME: "OpenLayers.StyleMap"
11592 /* ======================================================================
11593 OpenLayers/Request.js
11594 ====================================================================== */
11596 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
11597 * full list of contributors). Published under the 2-clause BSD license.
11598 * See license.txt in the OpenLayers distribution or repository for the
11599 * full text of the license. */
11602 * @requires OpenLayers/Events.js
11603 * @requires OpenLayers/Request/XMLHttpRequest.js
11607 * TODO: deprecate me
11608 * Use OpenLayers.Request.proxy instead.
11610 OpenLayers.ProxyHost = "";
11613 * Namespace: OpenLayers.Request
11614 * The OpenLayers.Request namespace contains convenience methods for working
11615 * with XMLHttpRequests. These methods work with a cross-browser
11616 * W3C compliant <OpenLayers.Request.XMLHttpRequest> class.
11618 if (!OpenLayers.Request) {
11620 * This allows for OpenLayers/Request/XMLHttpRequest.js to be included
11621 * before or after this script.
11623 OpenLayers.Request = {};
11625 OpenLayers.Util.extend(OpenLayers.Request, {
11628 * Constant: DEFAULT_CONFIG
11629 * {Object} Default configuration for all requests.
11633 url: window.location.href,
11636 password: undefined,
11638 proxy: OpenLayers.ProxyHost,
11641 callback: function() {},
11648 * Constant: URL_SPLIT_REGEX
11650 URL_SPLIT_REGEX: /([^:]*:)\/\/([^:]*:?[^@]*@)?([^:\/\?]*):?([^\/\?]*)/,
11653 * APIProperty: events
11654 * {<OpenLayers.Events>} An events object that handles all
11655 * events on the {<OpenLayers.Request>} object.
11657 * All event listeners will receive an event object with three properties:
11658 * request - {<OpenLayers.Request.XMLHttpRequest>} The request object.
11659 * config - {Object} The config object sent to the specific request method.
11660 * requestUrl - {String} The request url.
11662 * Supported event types:
11663 * complete - Triggered when we have a response from the request, if a
11664 * listener returns false, no further response processing will take
11666 * success - Triggered when the HTTP response has a success code (200-299).
11667 * failure - Triggered when the HTTP response does not have a success code.
11669 events: new OpenLayers.Events(this),
11672 * Method: makeSameOrigin
11673 * Using the specified proxy, returns a same origin url of the provided url.
11676 * url - {String} An arbitrary url
11677 * proxy {String|Function} The proxy to use to make the provided url a
11681 * {String} the same origin url. If no proxy is provided, the returned url
11682 * will be the same as the provided url.
11684 makeSameOrigin: function(url, proxy) {
11685 var sameOrigin = url.indexOf("http") !== 0;
11686 var urlParts = !sameOrigin && url.match(this.URL_SPLIT_REGEX);
11688 var location = window.location;
11690 urlParts[1] == location.protocol &&
11691 urlParts[3] == location.hostname;
11692 var uPort = urlParts[4], lPort = location.port;
11693 if (uPort != 80 && uPort != "" || lPort != "80" && lPort != "") {
11694 sameOrigin = sameOrigin && uPort == lPort;
11699 if (typeof proxy == "function") {
11702 url = proxy + encodeURIComponent(url);
11711 * Create a new XMLHttpRequest object, open it, set any headers, bind
11712 * a callback to done state, and send any data. It is recommended that
11713 * you use one <GET>, <POST>, <PUT>, <DELETE>, <OPTIONS>, or <HEAD>.
11714 * This method is only documented to provide detail on the configuration
11715 * options available to all request methods.
11718 * config - {Object} Object containing properties for configuring the
11719 * request. Allowed configuration properties are described below.
11720 * This object is modified and should not be reused.
11722 * Allowed config properties:
11723 * method - {String} One of GET, POST, PUT, DELETE, HEAD, or
11724 * OPTIONS. Default is GET.
11725 * url - {String} URL for the request.
11726 * async - {Boolean} Open an asynchronous request. Default is true.
11727 * user - {String} User for relevant authentication scheme. Set
11728 * to null to clear current user.
11729 * password - {String} Password for relevant authentication scheme.
11730 * Set to null to clear current password.
11731 * proxy - {String} Optional proxy. Defaults to
11732 * <OpenLayers.ProxyHost>.
11733 * params - {Object} Any key:value pairs to be appended to the
11734 * url as a query string. Assumes url doesn't already include a query
11735 * string or hash. Typically, this is only appropriate for <GET>
11736 * requests where the query string will be appended to the url.
11737 * Parameter values that are arrays will be
11738 * concatenated with a comma (note that this goes against form-encoding)
11739 * as is done with <OpenLayers.Util.getParameterString>.
11740 * headers - {Object} Object with header:value pairs to be set on
11742 * data - {String | Document} Optional data to send with the request.
11743 * Typically, this is only used with <POST> and <PUT> requests.
11744 * Make sure to provide the appropriate "Content-Type" header for your
11745 * data. For <POST> and <PUT> requests, the content type defaults to
11746 * "application-xml". If your data is a different content type, or
11747 * if you are using a different HTTP method, set the "Content-Type"
11748 * header to match your data type.
11749 * callback - {Function} Function to call when request is done.
11750 * To determine if the request failed, check request.status (200
11751 * indicates success).
11752 * success - {Function} Optional function to call if request status is in
11753 * the 200s. This will be called in addition to callback above and
11754 * would typically only be used as an alternative.
11755 * failure - {Function} Optional function to call if request status is not
11756 * in the 200s. This will be called in addition to callback above and
11757 * would typically only be used as an alternative.
11758 * scope - {Object} If callback is a public method on some object,
11759 * set the scope to that object.
11762 * {XMLHttpRequest} Request object. To abort the request before a response
11763 * is received, call abort() on the request object.
11765 issue: function(config) {
11766 // apply default config - proxy host may have changed
11767 var defaultConfig = OpenLayers.Util.extend(
11768 this.DEFAULT_CONFIG,
11769 {proxy: OpenLayers.ProxyHost}
11771 config = config || {};
11772 config.headers = config.headers || {};
11773 config = OpenLayers.Util.applyDefaults(config, defaultConfig);
11774 config.headers = OpenLayers.Util.applyDefaults(config.headers, defaultConfig.headers);
11775 // Always set the "X-Requested-With" header to signal that this request
11776 // was issued through the XHR-object. Since header keys are case
11777 // insensitive and we want to allow overriding of the "X-Requested-With"
11778 // header through the user we cannot use applyDefaults, but have to
11779 // check manually whether we were called with a "X-Requested-With"
11781 var customRequestedWithHeader = false,
11783 for(headerKey in config.headers) {
11784 if (config.headers.hasOwnProperty( headerKey )) {
11785 if (headerKey.toLowerCase() === 'x-requested-with') {
11786 customRequestedWithHeader = true;
11790 if (customRequestedWithHeader === false) {
11791 // we did not have a custom "X-Requested-With" header
11792 config.headers['X-Requested-With'] = 'XMLHttpRequest';
11795 // create request, open, and set headers
11796 var request = new OpenLayers.Request.XMLHttpRequest();
11797 var url = OpenLayers.Util.urlAppend(config.url,
11798 OpenLayers.Util.getParameterString(config.params || {}));
11799 url = OpenLayers.Request.makeSameOrigin(url, config.proxy);
11801 config.method, url, config.async, config.user, config.password
11803 for(var header in config.headers) {
11804 request.setRequestHeader(header, config.headers[header]);
11807 var events = this.events;
11809 // we want to execute runCallbacks with "this" as the
11813 request.onreadystatechange = function() {
11814 if(request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) {
11815 var proceed = events.triggerEvent(
11817 {request: request, config: config, requestUrl: url}
11819 if(proceed !== false) {
11821 {request: request, config: config, requestUrl: url}
11827 // send request (optionally with data) and return
11828 // call in a timeout for asynchronous requests so the return is
11829 // available before readyState == 4 for cached docs
11830 if(config.async === false) {
11831 request.send(config.data);
11833 window.setTimeout(function(){
11834 if (request.readyState !== 0) { // W3C: 0-UNSENT
11835 request.send(config.data);
11843 * Method: runCallbacks
11844 * Calls the complete, success and failure callbacks. Application
11845 * can listen to the "complete" event, have the listener
11846 * display a confirm window and always return false, and
11847 * execute OpenLayers.Request.runCallbacks if the user
11848 * hits "yes" in the confirm window.
11851 * options - {Object} Hash containing request, config and requestUrl keys
11853 runCallbacks: function(options) {
11854 var request = options.request;
11855 var config = options.config;
11857 // bind callbacks to readyState 4 (done)
11858 var complete = (config.scope) ?
11859 OpenLayers.Function.bind(config.callback, config.scope) :
11862 // optional success callback
11864 if(config.success) {
11865 success = (config.scope) ?
11866 OpenLayers.Function.bind(config.success, config.scope) :
11870 // optional failure callback
11872 if(config.failure) {
11873 failure = (config.scope) ?
11874 OpenLayers.Function.bind(config.failure, config.scope) :
11878 if (OpenLayers.Util.createUrlObject(config.url).protocol == "file:" &&
11879 request.responseText) {
11880 request.status = 200;
11884 if (!request.status || (request.status >= 200 && request.status < 300)) {
11885 this.events.triggerEvent("success", options);
11890 if(request.status && (request.status < 200 || request.status >= 300)) {
11891 this.events.triggerEvent("failure", options);
11900 * Send an HTTP GET request. Additional configuration properties are
11901 * documented in the <issue> method, with the method property set
11905 * config - {Object} Object with properties for configuring the request.
11906 * See the <issue> method for documentation of allowed properties.
11907 * This object is modified and should not be reused.
11910 * {XMLHttpRequest} Request object.
11912 GET: function(config) {
11913 config = OpenLayers.Util.extend(config, {method: "GET"});
11914 return OpenLayers.Request.issue(config);
11919 * Send a POST request. Additional configuration properties are
11920 * documented in the <issue> method, with the method property set
11921 * to POST and "Content-Type" header set to "application/xml".
11924 * config - {Object} Object with properties for configuring the request.
11925 * See the <issue> method for documentation of allowed properties. The
11926 * default "Content-Type" header will be set to "application-xml" if
11927 * none is provided. This object is modified and should not be reused.
11930 * {XMLHttpRequest} Request object.
11932 POST: function(config) {
11933 config = OpenLayers.Util.extend(config, {method: "POST"});
11934 // set content type to application/xml if it isn't already set
11935 config.headers = config.headers ? config.headers : {};
11936 if(!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) {
11937 config.headers["Content-Type"] = "application/xml";
11939 return OpenLayers.Request.issue(config);
11944 * Send an HTTP PUT request. Additional configuration properties are
11945 * documented in the <issue> method, with the method property set
11946 * to PUT and "Content-Type" header set to "application/xml".
11949 * config - {Object} Object with properties for configuring the request.
11950 * See the <issue> method for documentation of allowed properties. The
11951 * default "Content-Type" header will be set to "application-xml" if
11952 * none is provided. This object is modified and should not be reused.
11955 * {XMLHttpRequest} Request object.
11957 PUT: function(config) {
11958 config = OpenLayers.Util.extend(config, {method: "PUT"});
11959 // set content type to application/xml if it isn't already set
11960 config.headers = config.headers ? config.headers : {};
11961 if(!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) {
11962 config.headers["Content-Type"] = "application/xml";
11964 return OpenLayers.Request.issue(config);
11968 * APIMethod: DELETE
11969 * Send an HTTP DELETE request. Additional configuration properties are
11970 * documented in the <issue> method, with the method property set
11974 * config - {Object} Object with properties for configuring the request.
11975 * See the <issue> method for documentation of allowed properties.
11976 * This object is modified and should not be reused.
11979 * {XMLHttpRequest} Request object.
11981 DELETE: function(config) {
11982 config = OpenLayers.Util.extend(config, {method: "DELETE"});
11983 return OpenLayers.Request.issue(config);
11988 * Send an HTTP HEAD request. Additional configuration properties are
11989 * documented in the <issue> method, with the method property set
11993 * config - {Object} Object with properties for configuring the request.
11994 * See the <issue> method for documentation of allowed properties.
11995 * This object is modified and should not be reused.
11998 * {XMLHttpRequest} Request object.
12000 HEAD: function(config) {
12001 config = OpenLayers.Util.extend(config, {method: "HEAD"});
12002 return OpenLayers.Request.issue(config);
12006 * APIMethod: OPTIONS
12007 * Send an HTTP OPTIONS request. Additional configuration properties are
12008 * documented in the <issue> method, with the method property set
12012 * config - {Object} Object with properties for configuring the request.
12013 * See the <issue> method for documentation of allowed properties.
12014 * This object is modified and should not be reused.
12017 * {XMLHttpRequest} Request object.
12019 OPTIONS: function(config) {
12020 config = OpenLayers.Util.extend(config, {method: "OPTIONS"});
12021 return OpenLayers.Request.issue(config);
12025 /* ======================================================================
12026 OpenLayers/Request/XMLHttpRequest.js
12027 ====================================================================== */
12029 // XMLHttpRequest.js Copyright (C) 2010 Sergey Ilinsky (http://www.ilinsky.com)
12031 // Licensed under the Apache License, Version 2.0 (the "License");
12032 // you may not use this file except in compliance with the License.
12033 // You may obtain a copy of the License at
12035 // http://www.apache.org/licenses/LICENSE-2.0
12037 // Unless required by applicable law or agreed to in writing, software
12038 // distributed under the License is distributed on an "AS IS" BASIS,
12039 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12040 // See the License for the specific language governing permissions and
12041 // limitations under the License.
12044 * @requires OpenLayers/Request.js
12049 // Save reference to earlier defined object implementation (if any)
12050 var oXMLHttpRequest = window.XMLHttpRequest;
12052 // Define on browser type
12053 var bGecko = !!window.controllers,
12054 bIE = window.document.all && !window.opera,
12055 bIE7 = bIE && window.navigator.userAgent.match(/MSIE 7.0/);
12057 // Enables "XMLHttpRequest()" call next to "new XMLHttpReques()"
12058 function fXMLHttpRequest() {
12059 this._object = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject("Microsoft.XMLHTTP");
12060 this._listeners = [];
12064 function cXMLHttpRequest() {
12065 return new fXMLHttpRequest;
12067 cXMLHttpRequest.prototype = fXMLHttpRequest.prototype;
12069 // BUGFIX: Firefox with Firebug installed would break pages if not executed
12070 if (bGecko && oXMLHttpRequest.wrapped)
12071 cXMLHttpRequest.wrapped = oXMLHttpRequest.wrapped;
12074 cXMLHttpRequest.UNSENT = 0;
12075 cXMLHttpRequest.OPENED = 1;
12076 cXMLHttpRequest.HEADERS_RECEIVED = 2;
12077 cXMLHttpRequest.LOADING = 3;
12078 cXMLHttpRequest.DONE = 4;
12080 // Public Properties
12081 cXMLHttpRequest.prototype.readyState = cXMLHttpRequest.UNSENT;
12082 cXMLHttpRequest.prototype.responseText = '';
12083 cXMLHttpRequest.prototype.responseXML = null;
12084 cXMLHttpRequest.prototype.status = 0;
12085 cXMLHttpRequest.prototype.statusText = '';
12087 // Priority proposal
12088 cXMLHttpRequest.prototype.priority = "NORMAL";
12090 // Instance-level Events Handlers
12091 cXMLHttpRequest.prototype.onreadystatechange = null;
12093 // Class-level Events Handlers
12094 cXMLHttpRequest.onreadystatechange = null;
12095 cXMLHttpRequest.onopen = null;
12096 cXMLHttpRequest.onsend = null;
12097 cXMLHttpRequest.onabort = null;
12100 cXMLHttpRequest.prototype.open = function(sMethod, sUrl, bAsync, sUser, sPassword) {
12101 // Delete headers, required when object is reused
12102 delete this._headers;
12104 // When bAsync parameter value is omitted, use true as default
12105 if (arguments.length < 3)
12108 // Save async parameter for fixing Gecko bug with missing readystatechange in synchronous requests
12109 this._async = bAsync;
12111 // Set the onreadystatechange handler
12112 var oRequest = this,
12113 nState = this.readyState,
12116 // BUGFIX: IE - memory leak on page unload (inter-page leak)
12117 if (bIE && bAsync) {
12118 fOnUnload = function() {
12119 if (nState != cXMLHttpRequest.DONE) {
12120 fCleanTransport(oRequest);
12121 // Safe to abort here since onreadystatechange handler removed
12125 window.attachEvent("onunload", fOnUnload);
12128 // Add method sniffer
12129 if (cXMLHttpRequest.onopen)
12130 cXMLHttpRequest.onopen.apply(this, arguments);
12132 if (arguments.length > 4)
12133 this._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
12135 if (arguments.length > 3)
12136 this._object.open(sMethod, sUrl, bAsync, sUser);
12138 this._object.open(sMethod, sUrl, bAsync);
12140 this.readyState = cXMLHttpRequest.OPENED;
12141 fReadyStateChange(this);
12143 this._object.onreadystatechange = function() {
12144 if (bGecko && !bAsync)
12147 // Synchronize state
12148 oRequest.readyState = oRequest._object.readyState;
12151 fSynchronizeValues(oRequest);
12153 // BUGFIX: Firefox fires unnecessary DONE when aborting
12154 if (oRequest._aborted) {
12155 // Reset readyState to UNSENT
12156 oRequest.readyState = cXMLHttpRequest.UNSENT;
12162 if (oRequest.readyState == cXMLHttpRequest.DONE) {
12164 delete oRequest._data;
12166 fQueue_remove(oRequest);*/
12168 fCleanTransport(oRequest);
12169 // Uncomment this block if you need a fix for IE cache
12171 // BUGFIX: IE - cache issue
12172 if (!oRequest._object.getResponseHeader("Date")) {
12173 // Save object to cache
12174 oRequest._cached = oRequest._object;
12176 // Instantiate a new transport object
12177 cXMLHttpRequest.call(oRequest);
12182 oRequest._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
12184 oRequest._object.open(sMethod, sUrl, bAsync, sUser);
12187 oRequest._object.open(sMethod, sUrl, bAsync);
12188 oRequest._object.setRequestHeader("If-Modified-Since", oRequest._cached.getResponseHeader("Last-Modified") || new window.Date(0));
12189 // Copy headers set
12190 if (oRequest._headers)
12191 for (var sHeader in oRequest._headers)
12192 if (typeof oRequest._headers[sHeader] == "string") // Some frameworks prototype objects with functions
12193 oRequest._object.setRequestHeader(sHeader, oRequest._headers[sHeader]);
12195 oRequest._object.onreadystatechange = function() {
12196 // Synchronize state
12197 oRequest.readyState = oRequest._object.readyState;
12199 if (oRequest._aborted) {
12201 oRequest.readyState = cXMLHttpRequest.UNSENT;
12207 if (oRequest.readyState == cXMLHttpRequest.DONE) {
12209 fCleanTransport(oRequest);
12211 // get cached request
12212 if (oRequest.status == 304)
12213 oRequest._object = oRequest._cached;
12216 delete oRequest._cached;
12219 fSynchronizeValues(oRequest);
12222 fReadyStateChange(oRequest);
12224 // BUGFIX: IE - memory leak in interrupted
12226 window.detachEvent("onunload", fOnUnload);
12229 oRequest._object.send(null);
12231 // Return now - wait until re-sent request is finished
12235 // BUGFIX: IE - memory leak in interrupted
12237 window.detachEvent("onunload", fOnUnload);
12240 // BUGFIX: Some browsers (Internet Explorer, Gecko) fire OPEN readystate twice
12241 if (nState != oRequest.readyState)
12242 fReadyStateChange(oRequest);
12244 nState = oRequest.readyState;
12247 function fXMLHttpRequest_send(oRequest) {
12248 oRequest._object.send(oRequest._data);
12250 // BUGFIX: Gecko - missing readystatechange calls in synchronous requests
12251 if (bGecko && !oRequest._async) {
12252 oRequest.readyState = cXMLHttpRequest.OPENED;
12254 // Synchronize state
12255 fSynchronizeValues(oRequest);
12257 // Simulate missing states
12258 while (oRequest.readyState < cXMLHttpRequest.DONE) {
12259 oRequest.readyState++;
12260 fReadyStateChange(oRequest);
12261 // Check if we are aborted
12262 if (oRequest._aborted)
12267 cXMLHttpRequest.prototype.send = function(vData) {
12268 // Add method sniffer
12269 if (cXMLHttpRequest.onsend)
12270 cXMLHttpRequest.onsend.apply(this, arguments);
12272 if (!arguments.length)
12275 // BUGFIX: Safari - fails sending documents created/modified dynamically, so an explicit serialization required
12276 // BUGFIX: IE - rewrites any custom mime-type to "text/xml" in case an XMLNode is sent
12277 // BUGFIX: Gecko - fails sending Element (this is up to the implementation either to standard)
12278 if (vData && vData.nodeType) {
12279 vData = window.XMLSerializer ? new window.XMLSerializer().serializeToString(vData) : vData.xml;
12280 if (!this._headers["Content-Type"])
12281 this._object.setRequestHeader("Content-Type", "application/xml");
12284 this._data = vData;
12290 fXMLHttpRequest_send(this);
12292 cXMLHttpRequest.prototype.abort = function() {
12293 // Add method sniffer
12294 if (cXMLHttpRequest.onabort)
12295 cXMLHttpRequest.onabort.apply(this, arguments);
12297 // BUGFIX: Gecko - unnecessary DONE when aborting
12298 if (this.readyState > cXMLHttpRequest.UNSENT)
12299 this._aborted = true;
12301 this._object.abort();
12303 // BUGFIX: IE - memory leak
12304 fCleanTransport(this);
12306 this.readyState = cXMLHttpRequest.UNSENT;
12309 /* if (this._async)
12310 fQueue_remove(this);*/
12312 cXMLHttpRequest.prototype.getAllResponseHeaders = function() {
12313 return this._object.getAllResponseHeaders();
12315 cXMLHttpRequest.prototype.getResponseHeader = function(sName) {
12316 return this._object.getResponseHeader(sName);
12318 cXMLHttpRequest.prototype.setRequestHeader = function(sName, sValue) {
12319 // BUGFIX: IE - cache issue
12320 if (!this._headers)
12321 this._headers = {};
12322 this._headers[sName] = sValue;
12324 return this._object.setRequestHeader(sName, sValue);
12327 // EventTarget interface implementation
12328 cXMLHttpRequest.prototype.addEventListener = function(sName, fHandler, bUseCapture) {
12329 for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
12330 if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)
12333 this._listeners.push([sName, fHandler, bUseCapture]);
12336 cXMLHttpRequest.prototype.removeEventListener = function(sName, fHandler, bUseCapture) {
12337 for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
12338 if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)
12342 this._listeners.splice(nIndex, 1);
12345 cXMLHttpRequest.prototype.dispatchEvent = function(oEvent) {
12346 var oEventPseudo = {
12347 'type': oEvent.type,
12349 'currentTarget':this,
12351 'bubbles': oEvent.bubbles,
12352 'cancelable': oEvent.cancelable,
12353 'timeStamp': oEvent.timeStamp,
12354 'stopPropagation': function() {}, // There is no flow
12355 'preventDefault': function() {}, // There is no default action
12356 'initEvent': function() {} // Original event object should be initialized
12359 // Execute onreadystatechange
12360 if (oEventPseudo.type == "readystatechange" && this.onreadystatechange)
12361 (this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEventPseudo]);
12363 // Execute listeners
12364 for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
12365 if (oListener[0] == oEventPseudo.type && !oListener[2])
12366 (oListener[1].handleEvent || oListener[1]).apply(this, [oEventPseudo]);
12370 cXMLHttpRequest.prototype.toString = function() {
12371 return '[' + "object" + ' ' + "XMLHttpRequest" + ']';
12374 cXMLHttpRequest.toString = function() {
12375 return '[' + "XMLHttpRequest" + ']';
12379 function fReadyStateChange(oRequest) {
12381 if (cXMLHttpRequest.onreadystatechange)
12382 cXMLHttpRequest.onreadystatechange.apply(oRequest);
12385 oRequest.dispatchEvent({
12386 'type': "readystatechange",
12388 'cancelable': false,
12389 'timeStamp': new Date + 0
12393 function fGetDocument(oRequest) {
12394 var oDocument = oRequest.responseXML,
12395 sResponse = oRequest.responseText;
12396 // Try parsing responseText
12397 if (bIE && sResponse && oDocument && !oDocument.documentElement && oRequest.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)) {
12398 oDocument = new window.ActiveXObject("Microsoft.XMLDOM");
12399 oDocument.async = false;
12400 oDocument.validateOnParse = false;
12401 oDocument.loadXML(sResponse);
12403 // Check if there is no error in document
12405 if ((bIE && oDocument.parseError != 0) || !oDocument.documentElement || (oDocument.documentElement && oDocument.documentElement.tagName == "parsererror"))
12410 function fSynchronizeValues(oRequest) {
12411 try { oRequest.responseText = oRequest._object.responseText; } catch (e) {}
12412 try { oRequest.responseXML = fGetDocument(oRequest._object); } catch (e) {}
12413 try { oRequest.status = oRequest._object.status; } catch (e) {}
12414 try { oRequest.statusText = oRequest._object.statusText; } catch (e) {}
12417 function fCleanTransport(oRequest) {
12418 // BUGFIX: IE - memory leak (on-page leak)
12419 oRequest._object.onreadystatechange = new window.Function;
12423 var oQueuePending = {"CRITICAL":[],"HIGH":[],"NORMAL":[],"LOW":[],"LOWEST":[]},
12424 aQueueRunning = [];
12425 function fQueue_add(oRequest) {
12426 oQueuePending[oRequest.priority in oQueuePending ? oRequest.priority : "NORMAL"].push(oRequest);
12428 setTimeout(fQueue_process);
12431 function fQueue_remove(oRequest) {
12432 for (var nIndex = 0, bFound = false; nIndex < aQueueRunning.length; nIndex++)
12434 aQueueRunning[nIndex - 1] = aQueueRunning[nIndex];
12436 if (aQueueRunning[nIndex] == oRequest)
12439 aQueueRunning.length--;
12441 setTimeout(fQueue_process);
12444 function fQueue_process() {
12445 if (aQueueRunning.length < 6) {
12446 for (var sPriority in oQueuePending) {
12447 if (oQueuePending[sPriority].length) {
12448 var oRequest = oQueuePending[sPriority][0];
12449 oQueuePending[sPriority] = oQueuePending[sPriority].slice(1);
12451 aQueueRunning.push(oRequest);
12453 fXMLHttpRequest_send(oRequest);
12460 // Internet Explorer 5.0 (missing apply)
12461 if (!window.Function.prototype.apply) {
12462 window.Function.prototype.apply = function(oRequest, oArguments) {
12465 oRequest.__func = this;
12466 oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]);
12467 delete oRequest.__func;
12471 // Register new object with window
12473 * Class: OpenLayers.Request.XMLHttpRequest
12474 * Standard-compliant (W3C) cross-browser implementation of the
12475 * XMLHttpRequest object. From
12476 * http://code.google.com/p/xmlhttprequest/.
12478 if (!OpenLayers.Request) {
12480 * This allows for OpenLayers/Request.js to be included
12481 * before or after this script.
12483 OpenLayers.Request = {};
12485 OpenLayers.Request.XMLHttpRequest = cXMLHttpRequest;
12487 /* ======================================================================
12488 OpenLayers/Protocol.js
12489 ====================================================================== */
12491 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
12492 * full list of contributors). Published under the 2-clause BSD license.
12493 * See license.txt in the OpenLayers distribution or repository for the
12494 * full text of the license. */
12497 * @requires OpenLayers/BaseTypes/Class.js
12501 * Class: OpenLayers.Protocol
12502 * Abstract vector layer protocol class. Not to be instantiated directly. Use
12503 * one of the protocol subclasses instead.
12505 OpenLayers.Protocol = OpenLayers.Class({
12509 * {<OpenLayers.Format>} The format used by this protocol.
12514 * Property: options
12515 * {Object} Any options sent to the constructor.
12520 * Property: autoDestroy
12521 * {Boolean} The creator of the protocol can set autoDestroy to false
12522 * to fully control when the protocol is destroyed. Defaults to
12528 * Property: defaultFilter
12529 * {<OpenLayers.Filter>} Optional default filter to read requests
12531 defaultFilter: null,
12534 * Constructor: OpenLayers.Protocol
12535 * Abstract class for vector protocols. Create instances of a subclass.
12538 * options - {Object} Optional object whose properties will be set on the
12541 initialize: function(options) {
12542 options = options || {};
12543 OpenLayers.Util.extend(this, options);
12544 this.options = options;
12548 * Method: mergeWithDefaultFilter
12549 * Merge filter passed to the read method with the default one
12552 * filter - {<OpenLayers.Filter>}
12554 mergeWithDefaultFilter: function(filter) {
12556 if (filter && this.defaultFilter) {
12557 merged = new OpenLayers.Filter.Logical({
12558 type: OpenLayers.Filter.Logical.AND,
12559 filters: [this.defaultFilter, filter]
12562 merged = filter || this.defaultFilter || undefined;
12568 * APIMethod: destroy
12569 * Clean up the protocol.
12571 destroy: function() {
12572 this.options = null;
12573 this.format = null;
12578 * Construct a request for reading new features.
12581 * options - {Object} Optional object for configuring the request.
12584 * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
12585 * object, the same object will be passed to the callback function passed
12586 * if one exists in the options object.
12588 read: function(options) {
12589 options = options || {};
12590 options.filter = this.mergeWithDefaultFilter(options.filter);
12595 * APIMethod: create
12596 * Construct a request for writing newly created features.
12599 * features - {Array({<OpenLayers.Feature.Vector>})} or
12600 * {<OpenLayers.Feature.Vector>}
12601 * options - {Object} Optional object for configuring the request.
12604 * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
12605 * object, the same object will be passed to the callback function passed
12606 * if one exists in the options object.
12608 create: function() {
12612 * APIMethod: update
12613 * Construct a request updating modified features.
12616 * features - {Array({<OpenLayers.Feature.Vector>})} or
12617 * {<OpenLayers.Feature.Vector>}
12618 * options - {Object} Optional object for configuring the request.
12621 * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
12622 * object, the same object will be passed to the callback function passed
12623 * if one exists in the options object.
12625 update: function() {
12629 * APIMethod: delete
12630 * Construct a request deleting a removed feature.
12633 * feature - {<OpenLayers.Feature.Vector>}
12634 * options - {Object} Optional object for configuring the request.
12637 * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
12638 * object, the same object will be passed to the callback function passed
12639 * if one exists in the options object.
12641 "delete": function() {
12645 * APIMethod: commit
12646 * Go over the features and for each take action
12647 * based on the feature state. Possible actions are create,
12648 * update and delete.
12651 * features - {Array({<OpenLayers.Feature.Vector>})}
12652 * options - {Object} Object whose possible keys are "create", "update",
12653 * "delete", "callback" and "scope", the values referenced by the
12654 * first three are objects as passed to the "create", "update", and
12655 * "delete" methods, the value referenced by the "callback" key is
12656 * a function which is called when the commit operation is complete
12657 * using the scope referenced by the "scope" key.
12660 * {Array({<OpenLayers.Protocol.Response>})} An array of
12661 * <OpenLayers.Protocol.Response> objects.
12663 commit: function() {
12668 * Abort an ongoing request.
12671 * response - {<OpenLayers.Protocol.Response>}
12673 abort: function(response) {
12677 * Method: createCallback
12678 * Returns a function that applies the given public method with resp and
12679 * options arguments.
12682 * method - {Function} The method to be applied by the callback.
12683 * response - {<OpenLayers.Protocol.Response>} The protocol response object.
12684 * options - {Object} Options sent to the protocol method
12686 createCallback: function(method, response, options) {
12687 return OpenLayers.Function.bind(function() {
12688 method.apply(this, [response, options]);
12692 CLASS_NAME: "OpenLayers.Protocol"
12696 * Class: OpenLayers.Protocol.Response
12697 * Protocols return Response objects to their users.
12699 OpenLayers.Protocol.Response = OpenLayers.Class({
12702 * {Number} - OpenLayers.Protocol.Response.SUCCESS or
12703 * OpenLayers.Protocol.Response.FAILURE
12708 * Property: requestType
12709 * {String} The type of request this response corresponds to. Either
12710 * "create", "read", "update" or "delete".
12716 * {Boolean} - true if this is the last response expected in a commit,
12717 * false otherwise, defaults to true.
12722 * Property: features
12723 * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}
12724 * The features returned in the response by the server. Depending on the
12725 * protocol's read payload, either features or data will be populated.
12732 * The data returned in the response by the server. Depending on the
12733 * protocol's read payload, either features or data will be populated.
12738 * Property: reqFeatures
12739 * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}
12740 * The features provided by the user and placed in the request by the
12752 * {Object} The error object in case a service exception was encountered.
12757 * Constructor: OpenLayers.Protocol.Response
12760 * options - {Object} Optional object whose properties will be set on the
12763 initialize: function(options) {
12764 OpenLayers.Util.extend(this, options);
12771 * {Boolean} - true on success, false otherwise
12773 success: function() {
12774 return this.code > 0;
12777 CLASS_NAME: "OpenLayers.Protocol.Response"
12780 OpenLayers.Protocol.Response.SUCCESS = 1;
12781 OpenLayers.Protocol.Response.FAILURE = 0;
12782 /* ======================================================================
12783 OpenLayers/Protocol/WFS.js
12784 ====================================================================== */
12786 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
12787 * full list of contributors). Published under the 2-clause BSD license.
12788 * See license.txt in the OpenLayers distribution or repository for the
12789 * full text of the license. */
12792 * @requires OpenLayers/Protocol.js
12796 * Class: OpenLayers.Protocol.WFS
12797 * Used to create a versioned WFS protocol. Default version is 1.0.0.
12800 * {<OpenLayers.Protocol>} A WFS protocol of the given version.
12804 * var protocol = new OpenLayers.Protocol.WFS({
12805 * version: "1.1.0",
12806 * url: "http://demo.opengeo.org/geoserver/wfs",
12807 * featureType: "tasmania_roads",
12808 * featureNS: "http://www.openplans.org/topp",
12809 * geometryName: "the_geom"
12813 * See the protocols for specific WFS versions for more detail.
12815 OpenLayers.Protocol.WFS = function(options) {
12816 options = OpenLayers.Util.applyDefaults(
12817 options, OpenLayers.Protocol.WFS.DEFAULTS
12819 var cls = OpenLayers.Protocol.WFS["v"+options.version.replace(/\./g, "_")];
12821 throw "Unsupported WFS version: " + options.version;
12823 return new cls(options);
12827 * Function: fromWMSLayer
12828 * Convenience function to create a WFS protocol from a WMS layer. This makes
12829 * the assumption that a WFS requests can be issued at the same URL as
12830 * WMS requests and that a WFS featureType exists with the same name as the
12833 * This function is designed to auto-configure <url>, <featureType>,
12834 * <featurePrefix> and <srsName> for WFS <version> 1.1.0. Note that
12835 * srsName matching with the WMS layer will not work with WFS 1.0.0.
12838 * layer - {<OpenLayers.Layer.WMS>} WMS layer that has a matching WFS
12839 * FeatureType at the same server url with the same typename.
12840 * options - {Object} Default properties to be set on the protocol.
12843 * {<OpenLayers.Protocol.WFS>}
12845 OpenLayers.Protocol.WFS.fromWMSLayer = function(layer, options) {
12846 var typeName, featurePrefix;
12847 var param = layer.params["LAYERS"];
12848 var parts = (OpenLayers.Util.isArray(param) ? param[0] : param).split(":");
12849 if(parts.length > 1) {
12850 featurePrefix = parts[0];
12852 typeName = parts.pop();
12853 var protocolOptions = {
12855 featureType: typeName,
12856 featurePrefix: featurePrefix,
12857 srsName: layer.projection && layer.projection.getCode() ||
12858 layer.map && layer.map.getProjectionObject().getCode(),
12861 return new OpenLayers.Protocol.WFS(OpenLayers.Util.applyDefaults(
12862 options, protocolOptions
12867 * Constant: OpenLayers.Protocol.WFS.DEFAULTS
12869 OpenLayers.Protocol.WFS.DEFAULTS = {
12872 /* ======================================================================
12873 OpenLayers/Protocol/WFS/v1.js
12874 ====================================================================== */
12876 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
12877 * full list of contributors). Published under the 2-clause BSD license.
12878 * See license.txt in the OpenLayers distribution or repository for the
12879 * full text of the license. */
12882 * @requires OpenLayers/Protocol/WFS.js
12886 * Class: OpenLayers.Protocol.WFS.v1
12887 * Abstract class for for v1.0.0 and v1.1.0 protocol.
12890 * - <OpenLayers.Protocol>
12892 OpenLayers.Protocol.WFS.v1 = OpenLayers.Class(OpenLayers.Protocol, {
12895 * Property: version
12896 * {String} WFS version number.
12901 * Property: srsName
12902 * {String} Name of spatial reference system. Default is "EPSG:4326".
12904 srsName: "EPSG:4326",
12907 * Property: featureType
12908 * {String} Local feature typeName.
12913 * Property: featureNS
12914 * {String} Feature namespace.
12919 * Property: geometryName
12920 * {String} Name of the geometry attribute for features. Default is
12921 * "the_geom" for WFS <version> 1.0, and null for higher versions.
12923 geometryName: "the_geom",
12926 * Property: maxFeatures
12927 * {Integer} Optional maximum number of features to retrieve.
12932 * {String} Optional schema location that will be included in the
12933 * schemaLocation attribute value. Note that the feature type schema
12934 * is required for a strict XML validator (on transactions with an
12935 * insert for example), but is *not* required by the WFS specification
12936 * (since the server is supposed to know about feature type schemas).
12941 * Property: featurePrefix
12942 * {String} Namespace alias for feature type. Default is "feature".
12944 featurePrefix: "feature",
12947 * Property: formatOptions
12948 * {Object} Optional options for the format. If a format is not provided,
12949 * this property can be used to extend the default format options.
12951 formatOptions: null,
12954 * Property: readFormat
12955 * {<OpenLayers.Format>} For WFS requests it is possible to get a
12956 * different output format than GML. In that case, we cannot parse
12957 * the response with the default format (WFST) and we need a different
12958 * format for reading.
12963 * Property: readOptions
12964 * {Object} Optional object to pass to format's read.
12969 * Constructor: OpenLayers.Protocol.WFS
12970 * A class for giving layers WFS protocol.
12973 * options - {Object} Optional object whose properties will be set on the
12976 * Valid options properties:
12977 * url - {String} URL to send requests to (required).
12978 * featureType - {String} Local (without prefix) feature typeName (required).
12979 * featureNS - {String} Feature namespace (required, but can be autodetected
12980 * during the first query if GML is used as readFormat and
12981 * featurePrefix is provided and matches the prefix used by the server
12982 * for this featureType).
12983 * featurePrefix - {String} Feature namespace alias (optional - only used
12984 * for writing if featureNS is provided). Default is 'feature'.
12985 * geometryName - {String} Name of geometry attribute. The default is
12986 * 'the_geom' for WFS <version> 1.0, and null for higher versions. If
12987 * null, it will be set to the name of the first geometry found in the
12988 * first read operation.
12989 * multi - {Boolean} If set to true, geometries will be casted to Multi
12990 * geometries before they are written in a transaction. No casting will
12991 * be done when reading features.
12993 initialize: function(options) {
12994 OpenLayers.Protocol.prototype.initialize.apply(this, [options]);
12995 if(!options.format) {
12996 this.format = OpenLayers.Format.WFST(OpenLayers.Util.extend({
12997 version: this.version,
12998 featureType: this.featureType,
12999 featureNS: this.featureNS,
13000 featurePrefix: this.featurePrefix,
13001 geometryName: this.geometryName,
13002 srsName: this.srsName,
13003 schema: this.schema
13004 }, this.formatOptions));
13006 if (!options.geometryName && parseFloat(this.format.version) > 1.0) {
13007 this.setGeometryName(null);
13012 * APIMethod: destroy
13013 * Clean up the protocol.
13015 destroy: function() {
13016 if(this.options && !this.options.format) {
13017 this.format.destroy();
13019 this.format = null;
13020 OpenLayers.Protocol.prototype.destroy.apply(this);
13025 * Construct a request for reading new features. Since WFS splits the
13026 * basic CRUD operations into GetFeature requests (for read) and
13027 * Transactions (for all others), this method does not make use of the
13028 * format's read method (that is only about reading transaction
13032 * options - {Object} Options for the read operation, in addition to the
13033 * options set on the instance (options set here will take precedence).
13035 * To use a configured protocol to get e.g. a WFS hit count, applications
13036 * could do the following:
13040 * readOptions: {output: "object"},
13041 * resultType: "hits",
13042 * maxFeatures: null,
13043 * callback: function(resp) {
13044 * // process resp.numberOfFeatures here
13049 * To use a configured protocol to use WFS paging (if supported by the
13050 * server), applications could do the following:
13059 * To limit the attributes returned by the GetFeature request, applications
13060 * can use the propertyNames option to specify the properties to include in
13065 * propertyNames: ["DURATION", "INTENSITY"]
13069 read: function(options) {
13070 OpenLayers.Protocol.prototype.read.apply(this, arguments);
13071 options = OpenLayers.Util.extend({}, options);
13072 OpenLayers.Util.applyDefaults(options, this.options || {});
13073 var response = new OpenLayers.Protocol.Response({requestType: "read"});
13075 var data = OpenLayers.Format.XML.prototype.write.apply(
13076 this.format, [this.format.writeNode("wfs:GetFeature", options)]
13079 response.priv = OpenLayers.Request.POST({
13081 callback: this.createCallback(this.handleRead, response, options),
13082 params: options.params,
13083 headers: options.headers,
13091 * APIMethod: setFeatureType
13092 * Change the feature type on the fly.
13095 * featureType - {String} Local (without prefix) feature typeName.
13097 setFeatureType: function(featureType) {
13098 this.featureType = featureType;
13099 this.format.featureType = featureType;
13103 * APIMethod: setGeometryName
13104 * Sets the geometryName option after instantiation.
13107 * geometryName - {String} Name of geometry attribute.
13109 setGeometryName: function(geometryName) {
13110 this.geometryName = geometryName;
13111 this.format.geometryName = geometryName;
13115 * Method: handleRead
13116 * Deal with response from the read request.
13119 * response - {<OpenLayers.Protocol.Response>} The response object to pass
13120 * to the user callback.
13121 * options - {Object} The user options passed to the read call.
13123 handleRead: function(response, options) {
13124 options = OpenLayers.Util.extend({}, options);
13125 OpenLayers.Util.applyDefaults(options, this.options);
13127 if(options.callback) {
13128 var request = response.priv;
13129 if(request.status >= 200 && request.status < 300) {
13131 var result = this.parseResponse(request, options.readOptions);
13132 if (result && result.success !== false) {
13133 if (options.readOptions && options.readOptions.output == "object") {
13134 OpenLayers.Util.extend(response, result);
13136 response.features = result;
13138 response.code = OpenLayers.Protocol.Response.SUCCESS;
13140 // failure (service exception)
13141 response.code = OpenLayers.Protocol.Response.FAILURE;
13142 response.error = result;
13146 response.code = OpenLayers.Protocol.Response.FAILURE;
13148 options.callback.call(options.scope, response);
13153 * Method: parseResponse
13154 * Read HTTP response body and return features
13157 * request - {XMLHttpRequest} The request object
13158 * options - {Object} Optional object to pass to format's read
13161 * {Object} or {Array({<OpenLayers.Feature.Vector>})} or
13162 * {<OpenLayers.Feature.Vector>}
13163 * An object with a features property, an array of features or a single
13166 parseResponse: function(request, options) {
13167 var doc = request.responseXML;
13168 if(!doc || !doc.documentElement) {
13169 doc = request.responseText;
13171 if(!doc || doc.length <= 0) {
13174 var result = (this.readFormat !== null) ? this.readFormat.read(doc) :
13175 this.format.read(doc, options);
13176 if (!this.featureNS) {
13177 var format = this.readFormat || this.format;
13178 this.featureNS = format.featureNS;
13179 // no need to auto-configure again on subsequent reads
13180 format.autoConfig = false;
13181 if (!this.geometryName) {
13182 this.setGeometryName(format.geometryName);
13190 * Given a list of feature, assemble a batch request for update, create,
13191 * and delete transactions. A commit call on the prototype amounts
13192 * to writing a WFS transaction - so the write method on the format
13196 * features - {Array(<OpenLayers.Feature.Vector>)}
13197 * options - {Object}
13199 * Valid options properties:
13200 * nativeElements - {Array({Object})} Array of objects with information for writing
13201 * out <Native> elements, these objects have vendorId, safeToIgnore and
13202 * value properties. The <Native> element is intended to allow access to
13203 * vendor specific capabilities of any particular web feature server or
13207 * {<OpenLayers.Protocol.Response>} A response object with a features
13208 * property containing any insertIds and a priv property referencing
13209 * the XMLHttpRequest object.
13211 commit: function(features, options) {
13213 options = OpenLayers.Util.extend({}, options);
13214 OpenLayers.Util.applyDefaults(options, this.options);
13216 var response = new OpenLayers.Protocol.Response({
13217 requestType: "commit",
13218 reqFeatures: features
13220 response.priv = OpenLayers.Request.POST({
13222 headers: options.headers,
13223 data: this.format.write(features, options),
13224 callback: this.createCallback(this.handleCommit, response, options)
13231 * Method: handleCommit
13232 * Called when the commit request returns.
13235 * response - {<OpenLayers.Protocol.Response>} The response object to pass
13236 * to the user callback.
13237 * options - {Object} The user options passed to the commit call.
13239 handleCommit: function(response, options) {
13240 if(options.callback) {
13241 var request = response.priv;
13243 // ensure that we have an xml doc
13244 var data = request.responseXML;
13245 if(!data || !data.documentElement) {
13246 data = request.responseText;
13249 var obj = this.format.read(data) || {};
13251 response.insertIds = obj.insertIds || [];
13253 response.code = OpenLayers.Protocol.Response.SUCCESS;
13255 response.code = OpenLayers.Protocol.Response.FAILURE;
13256 response.error = obj;
13258 options.callback.call(options.scope, response);
13263 * Method: filterDelete
13264 * Send a request that deletes all features by their filter.
13267 * filter - {<OpenLayers.Filter>} filter
13269 filterDelete: function(filter, options) {
13270 options = OpenLayers.Util.extend({}, options);
13271 OpenLayers.Util.applyDefaults(options, this.options);
13273 var response = new OpenLayers.Protocol.Response({
13274 requestType: "commit"
13277 var root = this.format.createElementNSPlus("wfs:Transaction", {
13280 version: this.version
13284 var deleteNode = this.format.createElementNSPlus("wfs:Delete", {
13286 typeName: (options.featureNS ? this.featurePrefix + ":" : "") +
13287 options.featureType
13291 if(options.featureNS) {
13292 deleteNode.setAttribute("xmlns:" + this.featurePrefix, options.featureNS);
13294 var filterNode = this.format.writeNode("ogc:Filter", filter);
13296 deleteNode.appendChild(filterNode);
13298 root.appendChild(deleteNode);
13300 var data = OpenLayers.Format.XML.prototype.write.apply(
13301 this.format, [root]
13304 return OpenLayers.Request.POST({
13306 callback : options.callback || function(){},
13314 * Abort an ongoing request, the response object passed to
13315 * this method must come from this protocol (as a result
13316 * of a read, or commit operation).
13319 * response - {<OpenLayers.Protocol.Response>}
13321 abort: function(response) {
13323 response.priv.abort();
13327 CLASS_NAME: "OpenLayers.Protocol.WFS.v1"
13329 /* ======================================================================
13330 OpenLayers/Format.js
13331 ====================================================================== */
13333 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
13334 * full list of contributors). Published under the 2-clause BSD license.
13335 * See license.txt in the OpenLayers distribution or repository for the
13336 * full text of the license. */
13339 * @requires OpenLayers/BaseTypes/Class.js
13340 * @requires OpenLayers/Util.js
13344 * Class: OpenLayers.Format
13345 * Base class for format reading/writing a variety of formats. Subclasses
13346 * of OpenLayers.Format are expected to have read and write methods.
13348 OpenLayers.Format = OpenLayers.Class({
13351 * Property: options
13352 * {Object} A reference to options passed to the constructor.
13357 * APIProperty: externalProjection
13358 * {<OpenLayers.Projection>} When passed a externalProjection and
13359 * internalProjection, the format will reproject the geometries it
13360 * reads or writes. The externalProjection is the projection used by
13361 * the content which is passed into read or which comes out of write.
13362 * In order to reproject, a projection transformation function for the
13363 * specified projections must be available. This support may be
13364 * provided via proj4js or via a custom transformation function. See
13365 * {<OpenLayers.Projection.addTransform>} for more information on
13366 * custom transformations.
13368 externalProjection: null,
13371 * APIProperty: internalProjection
13372 * {<OpenLayers.Projection>} When passed a externalProjection and
13373 * internalProjection, the format will reproject the geometries it
13374 * reads or writes. The internalProjection is the projection used by
13375 * the geometries which are returned by read or which are passed into
13376 * write. In order to reproject, a projection transformation function
13377 * for the specified projections must be available. This support may be
13378 * provided via proj4js or via a custom transformation function. See
13379 * {<OpenLayers.Projection.addTransform>} for more information on
13380 * custom transformations.
13382 internalProjection: null,
13385 * APIProperty: data
13386 * {Object} When <keepData> is true, this is the parsed string sent to
13392 * APIProperty: keepData
13393 * {Object} Maintain a reference (<data>) to the most recently read data.
13394 * Default is false.
13399 * Constructor: OpenLayers.Format
13400 * Instances of this class are not useful. See one of the subclasses.
13403 * options - {Object} An optional object with properties to set on the
13407 * keepData - {Boolean} If true, upon <read>, the data property will be
13408 * set to the parsed object (e.g. the json or xml object).
13411 * An instance of OpenLayers.Format
13413 initialize: function(options) {
13414 OpenLayers.Util.extend(this, options);
13415 this.options = options;
13419 * APIMethod: destroy
13422 destroy: function() {
13427 * Read data from a string, and return an object whose type depends on the
13431 * data - {string} Data to read/parse.
13434 * Depends on the subclass
13436 read: function(data) {
13437 throw new Error('Read not implemented.');
13442 * Accept an object, and return a string.
13445 * object - {Object} Object to be serialized
13448 * {String} A string representation of the object.
13450 write: function(object) {
13451 throw new Error('Write not implemented.');
13454 CLASS_NAME: "OpenLayers.Format"
13456 /* ======================================================================
13457 OpenLayers/Format/XML.js
13458 ====================================================================== */
13460 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
13461 * full list of contributors). Published under the 2-clause BSD license.
13462 * See license.txt in the OpenLayers distribution or repository for the
13463 * full text of the license. */
13466 * @requires OpenLayers/Format.js
13470 * Class: OpenLayers.Format.XML
13471 * Read and write XML. For cross-browser XML generation, use methods on an
13472 * instance of the XML format class instead of on <code>document<end>.
13473 * The DOM creation and traversing methods exposed here all mimic the
13474 * W3C XML DOM methods. Create a new parser with the
13475 * <OpenLayers.Format.XML> constructor.
13478 * - <OpenLayers.Format>
13480 OpenLayers.Format.XML = OpenLayers.Class(OpenLayers.Format, {
13483 * Property: namespaces
13484 * {Object} Mapping of namespace aliases to namespace URIs. Properties
13485 * of this object should not be set individually. Read-only. All
13486 * XML subclasses should have their own namespaces object. Use
13487 * <setNamespace> to add or set a namespace alias after construction.
13492 * Property: namespaceAlias
13493 * {Object} Mapping of namespace URI to namespace alias. This object
13494 * is read-only. Use <setNamespace> to add or set a namespace alias.
13496 namespaceAlias: null,
13499 * Property: defaultPrefix
13500 * {String} The default namespace alias for creating element nodes.
13502 defaultPrefix: null,
13505 * Property: readers
13506 * Contains public functions, grouped by namespace prefix, that will
13507 * be applied when a namespaced node is found matching the function
13508 * name. The function will be applied in the scope of this parser
13509 * with two arguments: the node being read and a context object passed
13515 * Property: writers
13516 * As a compliment to the <readers> property, this structure contains public
13517 * writing functions grouped by namespace alias and named like the
13518 * node names they produce.
13524 * {XMLDom} If this browser uses ActiveX, this will be set to a XMLDOM
13525 * object. It is not intended to be a browser sniffing property.
13526 * Instead, the xmldom property is used instead of <code>document<end>
13527 * where namespaced node creation methods are not supported. In all
13528 * other browsers, this remains null.
13533 * Constructor: OpenLayers.Format.XML
13534 * Construct an XML parser. The parser is used to read and write XML.
13535 * Reading XML from a string returns a DOM element. Writing XML from
13536 * a DOM element returns a string.
13539 * options - {Object} Optional object whose properties will be set on
13542 initialize: function(options) {
13543 if (OpenLayers.Format.XML.supportActiveX) {
13544 this.xmldom = new ActiveXObject("Microsoft.XMLDOM");
13546 OpenLayers.Format.prototype.initialize.apply(this, [options]);
13547 // clone the namespace object and set all namespace aliases
13548 this.namespaces = OpenLayers.Util.extend({}, this.namespaces);
13549 this.namespaceAlias = {};
13550 for(var alias in this.namespaces) {
13551 this.namespaceAlias[this.namespaces[alias]] = alias;
13556 * APIMethod: destroy
13559 destroy: function() {
13560 this.xmldom = null;
13561 OpenLayers.Format.prototype.destroy.apply(this, arguments);
13565 * Method: setNamespace
13566 * Set a namespace alias and URI for the format.
13569 * alias - {String} The namespace alias (prefix).
13570 * uri - {String} The namespace URI.
13572 setNamespace: function(alias, uri) {
13573 this.namespaces[alias] = uri;
13574 this.namespaceAlias[uri] = alias;
13579 * Deserialize a XML string and return a DOM node.
13582 * text - {String} A XML string
13585 * {DOMElement} A DOM node
13587 read: function(text) {
13588 var index = text.indexOf('<');
13590 text = text.substring(index);
13592 var node = OpenLayers.Util.Try(
13593 OpenLayers.Function.bind((
13597 * Since we want to be able to call this method on the prototype
13598 * itself, this.xmldom may not exist even if in IE.
13600 if (OpenLayers.Format.XML.supportActiveX && !this.xmldom) {
13601 xmldom = new ActiveXObject("Microsoft.XMLDOM");
13603 xmldom = this.xmldom;
13606 xmldom.loadXML(text);
13611 return new DOMParser().parseFromString(text, 'text/xml');
13614 var req = new XMLHttpRequest();
13615 req.open("GET", "data:" + "text/xml" +
13616 ";charset=utf-8," + encodeURIComponent(text), false);
13617 if(req.overrideMimeType) {
13618 req.overrideMimeType("text/xml");
13621 return req.responseXML;
13625 if(this.keepData) {
13634 * Serialize a DOM node into a XML string.
13637 * node - {DOMElement} A DOM node.
13640 * {String} The XML string representation of the input node.
13642 write: function(node) {
13647 var serializer = new XMLSerializer();
13648 if (node.nodeType == 1) {
13649 // Add nodes to a document before serializing. Everything else
13650 // is serialized as is. This may need more work. See #1218 .
13651 var doc = document.implementation.createDocument("", "", null);
13652 if (doc.importNode) {
13653 node = doc.importNode(node, true);
13655 doc.appendChild(node);
13656 data = serializer.serializeToString(doc);
13658 data = serializer.serializeToString(node);
13665 * APIMethod: createElementNS
13666 * Create a new element with namespace. This node can be appended to
13667 * another node with the standard node.appendChild method. For
13668 * cross-browser support, this method must be used instead of
13669 * document.createElementNS.
13672 * uri - {String} Namespace URI for the element.
13673 * name - {String} The qualified name of the element (prefix:localname).
13676 * {Element} A DOM element with namespace.
13678 createElementNS: function(uri, name) {
13681 if(typeof uri == "string") {
13682 element = this.xmldom.createNode(1, name, uri);
13684 element = this.xmldom.createNode(1, name, "");
13687 element = document.createElementNS(uri, name);
13693 * APIMethod: createDocumentFragment
13694 * Create a document fragment node that can be appended to another node
13695 * created by createElementNS. This will call
13696 * document.createDocumentFragment outside of IE. In IE, the ActiveX
13697 * object's createDocumentFragment method is used.
13700 * {Element} A document fragment.
13702 createDocumentFragment: function() {
13705 element = this.xmldom.createDocumentFragment();
13707 element = document.createDocumentFragment();
13713 * APIMethod: createTextNode
13714 * Create a text node. This node can be appended to another node with
13715 * the standard node.appendChild method. For cross-browser support,
13716 * this method must be used instead of document.createTextNode.
13719 * text - {String} The text of the node.
13722 * {DOMElement} A DOM text node.
13724 createTextNode: function(text) {
13726 if (typeof text !== "string") {
13727 text = String(text);
13730 node = this.xmldom.createTextNode(text);
13732 node = document.createTextNode(text);
13738 * APIMethod: getElementsByTagNameNS
13739 * Get a list of elements on a node given the namespace URI and local name.
13740 * To return all nodes in a given namespace, use '*' for the name
13741 * argument. To return all nodes of a given (local) name, regardless
13742 * of namespace, use '*' for the uri argument.
13745 * node - {Element} Node on which to search for other nodes.
13746 * uri - {String} Namespace URI.
13747 * name - {String} Local name of the tag (without the prefix).
13750 * {NodeList} A node list or array of elements.
13752 getElementsByTagNameNS: function(node, uri, name) {
13754 if(node.getElementsByTagNameNS) {
13755 elements = node.getElementsByTagNameNS(uri, name);
13757 // brute force method
13758 var allNodes = node.getElementsByTagName("*");
13759 var potentialNode, fullName;
13760 for(var i=0, len=allNodes.length; i<len; ++i) {
13761 potentialNode = allNodes[i];
13762 fullName = (potentialNode.prefix) ?
13763 (potentialNode.prefix + ":" + name) : name;
13764 if((name == "*") || (fullName == potentialNode.nodeName)) {
13765 if((uri == "*") || (uri == potentialNode.namespaceURI)) {
13766 elements.push(potentialNode);
13775 * APIMethod: getAttributeNodeNS
13776 * Get an attribute node given the namespace URI and local name.
13779 * node - {Element} Node on which to search for attribute nodes.
13780 * uri - {String} Namespace URI.
13781 * name - {String} Local name of the attribute (without the prefix).
13784 * {DOMElement} An attribute node or null if none found.
13786 getAttributeNodeNS: function(node, uri, name) {
13787 var attributeNode = null;
13788 if(node.getAttributeNodeNS) {
13789 attributeNode = node.getAttributeNodeNS(uri, name);
13791 var attributes = node.attributes;
13792 var potentialNode, fullName;
13793 for(var i=0, len=attributes.length; i<len; ++i) {
13794 potentialNode = attributes[i];
13795 if(potentialNode.namespaceURI == uri) {
13796 fullName = (potentialNode.prefix) ?
13797 (potentialNode.prefix + ":" + name) : name;
13798 if(fullName == potentialNode.nodeName) {
13799 attributeNode = potentialNode;
13805 return attributeNode;
13809 * APIMethod: getAttributeNS
13810 * Get an attribute value given the namespace URI and local name.
13813 * node - {Element} Node on which to search for an attribute.
13814 * uri - {String} Namespace URI.
13815 * name - {String} Local name of the attribute (without the prefix).
13818 * {String} An attribute value or and empty string if none found.
13820 getAttributeNS: function(node, uri, name) {
13821 var attributeValue = "";
13822 if(node.getAttributeNS) {
13823 attributeValue = node.getAttributeNS(uri, name) || "";
13825 var attributeNode = this.getAttributeNodeNS(node, uri, name);
13826 if(attributeNode) {
13827 attributeValue = attributeNode.nodeValue;
13830 return attributeValue;
13834 * APIMethod: getChildValue
13835 * Get the textual value of the node if it exists, or return an
13836 * optional default string. Returns an empty string if no first child
13837 * exists and no default value is supplied.
13840 * node - {DOMElement} The element used to look for a first child value.
13841 * def - {String} Optional string to return in the event that no
13842 * first child value exists.
13845 * {String} The value of the first child of the given node.
13847 getChildValue: function(node, def) {
13848 var value = def || "";
13850 for(var child=node.firstChild; child; child=child.nextSibling) {
13851 switch(child.nodeType) {
13852 case 3: // text node
13853 case 4: // cdata section
13854 value += child.nodeValue;
13862 * APIMethod: isSimpleContent
13863 * Test if the given node has only simple content (i.e. no child element
13867 * node - {DOMElement} An element node.
13870 * {Boolean} The node has no child element nodes (nodes of type 1).
13872 isSimpleContent: function(node) {
13874 for(var child=node.firstChild; child; child=child.nextSibling) {
13875 if(child.nodeType === 1) {
13884 * APIMethod: contentType
13885 * Determine the content type for a given node.
13888 * node - {DOMElement}
13891 * {Integer} One of OpenLayers.Format.XML.CONTENT_TYPE.{EMPTY,SIMPLE,COMPLEX,MIXED}
13892 * if the node has no, simple, complex, or mixed content.
13894 contentType: function(node) {
13895 var simple = false,
13898 var type = OpenLayers.Format.XML.CONTENT_TYPE.EMPTY;
13900 for(var child=node.firstChild; child; child=child.nextSibling) {
13901 switch(child.nodeType) {
13910 if(complex && simple) {
13915 if(complex && simple) {
13916 type = OpenLayers.Format.XML.CONTENT_TYPE.MIXED;
13917 } else if(complex) {
13918 return OpenLayers.Format.XML.CONTENT_TYPE.COMPLEX;
13919 } else if(simple) {
13920 return OpenLayers.Format.XML.CONTENT_TYPE.SIMPLE;
13926 * APIMethod: hasAttributeNS
13927 * Determine whether a node has a particular attribute matching the given
13928 * name and namespace.
13931 * node - {Element} Node on which to search for an attribute.
13932 * uri - {String} Namespace URI.
13933 * name - {String} Local name of the attribute (without the prefix).
13936 * {Boolean} The node has an attribute matching the name and namespace.
13938 hasAttributeNS: function(node, uri, name) {
13940 if(node.hasAttributeNS) {
13941 found = node.hasAttributeNS(uri, name);
13943 found = !!this.getAttributeNodeNS(node, uri, name);
13949 * APIMethod: setAttributeNS
13950 * Adds a new attribute or changes the value of an attribute with the given
13951 * namespace and name.
13954 * node - {Element} Element node on which to set the attribute.
13955 * uri - {String} Namespace URI for the attribute.
13956 * name - {String} Qualified name (prefix:localname) for the attribute.
13957 * value - {String} Attribute value.
13959 setAttributeNS: function(node, uri, name, value) {
13960 if(node.setAttributeNS) {
13961 node.setAttributeNS(uri, name, value);
13965 var attribute = node.ownerDocument.createNode(
13968 attribute.nodeValue = value;
13969 node.setAttributeNode(attribute);
13971 node.setAttribute(name, value);
13974 throw "setAttributeNS not implemented";
13980 * Method: createElementNSPlus
13981 * Shorthand for creating namespaced elements with optional attributes and
13982 * child text nodes.
13985 * name - {String} The qualified node name.
13986 * options - {Object} Optional object for node configuration.
13989 * uri - {String} Optional namespace uri for the element - supply a prefix
13990 * instead if the namespace uri is a property of the format's namespace
13992 * attributes - {Object} Optional attributes to be set using the
13993 * <setAttributes> method.
13994 * value - {String} Optional text to be appended as a text node.
13997 * {Element} An element node.
13999 createElementNSPlus: function(name, options) {
14000 options = options || {};
14001 // order of prefix preference
14002 // 1. in the uri option
14003 // 2. in the prefix option
14004 // 3. in the qualified name
14005 // 4. from the defaultPrefix
14006 var uri = options.uri || this.namespaces[options.prefix];
14008 var loc = name.indexOf(":");
14009 uri = this.namespaces[name.substring(0, loc)];
14012 uri = this.namespaces[this.defaultPrefix];
14014 var node = this.createElementNS(uri, name);
14015 if(options.attributes) {
14016 this.setAttributes(node, options.attributes);
14018 var value = options.value;
14019 if(value != null) {
14020 node.appendChild(this.createTextNode(value));
14026 * Method: setAttributes
14027 * Set multiple attributes given key value pairs from an object.
14030 * node - {Element} An element node.
14031 * obj - {Object || Array} An object whose properties represent attribute
14032 * names and values represent attribute values. If an attribute name
14033 * is a qualified name ("prefix:local"), the prefix will be looked up
14034 * in the parsers {namespaces} object. If the prefix is found,
14035 * setAttributeNS will be used instead of setAttribute.
14037 setAttributes: function(node, obj) {
14039 for(var name in obj) {
14040 if(obj[name] != null && obj[name].toString) {
14041 value = obj[name].toString();
14042 // check for qualified attribute name ("prefix:local")
14043 uri = this.namespaces[name.substring(0, name.indexOf(":"))] || null;
14044 this.setAttributeNS(node, uri, name, value);
14050 * Method: getFirstElementChild
14051 * Implementation of firstElementChild attribute that works on ie7 and ie8.
14054 * node - {DOMElement} The parent node (required).
14057 * {DOMElement} The first child element.
14059 getFirstElementChild: function(node) {
14060 if (node.firstElementChild) {
14061 return node.firstElementChild;
14064 var child = node.firstChild;
14065 while (child.nodeType != 1 && (child = child.nextSibling)) {}
14072 * Shorthand for applying one of the named readers given the node
14073 * namespace and local name. Readers take two args (node, obj) and
14074 * generally extend or modify the second.
14077 * node - {DOMElement} The node to be read (required).
14078 * obj - {Object} The object to be modified (optional).
14081 * {Object} The input object, modified (or a new one if none was provided).
14083 readNode: function(node, obj) {
14087 var group = this.readers[node.namespaceURI ? this.namespaceAlias[node.namespaceURI]: this.defaultPrefix];
14089 var local = node.localName || node.nodeName.split(":").pop();
14090 var reader = group[local] || group["*"];
14092 reader.apply(this, [node, obj]);
14099 * Method: readChildNodes
14100 * Shorthand for applying the named readers to all children of a node.
14101 * For each child of type 1 (element), <readSelf> is called.
14104 * node - {DOMElement} The node to be read (required).
14105 * obj - {Object} The object to be modified (optional).
14108 * {Object} The input object, modified.
14110 readChildNodes: function(node, obj) {
14114 var children = node.childNodes;
14116 for(var i=0, len=children.length; i<len; ++i) {
14117 child = children[i];
14118 if(child.nodeType == 1) {
14119 this.readNode(child, obj);
14126 * Method: writeNode
14127 * Shorthand for applying one of the named writers and appending the
14128 * results to a node. If a qualified name is not provided for the
14129 * second argument (and a local name is used instead), the namespace
14130 * of the parent node will be assumed.
14133 * name - {String} The name of a node to generate. If a qualified name
14134 * (e.g. "pre:Name") is used, the namespace prefix is assumed to be
14135 * in the <writers> group. If a local name is used (e.g. "Name") then
14136 * the namespace of the parent is assumed. If a local name is used
14137 * and no parent is supplied, then the default namespace is assumed.
14138 * obj - {Object} Structure containing data for the writer.
14139 * parent - {DOMElement} Result will be appended to this node. If no parent
14140 * is supplied, the node will not be appended to anything.
14143 * {DOMElement} The child node.
14145 writeNode: function(name, obj, parent) {
14147 var split = name.indexOf(":");
14149 prefix = name.substring(0, split);
14150 local = name.substring(split + 1);
14153 prefix = this.namespaceAlias[parent.namespaceURI];
14155 prefix = this.defaultPrefix;
14159 var child = this.writers[prefix][local].apply(this, [obj]);
14161 parent.appendChild(child);
14167 * APIMethod: getChildEl
14168 * Get the first child element. Optionally only return the first child
14169 * if it matches the given name and namespace URI.
14172 * node - {DOMElement} The parent node.
14173 * name - {String} Optional node name (local) to search for.
14174 * uri - {String} Optional namespace URI to search for.
14177 * {DOMElement} The first child. Returns null if no element is found, if
14178 * something significant besides an element is found, or if the element
14179 * found does not match the optional name and uri.
14181 getChildEl: function(node, name, uri) {
14182 return node && this.getThisOrNextEl(node.firstChild, name, uri);
14186 * APIMethod: getNextEl
14187 * Get the next sibling element. Optionally get the first sibling only
14188 * if it matches the given local name and namespace URI.
14191 * node - {DOMElement} The node.
14192 * name - {String} Optional local name of the sibling to search for.
14193 * uri - {String} Optional namespace URI of the sibling to search for.
14196 * {DOMElement} The next sibling element. Returns null if no element is
14197 * found, something significant besides an element is found, or the
14198 * found element does not match the optional name and uri.
14200 getNextEl: function(node, name, uri) {
14201 return node && this.getThisOrNextEl(node.nextSibling, name, uri);
14205 * Method: getThisOrNextEl
14206 * Return this node or the next element node. Optionally get the first
14207 * sibling with the given local name or namespace URI.
14210 * node - {DOMElement} The node.
14211 * name - {String} Optional local name of the sibling to search for.
14212 * uri - {String} Optional namespace URI of the sibling to search for.
14215 * {DOMElement} The next sibling element. Returns null if no element is
14216 * found, something significant besides an element is found, or the
14217 * found element does not match the query.
14219 getThisOrNextEl: function(node, name, uri) {
14220 outer: for(var sibling=node; sibling; sibling=sibling.nextSibling) {
14221 switch(sibling.nodeType) {
14223 if((!name || name === (sibling.localName || sibling.nodeName.split(":").pop())) &&
14224 (!uri || uri === sibling.namespaceURI)) {
14231 if(/^\s*$/.test(sibling.nodeValue)) {
14235 case 6: // ENTITY_NODE
14236 case 12: // NOTATION_NODE
14237 case 10: // DOCUMENT_TYPE_NODE
14238 case 11: // DOCUMENT_FRAGMENT_NODE
14241 } // ignore comments and processing instructions
14243 return sibling || null;
14247 * APIMethod: lookupNamespaceURI
14248 * Takes a prefix and returns the namespace URI associated with it on the given
14249 * node if found (and null if not). Supplying null for the prefix will
14250 * return the default namespace.
14252 * For browsers that support it, this calls the native lookupNamesapceURI
14253 * function. In other browsers, this is an implementation of
14254 * http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI.
14256 * For browsers that don't support the attribute.ownerElement property, this
14257 * method cannot be called on attribute nodes.
14260 * node - {DOMElement} The node from which to start looking.
14261 * prefix - {String} The prefix to lookup or null to lookup the default namespace.
14264 * {String} The namespace URI for the given prefix. Returns null if the prefix
14265 * cannot be found or the node is the wrong type.
14267 lookupNamespaceURI: function(node, prefix) {
14270 if(node.lookupNamespaceURI) {
14271 uri = node.lookupNamespaceURI(prefix);
14273 outer: switch(node.nodeType) {
14274 case 1: // ELEMENT_NODE
14275 if(node.namespaceURI !== null && node.prefix === prefix) {
14276 uri = node.namespaceURI;
14279 var len = node.attributes.length;
14282 for(var i=0; i<len; ++i) {
14283 attr = node.attributes[i];
14284 if(attr.prefix === "xmlns" && attr.name === "xmlns:" + prefix) {
14285 uri = attr.value || null;
14287 } else if(attr.name === "xmlns" && prefix === null) {
14288 uri = attr.value || null;
14293 uri = this.lookupNamespaceURI(node.parentNode, prefix);
14295 case 2: // ATTRIBUTE_NODE
14296 uri = this.lookupNamespaceURI(node.ownerElement, prefix);
14298 case 9: // DOCUMENT_NODE
14299 uri = this.lookupNamespaceURI(node.documentElement, prefix);
14301 case 6: // ENTITY_NODE
14302 case 12: // NOTATION_NODE
14303 case 10: // DOCUMENT_TYPE_NODE
14304 case 11: // DOCUMENT_FRAGMENT_NODE
14307 // TEXT_NODE (3), CDATA_SECTION_NODE (4), ENTITY_REFERENCE_NODE (5),
14308 // PROCESSING_INSTRUCTION_NODE (7), COMMENT_NODE (8)
14309 uri = this.lookupNamespaceURI(node.parentNode, prefix);
14318 * Method: getXMLDoc
14319 * Get an XML document for nodes that are not supported in HTML (e.g.
14320 * createCDATASection). On IE, this will either return an existing or
14321 * create a new <xmldom> on the instance. On other browsers, this will
14322 * either return an existing or create a new shared document (see
14323 * <OpenLayers.Format.XML.document>).
14328 getXMLDoc: function() {
14329 if (!OpenLayers.Format.XML.document && !this.xmldom) {
14330 if (document.implementation && document.implementation.createDocument) {
14331 OpenLayers.Format.XML.document =
14332 document.implementation.createDocument("", "", null);
14333 } else if (!this.xmldom && OpenLayers.Format.XML.supportActiveX) {
14334 this.xmldom = new ActiveXObject("Microsoft.XMLDOM");
14337 return OpenLayers.Format.XML.document || this.xmldom;
14340 CLASS_NAME: "OpenLayers.Format.XML"
14344 OpenLayers.Format.XML.CONTENT_TYPE = {EMPTY: 0, SIMPLE: 1, COMPLEX: 2, MIXED: 3};
14347 * APIFunction: OpenLayers.Format.XML.lookupNamespaceURI
14348 * Takes a prefix and returns the namespace URI associated with it on the given
14349 * node if found (and null if not). Supplying null for the prefix will
14350 * return the default namespace.
14352 * For browsers that support it, this calls the native lookupNamesapceURI
14353 * function. In other browsers, this is an implementation of
14354 * http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI.
14356 * For browsers that don't support the attribute.ownerElement property, this
14357 * method cannot be called on attribute nodes.
14360 * node - {DOMElement} The node from which to start looking.
14361 * prefix - {String} The prefix to lookup or null to lookup the default namespace.
14364 * {String} The namespace URI for the given prefix. Returns null if the prefix
14365 * cannot be found or the node is the wrong type.
14367 OpenLayers.Format.XML.lookupNamespaceURI = OpenLayers.Function.bind(
14368 OpenLayers.Format.XML.prototype.lookupNamespaceURI,
14369 OpenLayers.Format.XML.prototype
14373 * Property: OpenLayers.Format.XML.document
14374 * {XMLDocument} XML document to reuse for creating non-HTML compliant nodes,
14375 * like document.createCDATASection.
14377 OpenLayers.Format.XML.document = null;
14380 * APIFunction: OpenLayers.Format.XML.supportActiveX
14381 * Returns a poolean flag to check if this browser uses ActiveX.
14383 OpenLayers.Format.XML.supportActiveX = (function () {
14384 return (Object.getOwnPropertyDescriptor &&
14385 Object.getOwnPropertyDescriptor(window, "ActiveXObject")) ||
14386 ("ActiveXObject" in window);
14388 /* ======================================================================
14389 OpenLayers/Format/WFST.js
14390 ====================================================================== */
14392 /* Copyright (c) 2006-2015 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. */
14398 * @requires OpenLayers/Format.js
14402 * Function: OpenLayers.Format.WFST
14403 * Used to create a versioned WFS protocol. Default version is 1.0.0.
14406 * {<OpenLayers.Format>} A WFST format of the given version.
14408 OpenLayers.Format.WFST = function(options) {
14409 options = OpenLayers.Util.applyDefaults(
14410 options, OpenLayers.Format.WFST.DEFAULTS
14412 var cls = OpenLayers.Format.WFST["v"+options.version.replace(/\./g, "_")];
14414 throw "Unsupported WFST version: " + options.version;
14416 return new cls(options);
14420 * Constant: OpenLayers.Format.WFST.DEFAULTS
14421 * {Object} Default properties for the WFST format.
14423 OpenLayers.Format.WFST.DEFAULTS = {
14426 /* ======================================================================
14427 OpenLayers/Filter.js
14428 ====================================================================== */
14430 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
14431 * full list of contributors). Published under the 2-clause BSD license.
14432 * See license.txt in the OpenLayers distribution or repository for the
14433 * full text of the license. */
14437 * @requires OpenLayers/BaseTypes/Class.js
14438 * @requires OpenLayers/Util.js
14439 * @requires OpenLayers/Style.js
14443 * Class: OpenLayers.Filter
14444 * This class represents an OGC Filter.
14446 OpenLayers.Filter = OpenLayers.Class({
14449 * Constructor: OpenLayers.Filter
14450 * This class represents a generic filter.
14453 * options - {Object} Optional object whose properties will be set on the
14457 * {<OpenLayers.Filter>}
14459 initialize: function(options) {
14460 OpenLayers.Util.extend(this, options);
14464 * APIMethod: destroy
14465 * Remove reference to anything added.
14467 destroy: function() {
14471 * APIMethod: evaluate
14472 * Evaluates this filter in a specific context. Instances or subclasses
14473 * are supposed to override this method.
14476 * context - {Object} Context to use in evaluating the filter. If a vector
14477 * feature is provided, the feature.attributes will be used as context.
14480 * {Boolean} The filter applies.
14482 evaluate: function(context) {
14488 * Clones this filter. Should be implemented by subclasses.
14491 * {<OpenLayers.Filter>} Clone of this filter.
14493 clone: function() {
14498 * APIMethod: toString
14501 * {String} Include <OpenLayers.Format.CQL> in your build to get a CQL
14502 * representation of the filter returned. Otherwise "[Object object]"
14503 * will be returned.
14505 toString: function() {
14507 if (OpenLayers.Format && OpenLayers.Format.CQL) {
14508 string = OpenLayers.Format.CQL.prototype.write(this);
14510 string = Object.prototype.toString.call(this);
14515 CLASS_NAME: "OpenLayers.Filter"
14517 /* ======================================================================
14518 OpenLayers/Filter/Spatial.js
14519 ====================================================================== */
14521 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
14522 * full list of contributors). Published under the 2-clause BSD license.
14523 * See license.txt in the OpenLayers distribution or repository for the
14524 * full text of the license. */
14527 * @requires OpenLayers/Filter.js
14531 * Class: OpenLayers.Filter.Spatial
14532 * This class represents a spatial filter.
14533 * Currently implemented: BBOX, DWithin and Intersects
14536 * - <OpenLayers.Filter>
14538 OpenLayers.Filter.Spatial = OpenLayers.Class(OpenLayers.Filter, {
14541 * APIProperty: type
14542 * {String} Type of spatial filter.
14544 * The type should be one of:
14545 * - OpenLayers.Filter.Spatial.BBOX
14546 * - OpenLayers.Filter.Spatial.INTERSECTS
14547 * - OpenLayers.Filter.Spatial.DWITHIN
14548 * - OpenLayers.Filter.Spatial.WITHIN
14549 * - OpenLayers.Filter.Spatial.CONTAINS
14554 * APIProperty: property
14555 * {String} Name of the context property to compare.
14560 * APIProperty: value
14561 * {<OpenLayers.Bounds> || <OpenLayers.Geometry>} The bounds or geometry
14562 * to be used by the filter. Use bounds for BBOX filters and geometry
14563 * for INTERSECTS or DWITHIN filters.
14568 * APIProperty: distance
14569 * {Number} The distance to use in a DWithin spatial filter.
14574 * APIProperty: distanceUnits
14575 * {String} The units to use for the distance, e.g. 'm'.
14577 distanceUnits: null,
14580 * Constructor: OpenLayers.Filter.Spatial
14581 * Creates a spatial filter.
14584 * options - {Object} An optional object with properties to set on the
14588 * {<OpenLayers.Filter.Spatial>}
14593 * Evaluates this filter for a specific feature.
14596 * feature - {<OpenLayers.Feature.Vector>} feature to apply the filter to.
14599 * {Boolean} The feature meets filter criteria.
14601 evaluate: function(feature) {
14602 var intersect = false;
14603 switch(this.type) {
14604 case OpenLayers.Filter.Spatial.BBOX:
14605 case OpenLayers.Filter.Spatial.INTERSECTS:
14606 if(feature.geometry) {
14607 var geom = this.value;
14608 if(this.value.CLASS_NAME == "OpenLayers.Bounds") {
14609 geom = this.value.toGeometry();
14611 if(feature.geometry.intersects(geom)) {
14617 throw new Error('evaluate is not implemented for this filter type.');
14624 * Clones this filter.
14627 * {<OpenLayers.Filter.Spatial>} Clone of this filter.
14629 clone: function() {
14630 var options = OpenLayers.Util.applyDefaults({
14631 value: this.value && this.value.clone && this.value.clone()
14633 return new OpenLayers.Filter.Spatial(options);
14635 CLASS_NAME: "OpenLayers.Filter.Spatial"
14638 OpenLayers.Filter.Spatial.BBOX = "BBOX";
14639 OpenLayers.Filter.Spatial.INTERSECTS = "INTERSECTS";
14640 OpenLayers.Filter.Spatial.DWITHIN = "DWITHIN";
14641 OpenLayers.Filter.Spatial.WITHIN = "WITHIN";
14642 OpenLayers.Filter.Spatial.CONTAINS = "CONTAINS";
14643 /* ======================================================================
14644 OpenLayers/Filter/FeatureId.js
14645 ====================================================================== */
14647 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
14648 * full list of contributors). Published under the 2-clause BSD license.
14649 * See license.txt in the OpenLayers distribution or repository for the
14650 * full text of the license. */
14654 * @requires OpenLayers/Filter.js
14658 * Class: OpenLayers.Filter.FeatureId
14659 * This class represents a ogc:FeatureId Filter, as being used for rule-based SLD
14663 * - <OpenLayers.Filter>
14665 OpenLayers.Filter.FeatureId = OpenLayers.Class(OpenLayers.Filter, {
14668 * APIProperty: fids
14669 * {Array(String)} Feature Ids to evaluate this rule against.
14670 * To be passed inside the params object.
14676 * {String} Type to identify this filter.
14681 * Constructor: OpenLayers.Filter.FeatureId
14682 * Creates an ogc:FeatureId rule.
14685 * options - {Object} An optional object with properties to set on the
14689 * {<OpenLayers.Filter.FeatureId>}
14691 initialize: function(options) {
14693 OpenLayers.Filter.prototype.initialize.apply(this, [options]);
14697 * APIMethod: evaluate
14698 * evaluates this rule for a specific feature
14701 * feature - {<OpenLayers.Feature>} feature to apply the rule to.
14702 * For vector features, the check is run against the fid,
14703 * for plain features against the id.
14706 * {Boolean} true if the rule applies, false if it does not
14708 evaluate: function(feature) {
14709 for (var i=0, len=this.fids.length; i<len; i++) {
14710 var fid = feature.fid || feature.id;
14711 if (fid == this.fids[i]) {
14720 * Clones this filter.
14723 * {<OpenLayers.Filter.FeatureId>} Clone of this filter.
14725 clone: function() {
14726 var filter = new OpenLayers.Filter.FeatureId();
14727 OpenLayers.Util.extend(filter, this);
14728 filter.fids = this.fids.slice();
14732 CLASS_NAME: "OpenLayers.Filter.FeatureId"
14734 /* ======================================================================
14735 OpenLayers/Format/WFST/v1.js
14736 ====================================================================== */
14738 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
14739 * full list of contributors). Published under the 2-clause BSD license.
14740 * See license.txt in the OpenLayers distribution or repository for the
14741 * full text of the license. */
14744 * @requires OpenLayers/Format/XML.js
14745 * @requires OpenLayers/Format/WFST.js
14746 * @requires OpenLayers/Filter/Spatial.js
14747 * @requires OpenLayers/Filter/FeatureId.js
14751 * Class: OpenLayers.Format.WFST.v1
14752 * Superclass for WFST parsers.
14755 * - <OpenLayers.Format.XML>
14757 OpenLayers.Format.WFST.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
14760 * Property: namespaces
14761 * {Object} Mapping of namespace aliases to namespace URIs.
14764 xlink: "http://www.w3.org/1999/xlink",
14765 xsi: "http://www.w3.org/2001/XMLSchema-instance",
14766 wfs: "http://www.opengis.net/wfs",
14767 gml: "http://www.opengis.net/gml",
14768 ogc: "http://www.opengis.net/ogc",
14769 ows: "http://www.opengis.net/ows",
14770 xmlns: "http://www.w3.org/2000/xmlns/"
14774 * Property: defaultPrefix
14776 defaultPrefix: "wfs",
14779 * Property: version
14780 * {String} WFS version number.
14785 * Property: schemaLocation
14786 * {String} Schema location for a particular minor version.
14788 schemaLocations: null,
14791 * APIProperty: srsName
14792 * {String} URI for spatial reference system.
14797 * APIProperty: extractAttributes
14798 * {Boolean} Extract attributes from GML. Default is true.
14800 extractAttributes: true,
14804 * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
14805 * Changing is not recommended, a new Format should be instantiated.
14810 * Property: stateName
14811 * {Object} Maps feature states to node names.
14816 * Constructor: OpenLayers.Format.WFST.v1
14817 * Instances of this class are not created directly. Use the
14818 * <OpenLayers.Format.WFST.v1_0_0> or <OpenLayers.Format.WFST.v1_1_0>
14819 * constructor instead.
14822 * options - {Object} An optional object whose properties will be set on
14825 initialize: function(options) {
14826 // set state name mapping
14827 this.stateName = {};
14828 this.stateName[OpenLayers.State.INSERT] = "wfs:Insert";
14829 this.stateName[OpenLayers.State.UPDATE] = "wfs:Update";
14830 this.stateName[OpenLayers.State.DELETE] = "wfs:Delete";
14831 OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
14835 * Method: getSrsName
14837 getSrsName: function(feature, options) {
14838 var srsName = options && options.srsName;
14840 if(feature && feature.layer) {
14841 srsName = feature.layer.projection.getCode();
14843 srsName = this.srsName;
14851 * Parse the response from a transaction. Because WFS is split into
14852 * Transaction requests (create, update, and delete) and GetFeature
14853 * requests (read), this method handles parsing of both types of
14857 * data - {String | Document} The WFST document to read
14858 * options - {Object} Options for the reader
14860 * Valid options properties:
14861 * output - {String} either "features" or "object". The default is
14862 * "features", which means that the method will return an array of
14863 * features. If set to "object", an object with a "features" property
14864 * and other properties read by the parser will be returned.
14867 * {Array | Object} Output depending on the output option.
14869 read: function(data, options) {
14870 options = options || {};
14871 OpenLayers.Util.applyDefaults(options, {
14875 if(typeof data == "string") {
14876 data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
14878 if(data && data.nodeType == 9) {
14879 data = data.documentElement;
14883 this.readNode(data, obj, true);
14885 if(obj.features && options.output === "features") {
14886 obj = obj.features;
14892 * Property: readers
14893 * Contains public functions, grouped by namespace prefix, that will
14894 * be applied when a namespaced node is found matching the function
14895 * name. The function will be applied in the scope of this parser
14896 * with two arguments: the node being read and a context object passed
14901 "FeatureCollection": function(node, obj) {
14903 this.readChildNodes(node, obj);
14910 * Given an array of features, write a WFS transaction. This assumes
14911 * the features have a state property that determines the operation
14912 * type - insert, update, or delete.
14915 * features - {Array(<OpenLayers.Feature.Vector>)} A list of features. See
14916 * below for a more detailed description of the influence of the
14917 * feature's *modified* property.
14918 * options - {Object}
14920 * feature.modified rules:
14921 * If a feature has a modified property set, the following checks will be
14922 * made before a feature's geometry or attribute is included in an Update
14924 * - *modified* is not set at all: The geometry and all attributes will be
14926 * - *modified.geometry* is set (null or a geometry): The geometry will be
14927 * included. If *modified.attributes* is not set, all attributes will
14929 * - *modified.attributes* is set: Only the attributes set in
14930 * *modified.attributes* will be included.
14931 * If *modified.geometry* is not set, the geometry will not be included.
14933 * Valid options include:
14934 * - *multi* {Boolean} If set to true, geometries will be casted to
14935 * Multi geometries before writing.
14938 * {String} A serialized WFS transaction.
14940 write: function(features, options) {
14941 var node = this.writeNode("wfs:Transaction", {
14945 var value = this.schemaLocationAttr();
14947 this.setAttributeNS(
14948 node, this.namespaces["xsi"], "xsi:schemaLocation", value
14951 return OpenLayers.Format.XML.prototype.write.apply(this, [node]);
14955 * Property: writers
14956 * As a compliment to the readers property, this structure contains public
14957 * writing functions grouped by namespace alias and named like the
14958 * node names they produce.
14962 "GetFeature": function(options) {
14963 var node = this.createElementNSPlus("wfs:GetFeature", {
14966 version: this.version,
14967 handle: options && options.handle,
14968 outputFormat: options && options.outputFormat,
14969 maxFeatures: options && options.maxFeatures,
14970 viewParams: options && options.viewParams,
14971 "xsi:schemaLocation": this.schemaLocationAttr(options)
14974 if (typeof this.featureType == "string") {
14975 this.writeNode("Query", options, node);
14977 for (var i=0,len = this.featureType.length; i<len; i++) {
14978 options.featureType = this.featureType[i];
14979 this.writeNode("Query", options, node);
14984 "Transaction": function(obj) {
14986 var options = obj.options || {};
14987 var node = this.createElementNSPlus("wfs:Transaction", {
14990 version: this.version,
14991 handle: options.handle
14995 var features = obj.features;
14997 // temporarily re-assigning geometry types
14998 if (options.multi === true) {
14999 OpenLayers.Util.extend(this.geometryTypes, {
15000 "OpenLayers.Geometry.Point": "MultiPoint",
15001 "OpenLayers.Geometry.LineString": (this.multiCurve === true) ? "MultiCurve": "MultiLineString",
15002 "OpenLayers.Geometry.Polygon": (this.multiSurface === true) ? "MultiSurface" : "MultiPolygon"
15006 for(i=0, len=features.length; i<len; ++i) {
15007 feature = features[i];
15008 name = this.stateName[feature.state];
15010 this.writeNode(name, {
15016 // switch back to original geometry types assignment
15017 if (options.multi === true) {
15018 this.setGeometryTypes();
15021 if (options.nativeElements) {
15022 for (i=0, len=options.nativeElements.length; i<len; ++i) {
15023 this.writeNode("wfs:Native",
15024 options.nativeElements[i], node);
15029 "Native": function(nativeElement) {
15030 var node = this.createElementNSPlus("wfs:Native", {
15032 vendorId: nativeElement.vendorId,
15033 safeToIgnore: nativeElement.safeToIgnore
15035 value: nativeElement.value
15039 "Insert": function(obj) {
15040 var feature = obj.feature;
15041 var options = obj.options;
15042 var node = this.createElementNSPlus("wfs:Insert", {
15044 handle: options && options.handle
15047 this.srsName = this.getSrsName(feature);
15048 this.writeNode("feature:_typeName", feature, node);
15051 "Update": function(obj) {
15052 var feature = obj.feature;
15053 var options = obj.options;
15054 var node = this.createElementNSPlus("wfs:Update", {
15056 handle: options && options.handle,
15057 typeName: (this.featureNS ? this.featurePrefix + ":" : "") +
15061 if(this.featureNS) {
15062 this.setAttributeNS(
15063 node, this.namespaces.xmlns,
15064 "xmlns:" + this.featurePrefix, this.featureNS
15069 var modified = feature.modified;
15070 if (this.geometryName !== null && (!modified || modified.geometry !== undefined)) {
15071 this.srsName = this.getSrsName(feature);
15073 "Property", {name: this.geometryName, value: feature.geometry}, node
15077 // add in attributes
15078 for(var key in feature.attributes) {
15079 if(feature.attributes[key] !== undefined &&
15080 (!modified || !modified.attributes ||
15081 (modified.attributes && (key in modified.attributes)))) {
15083 "Property", {name: key, value: feature.attributes[key]}, node
15088 // add feature id filter
15089 this.writeNode("ogc:Filter", new OpenLayers.Filter.FeatureId({
15090 fids: [feature.fid]
15095 "Property": function(obj) {
15096 var node = this.createElementNSPlus("wfs:Property");
15097 this.writeNode("Name", obj.name, node);
15098 if(obj.value !== null) {
15099 this.writeNode("Value", obj.value, node);
15103 "Name": function(name) {
15104 return this.createElementNSPlus("wfs:Name", {value: name});
15106 "Value": function(obj) {
15108 if(obj instanceof OpenLayers.Geometry) {
15109 node = this.createElementNSPlus("wfs:Value");
15110 var geom = this.writeNode("feature:_geometry", obj).firstChild;
15111 node.appendChild(geom);
15113 node = this.createElementNSPlus("wfs:Value", {value: obj});
15117 "Delete": function(obj) {
15118 var feature = obj.feature;
15119 var options = obj.options;
15120 var node = this.createElementNSPlus("wfs:Delete", {
15122 handle: options && options.handle,
15123 typeName: (this.featureNS ? this.featurePrefix + ":" : "") +
15127 if(this.featureNS) {
15128 this.setAttributeNS(
15129 node, this.namespaces.xmlns,
15130 "xmlns:" + this.featurePrefix, this.featureNS
15133 this.writeNode("ogc:Filter", new OpenLayers.Filter.FeatureId({
15134 fids: [feature.fid]
15142 * Method: schemaLocationAttr
15143 * Generate the xsi:schemaLocation attribute value.
15146 * {String} The xsi:schemaLocation attribute or undefined if none.
15148 schemaLocationAttr: function(options) {
15149 options = OpenLayers.Util.extend({
15150 featurePrefix: this.featurePrefix,
15151 schema: this.schema
15153 var schemaLocations = OpenLayers.Util.extend({}, this.schemaLocations);
15154 if(options.schema) {
15155 schemaLocations[options.featurePrefix] = options.schema;
15159 for(var key in schemaLocations) {
15160 uri = this.namespaces[key];
15162 parts.push(uri + " " + schemaLocations[key]);
15165 var value = parts.join(" ") || undefined;
15170 * Method: setFilterProperty
15171 * Set the property of each spatial filter.
15174 * filter - {<OpenLayers.Filter>}
15176 setFilterProperty: function(filter) {
15177 if(filter.filters) {
15178 for(var i=0, len=filter.filters.length; i<len; ++i) {
15179 OpenLayers.Format.WFST.v1.prototype.setFilterProperty.call(this, filter.filters[i]);
15182 if(filter instanceof OpenLayers.Filter.Spatial && !filter.property) {
15183 // got a spatial filter without property, so set it
15184 filter.property = this.geometryName;
15189 CLASS_NAME: "OpenLayers.Format.WFST.v1"
15192 /* ======================================================================
15193 OpenLayers/Geometry.js
15194 ====================================================================== */
15196 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
15197 * full list of contributors). Published under the 2-clause BSD license.
15198 * See license.txt in the OpenLayers distribution or repository for the
15199 * full text of the license. */
15202 * @requires OpenLayers/BaseTypes/Class.js
15206 * Class: OpenLayers.Geometry
15207 * A Geometry is a description of a geographic object. Create an instance of
15208 * this class with the <OpenLayers.Geometry> constructor. This is a base class,
15209 * typical geometry types are described by subclasses of this class.
15211 * Note that if you use the <OpenLayers.Geometry.fromWKT> method, you must
15212 * explicitly include the OpenLayers.Format.WKT in your build.
15214 OpenLayers.Geometry = OpenLayers.Class({
15218 * {String} A unique identifier for this geometry.
15224 * {<OpenLayers.Geometry>}This is set when a Geometry is added as component
15225 * of another geometry
15231 * {<OpenLayers.Bounds>} The bounds of this geometry
15236 * Constructor: OpenLayers.Geometry
15237 * Creates a geometry object.
15239 initialize: function() {
15240 this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME+ "_");
15245 * Destroy this geometry.
15247 destroy: function() {
15249 this.bounds = null;
15254 * Create a clone of this geometry. Does not set any non-standard
15255 * properties of the cloned geometry.
15258 * {<OpenLayers.Geometry>} An exact clone of this geometry.
15260 clone: function() {
15261 return new OpenLayers.Geometry();
15265 * Method: setBounds
15266 * Set the bounds for this Geometry.
15269 * bounds - {<OpenLayers.Bounds>}
15271 setBounds: function(bounds) {
15273 this.bounds = bounds.clone();
15278 * Method: clearBounds
15279 * Nullify this components bounds and that of its parent as well.
15281 clearBounds: function() {
15282 this.bounds = null;
15284 this.parent.clearBounds();
15289 * Method: extendBounds
15290 * Extend the existing bounds to include the new bounds.
15291 * If geometry's bounds is not yet set, then set a new Bounds.
15294 * newBounds - {<OpenLayers.Bounds>}
15296 extendBounds: function(newBounds){
15297 var bounds = this.getBounds();
15299 this.setBounds(newBounds);
15301 this.bounds.extend(newBounds);
15306 * APIMethod: getBounds
15307 * Get the bounds for this Geometry. If bounds is not set, it
15308 * is calculated again, this makes queries faster.
15311 * {<OpenLayers.Bounds>}
15313 getBounds: function() {
15314 if (this.bounds == null) {
15315 this.calculateBounds();
15317 return this.bounds;
15321 * APIMethod: calculateBounds
15322 * Recalculate the bounds for the geometry.
15324 calculateBounds: function() {
15326 // This should be overridden by subclasses.
15331 * APIMethod: distanceTo
15332 * Calculate the closest distance between two geometries (on the x-y plane).
15335 * geometry - {<OpenLayers.Geometry>} The target geometry.
15336 * options - {Object} Optional properties for configuring the distance
15339 * Valid options depend on the specific geometry type.
15342 * {Number | Object} The distance between this geometry and the target.
15343 * If details is true, the return will be an object with distance,
15344 * x0, y0, x1, and x2 properties. The x0 and y0 properties represent
15345 * the coordinates of the closest point on this geometry. The x1 and y1
15346 * properties represent the coordinates of the closest point on the
15349 distanceTo: function(geometry, options) {
15353 * APIMethod: getVertices
15354 * Return a list of all points in this geometry.
15357 * nodes - {Boolean} For lines, only return vertices that are
15358 * endpoints. If false, for lines, only vertices that are not
15359 * endpoints will be returned. If not provided, all vertices will
15363 * {Array} A list of all vertices in the geometry.
15365 getVertices: function(nodes) {
15370 * Note - This is only an approximation based on the bounds of the
15374 * lonlat - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an
15375 * object with a 'lon' and 'lat' properties.
15376 * toleranceLon - {float} Optional tolerance in Geometric Coords
15377 * toleranceLat - {float} Optional tolerance in Geographic Coords
15380 * {Boolean} Whether or not the geometry is at the specified location
15382 atPoint: function(lonlat, toleranceLon, toleranceLat) {
15383 var atPoint = false;
15384 var bounds = this.getBounds();
15385 if ((bounds != null) && (lonlat != null)) {
15387 var dX = (toleranceLon != null) ? toleranceLon : 0;
15388 var dY = (toleranceLat != null) ? toleranceLat : 0;
15390 var toleranceBounds =
15391 new OpenLayers.Bounds(this.bounds.left - dX,
15392 this.bounds.bottom - dY,
15393 this.bounds.right + dX,
15394 this.bounds.top + dY);
15396 atPoint = toleranceBounds.containsLonLat(lonlat);
15402 * Method: getLength
15403 * Calculate the length of this geometry. This method is defined in
15407 * {Float} The length of the collection by summing its parts
15409 getLength: function() {
15410 //to be overridden by geometries that actually have a length
15417 * Calculate the area of this geometry. This method is defined in subclasses.
15420 * {Float} The area of the collection by summing its parts
15422 getArea: function() {
15423 //to be overridden by geometries that actually have an area
15429 * APIMethod: getCentroid
15430 * Calculate the centroid of this geometry. This method is defined in subclasses.
15433 * {<OpenLayers.Geometry.Point>} The centroid of the collection
15435 getCentroid: function() {
15441 * Returns a text representation of the geometry. If the WKT format is
15442 * included in a build, this will be the Well-Known Text
15446 * {String} String representation of this geometry.
15448 toString: function() {
15450 if (OpenLayers.Format && OpenLayers.Format.WKT) {
15451 string = OpenLayers.Format.WKT.prototype.write(
15452 new OpenLayers.Feature.Vector(this)
15455 string = Object.prototype.toString.call(this);
15460 CLASS_NAME: "OpenLayers.Geometry"
15464 * Function: OpenLayers.Geometry.fromWKT
15465 * Generate a geometry given a Well-Known Text string. For this method to
15466 * work, you must include the OpenLayers.Format.WKT in your build
15470 * wkt - {String} A string representing the geometry in Well-Known Text.
15473 * {<OpenLayers.Geometry>} A geometry of the appropriate class.
15475 OpenLayers.Geometry.fromWKT = function(wkt) {
15477 if (OpenLayers.Format && OpenLayers.Format.WKT) {
15478 var format = OpenLayers.Geometry.fromWKT.format;
15480 format = new OpenLayers.Format.WKT();
15481 OpenLayers.Geometry.fromWKT.format = format;
15483 var result = format.read(wkt);
15484 if (result instanceof OpenLayers.Feature.Vector) {
15485 geom = result.geometry;
15486 } else if (OpenLayers.Util.isArray(result)) {
15487 var len = result.length;
15488 var components = new Array(len);
15489 for (var i=0; i<len; ++i) {
15490 components[i] = result[i].geometry;
15492 geom = new OpenLayers.Geometry.Collection(components);
15499 * Method: OpenLayers.Geometry.segmentsIntersect
15500 * Determine whether two line segments intersect. Optionally calculates
15501 * and returns the intersection point. This function is optimized for
15502 * cases where seg1.x2 >= seg2.x1 || seg2.x2 >= seg1.x1. In those
15503 * obvious cases where there is no intersection, the function should
15507 * seg1 - {Object} Object representing a segment with properties x1, y1, x2,
15508 * and y2. The start point is represented by x1 and y1. The end point
15509 * is represented by x2 and y2. Start and end are ordered so that x1 < x2.
15510 * seg2 - {Object} Object representing a segment with properties x1, y1, x2,
15511 * and y2. The start point is represented by x1 and y1. The end point
15512 * is represented by x2 and y2. Start and end are ordered so that x1 < x2.
15513 * options - {Object} Optional properties for calculating the intersection.
15516 * point - {Boolean} Return the intersection point. If false, the actual
15517 * intersection point will not be calculated. If true and the segments
15518 * intersect, the intersection point will be returned. If true and
15519 * the segments do not intersect, false will be returned. If true and
15520 * the segments are coincident, true will be returned.
15521 * tolerance - {Number} If a non-null value is provided, if the segments are
15522 * within the tolerance distance, this will be considered an intersection.
15523 * In addition, if the point option is true and the calculated intersection
15524 * is within the tolerance distance of an end point, the endpoint will be
15525 * returned instead of the calculated intersection. Further, if the
15526 * intersection is within the tolerance of endpoints on both segments, or
15527 * if two segment endpoints are within the tolerance distance of eachother
15528 * (but no intersection is otherwise calculated), an endpoint on the
15529 * first segment provided will be returned.
15532 * {Boolean | <OpenLayers.Geometry.Point>} The two segments intersect.
15533 * If the point argument is true, the return will be the intersection
15534 * point or false if none exists. If point is true and the segments
15535 * are coincident, return will be true (and the instersection is equal
15536 * to the shorter segment).
15538 OpenLayers.Geometry.segmentsIntersect = function(seg1, seg2, options) {
15539 var point = options && options.point;
15540 var tolerance = options && options.tolerance;
15541 var intersection = false;
15542 var x11_21 = seg1.x1 - seg2.x1;
15543 var y11_21 = seg1.y1 - seg2.y1;
15544 var x12_11 = seg1.x2 - seg1.x1;
15545 var y12_11 = seg1.y2 - seg1.y1;
15546 var y22_21 = seg2.y2 - seg2.y1;
15547 var x22_21 = seg2.x2 - seg2.x1;
15548 var d = (y22_21 * x12_11) - (x22_21 * y12_11);
15549 var n1 = (x22_21 * y11_21) - (y22_21 * x11_21);
15550 var n2 = (x12_11 * y11_21) - (y12_11 * x11_21);
15553 if(n1 == 0 && n2 == 0) {
15555 intersection = true;
15558 var along1 = n1 / d;
15559 var along2 = n2 / d;
15560 if(along1 >= 0 && along1 <= 1 && along2 >=0 && along2 <= 1) {
15563 intersection = true;
15565 // calculate the intersection point
15566 var x = seg1.x1 + (along1 * x12_11);
15567 var y = seg1.y1 + (along1 * y12_11);
15568 intersection = new OpenLayers.Geometry.Point(x, y);
15576 var segs = [seg1, seg2];
15578 // check segment endpoints for proximity to intersection
15579 // set intersection to first endpoint within the tolerance
15580 outer: for(var i=0; i<2; ++i) {
15582 for(var j=1; j<3; ++j) {
15586 Math.pow(x - intersection.x, 2) +
15587 Math.pow(y - intersection.y, 2)
15589 if(dist < tolerance) {
15590 intersection.x = x;
15591 intersection.y = y;
15599 // no calculated intersection, but segments could be within
15600 // the tolerance of one another
15601 var segs = [seg1, seg2];
15602 var source, target, x, y, p, result;
15603 // check segment endpoints for proximity to intersection
15604 // set intersection to first endpoint within the tolerance
15605 outer: for(var i=0; i<2; ++i) {
15607 target = segs[(i+1)%2];
15608 for(var j=1; j<3; ++j) {
15609 p = {x: source["x"+j], y: source["y"+j]};
15610 result = OpenLayers.Geometry.distanceToSegment(p, target);
15611 if(result.distance < tolerance) {
15613 intersection = new OpenLayers.Geometry.Point(p.x, p.y);
15615 intersection = true;
15623 return intersection;
15627 * Function: OpenLayers.Geometry.distanceToSegment
15630 * point - {Object} An object with x and y properties representing the
15631 * point coordinates.
15632 * segment - {Object} An object with x1, y1, x2, and y2 properties
15633 * representing endpoint coordinates.
15636 * {Object} An object with distance, along, x, and y properties. The distance
15637 * will be the shortest distance between the input point and segment.
15638 * The x and y properties represent the coordinates along the segment
15639 * where the shortest distance meets the segment. The along attribute
15640 * describes how far between the two segment points the given point is.
15642 OpenLayers.Geometry.distanceToSegment = function(point, segment) {
15643 var result = OpenLayers.Geometry.distanceSquaredToSegment(point, segment);
15644 result.distance = Math.sqrt(result.distance);
15649 * Function: OpenLayers.Geometry.distanceSquaredToSegment
15651 * Usually the distanceToSegment function should be used. This variant however
15652 * can be used for comparisons where the exact distance is not important.
15655 * point - {Object} An object with x and y properties representing the
15656 * point coordinates.
15657 * segment - {Object} An object with x1, y1, x2, and y2 properties
15658 * representing endpoint coordinates.
15661 * {Object} An object with squared distance, along, x, and y properties.
15662 * The distance will be the shortest distance between the input point and
15663 * segment. The x and y properties represent the coordinates along the
15664 * segment where the shortest distance meets the segment. The along
15665 * attribute describes how far between the two segment points the given
15668 OpenLayers.Geometry.distanceSquaredToSegment = function(point, segment) {
15671 var x1 = segment.x1;
15672 var y1 = segment.y1;
15673 var x2 = segment.x2;
15674 var y2 = segment.y2;
15677 var along = (dx == 0 && dy == 0) ? 0 : ((dx * (x0 - x1)) + (dy * (y0 - y1))) /
15678 (Math.pow(dx, 2) + Math.pow(dy, 2));
15683 } else if(along >= 1.0) {
15687 x = x1 + along * dx;
15688 y = y1 + along * dy;
15691 distance: Math.pow(x - x0, 2) + Math.pow(y - y0, 2),
15696 /* ======================================================================
15697 OpenLayers/Geometry/Point.js
15698 ====================================================================== */
15700 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
15701 * full list of contributors). Published under the 2-clause BSD license.
15702 * See license.txt in the OpenLayers distribution or repository for the
15703 * full text of the license. */
15706 * @requires OpenLayers/Geometry.js
15710 * Class: OpenLayers.Geometry.Point
15711 * Point geometry class.
15714 * - <OpenLayers.Geometry>
15716 OpenLayers.Geometry.Point = OpenLayers.Class(OpenLayers.Geometry, {
15731 * Constructor: OpenLayers.Geometry.Point
15732 * Construct a point geometry.
15739 initialize: function(x, y) {
15740 OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
15742 this.x = parseFloat(x);
15743 this.y = parseFloat(y);
15750 * {<OpenLayers.Geometry.Point>} An exact clone of this OpenLayers.Geometry.Point
15752 clone: function(obj) {
15754 obj = new OpenLayers.Geometry.Point(this.x, this.y);
15757 // catch any randomly tagged-on properties
15758 OpenLayers.Util.applyDefaults(obj, this);
15764 * Method: calculateBounds
15765 * Create a new Bounds based on the lon/lat
15767 calculateBounds: function () {
15768 this.bounds = new OpenLayers.Bounds(this.x, this.y,
15773 * APIMethod: distanceTo
15774 * Calculate the closest distance between two geometries (on the x-y plane).
15777 * geometry - {<OpenLayers.Geometry>} The target geometry.
15778 * options - {Object} Optional properties for configuring the distance
15782 * details - {Boolean} Return details from the distance calculation.
15783 * Default is false.
15784 * edge - {Boolean} Calculate the distance from this geometry to the
15785 * nearest edge of the target geometry. Default is true. If true,
15786 * calling distanceTo from a geometry that is wholly contained within
15787 * the target will result in a non-zero distance. If false, whenever
15788 * geometries intersect, calling distanceTo will return 0. If false,
15789 * details cannot be returned.
15792 * {Number | Object} The distance between this geometry and the target.
15793 * If details is true, the return will be an object with distance,
15794 * x0, y0, x1, and x2 properties. The x0 and y0 properties represent
15795 * the coordinates of the closest point on this geometry. The x1 and y1
15796 * properties represent the coordinates of the closest point on the
15799 distanceTo: function(geometry, options) {
15800 var edge = !(options && options.edge === false);
15801 var details = edge && options && options.details;
15802 var distance, x0, y0, x1, y1, result;
15803 if(geometry instanceof OpenLayers.Geometry.Point) {
15808 distance = Math.sqrt(Math.pow(x0 - x1, 2) + Math.pow(y0 - y1, 2));
15809 result = !details ?
15810 distance : {x0: x0, y0: y0, x1: x1, y1: y1, distance: distance};
15812 result = geometry.distanceTo(this, options);
15814 // switch coord order since this geom is target
15816 x0: result.x1, y0: result.y1,
15817 x1: result.x0, y1: result.y0,
15818 distance: result.distance
15826 * APIMethod: equals
15827 * Determine whether another geometry is equivalent to this one. Geometries
15828 * are considered equivalent if all components have the same coordinates.
15831 * geom - {<OpenLayers.Geometry.Point>} The geometry to test.
15834 * {Boolean} The supplied geometry is equivalent to this geometry.
15836 equals: function(geom) {
15837 var equals = false;
15838 if (geom != null) {
15839 equals = ((this.x == geom.x && this.y == geom.y) ||
15840 (isNaN(this.x) && isNaN(this.y) && isNaN(geom.x) && isNaN(geom.y)));
15846 * Method: toShortString
15849 * {String} Shortened String representation of Point object.
15850 * (ex. <i>"5, 42"</i>)
15852 toShortString: function() {
15853 return (this.x + ", " + this.y);
15858 * Moves a geometry by the given displacement along positive x and y axes.
15859 * This modifies the position of the geometry and clears the cached
15863 * x - {Float} Distance to move geometry in positive x direction.
15864 * y - {Float} Distance to move geometry in positive y direction.
15866 move: function(x, y) {
15867 this.x = this.x + x;
15868 this.y = this.y + y;
15869 this.clearBounds();
15873 * APIMethod: rotate
15874 * Rotate a point around another.
15877 * angle - {Float} Rotation angle in degrees (measured counterclockwise
15878 * from the positive x-axis)
15879 * origin - {<OpenLayers.Geometry.Point>} Center point for the rotation
15881 rotate: function(angle, origin) {
15882 angle *= Math.PI / 180;
15883 var radius = this.distanceTo(origin);
15884 var theta = angle + Math.atan2(this.y - origin.y, this.x - origin.x);
15885 this.x = origin.x + (radius * Math.cos(theta));
15886 this.y = origin.y + (radius * Math.sin(theta));
15887 this.clearBounds();
15891 * APIMethod: getCentroid
15894 * {<OpenLayers.Geometry.Point>} The centroid of the collection
15896 getCentroid: function() {
15897 return new OpenLayers.Geometry.Point(this.x, this.y);
15901 * APIMethod: resize
15902 * Resize a point relative to some origin. For points, this has the effect
15903 * of scaling a vector (from the origin to the point). This method is
15904 * more useful on geometry collection subclasses.
15907 * scale - {Float} Ratio of the new distance from the origin to the old
15908 * distance from the origin. A scale of 2 doubles the
15909 * distance between the point and origin.
15910 * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing
15911 * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1.
15914 * {<OpenLayers.Geometry>} - The current geometry.
15916 resize: function(scale, origin, ratio) {
15917 ratio = (ratio == undefined) ? 1 : ratio;
15918 this.x = origin.x + (scale * ratio * (this.x - origin.x));
15919 this.y = origin.y + (scale * (this.y - origin.y));
15920 this.clearBounds();
15925 * APIMethod: intersects
15926 * Determine if the input geometry intersects this one.
15929 * geometry - {<OpenLayers.Geometry>} Any type of geometry.
15932 * {Boolean} The input geometry intersects this one.
15934 intersects: function(geometry) {
15935 var intersect = false;
15936 if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
15937 intersect = this.equals(geometry);
15939 intersect = geometry.intersects(this);
15945 * APIMethod: transform
15946 * Translate the x,y properties of the point from source to dest.
15949 * source - {<OpenLayers.Projection>}
15950 * dest - {<OpenLayers.Projection>}
15953 * {<OpenLayers.Geometry>}
15955 transform: function(source, dest) {
15956 if ((source && dest)) {
15957 OpenLayers.Projection.transform(
15958 this, source, dest);
15959 this.bounds = null;
15965 * APIMethod: getVertices
15966 * Return a list of all points in this geometry.
15969 * nodes - {Boolean} For lines, only return vertices that are
15970 * endpoints. If false, for lines, only vertices that are not
15971 * endpoints will be returned. If not provided, all vertices will
15975 * {Array} A list of all vertices in the geometry.
15977 getVertices: function(nodes) {
15981 CLASS_NAME: "OpenLayers.Geometry.Point"
15983 /* ======================================================================
15984 OpenLayers/Geometry/Collection.js
15985 ====================================================================== */
15987 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
15988 * full list of contributors). Published under the 2-clause BSD license.
15989 * See license.txt in the OpenLayers distribution or repository for the
15990 * full text of the license. */
15993 * @requires OpenLayers/Geometry.js
15997 * Class: OpenLayers.Geometry.Collection
15998 * A Collection is exactly what it sounds like: A collection of different
15999 * Geometries. These are stored in the local parameter <components> (which
16000 * can be passed as a parameter to the constructor).
16002 * As new geometries are added to the collection, they are NOT cloned.
16003 * When removing geometries, they need to be specified by reference (ie you
16004 * have to pass in the *exact* geometry to be removed).
16006 * The <getArea> and <getLength> functions here merely iterate through
16007 * the components, summing their respective areas and lengths.
16009 * Create a new instance with the <OpenLayers.Geometry.Collection> constructor.
16012 * - <OpenLayers.Geometry>
16014 OpenLayers.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, {
16017 * APIProperty: components
16018 * {Array(<OpenLayers.Geometry>)} The component parts of this geometry
16023 * Property: componentTypes
16024 * {Array(String)} An array of class names representing the types of
16025 * components that the collection can include. A null value means the
16026 * component types are not restricted.
16028 componentTypes: null,
16031 * Constructor: OpenLayers.Geometry.Collection
16032 * Creates a Geometry Collection -- a list of geoms.
16035 * components - {Array(<OpenLayers.Geometry>)} Optional array of geometries
16038 initialize: function (components) {
16039 OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
16040 this.components = [];
16041 if (components != null) {
16042 this.addComponents(components);
16047 * APIMethod: destroy
16048 * Destroy this geometry.
16050 destroy: function () {
16051 this.components.length = 0;
16052 this.components = null;
16053 OpenLayers.Geometry.prototype.destroy.apply(this, arguments);
16058 * Clone this geometry.
16061 * {<OpenLayers.Geometry.Collection>} An exact clone of this collection
16063 clone: function() {
16064 var Constructor = OpenLayers.Util.getConstructor(this.CLASS_NAME);
16065 var geometry = new Constructor();
16066 for(var i=0, len=this.components.length; i<len; i++) {
16067 geometry.addComponent(this.components[i].clone());
16070 // catch any randomly tagged-on properties
16071 OpenLayers.Util.applyDefaults(geometry, this);
16077 * Method: getComponentsString
16078 * Get a string representing the components for this collection
16081 * {String} A string representation of the components of this geometry
16083 getComponentsString: function(){
16085 for(var i=0, len=this.components.length; i<len; i++) {
16086 strings.push(this.components[i].toShortString());
16088 return strings.join(",");
16092 * APIMethod: calculateBounds
16093 * Recalculate the bounds by iterating through the components and
16094 * calling calling extendBounds() on each item.
16096 calculateBounds: function() {
16097 this.bounds = null;
16098 var bounds = new OpenLayers.Bounds();
16099 var components = this.components;
16101 for (var i=0, len=components.length; i<len; i++) {
16102 bounds.extend(components[i].getBounds());
16105 // to preserve old behavior, we only set bounds if non-null
16106 // in the future, we could add bounds.isEmpty()
16107 if (bounds.left != null && bounds.bottom != null &&
16108 bounds.right != null && bounds.top != null) {
16109 this.setBounds(bounds);
16114 * APIMethod: addComponents
16115 * Add components to this geometry.
16118 * components - {Array(<OpenLayers.Geometry>)} An array of geometries to add
16120 addComponents: function(components){
16121 if(!(OpenLayers.Util.isArray(components))) {
16122 components = [components];
16124 for(var i=0, len=components.length; i<len; i++) {
16125 this.addComponent(components[i]);
16130 * Method: addComponent
16131 * Add a new component (geometry) to the collection. If this.componentTypes
16132 * is set, then the component class name must be in the componentTypes array.
16134 * The bounds cache is reset.
16137 * component - {<OpenLayers.Geometry>} A geometry to add
16138 * index - {int} Optional index into the array to insert the component
16141 * {Boolean} The component geometry was successfully added
16143 addComponent: function(component, index) {
16146 if(this.componentTypes == null ||
16147 (OpenLayers.Util.indexOf(this.componentTypes,
16148 component.CLASS_NAME) > -1)) {
16150 if(index != null && (index < this.components.length)) {
16151 var components1 = this.components.slice(0, index);
16152 var components2 = this.components.slice(index,
16153 this.components.length);
16154 components1.push(component);
16155 this.components = components1.concat(components2);
16157 this.components.push(component);
16159 component.parent = this;
16160 this.clearBounds();
16168 * APIMethod: removeComponents
16169 * Remove components from this geometry.
16172 * components - {Array(<OpenLayers.Geometry>)} The components to be removed
16175 * {Boolean} A component was removed.
16177 removeComponents: function(components) {
16178 var removed = false;
16180 if(!(OpenLayers.Util.isArray(components))) {
16181 components = [components];
16183 for(var i=components.length-1; i>=0; --i) {
16184 removed = this.removeComponent(components[i]) || removed;
16190 * Method: removeComponent
16191 * Remove a component from this geometry.
16194 * component - {<OpenLayers.Geometry>}
16197 * {Boolean} The component was removed.
16199 removeComponent: function(component) {
16201 OpenLayers.Util.removeItem(this.components, component);
16203 // clearBounds() so that it gets recalculated on the next call
16204 // to this.getBounds();
16205 this.clearBounds();
16210 * APIMethod: getLength
16211 * Calculate the length of this geometry
16214 * {Float} The length of the geometry
16216 getLength: function() {
16218 for (var i=0, len=this.components.length; i<len; i++) {
16219 length += this.components[i].getLength();
16225 * APIMethod: getArea
16226 * Calculate the area of this geometry. Note how this function is overridden
16227 * in <OpenLayers.Geometry.Polygon>.
16230 * {Float} The area of the collection by summing its parts
16232 getArea: function() {
16234 for (var i=0, len=this.components.length; i<len; i++) {
16235 area += this.components[i].getArea();
16241 * APIMethod: getGeodesicArea
16242 * Calculate the approximate area of the polygon were it projected onto
16246 * projection - {<OpenLayers.Projection>} The spatial reference system
16247 * for the geometry coordinates. If not provided, Geographic/WGS84 is
16251 * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
16252 * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
16253 * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
16256 * {float} The approximate geodesic area of the geometry in square meters.
16258 getGeodesicArea: function(projection) {
16260 for(var i=0, len=this.components.length; i<len; i++) {
16261 area += this.components[i].getGeodesicArea(projection);
16267 * APIMethod: getCentroid
16269 * Compute the centroid for this geometry collection.
16272 * weighted - {Boolean} Perform the getCentroid computation recursively,
16273 * returning an area weighted average of all geometries in this collection.
16276 * {<OpenLayers.Geometry.Point>} The centroid of the collection
16278 getCentroid: function(weighted) {
16280 return this.components.length && this.components[0].getCentroid();
16282 var len = this.components.length;
16288 var centroids = [];
16290 var minArea = Number.MAX_VALUE;
16292 for (var i=0; i<len; ++i) {
16293 component = this.components[i];
16294 var area = component.getArea();
16295 var centroid = component.getCentroid(true);
16296 if (isNaN(area) || isNaN(centroid.x) || isNaN(centroid.y)) {
16301 minArea = (area < minArea && area > 0) ? area : minArea;
16302 centroids.push(centroid);
16304 len = areas.length;
16305 if (areaSum === 0) {
16306 // all the components in this collection have 0 area
16307 // probably a collection of points -- weight all the points the same
16308 for (var i=0; i<len; ++i) {
16311 areaSum = areas.length;
16313 // normalize all the areas where the smallest area will get
16315 for (var i=0; i<len; ++i) {
16316 areas[i] /= minArea;
16318 areaSum /= minArea;
16321 var xSum = 0, ySum = 0, centroid, area;
16322 for (var i=0; i<len; ++i) {
16323 centroid = centroids[i];
16325 xSum += centroid.x * area;
16326 ySum += centroid.y * area;
16329 return new OpenLayers.Geometry.Point(xSum/areaSum, ySum/areaSum);
16333 * APIMethod: getGeodesicLength
16334 * Calculate the approximate length of the geometry were it projected onto
16337 * projection - {<OpenLayers.Projection>} The spatial reference system
16338 * for the geometry coordinates. If not provided, Geographic/WGS84 is
16342 * {Float} The appoximate geodesic length of the geometry in meters.
16344 getGeodesicLength: function(projection) {
16346 for(var i=0, len=this.components.length; i<len; i++) {
16347 length += this.components[i].getGeodesicLength(projection);
16354 * Moves a geometry by the given displacement along positive x and y axes.
16355 * This modifies the position of the geometry and clears the cached
16359 * x - {Float} Distance to move geometry in positive x direction.
16360 * y - {Float} Distance to move geometry in positive y direction.
16362 move: function(x, y) {
16363 for(var i=0, len=this.components.length; i<len; i++) {
16364 this.components[i].move(x, y);
16369 * APIMethod: rotate
16370 * Rotate a geometry around some origin
16373 * angle - {Float} Rotation angle in degrees (measured counterclockwise
16374 * from the positive x-axis)
16375 * origin - {<OpenLayers.Geometry.Point>} Center point for the rotation
16377 rotate: function(angle, origin) {
16378 for(var i=0, len=this.components.length; i<len; ++i) {
16379 this.components[i].rotate(angle, origin);
16384 * APIMethod: resize
16385 * Resize a geometry relative to some origin. Use this method to apply
16386 * a uniform scaling to a geometry.
16389 * scale - {Float} Factor by which to scale the geometry. A scale of 2
16390 * doubles the size of the geometry in each dimension
16391 * (lines, for example, will be twice as long, and polygons
16392 * will have four times the area).
16393 * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing
16394 * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1.
16397 * {<OpenLayers.Geometry>} - The current geometry.
16399 resize: function(scale, origin, ratio) {
16400 for(var i=0; i<this.components.length; ++i) {
16401 this.components[i].resize(scale, origin, ratio);
16407 * APIMethod: distanceTo
16408 * Calculate the closest distance between two geometries (on the x-y plane).
16411 * geometry - {<OpenLayers.Geometry>} The target geometry.
16412 * options - {Object} Optional properties for configuring the distance
16416 * details - {Boolean} Return details from the distance calculation.
16417 * Default is false.
16418 * edge - {Boolean} Calculate the distance from this geometry to the
16419 * nearest edge of the target geometry. Default is true. If true,
16420 * calling distanceTo from a geometry that is wholly contained within
16421 * the target will result in a non-zero distance. If false, whenever
16422 * geometries intersect, calling distanceTo will return 0. If false,
16423 * details cannot be returned.
16426 * {Number | Object} The distance between this geometry and the target.
16427 * If details is true, the return will be an object with distance,
16428 * x0, y0, x1, and y1 properties. The x0 and y0 properties represent
16429 * the coordinates of the closest point on this geometry. The x1 and y1
16430 * properties represent the coordinates of the closest point on the
16433 distanceTo: function(geometry, options) {
16434 var edge = !(options && options.edge === false);
16435 var details = edge && options && options.details;
16436 var result, best, distance;
16437 var min = Number.POSITIVE_INFINITY;
16438 for(var i=0, len=this.components.length; i<len; ++i) {
16439 result = this.components[i].distanceTo(geometry, options);
16440 distance = details ? result.distance : result;
16441 if(distance < min) {
16453 * APIMethod: equals
16454 * Determine whether another geometry is equivalent to this one. Geometries
16455 * are considered equivalent if all components have the same coordinates.
16458 * geometry - {<OpenLayers.Geometry>} The geometry to test.
16461 * {Boolean} The supplied geometry is equivalent to this geometry.
16463 equals: function(geometry) {
16464 var equivalent = true;
16465 if(!geometry || !geometry.CLASS_NAME ||
16466 (this.CLASS_NAME != geometry.CLASS_NAME)) {
16467 equivalent = false;
16468 } else if(!(OpenLayers.Util.isArray(geometry.components)) ||
16469 (geometry.components.length != this.components.length)) {
16470 equivalent = false;
16472 for(var i=0, len=this.components.length; i<len; ++i) {
16473 if(!this.components[i].equals(geometry.components[i])) {
16474 equivalent = false;
16483 * APIMethod: transform
16484 * Reproject the components geometry from source to dest.
16487 * source - {<OpenLayers.Projection>}
16488 * dest - {<OpenLayers.Projection>}
16491 * {<OpenLayers.Geometry>}
16493 transform: function(source, dest) {
16494 if (source && dest) {
16495 for (var i=0, len=this.components.length; i<len; i++) {
16496 var component = this.components[i];
16497 component.transform(source, dest);
16499 this.bounds = null;
16505 * APIMethod: intersects
16506 * Determine if the input geometry intersects this one.
16509 * geometry - {<OpenLayers.Geometry>} Any type of geometry.
16512 * {Boolean} The input geometry intersects this one.
16514 intersects: function(geometry) {
16515 var intersect = false;
16516 for(var i=0, len=this.components.length; i<len; ++ i) {
16517 intersect = geometry.intersects(this.components[i]);
16526 * APIMethod: getVertices
16527 * Return a list of all points in this geometry.
16530 * nodes - {Boolean} For lines, only return vertices that are
16531 * endpoints. If false, for lines, only vertices that are not
16532 * endpoints will be returned. If not provided, all vertices will
16536 * {Array} A list of all vertices in the geometry.
16538 getVertices: function(nodes) {
16540 for(var i=0, len=this.components.length; i<len; ++i) {
16541 Array.prototype.push.apply(
16542 vertices, this.components[i].getVertices(nodes)
16549 CLASS_NAME: "OpenLayers.Geometry.Collection"
16551 /* ======================================================================
16552 OpenLayers/Geometry/MultiPoint.js
16553 ====================================================================== */
16555 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
16556 * full list of contributors). Published under the 2-clause BSD license.
16557 * See license.txt in the OpenLayers distribution or repository for the
16558 * full text of the license. */
16561 * @requires OpenLayers/Geometry/Collection.js
16562 * @requires OpenLayers/Geometry/Point.js
16566 * Class: OpenLayers.Geometry.MultiPoint
16567 * MultiPoint is a collection of Points. Create a new instance with the
16568 * <OpenLayers.Geometry.MultiPoint> constructor.
16571 * - <OpenLayers.Geometry.Collection>
16572 * - <OpenLayers.Geometry>
16574 OpenLayers.Geometry.MultiPoint = OpenLayers.Class(
16575 OpenLayers.Geometry.Collection, {
16578 * Property: componentTypes
16579 * {Array(String)} An array of class names representing the types of
16580 * components that the collection can include. A null value means the
16581 * component types are not restricted.
16583 componentTypes: ["OpenLayers.Geometry.Point"],
16586 * Constructor: OpenLayers.Geometry.MultiPoint
16587 * Create a new MultiPoint Geometry
16590 * components - {Array(<OpenLayers.Geometry.Point>)}
16593 * {<OpenLayers.Geometry.MultiPoint>}
16597 * APIMethod: addPoint
16598 * Wrapper for <OpenLayers.Geometry.Collection.addComponent>
16601 * point - {<OpenLayers.Geometry.Point>} Point to be added
16602 * index - {Integer} Optional index
16604 addPoint: function(point, index) {
16605 this.addComponent(point, index);
16609 * APIMethod: removePoint
16610 * Wrapper for <OpenLayers.Geometry.Collection.removeComponent>
16613 * point - {<OpenLayers.Geometry.Point>} Point to be removed
16615 removePoint: function(point){
16616 this.removeComponent(point);
16619 CLASS_NAME: "OpenLayers.Geometry.MultiPoint"
16621 /* ======================================================================
16622 OpenLayers/Geometry/Curve.js
16623 ====================================================================== */
16625 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
16626 * full list of contributors). Published under the 2-clause BSD license.
16627 * See license.txt in the OpenLayers distribution or repository for the
16628 * full text of the license. */
16631 * @requires OpenLayers/Geometry/MultiPoint.js
16635 * Class: OpenLayers.Geometry.Curve
16636 * A Curve is a MultiPoint, whose points are assumed to be connected. To
16637 * this end, we provide a "getLength()" function, which iterates through
16638 * the points, summing the distances between them.
16641 * - <OpenLayers.Geometry.MultiPoint>
16643 OpenLayers.Geometry.Curve = OpenLayers.Class(OpenLayers.Geometry.MultiPoint, {
16646 * Property: componentTypes
16647 * {Array(String)} An array of class names representing the types of
16648 * components that the collection can include. A null
16649 * value means the component types are not restricted.
16651 componentTypes: ["OpenLayers.Geometry.Point"],
16654 * Constructor: OpenLayers.Geometry.Curve
16657 * point - {Array(<OpenLayers.Geometry.Point>)}
16661 * APIMethod: getLength
16664 * {Float} The length of the curve
16666 getLength: function() {
16668 if ( this.components && (this.components.length > 1)) {
16669 for(var i=1, len=this.components.length; i<len; i++) {
16670 length += this.components[i-1].distanceTo(this.components[i]);
16677 * APIMethod: getGeodesicLength
16678 * Calculate the approximate length of the geometry were it projected onto
16681 * projection - {<OpenLayers.Projection>} The spatial reference system
16682 * for the geometry coordinates. If not provided, Geographic/WGS84 is
16686 * {Float} The appoximate geodesic length of the geometry in meters.
16688 getGeodesicLength: function(projection) {
16689 var geom = this; // so we can work with a clone if needed
16691 var gg = new OpenLayers.Projection("EPSG:4326");
16692 if(!gg.equals(projection)) {
16693 geom = this.clone().transform(projection, gg);
16697 if(geom.components && (geom.components.length > 1)) {
16699 for(var i=1, len=geom.components.length; i<len; i++) {
16700 p1 = geom.components[i-1];
16701 p2 = geom.components[i];
16702 // this returns km and requires lon/lat properties
16703 length += OpenLayers.Util.distVincenty(
16704 {lon: p1.x, lat: p1.y}, {lon: p2.x, lat: p2.y}
16709 return length * 1000;
16712 CLASS_NAME: "OpenLayers.Geometry.Curve"
16714 /* ======================================================================
16715 OpenLayers/Geometry/LineString.js
16716 ====================================================================== */
16718 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
16719 * full list of contributors). Published under the 2-clause BSD license.
16720 * See license.txt in the OpenLayers distribution or repository for the
16721 * full text of the license. */
16724 * @requires OpenLayers/Geometry/Curve.js
16728 * Class: OpenLayers.Geometry.LineString
16729 * A LineString is a Curve which, once two points have been added to it, can
16730 * never be less than two points long.
16733 * - <OpenLayers.Geometry.Curve>
16735 OpenLayers.Geometry.LineString = OpenLayers.Class(OpenLayers.Geometry.Curve, {
16738 * Constructor: OpenLayers.Geometry.LineString
16739 * Create a new LineString geometry
16742 * points - {Array(<OpenLayers.Geometry.Point>)} An array of points used to
16743 * generate the linestring
16748 * APIMethod: removeComponent
16749 * Only allows removal of a point if there are three or more points in
16750 * the linestring. (otherwise the result would be just a single point)
16753 * point - {<OpenLayers.Geometry.Point>} The point to be removed
16756 * {Boolean} The component was removed.
16758 removeComponent: function(point) {
16759 var removed = this.components && (this.components.length > 2);
16761 OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this,
16768 * APIMethod: intersects
16769 * Test for instersection between two geometries. This is a cheapo
16770 * implementation of the Bently-Ottmann algorigithm. It doesn't
16771 * really keep track of a sweep line data structure. It is closer
16772 * to the brute force method, except that segments are sorted and
16773 * potential intersections are only calculated when bounding boxes
16777 * geometry - {<OpenLayers.Geometry>}
16780 * {Boolean} The input geometry intersects this geometry.
16782 intersects: function(geometry) {
16783 var intersect = false;
16784 var type = geometry.CLASS_NAME;
16785 if(type == "OpenLayers.Geometry.LineString" ||
16786 type == "OpenLayers.Geometry.LinearRing" ||
16787 type == "OpenLayers.Geometry.Point") {
16788 var segs1 = this.getSortedSegments();
16790 if(type == "OpenLayers.Geometry.Point") {
16792 x1: geometry.x, y1: geometry.y,
16793 x2: geometry.x, y2: geometry.y
16796 segs2 = geometry.getSortedSegments();
16798 var seg1, seg1x1, seg1x2, seg1y1, seg1y2,
16799 seg2, seg2y1, seg2y2;
16801 outer: for(var i=0, len=segs1.length; i<len; ++i) {
16807 inner: for(var j=0, jlen=segs2.length; j<jlen; ++j) {
16809 if(seg2.x1 > seg1x2) {
16810 // seg1 still left of seg2
16813 if(seg2.x2 < seg1x1) {
16814 // seg2 still left of seg1
16819 if(Math.min(seg2y1, seg2y2) > Math.max(seg1y1, seg1y2)) {
16823 if(Math.max(seg2y1, seg2y2) < Math.min(seg1y1, seg1y2)) {
16827 if(OpenLayers.Geometry.segmentsIntersect(seg1, seg2)) {
16834 intersect = geometry.intersects(this);
16840 * Method: getSortedSegments
16843 * {Array} An array of segment objects. Segment objects have properties
16844 * x1, y1, x2, and y2. The start point is represented by x1 and y1.
16845 * The end point is represented by x2 and y2. Start and end are
16846 * ordered so that x1 < x2.
16848 getSortedSegments: function() {
16849 var numSeg = this.components.length - 1;
16850 var segments = new Array(numSeg), point1, point2;
16851 for(var i=0; i<numSeg; ++i) {
16852 point1 = this.components[i];
16853 point2 = this.components[i + 1];
16854 if(point1.x < point2.x) {
16870 // more efficient to define this somewhere static
16871 function byX1(seg1, seg2) {
16872 return seg1.x1 - seg2.x1;
16874 return segments.sort(byX1);
16878 * Method: splitWithSegment
16879 * Split this geometry with the given segment.
16882 * seg - {Object} An object with x1, y1, x2, and y2 properties referencing
16883 * segment endpoint coordinates.
16884 * options - {Object} Properties of this object will be used to determine
16885 * how the split is conducted.
16888 * edge - {Boolean} Allow splitting when only edges intersect. Default is
16889 * true. If false, a vertex on the source segment must be within the
16890 * tolerance distance of the intersection to be considered a split.
16891 * tolerance - {Number} If a non-null value is provided, intersections
16892 * within the tolerance distance of one of the source segment's
16893 * endpoints will be assumed to occur at the endpoint.
16896 * {Object} An object with *lines* and *points* properties. If the given
16897 * segment intersects this linestring, the lines array will reference
16898 * geometries that result from the split. The points array will contain
16899 * all intersection points. Intersection points are sorted along the
16900 * segment (in order from x1,y1 to x2,y2).
16902 splitWithSegment: function(seg, options) {
16903 var edge = !(options && options.edge === false);
16904 var tolerance = options && options.tolerance;
16906 var verts = this.getVertices();
16908 var intersections = [];
16910 var vert1, vert2, point;
16911 var node, vertex, target;
16912 var interOptions = {point: true, tolerance: tolerance};
16914 for(var i=0, stop=verts.length-2; i<=stop; ++i) {
16916 points.push(vert1.clone());
16917 vert2 = verts[i+1];
16918 target = {x1: vert1.x, y1: vert1.y, x2: vert2.x, y2: vert2.y};
16919 point = OpenLayers.Geometry.segmentsIntersect(
16920 seg, target, interOptions
16922 if(point instanceof OpenLayers.Geometry.Point) {
16923 if((point.x === seg.x1 && point.y === seg.y1) ||
16924 (point.x === seg.x2 && point.y === seg.y2) ||
16925 point.equals(vert1) || point.equals(vert2)) {
16930 if(vertex || edge) {
16931 // push intersections different than the previous
16932 if(!point.equals(intersections[intersections.length-1])) {
16933 intersections.push(point.clone());
16936 if(point.equals(vert1)) {
16940 if(point.equals(vert2)) {
16944 if(!point.equals(vert1)) {
16945 points.push(point);
16947 lines.push(new OpenLayers.Geometry.LineString(points));
16948 points = [point.clone()];
16953 points.push(vert2.clone());
16954 lines.push(new OpenLayers.Geometry.LineString(points));
16956 if(intersections.length > 0) {
16957 // sort intersections along segment
16958 var xDir = seg.x1 < seg.x2 ? 1 : -1;
16959 var yDir = seg.y1 < seg.y2 ? 1 : -1;
16962 points: intersections.sort(function(p1, p2) {
16963 return (xDir * p1.x - xDir * p2.x) || (yDir * p1.y - yDir * p2.y);
16972 * Use this geometry (the source) to attempt to split a target geometry.
16975 * target - {<OpenLayers.Geometry>} The target geometry.
16976 * options - {Object} Properties of this object will be used to determine
16977 * how the split is conducted.
16980 * mutual - {Boolean} Split the source geometry in addition to the target
16981 * geometry. Default is false.
16982 * edge - {Boolean} Allow splitting when only edges intersect. Default is
16983 * true. If false, a vertex on the source must be within the tolerance
16984 * distance of the intersection to be considered a split.
16985 * tolerance - {Number} If a non-null value is provided, intersections
16986 * within the tolerance distance of an existing vertex on the source
16987 * will be assumed to occur at the vertex.
16990 * {Array} A list of geometries (of this same type as the target) that
16991 * result from splitting the target with the source geometry. The
16992 * source and target geometry will remain unmodified. If no split
16993 * results, null will be returned. If mutual is true and a split
16994 * results, return will be an array of two arrays - the first will be
16995 * all geometries that result from splitting the source geometry and
16996 * the second will be all geometries that result from splitting the
16999 split: function(target, options) {
17000 var results = null;
17001 var mutual = options && options.mutual;
17002 var sourceSplit, targetSplit, sourceParts, targetParts;
17003 if(target instanceof OpenLayers.Geometry.LineString) {
17004 var verts = this.getVertices();
17005 var vert1, vert2, seg, splits, lines, point;
17008 for(var i=0, stop=verts.length-2; i<=stop; ++i) {
17010 vert2 = verts[i+1];
17012 x1: vert1.x, y1: vert1.y,
17013 x2: vert2.x, y2: vert2.y
17015 targetParts = targetParts || [target];
17017 points.push(vert1.clone());
17019 for(var j=0; j<targetParts.length; ++j) {
17020 splits = targetParts[j].splitWithSegment(seg, options);
17022 // splice in new features
17023 lines = splits.lines;
17024 if(lines.length > 0) {
17025 lines.unshift(j, 1);
17026 Array.prototype.splice.apply(targetParts, lines);
17027 j += lines.length - 2;
17030 for(var k=0, len=splits.points.length; k<len; ++k) {
17031 point = splits.points[k];
17032 if(!point.equals(vert1)) {
17033 points.push(point);
17034 sourceParts.push(new OpenLayers.Geometry.LineString(points));
17035 if(point.equals(vert2)) {
17038 points = [point.clone()];
17046 if(mutual && sourceParts.length > 0 && points.length > 0) {
17047 points.push(vert2.clone());
17048 sourceParts.push(new OpenLayers.Geometry.LineString(points));
17051 results = target.splitWith(this, options);
17053 if(targetParts && targetParts.length > 1) {
17054 targetSplit = true;
17058 if(sourceParts && sourceParts.length > 1) {
17059 sourceSplit = true;
17063 if(targetSplit || sourceSplit) {
17065 results = [sourceParts, targetParts];
17067 results = targetParts;
17074 * Method: splitWith
17075 * Split this geometry (the target) with the given geometry (the source).
17078 * geometry - {<OpenLayers.Geometry>} A geometry used to split this
17079 * geometry (the source).
17080 * options - {Object} Properties of this object will be used to determine
17081 * how the split is conducted.
17084 * mutual - {Boolean} Split the source geometry in addition to the target
17085 * geometry. Default is false.
17086 * edge - {Boolean} Allow splitting when only edges intersect. Default is
17087 * true. If false, a vertex on the source must be within the tolerance
17088 * distance of the intersection to be considered a split.
17089 * tolerance - {Number} If a non-null value is provided, intersections
17090 * within the tolerance distance of an existing vertex on the source
17091 * will be assumed to occur at the vertex.
17094 * {Array} A list of geometries (of this same type as the target) that
17095 * result from splitting the target with the source geometry. The
17096 * source and target geometry will remain unmodified. If no split
17097 * results, null will be returned. If mutual is true and a split
17098 * results, return will be an array of two arrays - the first will be
17099 * all geometries that result from splitting the source geometry and
17100 * the second will be all geometries that result from splitting the
17103 splitWith: function(geometry, options) {
17104 return geometry.split(this, options);
17109 * APIMethod: getVertices
17110 * Return a list of all points in this geometry.
17113 * nodes - {Boolean} For lines, only return vertices that are
17114 * endpoints. If false, for lines, only vertices that are not
17115 * endpoints will be returned. If not provided, all vertices will
17119 * {Array} A list of all vertices in the geometry.
17121 getVertices: function(nodes) {
17123 if(nodes === true) {
17125 this.components[0],
17126 this.components[this.components.length-1]
17128 } else if (nodes === false) {
17129 vertices = this.components.slice(1, this.components.length-1);
17131 vertices = this.components.slice();
17137 * APIMethod: distanceTo
17138 * Calculate the closest distance between two geometries (on the x-y plane).
17141 * geometry - {<OpenLayers.Geometry>} The target geometry.
17142 * options - {Object} Optional properties for configuring the distance
17146 * details - {Boolean} Return details from the distance calculation.
17147 * Default is false.
17148 * edge - {Boolean} Calculate the distance from this geometry to the
17149 * nearest edge of the target geometry. Default is true. If true,
17150 * calling distanceTo from a geometry that is wholly contained within
17151 * the target will result in a non-zero distance. If false, whenever
17152 * geometries intersect, calling distanceTo will return 0. If false,
17153 * details cannot be returned.
17156 * {Number | Object} The distance between this geometry and the target.
17157 * If details is true, the return will be an object with distance,
17158 * x0, y0, x1, and x2 properties. The x0 and y0 properties represent
17159 * the coordinates of the closest point on this geometry. The x1 and y1
17160 * properties represent the coordinates of the closest point on the
17163 distanceTo: function(geometry, options) {
17164 var edge = !(options && options.edge === false);
17165 var details = edge && options && options.details;
17166 var result, best = {};
17167 var min = Number.POSITIVE_INFINITY;
17168 if(geometry instanceof OpenLayers.Geometry.Point) {
17169 var segs = this.getSortedSegments();
17170 var x = geometry.x;
17171 var y = geometry.y;
17173 for(var i=0, len=segs.length; i<len; ++i) {
17175 result = OpenLayers.Geometry.distanceToSegment(geometry, seg);
17176 if(result.distance < min) {
17177 min = result.distance;
17181 x0: result.x, y0: result.y,
17184 indexDistance: new OpenLayers.Geometry.Point(seg.x1, seg.y1).distanceTo(geometry)
17194 } else if(geometry instanceof OpenLayers.Geometry.LineString) {
17195 var segs0 = this.getSortedSegments();
17196 var segs1 = geometry.getSortedSegments();
17197 var seg0, seg1, intersection, x0, y0;
17198 var len1 = segs1.length;
17199 var interOptions = {point: true};
17200 outer: for(var i=0, len=segs0.length; i<len; ++i) {
17204 for(var j=0; j<len1; ++j) {
17206 intersection = OpenLayers.Geometry.segmentsIntersect(seg0, seg1, interOptions);
17211 x0: intersection.x, y0: intersection.y,
17212 x1: intersection.x, y1: intersection.y
17216 result = OpenLayers.Geometry.distanceToSegment({x: x0, y: y0}, seg1);
17217 if(result.distance < min) {
17218 min = result.distance;
17222 x1: result.x, y1: result.y
17229 best = best.distance;
17232 // check the final vertex in this line's sorted segments
17234 result = geometry.distanceTo(
17235 new OpenLayers.Geometry.Point(seg0.x2, seg0.y2),
17238 var dist = details ? result.distance : result;
17243 x0: result.x1, y0: result.y1,
17244 x1: result.x0, y1: result.y0
17253 best = geometry.distanceTo(this, options);
17254 // swap since target comes from this line
17257 distance: best.distance,
17258 x0: best.x1, y0: best.y1,
17259 x1: best.x0, y1: best.y0
17267 * APIMethod: simplify
17268 * This function will return a simplified LineString.
17269 * Simplification is based on the Douglas-Peucker algorithm.
17273 * tolerance - {number} threshold for simplification in map units
17276 * {OpenLayers.Geometry.LineString} the simplified LineString
17278 simplify: function(tolerance){
17279 if (this && this !== null) {
17280 var points = this.getVertices();
17281 if (points.length < 3) {
17285 var compareNumbers = function(a, b){
17290 * Private function doing the Douglas-Peucker reduction
17292 var douglasPeuckerReduction = function(points, firstPoint, lastPoint, tolerance){
17293 var maxDistance = 0;
17294 var indexFarthest = 0;
17296 for (var index = firstPoint, distance; index < lastPoint; index++) {
17297 distance = perpendicularDistance(points[firstPoint], points[lastPoint], points[index]);
17298 if (distance > maxDistance) {
17299 maxDistance = distance;
17300 indexFarthest = index;
17304 if (maxDistance > tolerance && indexFarthest != firstPoint) {
17305 //Add the largest point that exceeds the tolerance
17306 pointIndexsToKeep.push(indexFarthest);
17307 douglasPeuckerReduction(points, firstPoint, indexFarthest, tolerance);
17308 douglasPeuckerReduction(points, indexFarthest, lastPoint, tolerance);
17313 * Private function calculating the perpendicular distance
17314 * TODO: check whether OpenLayers.Geometry.LineString::distanceTo() is faster or slower
17316 var perpendicularDistance = function(point1, point2, point){
17317 //Area = |(1/2)(x1y2 + x2y3 + x3y1 - x2y1 - x3y2 - x1y3)| *Area of triangle
17318 //Base = v((x1-x2)²+(x1-x2)²) *Base of Triangle*
17319 //Area = .5*Base*H *Solve for height
17320 //Height = Area/.5/Base
17322 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));
17323 var bottom = Math.sqrt(Math.pow(point1.x - point2.x, 2) + Math.pow(point1.y - point2.y, 2));
17324 var height = area / bottom * 2;
17329 var firstPoint = 0;
17330 var lastPoint = points.length - 1;
17331 var pointIndexsToKeep = [];
17333 //Add the first and last index to the keepers
17334 pointIndexsToKeep.push(firstPoint);
17335 pointIndexsToKeep.push(lastPoint);
17337 //The first and the last point cannot be the same
17338 while (points[firstPoint].equals(points[lastPoint])) {
17340 //Addition: the first point not equal to first point in the LineString is kept as well
17341 pointIndexsToKeep.push(lastPoint);
17344 douglasPeuckerReduction(points, firstPoint, lastPoint, tolerance);
17345 var returnPoints = [];
17346 pointIndexsToKeep.sort(compareNumbers);
17347 for (var index = 0; index < pointIndexsToKeep.length; index++) {
17348 returnPoints.push(points[pointIndexsToKeep[index]]);
17350 return new OpenLayers.Geometry.LineString(returnPoints);
17358 CLASS_NAME: "OpenLayers.Geometry.LineString"
17363 * Function: OpenLayers.Geometry.LineString.geodesic
17366 * interpolate - {function(number): OpenLayers.Geometry.Point} Interpolate
17368 * transform - {function(OpenLayers.Geometry.Point): OpenLayers.Geometry.Point}
17369 * Transform from longitude/latitude to projected coordinates.
17370 * squaredTolerance - {number} Squared tolerance.
17373 * {OpenLayers.Geometry.LineString}
17375 OpenLayers.Geometry.LineString.geodesic =
17376 function(interpolate, transform, squaredTolerance) {
17377 // FIXME reduce garbage generation
17378 // FIXME optimize stack operations
17380 var components = [];
17382 var geoA = interpolate(0);
17383 var geoB = interpolate(1);
17385 var a = transform(geoA);
17386 var b = transform(geoB);
17388 var geoStack = [geoB, geoA];
17389 var stack = [b, a];
17390 var fractionStack = [1, 0];
17392 var fractions = {};
17394 var maxIterations = 1e5;
17395 var geoM, m, fracA, fracB, fracM, key;
17397 while (--maxIterations > 0 && fractionStack.length > 0) {
17398 // Pop the a coordinate off the stack
17399 fracA = fractionStack.pop();
17400 geoA = geoStack.pop();
17402 // Add the a coordinate if it has not been added yet
17403 key = fracA.toString();
17404 if (!(key in fractions)) {
17405 components.push(a);
17406 fractions[key] = true;
17408 // Pop the b coordinate off the stack
17409 fracB = fractionStack.pop();
17410 geoB = geoStack.pop();
17412 // Find the m point between the a and b coordinates
17413 fracM = (fracA + fracB) / 2;
17414 geoM = interpolate(fracM);
17415 m = transform(geoM);
17416 if (OpenLayers.Geometry.distanceSquaredToSegment(m, {x1: a.x, y1: a.y,
17417 x2: b.x, y2: b.y}).distance < squaredTolerance) {
17418 // If the m point is sufficiently close to the straight line, then
17419 // we discard it. Just use the b coordinate and move on to the next
17421 components.push(b);
17422 key = fracB.toString();
17423 fractions[key] = true;
17425 // Otherwise, we need to subdivide the current line segment.
17426 // Split it into two and push the two line segments onto the stack.
17427 fractionStack.push(fracB, fracM, fracM, fracA);
17428 stack.push(b, m, m, a);
17429 geoStack.push(geoB, geoM, geoM, geoA);
17433 return new OpenLayers.Geometry.LineString(components);
17438 * Function: OpenLayers.Geometry.LineString.geodesicMeridian
17439 * Generate a meridian (line at constant longitude).
17442 * lon - {number} Longitude.
17443 * lat1 - {number} Latitude 1.
17444 * lat2 - {number} Latitude 2.
17445 * projection - {OpenLayers.Projection} Projection.
17446 * squaredTolerance - {number} Squared tolerance.
17449 * {OpenLayers.Geometry.LineString} Line geometry for the meridian at <lon>.
17451 OpenLayers.Geometry.LineString.geodesicMeridian =
17452 function(lon, lat1, lat2, projection, squaredTolerance) {
17453 var epsg4326Projection = new OpenLayers.Projection('EPSG:4326');
17454 return OpenLayers.Geometry.LineString.geodesic(
17456 return new OpenLayers.Geometry.Point(
17457 lon, lat1 + ((lat2 - lat1) * frac));
17460 return point.transform(epsg4326Projection, projection);
17468 * Function: OpenLayers.Geometry.LineString.geodesicParallel
17469 * Generate a parallel (line at constant latitude).
17472 * lat - {number} Latitude.
17473 * lon1 - {number} Longitude 1.
17474 * lon2 - {number} Longitude 2.
17475 * projection {OpenLayers.Projection} Projection.
17476 * squaredTolerance - {number} Squared tolerance.
17479 * {OpenLayers.Geometry.LineString} Line geometry for the parallel at <lat>.
17481 OpenLayers.Geometry.LineString.geodesicParallel =
17482 function(lat, lon1, lon2, projection, squaredTolerance) {
17483 var epsg4326Projection = new OpenLayers.Projection('EPSG:4326');
17484 return OpenLayers.Geometry.LineString.geodesic(
17486 return new OpenLayers.Geometry.Point(
17487 lon1 + ((lon2 - lon1) * frac), lat);
17490 return point.transform(epsg4326Projection, projection);
17496 /* ======================================================================
17497 OpenLayers/Geometry/MultiLineString.js
17498 ====================================================================== */
17500 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
17501 * full list of contributors). Published under the 2-clause BSD license.
17502 * See license.txt in the OpenLayers distribution or repository for the
17503 * full text of the license. */
17506 * @requires OpenLayers/Geometry/Collection.js
17507 * @requires OpenLayers/Geometry/LineString.js
17511 * Class: OpenLayers.Geometry.MultiLineString
17512 * A MultiLineString is a geometry with multiple <OpenLayers.Geometry.LineString>
17516 * - <OpenLayers.Geometry.Collection>
17517 * - <OpenLayers.Geometry>
17519 OpenLayers.Geometry.MultiLineString = OpenLayers.Class(
17520 OpenLayers.Geometry.Collection, {
17523 * Property: componentTypes
17524 * {Array(String)} An array of class names representing the types of
17525 * components that the collection can include. A null value means the
17526 * component types are not restricted.
17528 componentTypes: ["OpenLayers.Geometry.LineString"],
17531 * Constructor: OpenLayers.Geometry.MultiLineString
17532 * Constructor for a MultiLineString Geometry.
17535 * components - {Array(<OpenLayers.Geometry.LineString>)}
17541 * Use this geometry (the source) to attempt to split a target geometry.
17544 * geometry - {<OpenLayers.Geometry>} The target geometry.
17545 * options - {Object} Properties of this object will be used to determine
17546 * how the split is conducted.
17549 * mutual - {Boolean} Split the source geometry in addition to the target
17550 * geometry. Default is false.
17551 * edge - {Boolean} Allow splitting when only edges intersect. Default is
17552 * true. If false, a vertex on the source must be within the tolerance
17553 * distance of the intersection to be considered a split.
17554 * tolerance - {Number} If a non-null value is provided, intersections
17555 * within the tolerance distance of an existing vertex on the source
17556 * will be assumed to occur at the vertex.
17559 * {Array} A list of geometries (of this same type as the target) that
17560 * result from splitting the target with the source geometry. The
17561 * source and target geometry will remain unmodified. If no split
17562 * results, null will be returned. If mutual is true and a split
17563 * results, return will be an array of two arrays - the first will be
17564 * all geometries that result from splitting the source geometry and
17565 * the second will be all geometries that result from splitting the
17568 split: function(geometry, options) {
17569 var results = null;
17570 var mutual = options && options.mutual;
17571 var splits, sourceLine, sourceLines, sourceSplit, targetSplit;
17572 var sourceParts = [];
17573 var targetParts = [geometry];
17574 for(var i=0, len=this.components.length; i<len; ++i) {
17575 sourceLine = this.components[i];
17576 sourceSplit = false;
17577 for(var j=0; j < targetParts.length; ++j) {
17578 splits = sourceLine.split(targetParts[j], options);
17581 sourceLines = splits[0];
17582 for(var k=0, klen=sourceLines.length; k<klen; ++k) {
17583 if(k===0 && sourceParts.length) {
17584 sourceParts[sourceParts.length-1].addComponent(
17589 new OpenLayers.Geometry.MultiLineString([
17595 sourceSplit = true;
17596 splits = splits[1];
17598 if(splits.length) {
17599 // splice in new target parts
17600 splits.unshift(j, 1);
17601 Array.prototype.splice.apply(targetParts, splits);
17607 // source line was not hit
17608 if(sourceParts.length) {
17609 // add line to existing multi
17610 sourceParts[sourceParts.length-1].addComponent(
17614 // create a fresh multi
17616 new OpenLayers.Geometry.MultiLineString(
17623 if(sourceParts && sourceParts.length > 1) {
17624 sourceSplit = true;
17628 if(targetParts && targetParts.length > 1) {
17629 targetSplit = true;
17633 if(sourceSplit || targetSplit) {
17635 results = [sourceParts, targetParts];
17637 results = targetParts;
17644 * Method: splitWith
17645 * Split this geometry (the target) with the given geometry (the source).
17648 * geometry - {<OpenLayers.Geometry>} A geometry used to split this
17649 * geometry (the source).
17650 * options - {Object} Properties of this object will be used to determine
17651 * how the split is conducted.
17654 * mutual - {Boolean} Split the source geometry in addition to the target
17655 * geometry. Default is false.
17656 * edge - {Boolean} Allow splitting when only edges intersect. Default is
17657 * true. If false, a vertex on the source must be within the tolerance
17658 * distance of the intersection to be considered a split.
17659 * tolerance - {Number} If a non-null value is provided, intersections
17660 * within the tolerance distance of an existing vertex on the source
17661 * will be assumed to occur at the vertex.
17664 * {Array} A list of geometries (of this same type as the target) that
17665 * result from splitting the target with the source geometry. The
17666 * source and target geometry will remain unmodified. If no split
17667 * results, null will be returned. If mutual is true and a split
17668 * results, return will be an array of two arrays - the first will be
17669 * all geometries that result from splitting the source geometry and
17670 * the second will be all geometries that result from splitting the
17673 splitWith: function(geometry, options) {
17674 var results = null;
17675 var mutual = options && options.mutual;
17676 var splits, targetLine, sourceLines, sourceSplit, targetSplit, sourceParts, targetParts;
17677 if(geometry instanceof OpenLayers.Geometry.LineString) {
17679 sourceParts = [geometry];
17680 for(var i=0, len=this.components.length; i<len; ++i) {
17681 targetSplit = false;
17682 targetLine = this.components[i];
17683 for(var j=0; j<sourceParts.length; ++j) {
17684 splits = sourceParts[j].split(targetLine, options);
17687 sourceLines = splits[0];
17688 if(sourceLines.length) {
17689 // splice in new source parts
17690 sourceLines.unshift(j, 1);
17691 Array.prototype.splice.apply(sourceParts, sourceLines);
17692 j += sourceLines.length - 2;
17694 splits = splits[1];
17695 if(splits.length === 0) {
17696 splits = [targetLine.clone()];
17699 for(var k=0, klen=splits.length; k<klen; ++k) {
17700 if(k===0 && targetParts.length) {
17701 targetParts[targetParts.length-1].addComponent(
17706 new OpenLayers.Geometry.MultiLineString([
17712 targetSplit = true;
17716 // target component was not hit
17717 if(targetParts.length) {
17718 // add it to any existing multi-line
17719 targetParts[targetParts.length-1].addComponent(
17723 // or start with a fresh multi-line
17725 new OpenLayers.Geometry.MultiLineString([
17734 results = geometry.split(this);
17736 if(sourceParts && sourceParts.length > 1) {
17737 sourceSplit = true;
17741 if(targetParts && targetParts.length > 1) {
17742 targetSplit = true;
17746 if(sourceSplit || targetSplit) {
17748 results = [sourceParts, targetParts];
17750 results = targetParts;
17756 CLASS_NAME: "OpenLayers.Geometry.MultiLineString"
17758 /* ======================================================================
17759 OpenLayers/Geometry/LinearRing.js
17760 ====================================================================== */
17762 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
17763 * full list of contributors). Published under the 2-clause BSD license.
17764 * See license.txt in the OpenLayers distribution or repository for the
17765 * full text of the license. */
17768 * @requires OpenLayers/Geometry/LineString.js
17772 * Class: OpenLayers.Geometry.LinearRing
17774 * A Linear Ring is a special LineString which is closed. It closes itself
17775 * automatically on every addPoint/removePoint by adding a copy of the first
17776 * point as the last point.
17778 * Also, as it is the first in the line family to close itself, a getArea()
17779 * function is defined to calculate the enclosed area of the linearRing
17782 * - <OpenLayers.Geometry.LineString>
17784 OpenLayers.Geometry.LinearRing = OpenLayers.Class(
17785 OpenLayers.Geometry.LineString, {
17788 * Property: componentTypes
17789 * {Array(String)} An array of class names representing the types of
17790 * components that the collection can include. A null
17791 * value means the component types are not restricted.
17793 componentTypes: ["OpenLayers.Geometry.Point"],
17796 * Constructor: OpenLayers.Geometry.LinearRing
17797 * Linear rings are constructed with an array of points. This array
17798 * can represent a closed or open ring. If the ring is open (the last
17799 * point does not equal the first point), the constructor will close
17800 * the ring. If the ring is already closed (the last point does equal
17801 * the first point), it will be left closed.
17804 * points - {Array(<OpenLayers.Geometry.Point>)} points
17808 * APIMethod: addComponent
17809 * Adds a point to geometry components. If the point is to be added to
17810 * the end of the components array and it is the same as the last point
17811 * already in that array, the duplicate point is not added. This has
17812 * the effect of closing the ring if it is not already closed, and
17813 * doing the right thing if it is already closed. This behavior can
17814 * be overridden by calling the method with a non-null index as the
17818 * point - {<OpenLayers.Geometry.Point>}
17819 * index - {Integer} Index into the array to insert the component
17822 * {Boolean} Was the Point successfully added?
17824 addComponent: function(point, index) {
17827 //remove last point
17828 var lastPoint = this.components.pop();
17830 // given an index, add the point
17831 // without an index only add non-duplicate points
17832 if(index != null || !point.equals(lastPoint)) {
17833 added = OpenLayers.Geometry.Collection.prototype.addComponent.apply(this,
17837 //append copy of first point
17838 var firstPoint = this.components[0];
17839 OpenLayers.Geometry.Collection.prototype.addComponent.apply(this,
17846 * APIMethod: removeComponent
17847 * Removes a point from geometry components.
17850 * point - {<OpenLayers.Geometry.Point>}
17853 * {Boolean} The component was removed.
17855 removeComponent: function(point) {
17856 var removed = this.components && (this.components.length > 3);
17858 //remove last point
17859 this.components.pop();
17862 OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this,
17864 //append copy of first point
17865 var firstPoint = this.components[0];
17866 OpenLayers.Geometry.Collection.prototype.addComponent.apply(this,
17874 * Moves a geometry by the given displacement along positive x and y axes.
17875 * This modifies the position of the geometry and clears the cached
17879 * x - {Float} Distance to move geometry in positive x direction.
17880 * y - {Float} Distance to move geometry in positive y direction.
17882 move: function(x, y) {
17883 for(var i = 0, len=this.components.length; i<len - 1; i++) {
17884 this.components[i].move(x, y);
17889 * APIMethod: rotate
17890 * Rotate a geometry around some origin
17893 * angle - {Float} Rotation angle in degrees (measured counterclockwise
17894 * from the positive x-axis)
17895 * origin - {<OpenLayers.Geometry.Point>} Center point for the rotation
17897 rotate: function(angle, origin) {
17898 for(var i=0, len=this.components.length; i<len - 1; ++i) {
17899 this.components[i].rotate(angle, origin);
17904 * APIMethod: resize
17905 * Resize a geometry relative to some origin. Use this method to apply
17906 * a uniform scaling to a geometry.
17909 * scale - {Float} Factor by which to scale the geometry. A scale of 2
17910 * doubles the size of the geometry in each dimension
17911 * (lines, for example, will be twice as long, and polygons
17912 * will have four times the area).
17913 * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing
17914 * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1.
17917 * {<OpenLayers.Geometry>} - The current geometry.
17919 resize: function(scale, origin, ratio) {
17920 for(var i=0, len=this.components.length; i<len - 1; ++i) {
17921 this.components[i].resize(scale, origin, ratio);
17927 * APIMethod: transform
17928 * Reproject the components geometry from source to dest.
17931 * source - {<OpenLayers.Projection>}
17932 * dest - {<OpenLayers.Projection>}
17935 * {<OpenLayers.Geometry>}
17937 transform: function(source, dest) {
17938 if (source && dest) {
17939 for (var i=0, len=this.components.length; i<len - 1; i++) {
17940 var component = this.components[i];
17941 component.transform(source, dest);
17943 this.bounds = null;
17949 * APIMethod: getCentroid
17952 * {<OpenLayers.Geometry.Point>} The centroid of the collection
17954 getCentroid: function() {
17955 if (this.components) {
17956 var len = this.components.length;
17957 if (len > 0 && len <= 2) {
17958 return this.components[0].clone();
17959 } else if (len > 2) {
17962 var x0 = this.components[0].x;
17963 var y0 = this.components[0].y;
17964 var area = -1 * this.getArea();
17966 for (var i = 0; i < len - 1; i++) {
17967 var b = this.components[i];
17968 var c = this.components[i+1];
17969 sumX += (b.x + c.x - 2 * x0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0));
17970 sumY += (b.y + c.y - 2 * y0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0));
17972 var x = x0 + sumX / (6 * area);
17973 var y = y0 + sumY / (6 * area);
17975 for (var i = 0; i < len - 1; i++) {
17976 sumX += this.components[i].x;
17977 sumY += this.components[i].y;
17979 var x = sumX / (len - 1);
17980 var y = sumY / (len - 1);
17982 return new OpenLayers.Geometry.Point(x, y);
17990 * APIMethod: getArea
17991 * Note - The area is positive if the ring is oriented CW, otherwise
17992 * it will be negative.
17995 * {Float} The signed area for a ring.
17997 getArea: function() {
17999 if ( this.components && (this.components.length > 2)) {
18001 for (var i=0, len=this.components.length; i<len - 1; i++) {
18002 var b = this.components[i];
18003 var c = this.components[i+1];
18004 sum += (b.x + c.x) * (c.y - b.y);
18006 area = - sum / 2.0;
18012 * APIMethod: getGeodesicArea
18013 * Calculate the approximate area of the polygon were it projected onto
18014 * the earth. Note that this area will be positive if ring is oriented
18015 * clockwise, otherwise it will be negative.
18018 * projection - {<OpenLayers.Projection>} The spatial reference system
18019 * for the geometry coordinates. If not provided, Geographic/WGS84 is
18023 * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
18024 * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
18025 * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
18028 * {float} The approximate signed geodesic area of the polygon in square
18031 getGeodesicArea: function(projection) {
18032 var ring = this; // so we can work with a clone if needed
18034 var gg = new OpenLayers.Projection("EPSG:4326");
18035 if(!gg.equals(projection)) {
18036 ring = this.clone().transform(projection, gg);
18040 var len = ring.components && ring.components.length;
18043 for(var i=0; i<len-1; i++) {
18044 p1 = ring.components[i];
18045 p2 = ring.components[i+1];
18046 area += OpenLayers.Util.rad(p2.x - p1.x) *
18047 (2 + Math.sin(OpenLayers.Util.rad(p1.y)) +
18048 Math.sin(OpenLayers.Util.rad(p2.y)));
18050 area = area * OpenLayers.Util.VincentyConstants.a * OpenLayers.Util.VincentyConstants.a / 2.0;
18056 * Method: containsPoint
18057 * Test if a point is inside a linear ring. For the case where a point
18058 * is coincident with a linear ring edge, returns 1. Otherwise,
18062 * point - {<OpenLayers.Geometry.Point>}
18065 * {Boolean | Number} The point is inside the linear ring. Returns 1 if
18066 * the point is coincident with an edge. Returns boolean otherwise.
18068 containsPoint: function(point) {
18069 var approx = OpenLayers.Number.limitSigDigs;
18071 var px = approx(point.x, digs);
18072 var py = approx(point.y, digs);
18073 function getX(y, x1, y1, x2, y2) {
18074 return (y - y2) * ((x2 - x1) / (y2 - y1)) + x2;
18076 var numSeg = this.components.length - 1;
18077 var start, end, x1, y1, x2, y2, cx, cy;
18079 for(var i=0; i<numSeg; ++i) {
18080 start = this.components[i];
18081 x1 = approx(start.x, digs);
18082 y1 = approx(start.y, digs);
18083 end = this.components[i + 1];
18084 x2 = approx(end.x, digs);
18085 y2 = approx(end.y, digs);
18088 * The following conditions enforce five edge-crossing rules:
18089 * 1. points coincident with edges are considered contained;
18090 * 2. an upward edge includes its starting endpoint, and
18091 * excludes its final endpoint;
18092 * 3. a downward edge excludes its starting endpoint, and
18093 * includes its final endpoint;
18094 * 4. horizontal edges are excluded; and
18095 * 5. the edge-ray intersection point must be strictly right
18101 // point on horizontal line
18102 if(x1 <= x2 && (px >= x1 && px <= x2) || // right or vert
18103 x1 >= x2 && (px <= x1 && px >= x2)) { // left or vert
18109 // ignore other horizontal edges
18112 cx = approx(getX(py, x1, y1, x2, y2), digs);
18115 if(y1 < y2 && (py >= y1 && py <= y2) || // upward
18116 y1 > y2 && (py <= y1 && py >= y2)) { // downward
18123 // no crossing to the right
18126 if(x1 != x2 && (cx < Math.min(x1, x2) || cx > Math.max(x1, x2))) {
18130 if(y1 < y2 && (py >= y1 && py < y2) || // upward
18131 y1 > y2 && (py < y1 && py >= y2)) { // downward
18135 var contained = (crosses == -1) ?
18138 // even (out) or odd (in)
18145 * APIMethod: intersects
18146 * Determine if the input geometry intersects this one.
18149 * geometry - {<OpenLayers.Geometry>} Any type of geometry.
18152 * {Boolean} The input geometry intersects this one.
18154 intersects: function(geometry) {
18155 var intersect = false;
18156 if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
18157 intersect = this.containsPoint(geometry);
18158 } else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LineString") {
18159 intersect = geometry.intersects(this);
18160 } else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
18161 intersect = OpenLayers.Geometry.LineString.prototype.intersects.apply(
18165 // check for component intersections
18166 for(var i=0, len=geometry.components.length; i<len; ++ i) {
18167 intersect = geometry.components[i].intersects(this);
18177 * APIMethod: getVertices
18178 * Return a list of all points in this geometry.
18181 * nodes - {Boolean} For lines, only return vertices that are
18182 * endpoints. If false, for lines, only vertices that are not
18183 * endpoints will be returned. If not provided, all vertices will
18187 * {Array} A list of all vertices in the geometry.
18189 getVertices: function(nodes) {
18190 return (nodes === true) ? [] : this.components.slice(0, this.components.length-1);
18193 CLASS_NAME: "OpenLayers.Geometry.LinearRing"
18195 /* ======================================================================
18196 OpenLayers/Geometry/Polygon.js
18197 ====================================================================== */
18199 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
18200 * full list of contributors). Published under the 2-clause BSD license.
18201 * See license.txt in the OpenLayers distribution or repository for the
18202 * full text of the license. */
18205 * @requires OpenLayers/Geometry/Collection.js
18206 * @requires OpenLayers/Geometry/LinearRing.js
18210 * Class: OpenLayers.Geometry.Polygon
18211 * Polygon is a collection of Geometry.LinearRings.
18214 * - <OpenLayers.Geometry.Collection>
18215 * - <OpenLayers.Geometry>
18217 OpenLayers.Geometry.Polygon = OpenLayers.Class(
18218 OpenLayers.Geometry.Collection, {
18221 * Property: componentTypes
18222 * {Array(String)} An array of class names representing the types of
18223 * components that the collection can include. A null value means the
18224 * component types are not restricted.
18226 componentTypes: ["OpenLayers.Geometry.LinearRing"],
18229 * Constructor: OpenLayers.Geometry.Polygon
18230 * Constructor for a Polygon geometry.
18231 * The first ring (this.component[0])is the outer bounds of the polygon and
18232 * all subsequent rings (this.component[1-n]) are internal holes.
18236 * components - {Array(<OpenLayers.Geometry.LinearRing>)}
18240 * APIMethod: getArea
18241 * Calculated by subtracting the areas of the internal holes from the
18242 * area of the outer hole.
18245 * {float} The area of the geometry
18247 getArea: function() {
18249 if ( this.components && (this.components.length > 0)) {
18250 area += Math.abs(this.components[0].getArea());
18251 for (var i=1, len=this.components.length; i<len; i++) {
18252 area -= Math.abs(this.components[i].getArea());
18259 * APIMethod: getGeodesicArea
18260 * Calculate the approximate area of the polygon were it projected onto
18264 * projection - {<OpenLayers.Projection>} The spatial reference system
18265 * for the geometry coordinates. If not provided, Geographic/WGS84 is
18269 * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
18270 * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
18271 * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
18274 * {float} The approximate geodesic area of the polygon in square meters.
18276 getGeodesicArea: function(projection) {
18278 if(this.components && (this.components.length > 0)) {
18279 area += Math.abs(this.components[0].getGeodesicArea(projection));
18280 for(var i=1, len=this.components.length; i<len; i++) {
18281 area -= Math.abs(this.components[i].getGeodesicArea(projection));
18288 * Method: containsPoint
18289 * Test if a point is inside a polygon. Points on a polygon edge are
18290 * considered inside.
18293 * point - {<OpenLayers.Geometry.Point>}
18296 * {Boolean | Number} The point is inside the polygon. Returns 1 if the
18297 * point is on an edge. Returns boolean otherwise.
18299 containsPoint: function(point) {
18300 var numRings = this.components.length;
18301 var contained = false;
18303 // check exterior ring - 1 means on edge, boolean otherwise
18304 contained = this.components[0].containsPoint(point);
18305 if(contained !== 1) {
18306 if(contained && numRings > 1) {
18307 // check interior rings
18309 for(var i=1; i<numRings; ++i) {
18310 hole = this.components[i].containsPoint(point);
18329 * APIMethod: intersects
18330 * Determine if the input geometry intersects this one.
18333 * geometry - {<OpenLayers.Geometry>} Any type of geometry.
18336 * {Boolean} The input geometry intersects this one.
18338 intersects: function(geometry) {
18339 var intersect = false;
18341 if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
18342 intersect = this.containsPoint(geometry);
18343 } else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LineString" ||
18344 geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
18345 // check if rings/linestrings intersect
18346 for(i=0, len=this.components.length; i<len; ++i) {
18347 intersect = geometry.intersects(this.components[i]);
18353 // check if this poly contains points of the ring/linestring
18354 for(i=0, len=geometry.components.length; i<len; ++i) {
18355 intersect = this.containsPoint(geometry.components[i]);
18362 for(i=0, len=geometry.components.length; i<len; ++ i) {
18363 intersect = this.intersects(geometry.components[i]);
18369 // check case where this poly is wholly contained by another
18370 if(!intersect && geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") {
18371 // exterior ring points will be contained in the other geometry
18372 var ring = this.components[0];
18373 for(i=0, len=ring.components.length; i<len; ++i) {
18374 intersect = geometry.containsPoint(ring.components[i]);
18384 * APIMethod: distanceTo
18385 * Calculate the closest distance between two geometries (on the x-y plane).
18388 * geometry - {<OpenLayers.Geometry>} The target geometry.
18389 * options - {Object} Optional properties for configuring the distance
18393 * details - {Boolean} Return details from the distance calculation.
18394 * Default is false.
18395 * edge - {Boolean} Calculate the distance from this geometry to the
18396 * nearest edge of the target geometry. Default is true. If true,
18397 * calling distanceTo from a geometry that is wholly contained within
18398 * the target will result in a non-zero distance. If false, whenever
18399 * geometries intersect, calling distanceTo will return 0. If false,
18400 * details cannot be returned.
18403 * {Number | Object} The distance between this geometry and the target.
18404 * If details is true, the return will be an object with distance,
18405 * x0, y0, x1, and y1 properties. The x0 and y0 properties represent
18406 * the coordinates of the closest point on this geometry. The x1 and y1
18407 * properties represent the coordinates of the closest point on the
18410 distanceTo: function(geometry, options) {
18411 var edge = !(options && options.edge === false);
18413 // this is the case where we might not be looking for distance to edge
18414 if(!edge && this.intersects(geometry)) {
18417 result = OpenLayers.Geometry.Collection.prototype.distanceTo.apply(
18418 this, [geometry, options]
18424 CLASS_NAME: "OpenLayers.Geometry.Polygon"
18428 * APIMethod: createRegularPolygon
18429 * Create a regular polygon around a radius. Useful for creating circles
18433 * origin - {<OpenLayers.Geometry.Point>} center of polygon.
18434 * radius - {Float} distance to vertex, in map units.
18435 * sides - {Integer} Number of sides. 20 approximates a circle.
18436 * rotation - {Float} original angle of rotation, in degrees.
18438 OpenLayers.Geometry.Polygon.createRegularPolygon = function(origin, radius, sides, rotation) {
18439 var angle = Math.PI * ((1/sides) - (1/2));
18441 angle += (rotation / 180) * Math.PI;
18443 var rotatedAngle, x, y;
18445 for(var i=0; i<sides; ++i) {
18446 rotatedAngle = angle + (i * 2 * Math.PI / sides);
18447 x = origin.x + (radius * Math.cos(rotatedAngle));
18448 y = origin.y + (radius * Math.sin(rotatedAngle));
18449 points.push(new OpenLayers.Geometry.Point(x, y));
18451 var ring = new OpenLayers.Geometry.LinearRing(points);
18452 return new OpenLayers.Geometry.Polygon([ring]);
18454 /* ======================================================================
18455 OpenLayers/Geometry/MultiPolygon.js
18456 ====================================================================== */
18458 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
18459 * full list of contributors). Published under the 2-clause BSD license.
18460 * See license.txt in the OpenLayers distribution or repository for the
18461 * full text of the license. */
18464 * @requires OpenLayers/Geometry/Collection.js
18465 * @requires OpenLayers/Geometry/Polygon.js
18469 * Class: OpenLayers.Geometry.MultiPolygon
18470 * MultiPolygon is a geometry with multiple <OpenLayers.Geometry.Polygon>
18471 * components. Create a new instance with the <OpenLayers.Geometry.MultiPolygon>
18475 * - <OpenLayers.Geometry.Collection>
18477 OpenLayers.Geometry.MultiPolygon = OpenLayers.Class(
18478 OpenLayers.Geometry.Collection, {
18481 * Property: componentTypes
18482 * {Array(String)} An array of class names representing the types of
18483 * components that the collection can include. A null value means the
18484 * component types are not restricted.
18486 componentTypes: ["OpenLayers.Geometry.Polygon"],
18489 * Constructor: OpenLayers.Geometry.MultiPolygon
18490 * Create a new MultiPolygon geometry
18493 * components - {Array(<OpenLayers.Geometry.Polygon>)} An array of polygons
18494 * used to generate the MultiPolygon
18498 CLASS_NAME: "OpenLayers.Geometry.MultiPolygon"
18500 /* ======================================================================
18501 OpenLayers/Format/GML.js
18502 ====================================================================== */
18504 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
18505 * full list of contributors). Published under the 2-clause BSD license.
18506 * See license.txt in the OpenLayers distribution or repository for the
18507 * full text of the license. */
18510 * @requires OpenLayers/Format/XML.js
18511 * @requires OpenLayers/Feature/Vector.js
18512 * @requires OpenLayers/Geometry/Point.js
18513 * @requires OpenLayers/Geometry/MultiPoint.js
18514 * @requires OpenLayers/Geometry/LineString.js
18515 * @requires OpenLayers/Geometry/MultiLineString.js
18516 * @requires OpenLayers/Geometry/Polygon.js
18517 * @requires OpenLayers/Geometry/MultiPolygon.js
18521 * Class: OpenLayers.Format.GML
18522 * Read/Write GML. Create a new instance with the <OpenLayers.Format.GML>
18523 * constructor. Supports the GML simple features profile.
18526 * - <OpenLayers.Format.XML>
18528 OpenLayers.Format.GML = OpenLayers.Class(OpenLayers.Format.XML, {
18531 * APIProperty: featureNS
18532 * {String} Namespace used for feature attributes. Default is
18533 * "http://mapserver.gis.umn.edu/mapserver".
18535 featureNS: "http://mapserver.gis.umn.edu/mapserver",
18538 * APIProperty: featurePrefix
18539 * {String} Namespace alias (or prefix) for feature nodes. Default is
18542 featurePrefix: "feature",
18545 * APIProperty: featureName
18546 * {String} Element name for features. Default is "featureMember".
18548 featureName: "featureMember",
18551 * APIProperty: layerName
18552 * {String} Name of data layer. Default is "features".
18554 layerName: "features",
18557 * APIProperty: geometryName
18558 * {String} Name of geometry element. Defaults to "geometry".
18560 geometryName: "geometry",
18563 * APIProperty: collectionName
18564 * {String} Name of featureCollection element.
18566 collectionName: "FeatureCollection",
18569 * APIProperty: gmlns
18570 * {String} GML Namespace.
18572 gmlns: "http://www.opengis.net/gml",
18575 * APIProperty: extractAttributes
18576 * {Boolean} Extract attributes from GML.
18578 extractAttributes: true,
18582 * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
18583 * Changing is not recommended, a new Format should be instantiated.
18588 * Constructor: OpenLayers.Format.GML
18589 * Create a new parser for GML.
18592 * options - {Object} An optional object whose properties will be set on
18595 initialize: function(options) {
18596 // compile regular expressions once instead of every time they are used
18598 trimSpace: (/^\s*|\s*$/g),
18599 removeSpace: (/\s*/g),
18600 splitSpace: (/\s+/),
18601 trimComma: (/\s*,\s*/g)
18603 OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
18608 * Read data from a string, and return a list of features.
18611 * data - {String} or {DOMElement} data to read/parse.
18614 * {Array(<OpenLayers.Feature.Vector>)} An array of features.
18616 read: function(data) {
18617 if(typeof data == "string") {
18618 data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
18620 var featureNodes = this.getElementsByTagNameNS(data.documentElement,
18624 for(var i=0; i<featureNodes.length; i++) {
18625 var feature = this.parseFeature(featureNodes[i]);
18627 features.push(feature);
18634 * Method: parseFeature
18635 * This function is the core of the GML parsing code in OpenLayers.
18636 * It creates the geometries that are then attached to the returned
18637 * feature, and calls parseAttributes() to get attribute data out.
18640 * node - {DOMElement} A GML feature node.
18642 parseFeature: function(node) {
18643 // only accept one geometry per feature - look for highest "order"
18644 var order = ["MultiPolygon", "Polygon",
18645 "MultiLineString", "LineString",
18646 "MultiPoint", "Point", "Envelope"];
18647 // FIXME: In case we parse a feature with no geometry, but boundedBy an Envelope,
18648 // this code creates a geometry derived from the Envelope. This is not correct.
18649 var type, nodeList, geometry, parser;
18650 for(var i=0; i<order.length; ++i) {
18652 nodeList = this.getElementsByTagNameNS(node, this.gmlns, type);
18653 if(nodeList.length > 0) {
18654 // only deal with first geometry of this type
18655 parser = this.parseGeometry[type.toLowerCase()];
18657 geometry = parser.apply(this, [nodeList[0]]);
18658 if (this.internalProjection && this.externalProjection) {
18659 geometry.transform(this.externalProjection,
18660 this.internalProjection);
18663 throw new TypeError("Unsupported geometry type: " + type);
18665 // stop looking for different geometry types
18671 var boxNodes = this.getElementsByTagNameNS(node, this.gmlns, "Box");
18672 for(i=0; i<boxNodes.length; ++i) {
18673 var boxNode = boxNodes[i];
18674 var box = this.parseGeometry["box"].apply(this, [boxNode]);
18675 var parentNode = boxNode.parentNode;
18676 var parentName = parentNode.localName ||
18677 parentNode.nodeName.split(":").pop();
18678 if(parentName === "boundedBy") {
18681 geometry = box.toGeometry();
18685 // construct feature (optionally with attributes)
18687 if(this.extractAttributes) {
18688 attributes = this.parseAttributes(node);
18690 var feature = new OpenLayers.Feature.Vector(geometry, attributes);
18691 feature.bounds = bounds;
18693 var firstChild = this.getFirstElementChild(node);
18695 featureType: firstChild.nodeName.split(":")[1],
18696 featureNS: firstChild.namespaceURI,
18697 featureNSPrefix: firstChild.prefix
18699 feature.type = feature.gml.featureType;
18701 // assign fid - this can come from a "fid" or "id" attribute
18702 var childNode = node.firstChild;
18705 if(childNode.nodeType == 1) {
18706 fid = childNode.getAttribute("fid") ||
18707 childNode.getAttribute("id");
18712 childNode = childNode.nextSibling;
18719 * Property: parseGeometry
18720 * Properties of this object are the functions that parse geometries based
18726 * Method: parseGeometry.point
18727 * Given a GML node representing a point geometry, create an OpenLayers
18731 * node - {DOMElement} A GML node.
18734 * {<OpenLayers.Geometry.Point>} A point geometry.
18736 point: function(node) {
18738 * Three coordinate variations to consider:
18739 * 1) <gml:pos>x y z</gml:pos>
18740 * 2) <gml:coordinates>x, y, z</gml:coordinates>
18741 * 3) <gml:coord><gml:X>x</gml:X><gml:Y>y</gml:Y></gml:coord>
18743 var nodeList, coordString;
18746 // look for <gml:pos>
18747 var nodeList = this.getElementsByTagNameNS(node, this.gmlns, "pos");
18748 if(nodeList.length > 0) {
18749 coordString = nodeList[0].firstChild.nodeValue;
18750 coordString = coordString.replace(this.regExes.trimSpace, "");
18751 coords = coordString.split(this.regExes.splitSpace);
18754 // look for <gml:coordinates>
18755 if(coords.length == 0) {
18756 nodeList = this.getElementsByTagNameNS(node, this.gmlns,
18758 if(nodeList.length > 0) {
18759 coordString = nodeList[0].firstChild.nodeValue;
18760 coordString = coordString.replace(this.regExes.removeSpace,
18762 coords = coordString.split(",");
18766 // look for <gml:coord>
18767 if(coords.length == 0) {
18768 nodeList = this.getElementsByTagNameNS(node, this.gmlns,
18770 if(nodeList.length > 0) {
18771 var xList = this.getElementsByTagNameNS(nodeList[0],
18773 var yList = this.getElementsByTagNameNS(nodeList[0],
18775 if(xList.length > 0 && yList.length > 0) {
18776 coords = [xList[0].firstChild.nodeValue,
18777 yList[0].firstChild.nodeValue];
18782 // preserve third dimension
18783 if(coords.length == 2) {
18788 return new OpenLayers.Geometry.Point(coords[0], coords[1],
18792 return new OpenLayers.Geometry.Point(coords[1], coords[0],
18798 * Method: parseGeometry.multipoint
18799 * Given a GML node representing a multipoint geometry, create an
18800 * OpenLayers multipoint geometry.
18803 * node - {DOMElement} A GML node.
18806 * {<OpenLayers.Geometry.MultiPoint>} A multipoint geometry.
18808 multipoint: function(node) {
18809 var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
18811 var components = [];
18812 if(nodeList.length > 0) {
18814 for(var i=0; i<nodeList.length; ++i) {
18815 point = this.parseGeometry.point.apply(this, [nodeList[i]]);
18817 components.push(point);
18821 return new OpenLayers.Geometry.MultiPoint(components);
18825 * Method: parseGeometry.linestring
18826 * Given a GML node representing a linestring geometry, create an
18827 * OpenLayers linestring geometry.
18830 * node - {DOMElement} A GML node.
18833 * {<OpenLayers.Geometry.LineString>} A linestring geometry.
18835 linestring: function(node, ring) {
18837 * Two coordinate variations to consider:
18838 * 1) <gml:posList dimension="d">x0 y0 z0 x1 y1 z1</gml:posList>
18839 * 2) <gml:coordinates>x0, y0, z0 x1, y1, z1</gml:coordinates>
18841 var nodeList, coordString;
18845 // look for <gml:posList>
18846 nodeList = this.getElementsByTagNameNS(node, this.gmlns, "posList");
18847 if(nodeList.length > 0) {
18848 coordString = this.getChildValue(nodeList[0]);
18849 coordString = coordString.replace(this.regExes.trimSpace, "");
18850 coords = coordString.split(this.regExes.splitSpace);
18851 var dim = parseInt(nodeList[0].getAttribute("dimension"));
18853 for(var i=0; i<coords.length/dim; ++i) {
18857 z = (dim == 2) ? null : coords[j+2];
18859 points.push(new OpenLayers.Geometry.Point(x, y, z));
18861 points.push(new OpenLayers.Geometry.Point(y, x, z));
18866 // look for <gml:coordinates>
18867 if(coords.length == 0) {
18868 nodeList = this.getElementsByTagNameNS(node, this.gmlns,
18870 if(nodeList.length > 0) {
18871 coordString = this.getChildValue(nodeList[0]);
18872 coordString = coordString.replace(this.regExes.trimSpace,
18874 coordString = coordString.replace(this.regExes.trimComma,
18876 var pointList = coordString.split(this.regExes.splitSpace);
18877 for(var i=0; i<pointList.length; ++i) {
18878 coords = pointList[i].split(",");
18879 if(coords.length == 2) {
18883 points.push(new OpenLayers.Geometry.Point(coords[0],
18887 points.push(new OpenLayers.Geometry.Point(coords[1],
18896 if(points.length != 0) {
18898 line = new OpenLayers.Geometry.LinearRing(points);
18900 line = new OpenLayers.Geometry.LineString(points);
18907 * Method: parseGeometry.multilinestring
18908 * Given a GML node representing a multilinestring geometry, create an
18909 * OpenLayers multilinestring geometry.
18912 * node - {DOMElement} A GML node.
18915 * {<OpenLayers.Geometry.MultiLineString>} A multilinestring geometry.
18917 multilinestring: function(node) {
18918 var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
18920 var components = [];
18921 if(nodeList.length > 0) {
18923 for(var i=0; i<nodeList.length; ++i) {
18924 line = this.parseGeometry.linestring.apply(this,
18927 components.push(line);
18931 return new OpenLayers.Geometry.MultiLineString(components);
18935 * Method: parseGeometry.polygon
18936 * Given a GML node representing a polygon geometry, create an
18937 * OpenLayers polygon geometry.
18940 * node - {DOMElement} A GML node.
18943 * {<OpenLayers.Geometry.Polygon>} A polygon geometry.
18945 polygon: function(node) {
18946 var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
18948 var components = [];
18949 if(nodeList.length > 0) {
18950 // this assumes exterior ring first, inner rings after
18952 for(var i=0; i<nodeList.length; ++i) {
18953 ring = this.parseGeometry.linestring.apply(this,
18954 [nodeList[i], true]);
18956 components.push(ring);
18960 return new OpenLayers.Geometry.Polygon(components);
18964 * Method: parseGeometry.multipolygon
18965 * Given a GML node representing a multipolygon geometry, create an
18966 * OpenLayers multipolygon geometry.
18969 * node - {DOMElement} A GML node.
18972 * {<OpenLayers.Geometry.MultiPolygon>} A multipolygon geometry.
18974 multipolygon: function(node) {
18975 var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
18977 var components = [];
18978 if(nodeList.length > 0) {
18980 for(var i=0; i<nodeList.length; ++i) {
18981 polygon = this.parseGeometry.polygon.apply(this,
18984 components.push(polygon);
18988 return new OpenLayers.Geometry.MultiPolygon(components);
18991 envelope: function(node) {
18992 var components = [];
18996 var lpoint = this.getElementsByTagNameNS(node, this.gmlns, "lowerCorner");
18997 if (lpoint.length > 0) {
19000 if(lpoint.length > 0) {
19001 coordString = lpoint[0].firstChild.nodeValue;
19002 coordString = coordString.replace(this.regExes.trimSpace, "");
19003 coords = coordString.split(this.regExes.splitSpace);
19006 if(coords.length == 2) {
19010 var lowerPoint = new OpenLayers.Geometry.Point(coords[0], coords[1],coords[2]);
19012 var lowerPoint = new OpenLayers.Geometry.Point(coords[1], coords[0],coords[2]);
19016 var upoint = this.getElementsByTagNameNS(node, this.gmlns, "upperCorner");
19017 if (upoint.length > 0) {
19020 if(upoint.length > 0) {
19021 coordString = upoint[0].firstChild.nodeValue;
19022 coordString = coordString.replace(this.regExes.trimSpace, "");
19023 coords = coordString.split(this.regExes.splitSpace);
19026 if(coords.length == 2) {
19030 var upperPoint = new OpenLayers.Geometry.Point(coords[0], coords[1],coords[2]);
19032 var upperPoint = new OpenLayers.Geometry.Point(coords[1], coords[0],coords[2]);
19036 if (lowerPoint && upperPoint) {
19037 components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y));
19038 components.push(new OpenLayers.Geometry.Point(upperPoint.x, lowerPoint.y));
19039 components.push(new OpenLayers.Geometry.Point(upperPoint.x, upperPoint.y));
19040 components.push(new OpenLayers.Geometry.Point(lowerPoint.x, upperPoint.y));
19041 components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y));
19043 var ring = new OpenLayers.Geometry.LinearRing(components);
19044 envelope = new OpenLayers.Geometry.Polygon([ring]);
19050 * Method: parseGeometry.box
19051 * Given a GML node representing a box geometry, create an
19052 * OpenLayers.Bounds.
19055 * node - {DOMElement} A GML node.
19058 * {<OpenLayers.Bounds>} A bounds representing the box.
19060 box: function(node) {
19061 var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
19064 var coords, beginPoint = null, endPoint = null;
19065 if (nodeList.length > 0) {
19066 coordString = nodeList[0].firstChild.nodeValue;
19067 coords = coordString.split(" ");
19068 if (coords.length == 2) {
19069 beginPoint = coords[0].split(",");
19070 endPoint = coords[1].split(",");
19073 if (beginPoint !== null && endPoint !== null) {
19074 return new OpenLayers.Bounds(parseFloat(beginPoint[0]),
19075 parseFloat(beginPoint[1]),
19076 parseFloat(endPoint[0]),
19077 parseFloat(endPoint[1]) );
19084 * Method: parseAttributes
19087 * node - {DOMElement}
19090 * {Object} An attributes object.
19092 parseAttributes: function(node) {
19093 var attributes = {};
19094 // assume attributes are children of the first type 1 child
19095 var childNode = node.firstChild;
19096 var children, i, child, grandchildren, grandchild, name, value;
19098 if(childNode.nodeType == 1) {
19099 // attributes are type 1 children with one type 3 child
19100 children = childNode.childNodes;
19101 for(i=0; i<children.length; ++i) {
19102 child = children[i];
19103 if(child.nodeType == 1) {
19104 grandchildren = child.childNodes;
19105 if(grandchildren.length == 1) {
19106 grandchild = grandchildren[0];
19107 if(grandchild.nodeType == 3 ||
19108 grandchild.nodeType == 4) {
19109 name = (child.prefix) ?
19110 child.nodeName.split(":")[1] :
19112 value = grandchild.nodeValue.replace(
19113 this.regExes.trimSpace, "");
19114 attributes[name] = value;
19117 // If child has no childNodes (grandchildren),
19118 // set an attribute with null value.
19119 // e.g. <prefix:fieldname/> becomes
19120 // {fieldname: null}
19121 attributes[child.nodeName.split(":").pop()] = null;
19127 childNode = childNode.nextSibling;
19134 * Generate a GML document string given a list of features.
19137 * features - {Array(<OpenLayers.Feature.Vector>)} List of features to
19138 * serialize into a string.
19141 * {String} A string representing the GML document.
19143 write: function(features) {
19144 if(!(OpenLayers.Util.isArray(features))) {
19145 features = [features];
19147 var gml = this.createElementNS("http://www.opengis.net/wfs",
19148 "wfs:" + this.collectionName);
19149 for(var i=0; i<features.length; i++) {
19150 gml.appendChild(this.createFeatureXML(features[i]));
19152 return OpenLayers.Format.XML.prototype.write.apply(this, [gml]);
19156 * Method: createFeatureXML
19157 * Accept an OpenLayers.Feature.Vector, and build a GML node for it.
19160 * feature - {<OpenLayers.Feature.Vector>} The feature to be built as GML.
19163 * {DOMElement} A node reprensting the feature in GML.
19165 createFeatureXML: function(feature) {
19166 var geometry = feature.geometry;
19167 var geometryNode = this.buildGeometryNode(geometry);
19168 var geomContainer = this.createElementNS(this.featureNS,
19169 this.featurePrefix + ":" +
19170 this.geometryName);
19171 geomContainer.appendChild(geometryNode);
19172 var featureNode = this.createElementNS(this.gmlns,
19173 "gml:" + this.featureName);
19174 var featureContainer = this.createElementNS(this.featureNS,
19175 this.featurePrefix + ":" +
19177 var fid = feature.fid || feature.id;
19178 featureContainer.setAttribute("fid", fid);
19179 featureContainer.appendChild(geomContainer);
19180 for(var attr in feature.attributes) {
19181 var attrText = this.createTextNode(feature.attributes[attr]);
19182 var nodename = attr.substring(attr.lastIndexOf(":") + 1);
19183 var attrContainer = this.createElementNS(this.featureNS,
19184 this.featurePrefix + ":" +
19186 attrContainer.appendChild(attrText);
19187 featureContainer.appendChild(attrContainer);
19189 featureNode.appendChild(featureContainer);
19190 return featureNode;
19194 * APIMethod: buildGeometryNode
19196 buildGeometryNode: function(geometry) {
19197 if (this.externalProjection && this.internalProjection) {
19198 geometry = geometry.clone();
19199 geometry.transform(this.internalProjection,
19200 this.externalProjection);
19202 var className = geometry.CLASS_NAME;
19203 var type = className.substring(className.lastIndexOf(".") + 1);
19204 var builder = this.buildGeometry[type.toLowerCase()];
19205 return builder.apply(this, [geometry]);
19209 * Property: buildGeometry
19210 * Object containing methods to do the actual geometry node building
19211 * based on geometry type.
19214 // TBD retrieve the srs from layer
19215 // srsName is non-standard, so not including it until it's right.
19216 // gml.setAttribute("srsName",
19217 // "http://www.opengis.net/gml/srs/epsg.xml#4326");
19220 * Method: buildGeometry.point
19221 * Given an OpenLayers point geometry, create a GML point.
19224 * geometry - {<OpenLayers.Geometry.Point>} A point geometry.
19227 * {DOMElement} A GML point node.
19229 point: function(geometry) {
19230 var gml = this.createElementNS(this.gmlns, "gml:Point");
19231 gml.appendChild(this.buildCoordinatesNode(geometry));
19236 * Method: buildGeometry.multipoint
19237 * Given an OpenLayers multipoint geometry, create a GML multipoint.
19240 * geometry - {<OpenLayers.Geometry.MultiPoint>} A multipoint geometry.
19243 * {DOMElement} A GML multipoint node.
19245 multipoint: function(geometry) {
19246 var gml = this.createElementNS(this.gmlns, "gml:MultiPoint");
19247 var points = geometry.components;
19248 var pointMember, pointGeom;
19249 for(var i=0; i<points.length; i++) {
19250 pointMember = this.createElementNS(this.gmlns,
19251 "gml:pointMember");
19252 pointGeom = this.buildGeometry.point.apply(this,
19254 pointMember.appendChild(pointGeom);
19255 gml.appendChild(pointMember);
19261 * Method: buildGeometry.linestring
19262 * Given an OpenLayers linestring geometry, create a GML linestring.
19265 * geometry - {<OpenLayers.Geometry.LineString>} A linestring geometry.
19268 * {DOMElement} A GML linestring node.
19270 linestring: function(geometry) {
19271 var gml = this.createElementNS(this.gmlns, "gml:LineString");
19272 gml.appendChild(this.buildCoordinatesNode(geometry));
19277 * Method: buildGeometry.multilinestring
19278 * Given an OpenLayers multilinestring geometry, create a GML
19282 * geometry - {<OpenLayers.Geometry.MultiLineString>} A multilinestring
19286 * {DOMElement} A GML multilinestring node.
19288 multilinestring: function(geometry) {
19289 var gml = this.createElementNS(this.gmlns, "gml:MultiLineString");
19290 var lines = geometry.components;
19291 var lineMember, lineGeom;
19292 for(var i=0; i<lines.length; ++i) {
19293 lineMember = this.createElementNS(this.gmlns,
19294 "gml:lineStringMember");
19295 lineGeom = this.buildGeometry.linestring.apply(this,
19297 lineMember.appendChild(lineGeom);
19298 gml.appendChild(lineMember);
19304 * Method: buildGeometry.linearring
19305 * Given an OpenLayers linearring geometry, create a GML linearring.
19308 * geometry - {<OpenLayers.Geometry.LinearRing>} A linearring geometry.
19311 * {DOMElement} A GML linearring node.
19313 linearring: function(geometry) {
19314 var gml = this.createElementNS(this.gmlns, "gml:LinearRing");
19315 gml.appendChild(this.buildCoordinatesNode(geometry));
19320 * Method: buildGeometry.polygon
19321 * Given an OpenLayers polygon geometry, create a GML polygon.
19324 * geometry - {<OpenLayers.Geometry.Polygon>} A polygon geometry.
19327 * {DOMElement} A GML polygon node.
19329 polygon: function(geometry) {
19330 var gml = this.createElementNS(this.gmlns, "gml:Polygon");
19331 var rings = geometry.components;
19332 var ringMember, ringGeom, type;
19333 for(var i=0; i<rings.length; ++i) {
19334 type = (i==0) ? "outerBoundaryIs" : "innerBoundaryIs";
19335 ringMember = this.createElementNS(this.gmlns,
19337 ringGeom = this.buildGeometry.linearring.apply(this,
19339 ringMember.appendChild(ringGeom);
19340 gml.appendChild(ringMember);
19346 * Method: buildGeometry.multipolygon
19347 * Given an OpenLayers multipolygon geometry, create a GML multipolygon.
19350 * geometry - {<OpenLayers.Geometry.MultiPolygon>} A multipolygon
19354 * {DOMElement} A GML multipolygon node.
19356 multipolygon: function(geometry) {
19357 var gml = this.createElementNS(this.gmlns, "gml:MultiPolygon");
19358 var polys = geometry.components;
19359 var polyMember, polyGeom;
19360 for(var i=0; i<polys.length; ++i) {
19361 polyMember = this.createElementNS(this.gmlns,
19362 "gml:polygonMember");
19363 polyGeom = this.buildGeometry.polygon.apply(this,
19365 polyMember.appendChild(polyGeom);
19366 gml.appendChild(polyMember);
19373 * Method: buildGeometry.bounds
19374 * Given an OpenLayers bounds, create a GML box.
19377 * bounds - {<OpenLayers.Geometry.Bounds>} A bounds object.
19380 * {DOMElement} A GML box node.
19382 bounds: function(bounds) {
19383 var gml = this.createElementNS(this.gmlns, "gml:Box");
19384 gml.appendChild(this.buildCoordinatesNode(bounds));
19390 * Method: buildCoordinates
19391 * builds the coordinates XmlNode
19393 * <gml:coordinates decimal="." cs="," ts=" ">...</gml:coordinates>
19397 * geometry - {<OpenLayers.Geometry>}
19400 * {XmlNode} created xmlNode
19402 buildCoordinatesNode: function(geometry) {
19403 var coordinatesNode = this.createElementNS(this.gmlns,
19404 "gml:coordinates");
19405 coordinatesNode.setAttribute("decimal", ".");
19406 coordinatesNode.setAttribute("cs", ",");
19407 coordinatesNode.setAttribute("ts", " ");
19411 if(geometry instanceof OpenLayers.Bounds){
19412 parts.push(geometry.left + "," + geometry.bottom);
19413 parts.push(geometry.right + "," + geometry.top);
19415 var points = (geometry.components) ? geometry.components : [geometry];
19416 for(var i=0; i<points.length; i++) {
19417 parts.push(points[i].x + "," + points[i].y);
19421 var txtNode = this.createTextNode(parts.join(" "));
19422 coordinatesNode.appendChild(txtNode);
19424 return coordinatesNode;
19427 CLASS_NAME: "OpenLayers.Format.GML"
19429 /* ======================================================================
19430 OpenLayers/Format/GML/Base.js
19431 ====================================================================== */
19433 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
19434 * full list of contributors). Published under the 2-clause BSD license.
19435 * See license.txt in the OpenLayers distribution or repository for the
19436 * full text of the license. */
19439 * @requires OpenLayers/Format/XML.js
19440 * @requires OpenLayers/Format/GML.js
19444 * Though required in the full build, if the GML format is excluded, we set
19445 * the namespace here.
19447 if(!OpenLayers.Format.GML) {
19448 OpenLayers.Format.GML = {};
19452 * Class: OpenLayers.Format.GML.Base
19453 * Superclass for GML parsers.
19456 * - <OpenLayers.Format.XML>
19458 OpenLayers.Format.GML.Base = OpenLayers.Class(OpenLayers.Format.XML, {
19461 * Property: namespaces
19462 * {Object} Mapping of namespace aliases to namespace URIs.
19465 gml: "http://www.opengis.net/gml",
19466 xlink: "http://www.w3.org/1999/xlink",
19467 xsi: "http://www.w3.org/2001/XMLSchema-instance",
19468 wfs: "http://www.opengis.net/wfs" // this is a convenience for reading wfs:FeatureCollection
19472 * Property: defaultPrefix
19474 defaultPrefix: "gml",
19477 * Property: schemaLocation
19478 * {String} Schema location for a particular minor version.
19480 schemaLocation: null,
19483 * APIProperty: featureType
19484 * {Array(String) or String} The local (without prefix) feature typeName(s).
19489 * APIProperty: featureNS
19490 * {String} The feature namespace. Must be set in the options at
19496 * APIProperty: featurePrefix
19497 * {String} Namespace alias (or prefix) for feature nodes. Default is
19500 featurePrefix: "feature",
19503 * APIProperty: geometry
19504 * {String} Name of geometry element. Defaults to "geometry". If null, it
19505 * will be set on <read> when the first geometry is parsed.
19507 geometryName: "geometry",
19510 * APIProperty: extractAttributes
19511 * {Boolean} Extract attributes from GML. Default is true.
19513 extractAttributes: true,
19516 * APIProperty: srsName
19517 * {String} URI for spatial reference system. This is optional for
19518 * single part geometries and mandatory for collections and multis.
19519 * If set, the srsName attribute will be written for all geometries.
19526 * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
19527 * Changing is not recommended, a new Format should be instantiated.
19532 * Property: geometryTypes
19533 * {Object} Maps OpenLayers geometry class names to GML element names.
19534 * Use <setGeometryTypes> before accessing this property.
19536 geometryTypes: null,
19539 * Property: singleFeatureType
19540 * {Boolean} True if there is only 1 featureType, and not an array
19543 singleFeatureType: null,
19546 * Property: autoConfig
19547 * {Boolean} Indicates if the format was configured without a <featureNS>,
19548 * but auto-configured <featureNS> and <featureType> during read.
19549 * Subclasses making use of <featureType> auto-configuration should make
19550 * the first call to the <readNode> method (usually in the read method)
19551 * with true as 3rd argument, so the auto-configured featureType can be
19552 * reset and the format can be reused for subsequent reads with data from
19553 * different featureTypes. Set to false after read if you want to keep the
19554 * auto-configured values.
19558 * Property: regExes
19559 * Compiled regular expressions for manipulating strings.
19562 trimSpace: (/^\s*|\s*$/g),
19563 removeSpace: (/\s*/g),
19564 splitSpace: (/\s+/),
19565 trimComma: (/\s*,\s*/g),
19566 featureMember: (/^(.*:)?featureMembers?$/)
19570 * Constructor: OpenLayers.Format.GML.Base
19571 * Instances of this class are not created directly. Use the
19572 * <OpenLayers.Format.GML.v2> or <OpenLayers.Format.GML.v3> constructor
19576 * options - {Object} An optional object whose properties will be set on
19579 * Valid options properties:
19580 * featureType - {Array(String) or String} Local (without prefix) feature
19581 * typeName(s) (required for write).
19582 * featureNS - {String} Feature namespace (required for write).
19583 * geometryName - {String} Geometry element name (required for write).
19585 initialize: function(options) {
19586 OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
19587 this.setGeometryTypes();
19588 if(options && options.featureNS) {
19589 this.setNamespace(this.featurePrefix, options.featureNS);
19591 this.singleFeatureType = !options || (typeof options.featureType === "string");
19598 * data - {DOMElement} A gml:featureMember element, a gml:featureMembers
19599 * element, or an element containing either of the above at any level.
19602 * {Array(<OpenLayers.Feature.Vector>)} An array of features.
19604 read: function(data) {
19605 if(typeof data == "string") {
19606 data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
19608 if(data && data.nodeType == 9) {
19609 data = data.documentElement;
19612 this.readNode(data, {features: features}, true);
19613 if(features.length == 0) {
19614 // look for gml:featureMember elements
19615 var elements = this.getElementsByTagNameNS(
19616 data, this.namespaces.gml, "featureMember"
19618 if(elements.length) {
19619 for(var i=0, len=elements.length; i<len; ++i) {
19620 this.readNode(elements[i], {features: features}, true);
19623 // look for gml:featureMembers elements (this is v3, but does no harm here)
19624 var elements = this.getElementsByTagNameNS(
19625 data, this.namespaces.gml, "featureMembers"
19627 if(elements.length) {
19628 // there can be only one
19629 this.readNode(elements[0], {features: features}, true);
19638 * Shorthand for applying one of the named readers given the node
19639 * namespace and local name. Readers take two args (node, obj) and
19640 * generally extend or modify the second.
19643 * node - {DOMElement} The node to be read (required).
19644 * obj - {Object} The object to be modified (optional).
19645 * first - {Boolean} Should be set to true for the first node read. This
19646 * is usually the readNode call in the read method. Without this being
19647 * set, auto-configured properties will stick on subsequent reads.
19650 * {Object} The input object, modified (or a new one if none was provided).
19652 readNode: function(node, obj, first) {
19653 // on subsequent calls of format.read(), we want to reset auto-
19654 // configured properties and auto-configure again.
19655 if (first === true && this.autoConfig === true) {
19656 this.featureType = null;
19657 delete this.namespaceAlias[this.featureNS];
19658 delete this.namespaces["feature"];
19659 this.featureNS = null;
19661 // featureType auto-configuration
19662 if (!this.featureNS && (!(node.prefix in this.namespaces) &&
19663 node.parentNode.namespaceURI == this.namespaces["gml"] &&
19664 this.regExes.featureMember.test(node.parentNode.nodeName))) {
19665 this.featureType = node.nodeName.split(":").pop();
19666 this.setNamespace("feature", node.namespaceURI);
19667 this.featureNS = node.namespaceURI;
19668 this.autoConfig = true;
19670 return OpenLayers.Format.XML.prototype.readNode.apply(this, [node, obj]);
19674 * Property: readers
19675 * Contains public functions, grouped by namespace prefix, that will
19676 * be applied when a namespaced node is found matching the function
19677 * name. The function will be applied in the scope of this parser
19678 * with two arguments: the node being read and a context object passed
19683 "_inherit": function(node, obj, container) {
19684 // To be implemented by version specific parsers
19686 "featureMember": function(node, obj) {
19687 this.readChildNodes(node, obj);
19689 "featureMembers": function(node, obj) {
19690 this.readChildNodes(node, obj);
19692 "name": function(node, obj) {
19693 obj.name = this.getChildValue(node);
19695 "boundedBy": function(node, obj) {
19696 var container = {};
19697 this.readChildNodes(node, container);
19698 if(container.components && container.components.length > 0) {
19699 obj.bounds = container.components[0];
19702 "Point": function(node, container) {
19703 var obj = {points: []};
19704 this.readChildNodes(node, obj);
19705 if(!container.components) {
19706 container.components = [];
19708 container.components.push(obj.points[0]);
19710 "coordinates": function(node, obj) {
19711 var str = this.getChildValue(node).replace(
19712 this.regExes.trimSpace, ""
19714 str = str.replace(this.regExes.trimComma, ",");
19715 var pointList = str.split(this.regExes.splitSpace);
19717 var numPoints = pointList.length;
19718 var points = new Array(numPoints);
19719 for(var i=0; i<numPoints; ++i) {
19720 coords = pointList[i].split(",");
19722 points[i] = new OpenLayers.Geometry.Point(
19723 coords[0], coords[1], coords[2]
19726 points[i] = new OpenLayers.Geometry.Point(
19727 coords[1], coords[0], coords[2]
19731 obj.points = points;
19733 "coord": function(node, obj) {
19735 this.readChildNodes(node, coord);
19739 obj.points.push(new OpenLayers.Geometry.Point(
19740 coord.x, coord.y, coord.z
19743 "X": function(node, coord) {
19744 coord.x = this.getChildValue(node);
19746 "Y": function(node, coord) {
19747 coord.y = this.getChildValue(node);
19749 "Z": function(node, coord) {
19750 coord.z = this.getChildValue(node);
19752 "MultiPoint": function(node, container) {
19753 var obj = {components: []};
19754 this.readers.gml._inherit.apply(this, [node, obj, container]);
19755 this.readChildNodes(node, obj);
19756 container.components = [
19757 new OpenLayers.Geometry.MultiPoint(obj.components)
19760 "pointMember": function(node, obj) {
19761 this.readChildNodes(node, obj);
19763 "LineString": function(node, container) {
19765 this.readers.gml._inherit.apply(this, [node, obj, container]);
19766 this.readChildNodes(node, obj);
19767 if(!container.components) {
19768 container.components = [];
19770 container.components.push(
19771 new OpenLayers.Geometry.LineString(obj.points)
19774 "MultiLineString": function(node, container) {
19775 var obj = {components: []};
19776 this.readers.gml._inherit.apply(this, [node, obj, container]);
19777 this.readChildNodes(node, obj);
19778 container.components = [
19779 new OpenLayers.Geometry.MultiLineString(obj.components)
19782 "lineStringMember": function(node, obj) {
19783 this.readChildNodes(node, obj);
19785 "Polygon": function(node, container) {
19786 var obj = {outer: null, inner: []};
19787 this.readers.gml._inherit.apply(this, [node, obj, container]);
19788 this.readChildNodes(node, obj);
19789 obj.inner.unshift(obj.outer);
19790 if(!container.components) {
19791 container.components = [];
19793 container.components.push(
19794 new OpenLayers.Geometry.Polygon(obj.inner)
19797 "LinearRing": function(node, obj) {
19798 var container = {};
19799 this.readers.gml._inherit.apply(this, [node, container]);
19800 this.readChildNodes(node, container);
19801 obj.components = [new OpenLayers.Geometry.LinearRing(
19805 "MultiPolygon": function(node, container) {
19806 var obj = {components: []};
19807 this.readers.gml._inherit.apply(this, [node, obj, container]);
19808 this.readChildNodes(node, obj);
19809 container.components = [
19810 new OpenLayers.Geometry.MultiPolygon(obj.components)
19813 "polygonMember": function(node, obj) {
19814 this.readChildNodes(node, obj);
19816 "GeometryCollection": function(node, container) {
19817 var obj = {components: []};
19818 this.readers.gml._inherit.apply(this, [node, obj, container]);
19819 this.readChildNodes(node, obj);
19820 container.components = [
19821 new OpenLayers.Geometry.Collection(obj.components)
19824 "geometryMember": function(node, obj) {
19825 this.readChildNodes(node, obj);
19829 "*": function(node, obj) {
19830 // The node can either be named like the featureType, or it
19831 // can be a child of the feature:featureType. Children can be
19832 // geometry or attributes.
19834 var local = node.localName || node.nodeName.split(":").pop();
19835 // Since an attribute can have the same name as the feature type
19836 // we only want to read the node as a feature if the parent
19837 // node can have feature nodes as children. In this case, the
19838 // obj.features property is set.
19839 if (obj.features) {
19840 if (!this.singleFeatureType &&
19841 (OpenLayers.Util.indexOf(this.featureType, local) !== -1)) {
19842 name = "_typeName";
19843 } else if(local === this.featureType) {
19844 name = "_typeName";
19847 // Assume attribute elements have one child node and that the child
19848 // is a text node. Otherwise assume it is a geometry node.
19849 if(node.childNodes.length == 0 ||
19850 (node.childNodes.length == 1 && node.firstChild.nodeType == 3)) {
19851 if(this.extractAttributes) {
19852 name = "_attribute";
19855 name = "_geometry";
19859 this.readers.feature[name].apply(this, [node, obj]);
19862 "_typeName": function(node, obj) {
19863 var container = {components: [], attributes: {}};
19864 this.readChildNodes(node, container);
19865 // look for common gml namespaced elements
19866 if(container.name) {
19867 container.attributes.name = container.name;
19869 var feature = new OpenLayers.Feature.Vector(
19870 container.components[0], container.attributes
19872 if (!this.singleFeatureType) {
19873 feature.type = node.nodeName.split(":").pop();
19874 feature.namespace = node.namespaceURI;
19876 var fid = node.getAttribute("fid") ||
19877 this.getAttributeNS(node, this.namespaces["gml"], "id");
19881 if(this.internalProjection && this.externalProjection &&
19882 feature.geometry) {
19883 feature.geometry.transform(
19884 this.externalProjection, this.internalProjection
19887 if(container.bounds) {
19888 feature.bounds = container.bounds;
19890 obj.features.push(feature);
19892 "_geometry": function(node, obj) {
19893 if (!this.geometryName) {
19894 this.geometryName = node.nodeName.split(":").pop();
19896 this.readChildNodes(node, obj);
19898 "_attribute": function(node, obj) {
19899 var local = node.localName || node.nodeName.split(":").pop();
19900 var value = this.getChildValue(node);
19901 obj.attributes[local] = value;
19905 "FeatureCollection": function(node, obj) {
19906 this.readChildNodes(node, obj);
19915 * features - {Array(<OpenLayers.Feature.Vector>) | OpenLayers.Feature.Vector}
19916 * An array of features or a single feature.
19919 * {String} Given an array of features, a doc with a gml:featureMembers
19920 * element will be returned. Given a single feature, a doc with a
19921 * gml:featureMember element will be returned.
19923 write: function(features) {
19925 if(OpenLayers.Util.isArray(features)) {
19926 name = "featureMembers";
19928 name = "featureMember";
19930 var root = this.writeNode("gml:" + name, features);
19931 this.setAttributeNS(
19932 root, this.namespaces["xsi"],
19933 "xsi:schemaLocation", this.schemaLocation
19936 return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
19940 * Property: writers
19941 * As a compliment to the readers property, this structure contains public
19942 * writing functions grouped by namespace alias and named like the
19943 * node names they produce.
19947 "featureMember": function(feature) {
19948 var node = this.createElementNSPlus("gml:featureMember");
19949 this.writeNode("feature:_typeName", feature, node);
19952 "MultiPoint": function(geometry) {
19953 var node = this.createElementNSPlus("gml:MultiPoint");
19954 var components = geometry.components || [geometry];
19955 for(var i=0, ii=components.length; i<ii; ++i) {
19956 this.writeNode("pointMember", components[i], node);
19960 "pointMember": function(geometry) {
19961 var node = this.createElementNSPlus("gml:pointMember");
19962 this.writeNode("Point", geometry, node);
19965 "MultiLineString": function(geometry) {
19966 var node = this.createElementNSPlus("gml:MultiLineString");
19967 var components = geometry.components || [geometry];
19968 for(var i=0, ii=components.length; i<ii; ++i) {
19969 this.writeNode("lineStringMember", components[i], node);
19973 "lineStringMember": function(geometry) {
19974 var node = this.createElementNSPlus("gml:lineStringMember");
19975 this.writeNode("LineString", geometry, node);
19978 "MultiPolygon": function(geometry) {
19979 var node = this.createElementNSPlus("gml:MultiPolygon");
19980 var components = geometry.components || [geometry];
19981 for(var i=0, ii=components.length; i<ii; ++i) {
19983 "polygonMember", components[i], node
19988 "polygonMember": function(geometry) {
19989 var node = this.createElementNSPlus("gml:polygonMember");
19990 this.writeNode("Polygon", geometry, node);
19993 "GeometryCollection": function(geometry) {
19994 var node = this.createElementNSPlus("gml:GeometryCollection");
19995 for(var i=0, len=geometry.components.length; i<len; ++i) {
19996 this.writeNode("geometryMember", geometry.components[i], node);
20000 "geometryMember": function(geometry) {
20001 var node = this.createElementNSPlus("gml:geometryMember");
20002 var child = this.writeNode("feature:_geometry", geometry);
20003 node.appendChild(child.firstChild);
20008 "_typeName": function(feature) {
20009 var node = this.createElementNSPlus(this.featurePrefix + ":" + this.featureType, {
20010 attributes: {fid: feature.fid}
20012 if(feature.geometry) {
20013 this.writeNode("feature:_geometry", feature.geometry, node);
20015 for(var name in feature.attributes) {
20016 var value = feature.attributes[name];
20017 if(value != null) {
20019 "feature:_attribute",
20020 {name: name, value: value}, node
20026 "_geometry": function(geometry) {
20027 if(this.externalProjection && this.internalProjection) {
20028 geometry = geometry.clone().transform(
20029 this.internalProjection, this.externalProjection
20032 var node = this.createElementNSPlus(
20033 this.featurePrefix + ":" + this.geometryName
20035 var type = this.geometryTypes[geometry.CLASS_NAME];
20036 var child = this.writeNode("gml:" + type, geometry, node);
20038 child.setAttribute("srsName", this.srsName);
20042 "_attribute": function(obj) {
20043 return this.createElementNSPlus(this.featurePrefix + ":" + obj.name, {
20049 "FeatureCollection": function(features) {
20051 * This is only here because GML2 only describes abstract
20052 * feature collections. Typically, you would not be using
20053 * the GML format to write wfs elements. This just provides
20054 * some way to write out lists of features. GML3 defines the
20055 * featureMembers element, so that is used by default instead.
20057 var node = this.createElementNSPlus("wfs:FeatureCollection");
20058 for(var i=0, len=features.length; i<len; ++i) {
20059 this.writeNode("gml:featureMember", features[i], node);
20067 * Method: setGeometryTypes
20068 * Sets the <geometryTypes> mapping.
20070 setGeometryTypes: function() {
20071 this.geometryTypes = {
20072 "OpenLayers.Geometry.Point": "Point",
20073 "OpenLayers.Geometry.MultiPoint": "MultiPoint",
20074 "OpenLayers.Geometry.LineString": "LineString",
20075 "OpenLayers.Geometry.MultiLineString": "MultiLineString",
20076 "OpenLayers.Geometry.Polygon": "Polygon",
20077 "OpenLayers.Geometry.MultiPolygon": "MultiPolygon",
20078 "OpenLayers.Geometry.Collection": "GeometryCollection"
20082 CLASS_NAME: "OpenLayers.Format.GML.Base"
20085 /* ======================================================================
20086 OpenLayers/Format/GML/v2.js
20087 ====================================================================== */
20089 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
20090 * full list of contributors). Published under the 2-clause BSD license.
20091 * See license.txt in the OpenLayers distribution or repository for the
20092 * full text of the license. */
20095 * @requires OpenLayers/Format/GML/Base.js
20099 * Class: OpenLayers.Format.GML.v2
20100 * Parses GML version 2.
20103 * - <OpenLayers.Format.GML.Base>
20105 OpenLayers.Format.GML.v2 = OpenLayers.Class(OpenLayers.Format.GML.Base, {
20108 * Property: schemaLocation
20109 * {String} Schema location for a particular minor version.
20111 schemaLocation: "http://www.opengis.net/gml http://schemas.opengis.net/gml/2.1.2/feature.xsd",
20114 * Constructor: OpenLayers.Format.GML.v2
20115 * Create a parser for GML v2.
20118 * options - {Object} An optional object whose properties will be set on
20121 * Valid options properties:
20122 * featureType - {String} Local (without prefix) feature typeName (required).
20123 * featureNS - {String} Feature namespace (required).
20124 * geometryName - {String} Geometry element name.
20126 initialize: function(options) {
20127 OpenLayers.Format.GML.Base.prototype.initialize.apply(this, [options]);
20131 * Property: readers
20132 * Contains public functions, grouped by namespace prefix, that will
20133 * be applied when a namespaced node is found matching the function
20134 * name. The function will be applied in the scope of this parser
20135 * with two arguments: the node being read and a context object passed
20139 "gml": OpenLayers.Util.applyDefaults({
20140 "outerBoundaryIs": function(node, container) {
20142 this.readChildNodes(node, obj);
20143 container.outer = obj.components[0];
20145 "innerBoundaryIs": function(node, container) {
20147 this.readChildNodes(node, obj);
20148 container.inner.push(obj.components[0]);
20150 "Box": function(node, container) {
20152 this.readChildNodes(node, obj);
20153 if(!container.components) {
20154 container.components = [];
20156 var min = obj.points[0];
20157 var max = obj.points[1];
20158 container.components.push(
20159 new OpenLayers.Bounds(min.x, min.y, max.x, max.y)
20162 }, OpenLayers.Format.GML.Base.prototype.readers["gml"]),
20163 "feature": OpenLayers.Format.GML.Base.prototype.readers["feature"],
20164 "wfs": OpenLayers.Format.GML.Base.prototype.readers["wfs"]
20171 * features - {Array(<OpenLayers.Feature.Vector>) | OpenLayers.Feature.Vector}
20172 * An array of features or a single feature.
20175 * {String} Given an array of features, a doc with a gml:featureMembers
20176 * element will be returned. Given a single feature, a doc with a
20177 * gml:featureMember element will be returned.
20179 write: function(features) {
20181 if(OpenLayers.Util.isArray(features)) {
20182 // GML2 only has abstract feature collections
20183 // wfs provides a feature collection from a well-known schema
20184 name = "wfs:FeatureCollection";
20186 name = "gml:featureMember";
20188 var root = this.writeNode(name, features);
20189 this.setAttributeNS(
20190 root, this.namespaces["xsi"],
20191 "xsi:schemaLocation", this.schemaLocation
20194 return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
20198 * Property: writers
20199 * As a compliment to the readers property, this structure contains public
20200 * writing functions grouped by namespace alias and named like the
20201 * node names they produce.
20204 "gml": OpenLayers.Util.applyDefaults({
20205 "Point": function(geometry) {
20206 var node = this.createElementNSPlus("gml:Point");
20207 this.writeNode("coordinates", [geometry], node);
20210 "coordinates": function(points) {
20211 var numPoints = points.length;
20212 var parts = new Array(numPoints);
20214 for(var i=0; i<numPoints; ++i) {
20217 parts[i] = point.x + "," + point.y;
20219 parts[i] = point.y + "," + point.x;
20221 if(point.z != undefined) { // allow null or undefined
20222 parts[i] += "," + point.z;
20225 return this.createElementNSPlus("gml:coordinates", {
20227 decimal: ".", cs: ",", ts: " "
20229 value: (numPoints == 1) ? parts[0] : parts.join(" ")
20232 "LineString": function(geometry) {
20233 var node = this.createElementNSPlus("gml:LineString");
20234 this.writeNode("coordinates", geometry.components, node);
20237 "Polygon": function(geometry) {
20238 var node = this.createElementNSPlus("gml:Polygon");
20239 this.writeNode("outerBoundaryIs", geometry.components[0], node);
20240 for(var i=1; i<geometry.components.length; ++i) {
20242 "innerBoundaryIs", geometry.components[i], node
20247 "outerBoundaryIs": function(ring) {
20248 var node = this.createElementNSPlus("gml:outerBoundaryIs");
20249 this.writeNode("LinearRing", ring, node);
20252 "innerBoundaryIs": function(ring) {
20253 var node = this.createElementNSPlus("gml:innerBoundaryIs");
20254 this.writeNode("LinearRing", ring, node);
20257 "LinearRing": function(ring) {
20258 var node = this.createElementNSPlus("gml:LinearRing");
20259 this.writeNode("coordinates", ring.components, node);
20262 "Box": function(bounds) {
20263 var node = this.createElementNSPlus("gml:Box");
20264 this.writeNode("coordinates", [
20265 {x: bounds.left, y: bounds.bottom},
20266 {x: bounds.right, y: bounds.top}
20268 // srsName attribute is optional for gml:Box
20270 node.setAttribute("srsName", this.srsName);
20274 }, OpenLayers.Format.GML.Base.prototype.writers["gml"]),
20275 "feature": OpenLayers.Format.GML.Base.prototype.writers["feature"],
20276 "wfs": OpenLayers.Format.GML.Base.prototype.writers["wfs"]
20279 CLASS_NAME: "OpenLayers.Format.GML.v2"
20282 /* ======================================================================
20283 OpenLayers/Format/OGCExceptionReport.js
20284 ====================================================================== */
20286 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
20287 * full list of contributors). Published under the 2-clause BSD license.
20288 * See license.txt in the OpenLayers distribution or repository for the
20289 * full text of the license. */
20292 * @requires OpenLayers/Format/XML.js
20296 * Class: OpenLayers.Format.OGCExceptionReport
20297 * Class to read exception reports for various OGC services and versions.
20300 * - <OpenLayers.Format.XML>
20302 OpenLayers.Format.OGCExceptionReport = OpenLayers.Class(OpenLayers.Format.XML, {
20305 * Property: namespaces
20306 * {Object} Mapping of namespace aliases to namespace URIs.
20309 ogc: "http://www.opengis.net/ogc"
20313 * Property: regExes
20314 * Compiled regular expressions for manipulating strings.
20317 trimSpace: (/^\s*|\s*$/g),
20318 removeSpace: (/\s*/g),
20319 splitSpace: (/\s+/),
20320 trimComma: (/\s*,\s*/g)
20324 * Property: defaultPrefix
20326 defaultPrefix: "ogc",
20329 * Constructor: OpenLayers.Format.OGCExceptionReport
20330 * Create a new parser for OGC exception reports.
20333 * options - {Object} An optional object whose properties will be set on
20339 * Read OGC exception report data from a string, and return an object with
20340 * information about the exceptions.
20343 * data - {String} or {DOMElement} data to read/parse.
20346 * {Object} Information about the exceptions that occurred.
20348 read: function(data) {
20350 if(typeof data == "string") {
20351 data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
20353 var root = data.documentElement;
20354 var exceptionInfo = {exceptionReport: null};
20356 this.readChildNodes(data, exceptionInfo);
20357 if (exceptionInfo.exceptionReport === null) {
20358 // fall-back to OWSCommon since this is a common output format for exceptions
20359 // we cannot easily use the ows readers directly since they differ for 1.0 and 1.1
20360 exceptionInfo = new OpenLayers.Format.OWSCommon().read(data);
20363 return exceptionInfo;
20367 * Property: readers
20368 * Contains public functions, grouped by namespace prefix, that will
20369 * be applied when a namespaced node is found matching the function
20370 * name. The function will be applied in the scope of this parser
20371 * with two arguments: the node being read and a context object passed
20376 "ServiceExceptionReport": function(node, obj) {
20377 obj.exceptionReport = {exceptions: []};
20378 this.readChildNodes(node, obj.exceptionReport);
20380 "ServiceException": function(node, exceptionReport) {
20382 code: node.getAttribute("code"),
20383 locator: node.getAttribute("locator"),
20384 text: this.getChildValue(node)
20386 exceptionReport.exceptions.push(exception);
20391 CLASS_NAME: "OpenLayers.Format.OGCExceptionReport"
20394 /* ======================================================================
20395 OpenLayers/Format/XML/VersionedOGC.js
20396 ====================================================================== */
20398 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
20399 * full list of contributors). Published under the 2-clause BSD license.
20400 * See license.txt in the OpenLayers distribution or repository for the
20401 * full text of the license. */
20404 * @requires OpenLayers/Format/XML.js
20405 * @requires OpenLayers/Format/OGCExceptionReport.js
20409 * Class: OpenLayers.Format.XML.VersionedOGC
20410 * Base class for versioned formats, i.e. a format which supports multiple
20413 * To enable checking if parsing succeeded, you will need to define a property
20414 * called errorProperty on the parser you want to check. The parser will then
20415 * check the returned object to see if that property is present. If it is, it
20416 * assumes the parsing was successful. If it is not present (or is null), it will
20417 * pass the document through an OGCExceptionReport parser.
20419 * If errorProperty is undefined for the parser, this error checking mechanism
20420 * will be disabled.
20425 * - <OpenLayers.Format.XML>
20427 OpenLayers.Format.XML.VersionedOGC = OpenLayers.Class(OpenLayers.Format.XML, {
20430 * APIProperty: defaultVersion
20431 * {String} Version number to assume if none found.
20433 defaultVersion: null,
20436 * APIProperty: version
20437 * {String} Specify a version string if one is known.
20442 * APIProperty: profile
20443 * {String} If provided, use a custom profile.
20448 * APIProperty: allowFallback
20449 * {Boolean} If a profiled parser cannot be found for the returned version,
20450 * use a non-profiled parser as the fallback. Application code using this
20451 * should take into account that the return object structure might be
20452 * missing the specifics of the profile. Defaults to false.
20454 allowFallback: false,
20458 * {String} The name of this parser, this is the part of the CLASS_NAME
20459 * except for "OpenLayers.Format."
20464 * APIProperty: stringifyOutput
20465 * {Boolean} If true, write will return a string otherwise a DOMElement.
20466 * Default is false.
20468 stringifyOutput: false,
20472 * {Object} Instance of the versioned parser. Cached for multiple read and
20473 * write calls of the same version.
20478 * Constructor: OpenLayers.Format.XML.VersionedOGC.
20482 * options - {Object} Optional object whose properties will be set on
20485 initialize: function(options) {
20486 OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
20487 var className = this.CLASS_NAME;
20488 this.name = className.substring(className.lastIndexOf(".")+1);
20492 * Method: getVersion
20493 * Returns the version to use. Subclasses can override this function
20494 * if a different version detection is needed.
20497 * root - {DOMElement}
20498 * options - {Object} Optional configuration object.
20501 * {String} The version to use.
20503 getVersion: function(root, options) {
20507 version = this.version;
20509 version = root.getAttribute("version");
20511 version = this.defaultVersion;
20515 version = (options && options.version) ||
20516 this.version || this.defaultVersion;
20522 * Method: getParser
20523 * Get an instance of the cached parser if available, otherwise create one.
20526 * version - {String}
20529 * {<OpenLayers.Format>}
20531 getParser: function(version) {
20532 version = version || this.defaultVersion;
20533 var profile = this.profile ? "_" + this.profile : "";
20534 if(!this.parser || this.parser.VERSION != version) {
20535 var format = OpenLayers.Format[this.name][
20536 "v" + version.replace(/\./g, "_") + profile
20539 if (profile !== "" && this.allowFallback) {
20540 // fallback to the non-profiled version of the parser
20542 format = OpenLayers.Format[this.name][
20543 "v" + version.replace(/\./g, "_")
20547 throw "Can't find a " + this.name + " parser for version " +
20551 this.parser = new format(this.options);
20553 return this.parser;
20558 * Write a document.
20561 * obj - {Object} An object representing the document.
20562 * options - {Object} Optional configuration object.
20565 * {String} The document as a string
20567 write: function(obj, options) {
20568 var version = this.getVersion(null, options);
20569 this.parser = this.getParser(version);
20570 var root = this.parser.write(obj, options);
20571 if (this.stringifyOutput === false) {
20574 return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
20580 * Read a doc and return an object representing the document.
20583 * data - {String | DOMElement} Data to read.
20584 * options - {Object} Options for the reader.
20587 * {Object} An object representing the document.
20589 read: function(data, options) {
20590 if(typeof data == "string") {
20591 data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
20593 var root = data.documentElement;
20594 var version = this.getVersion(root);
20595 this.parser = this.getParser(version); // Select the parser
20596 var obj = this.parser.read(data, options); // Parse the data
20598 var errorProperty = this.parser.errorProperty || null;
20599 if (errorProperty !== null && obj[errorProperty] === undefined) {
20600 // an error must have happened, so parse it and report back
20601 var format = new OpenLayers.Format.OGCExceptionReport();
20602 obj.error = format.read(data);
20604 obj.version = version;
20605 obj.requestType = this.name;
20609 CLASS_NAME: "OpenLayers.Format.XML.VersionedOGC"
20611 /* ======================================================================
20612 OpenLayers/Filter/Logical.js
20613 ====================================================================== */
20615 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
20616 * full list of contributors). Published under the 2-clause BSD license.
20617 * See license.txt in the OpenLayers distribution or repository for the
20618 * full text of the license. */
20622 * @requires OpenLayers/Filter.js
20626 * Class: OpenLayers.Filter.Logical
20627 * This class represents ogc:And, ogc:Or and ogc:Not rules.
20630 * - <OpenLayers.Filter>
20632 OpenLayers.Filter.Logical = OpenLayers.Class(OpenLayers.Filter, {
20635 * APIProperty: filters
20636 * {Array(<OpenLayers.Filter>)} Child filters for this filter.
20641 * APIProperty: type
20642 * {String} type of logical operator. Available types are:
20643 * - OpenLayers.Filter.Logical.AND = "&&";
20644 * - OpenLayers.Filter.Logical.OR = "||";
20645 * - OpenLayers.Filter.Logical.NOT = "!";
20650 * Constructor: OpenLayers.Filter.Logical
20651 * Creates a logical filter (And, Or, Not).
20654 * options - {Object} An optional object with properties to set on the
20658 * {<OpenLayers.Filter.Logical>}
20660 initialize: function(options) {
20662 OpenLayers.Filter.prototype.initialize.apply(this, [options]);
20666 * APIMethod: destroy
20667 * Remove reference to child filters.
20669 destroy: function() {
20670 this.filters = null;
20671 OpenLayers.Filter.prototype.destroy.apply(this);
20675 * APIMethod: evaluate
20676 * Evaluates this filter in a specific context.
20679 * context - {Object} Context to use in evaluating the filter. A vector
20680 * feature may also be provided to evaluate feature attributes in
20681 * comparison filters or geometries in spatial filters.
20684 * {Boolean} The filter applies.
20686 evaluate: function(context) {
20688 switch(this.type) {
20689 case OpenLayers.Filter.Logical.AND:
20690 for (i=0, len=this.filters.length; i<len; i++) {
20691 if (this.filters[i].evaluate(context) == false) {
20697 case OpenLayers.Filter.Logical.OR:
20698 for (i=0, len=this.filters.length; i<len; i++) {
20699 if (this.filters[i].evaluate(context) == true) {
20705 case OpenLayers.Filter.Logical.NOT:
20706 return (!this.filters[0].evaluate(context));
20713 * Clones this filter.
20716 * {<OpenLayers.Filter.Logical>} Clone of this filter.
20718 clone: function() {
20720 for(var i=0, len=this.filters.length; i<len; ++i) {
20721 filters.push(this.filters[i].clone());
20723 return new OpenLayers.Filter.Logical({
20729 CLASS_NAME: "OpenLayers.Filter.Logical"
20733 OpenLayers.Filter.Logical.AND = "&&";
20734 OpenLayers.Filter.Logical.OR = "||";
20735 OpenLayers.Filter.Logical.NOT = "!";
20736 /* ======================================================================
20737 OpenLayers/Filter/Comparison.js
20738 ====================================================================== */
20740 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
20741 * full list of contributors). Published under the 2-clause BSD license.
20742 * See license.txt in the OpenLayers distribution or repository for the
20743 * full text of the license. */
20746 * @requires OpenLayers/Filter.js
20750 * Class: OpenLayers.Filter.Comparison
20751 * This class represents a comparison filter.
20754 * - <OpenLayers.Filter>
20756 OpenLayers.Filter.Comparison = OpenLayers.Class(OpenLayers.Filter, {
20759 * APIProperty: type
20760 * {String} type: type of the comparison. This is one of
20761 * - OpenLayers.Filter.Comparison.EQUAL_TO = "==";
20762 * - OpenLayers.Filter.Comparison.NOT_EQUAL_TO = "!=";
20763 * - OpenLayers.Filter.Comparison.LESS_THAN = "<";
20764 * - OpenLayers.Filter.Comparison.GREATER_THAN = ">";
20765 * - OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = "<=";
20766 * - OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">=";
20767 * - OpenLayers.Filter.Comparison.BETWEEN = "..";
20768 * - OpenLayers.Filter.Comparison.LIKE = "~";
20769 * - OpenLayers.Filter.Comparison.IS_NULL = "NULL";
20774 * APIProperty: property
20776 * name of the context property to compare
20781 * APIProperty: value
20782 * {Number} or {String}
20783 * comparison value for binary comparisons. In the case of a String, this
20784 * can be a combination of text and propertyNames in the form
20785 * "literal ${propertyName}"
20790 * Property: matchCase
20791 * {Boolean} Force case sensitive searches for EQUAL_TO and NOT_EQUAL_TO
20792 * comparisons. The Filter Encoding 1.1 specification added a matchCase
20793 * attribute to ogc:PropertyIsEqualTo and ogc:PropertyIsNotEqualTo
20794 * elements. This property will be serialized with those elements only
20795 * if using the v1.1.0 filter format. However, when evaluating filters
20796 * here, the matchCase property will always be respected (for EQUAL_TO
20797 * and NOT_EQUAL_TO). Default is true.
20802 * APIProperty: lowerBoundary
20803 * {Number} or {String}
20804 * lower boundary for between comparisons. In the case of a String, this
20805 * can be a combination of text and propertyNames in the form
20806 * "literal ${propertyName}"
20808 lowerBoundary: null,
20811 * APIProperty: upperBoundary
20812 * {Number} or {String}
20813 * upper boundary for between comparisons. In the case of a String, this
20814 * can be a combination of text and propertyNames in the form
20815 * "literal ${propertyName}"
20817 upperBoundary: null,
20820 * Constructor: OpenLayers.Filter.Comparison
20821 * Creates a comparison rule.
20824 * options - {Object} An optional object with properties to set on the
20828 * {<OpenLayers.Filter.Comparison>}
20830 initialize: function(options) {
20831 OpenLayers.Filter.prototype.initialize.apply(this, [options]);
20832 // since matchCase on PropertyIsLike is not schema compliant, we only
20833 // want to use this if explicitly asked for
20834 if (this.type === OpenLayers.Filter.Comparison.LIKE
20835 && options.matchCase === undefined) {
20836 this.matchCase = null;
20841 * APIMethod: evaluate
20842 * Evaluates this filter in a specific context.
20845 * context - {Object} Context to use in evaluating the filter. If a vector
20846 * feature is provided, the feature.attributes will be used as context.
20849 * {Boolean} The filter applies.
20851 evaluate: function(context) {
20852 if (context instanceof OpenLayers.Feature.Vector) {
20853 context = context.attributes;
20855 var result = false;
20856 var got = context[this.property];
20857 if (got === undefined) {
20861 switch(this.type) {
20862 case OpenLayers.Filter.Comparison.EQUAL_TO:
20864 if(!this.matchCase &&
20865 typeof got == "string" && typeof exp == "string") {
20866 result = (got.toUpperCase() == exp.toUpperCase());
20868 result = (got == exp);
20871 case OpenLayers.Filter.Comparison.NOT_EQUAL_TO:
20873 if(!this.matchCase &&
20874 typeof got == "string" && typeof exp == "string") {
20875 result = (got.toUpperCase() != exp.toUpperCase());
20877 result = (got != exp);
20880 case OpenLayers.Filter.Comparison.LESS_THAN:
20881 result = got < this.value;
20883 case OpenLayers.Filter.Comparison.GREATER_THAN:
20884 result = got > this.value;
20886 case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO:
20887 result = got <= this.value;
20889 case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO:
20890 result = got >= this.value;
20892 case OpenLayers.Filter.Comparison.BETWEEN:
20893 result = (got >= this.lowerBoundary) &&
20894 (got <= this.upperBoundary);
20896 case OpenLayers.Filter.Comparison.LIKE:
20897 var regexp = new RegExp(this.value, "gi");
20898 result = regexp.test(got);
20900 case OpenLayers.Filter.Comparison.IS_NULL:
20901 result = (got === null);
20908 * APIMethod: value2regex
20909 * Converts the value of this rule into a regular expression string,
20910 * according to the wildcard characters specified. This method has to
20911 * be called after instantiation of this class, if the value is not a
20912 * regular expression already.
20915 * wildCard - {Char} wildcard character in the above value, default
20917 * singleChar - {Char} single-character wildcard in the above value
20919 * escapeChar - {Char} escape character in the above value, default is
20923 * {String} regular expression string
20925 value2regex: function(wildCard, singleChar, escapeChar) {
20926 if (wildCard == ".") {
20927 throw new Error("'.' is an unsupported wildCard character for " +
20928 "OpenLayers.Filter.Comparison");
20932 // set UMN MapServer defaults for unspecified parameters
20933 wildCard = wildCard ? wildCard : "*";
20934 singleChar = singleChar ? singleChar : ".";
20935 escapeChar = escapeChar ? escapeChar : "!";
20937 this.value = this.value.replace(
20938 new RegExp("\\"+escapeChar+"(.|$)", "g"), "\\$1");
20939 this.value = this.value.replace(
20940 new RegExp("\\"+singleChar, "g"), ".");
20941 this.value = this.value.replace(
20942 new RegExp("\\"+wildCard, "g"), ".*");
20943 this.value = this.value.replace(
20944 new RegExp("\\\\.\\*", "g"), "\\"+wildCard);
20945 this.value = this.value.replace(
20946 new RegExp("\\\\\\.", "g"), "\\"+singleChar);
20952 * Method: regex2value
20953 * Convert the value of this rule from a regular expression string into an
20954 * ogc literal string using a wildCard of *, a singleChar of ., and an
20955 * escape of !. Leaves the <value> property unmodified.
20958 * {String} A string value.
20960 regex2value: function() {
20962 var value = this.value;
20964 // replace ! with !!
20965 value = value.replace(/!/g, "!!");
20967 // replace \. with !. (watching out for \\.)
20968 value = value.replace(/(\\)?\\\./g, function($0, $1) {
20969 return $1 ? $0 : "!.";
20972 // replace \* with #* (watching out for \\*)
20973 value = value.replace(/(\\)?\\\*/g, function($0, $1) {
20974 return $1 ? $0 : "!*";
20977 // replace \\ with \
20978 value = value.replace(/\\\\/g, "\\");
20980 // convert .* to * (the sequence #.* is not allowed)
20981 value = value.replace(/\.\*/g, "*");
20988 * Clones this filter.
20991 * {<OpenLayers.Filter.Comparison>} Clone of this filter.
20993 clone: function() {
20994 return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison(), this);
20997 CLASS_NAME: "OpenLayers.Filter.Comparison"
21001 OpenLayers.Filter.Comparison.EQUAL_TO = "==";
21002 OpenLayers.Filter.Comparison.NOT_EQUAL_TO = "!=";
21003 OpenLayers.Filter.Comparison.LESS_THAN = "<";
21004 OpenLayers.Filter.Comparison.GREATER_THAN = ">";
21005 OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = "<=";
21006 OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">=";
21007 OpenLayers.Filter.Comparison.BETWEEN = "..";
21008 OpenLayers.Filter.Comparison.LIKE = "~";
21009 OpenLayers.Filter.Comparison.IS_NULL = "NULL";
21010 /* ======================================================================
21011 OpenLayers/Format/Filter.js
21012 ====================================================================== */
21014 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
21015 * full list of contributors). Published under the 2-clause BSD license.
21016 * See license.txt in the OpenLayers distribution or repository for the
21017 * full text of the license. */
21020 * @requires OpenLayers/Format/XML/VersionedOGC.js
21021 * @requires OpenLayers/Filter/FeatureId.js
21022 * @requires OpenLayers/Filter/Logical.js
21023 * @requires OpenLayers/Filter/Comparison.js
21027 * Class: OpenLayers.Format.Filter
21028 * Read/Write ogc:Filter. Create a new instance with the <OpenLayers.Format.Filter>
21032 * - <OpenLayers.Format.XML.VersionedOGC>
21034 OpenLayers.Format.Filter = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
21037 * APIProperty: defaultVersion
21038 * {String} Version number to assume if none found. Default is "1.0.0".
21040 defaultVersion: "1.0.0",
21044 * Write an ogc:Filter given a filter object.
21047 * filter - {<OpenLayers.Filter>} An filter.
21048 * options - {Object} Optional configuration object.
21051 * {Elment} An ogc:Filter element node.
21056 * Read and Filter doc and return an object representing the Filter.
21059 * data - {String | DOMElement} Data to read.
21062 * {<OpenLayers.Filter>} A filter object.
21065 CLASS_NAME: "OpenLayers.Format.Filter"
21067 /* ======================================================================
21068 OpenLayers/Filter/Function.js
21069 ====================================================================== */
21071 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
21072 * full list of contributors). Published under the 2-clause BSD license.
21073 * See license.txt in the OpenLayers distribution or repository for the
21074 * full text of the license. */
21077 * @requires OpenLayers/Filter.js
21081 * Class: OpenLayers.Filter.Function
21082 * This class represents a filter function.
21083 * We are using this class for creation of complex
21084 * filters that can contain filter functions as values.
21085 * Nesting function as other functions parameter is supported.
21088 * - <OpenLayers.Filter>
21090 OpenLayers.Filter.Function = OpenLayers.Class(OpenLayers.Filter, {
21093 * APIProperty: name
21094 * {String} Name of the function.
21099 * APIProperty: params
21100 * {Array(<OpenLayers.Filter.Function> || String || Number)} Function parameters
21101 * For now support only other Functions, String or Number
21106 * Constructor: OpenLayers.Filter.Function
21107 * Creates a filter function.
21110 * options - {Object} An optional object with properties to set on the
21114 * {<OpenLayers.Filter.Function>}
21117 CLASS_NAME: "OpenLayers.Filter.Function"
21120 /* ======================================================================
21121 OpenLayers/BaseTypes/Date.js
21122 ====================================================================== */
21124 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
21125 * full list of contributors). Published under the 2-clause BSD license.
21126 * See license.txt in the OpenLayers distribution or repository for the
21127 * full text of the license. */
21130 * @requires OpenLayers/SingleFile.js
21134 * Namespace: OpenLayers.Date
21135 * Contains implementations of Date.parse and date.toISOString that match the
21136 * ECMAScript 5 specification for parsing RFC 3339 dates.
21137 * http://tools.ietf.org/html/rfc3339
21139 OpenLayers.Date = {
21142 * APIProperty: dateRegEx
21143 * The regex to be used for validating dates. You can provide your own
21144 * regex for instance for adding support for years before BC. Default
21145 * value is: /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:(?:T(\d{1,2}):(\d{2}):(\d{2}(?:\.\d+)?)(Z|(?:[+-]\d{1,2}(?::(\d{2}))?)))|Z)?$/
21147 dateRegEx: /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:(?:T(\d{1,2}):(\d{2}):(\d{2}(?:\.\d+)?)(Z|(?:[+-]\d{1,2}(?::(\d{2}))?)))|Z)?$/,
21150 * APIMethod: toISOString
21151 * Generates a string representing a date. The format of the string follows
21152 * the profile of ISO 8601 for date and time on the Internet (see
21153 * http://tools.ietf.org/html/rfc3339). If the toISOString method is
21154 * available on the Date prototype, that is used. The toISOString
21155 * method for Date instances is defined in ECMA-262.
21158 * date - {Date} A date object.
21161 * {String} A string representing the date (e.g.
21162 * "2010-08-07T16:58:23.123Z"). If the date does not have a valid time
21163 * (i.e. isNaN(date.getTime())) this method returns the string "Invalid
21164 * Date". The ECMA standard says the toISOString method should throw
21165 * RangeError in this case, but Firefox returns a string instead. For
21166 * best results, use isNaN(date.getTime()) to determine date validity
21167 * before generating date strings.
21169 toISOString: (function() {
21170 if ("toISOString" in Date.prototype) {
21171 return function(date) {
21172 return date.toISOString();
21175 return function(date) {
21177 if (isNaN(date.getTime())) {
21178 // ECMA-262 says throw RangeError, Firefox returns
21180 str = "Invalid Date";
21183 date.getUTCFullYear() + "-" +
21184 OpenLayers.Number.zeroPad(date.getUTCMonth() + 1, 2) + "-" +
21185 OpenLayers.Number.zeroPad(date.getUTCDate(), 2) + "T" +
21186 OpenLayers.Number.zeroPad(date.getUTCHours(), 2) + ":" +
21187 OpenLayers.Number.zeroPad(date.getUTCMinutes(), 2) + ":" +
21188 OpenLayers.Number.zeroPad(date.getUTCSeconds(), 2) + "." +
21189 OpenLayers.Number.zeroPad(date.getUTCMilliseconds(), 3) + "Z";
21199 * Generate a date object from a string. The format for the string follows
21200 * the profile of ISO 8601 for date and time on the Internet (see
21201 * http://tools.ietf.org/html/rfc3339). We don't call the native
21202 * Date.parse because of inconsistency between implmentations. In
21203 * Chrome, calling Date.parse with a string that doesn't contain any
21204 * indication of the timezone (e.g. "2011"), the date is interpreted
21205 * in local time. On Firefox, the assumption is UTC.
21208 * str - {String} A string representing the date (e.g.
21209 * "2010", "2010-08", "2010-08-07", "2010-08-07T16:58:23.123Z",
21210 * "2010-08-07T11:58:23.123-06").
21213 * {Date} A date object. If the string could not be parsed, an invalid
21214 * date is returned (i.e. isNaN(date.getTime())).
21216 parse: function(str) {
21218 var match = str.match(this.dateRegEx);
21219 if (match && (match[1] || match[7])) { // must have at least year or time
21220 var year = parseInt(match[1], 10) || 0;
21221 var month = (parseInt(match[2], 10) - 1) || 0;
21222 var day = parseInt(match[3], 10) || 1;
21223 date = new Date(Date.UTC(year, month, day));
21225 var type = match[7];
21227 var hours = parseInt(match[4], 10);
21228 var minutes = parseInt(match[5], 10);
21229 var secFrac = parseFloat(match[6]);
21230 var seconds = secFrac | 0;
21231 var milliseconds = Math.round(1000 * (secFrac - seconds));
21232 date.setUTCHours(hours, minutes, seconds, milliseconds);
21234 if (type !== "Z") {
21235 var hoursOffset = parseInt(type, 10);
21236 var minutesOffset = parseInt(match[8], 10) || 0;
21237 var offset = -1000 * (60 * (hoursOffset * 60) + minutesOffset * 60);
21238 date = new Date(date.getTime() + offset);
21242 date = new Date("invalid");
21247 /* ======================================================================
21248 OpenLayers/Format/Filter/v1.js
21249 ====================================================================== */
21251 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
21252 * full list of contributors). Published under the 2-clause BSD license.
21253 * See license.txt in the OpenLayers distribution or repository for the
21254 * full text of the license. */
21256 * @requires OpenLayers/Format/Filter.js
21257 * @requires OpenLayers/Format/XML.js
21258 * @requires OpenLayers/Filter/Function.js
21259 * @requires OpenLayers/BaseTypes/Date.js
21263 * Class: OpenLayers.Format.Filter.v1
21264 * Superclass for Filter version 1 parsers.
21267 * - <OpenLayers.Format.XML>
21269 OpenLayers.Format.Filter.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
21272 * Property: namespaces
21273 * {Object} Mapping of namespace aliases to namespace URIs.
21276 ogc: "http://www.opengis.net/ogc",
21277 gml: "http://www.opengis.net/gml",
21278 xlink: "http://www.w3.org/1999/xlink",
21279 xsi: "http://www.w3.org/2001/XMLSchema-instance"
21283 * Property: defaultPrefix
21285 defaultPrefix: "ogc",
21288 * Property: schemaLocation
21289 * {String} Schema location for a particular minor version.
21291 schemaLocation: null,
21294 * Constructor: OpenLayers.Format.Filter.v1
21295 * Instances of this class are not created directly. Use the
21296 * <OpenLayers.Format.Filter> constructor instead.
21299 * options - {Object} An optional object whose properties will be set on
21302 initialize: function(options) {
21303 OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
21310 * data - {DOMElement} A Filter document element.
21313 * {<OpenLayers.Filter>} A filter object.
21315 read: function(data) {
21317 this.readers.ogc["Filter"].apply(this, [data, obj]);
21322 * Property: readers
21323 * Contains public functions, grouped by namespace prefix, that will
21324 * be applied when a namespaced node is found matching the function
21325 * name. The function will be applied in the scope of this parser
21326 * with two arguments: the node being read and a context object passed
21331 "_expression": function(node) {
21332 // only the simplest of ogc:expression handled
21333 // "some text and an <PropertyName>attribute</PropertyName>"}
21334 var obj, value = "";
21335 for(var child=node.firstChild; child; child=child.nextSibling) {
21336 switch(child.nodeType) {
21338 obj = this.readNode(child);
21339 if (obj.property) {
21340 value += "${" + obj.property + "}";
21341 } else if (obj.value !== undefined) {
21342 value += obj.value;
21345 case 3: // text node
21346 case 4: // cdata section
21347 value += child.nodeValue;
21352 "Filter": function(node, parent) {
21353 // Filters correspond to subclasses of OpenLayers.Filter.
21354 // Since they contain information we don't persist, we
21355 // create a temporary object and then pass on the filter
21356 // (ogc:Filter) to the parent obj.
21361 this.readChildNodes(node, obj);
21362 if(obj.fids.length > 0) {
21363 parent.filter = new OpenLayers.Filter.FeatureId({
21366 } else if(obj.filters.length > 0) {
21367 parent.filter = obj.filters[0];
21370 "FeatureId": function(node, obj) {
21371 var fid = node.getAttribute("fid");
21373 obj.fids.push(fid);
21376 "And": function(node, obj) {
21377 var filter = new OpenLayers.Filter.Logical({
21378 type: OpenLayers.Filter.Logical.AND
21380 this.readChildNodes(node, filter);
21381 obj.filters.push(filter);
21383 "Or": function(node, obj) {
21384 var filter = new OpenLayers.Filter.Logical({
21385 type: OpenLayers.Filter.Logical.OR
21387 this.readChildNodes(node, filter);
21388 obj.filters.push(filter);
21390 "Not": function(node, obj) {
21391 var filter = new OpenLayers.Filter.Logical({
21392 type: OpenLayers.Filter.Logical.NOT
21394 this.readChildNodes(node, filter);
21395 obj.filters.push(filter);
21397 "PropertyIsLessThan": function(node, obj) {
21398 var filter = new OpenLayers.Filter.Comparison({
21399 type: OpenLayers.Filter.Comparison.LESS_THAN
21401 this.readChildNodes(node, filter);
21402 obj.filters.push(filter);
21404 "PropertyIsGreaterThan": function(node, obj) {
21405 var filter = new OpenLayers.Filter.Comparison({
21406 type: OpenLayers.Filter.Comparison.GREATER_THAN
21408 this.readChildNodes(node, filter);
21409 obj.filters.push(filter);
21411 "PropertyIsLessThanOrEqualTo": function(node, obj) {
21412 var filter = new OpenLayers.Filter.Comparison({
21413 type: OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO
21415 this.readChildNodes(node, filter);
21416 obj.filters.push(filter);
21418 "PropertyIsGreaterThanOrEqualTo": function(node, obj) {
21419 var filter = new OpenLayers.Filter.Comparison({
21420 type: OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO
21422 this.readChildNodes(node, filter);
21423 obj.filters.push(filter);
21425 "PropertyIsBetween": function(node, obj) {
21426 var filter = new OpenLayers.Filter.Comparison({
21427 type: OpenLayers.Filter.Comparison.BETWEEN
21429 this.readChildNodes(node, filter);
21430 obj.filters.push(filter);
21432 "Literal": function(node, obj) {
21433 obj.value = OpenLayers.String.numericIf(
21434 this.getChildValue(node), true);
21436 "PropertyName": function(node, filter) {
21437 filter.property = this.getChildValue(node);
21439 "LowerBoundary": function(node, filter) {
21440 filter.lowerBoundary = OpenLayers.String.numericIf(
21441 this.readers.ogc._expression.call(this, node), true);
21443 "UpperBoundary": function(node, filter) {
21444 filter.upperBoundary = OpenLayers.String.numericIf(
21445 this.readers.ogc._expression.call(this, node), true);
21447 "Intersects": function(node, obj) {
21448 this.readSpatial(node, obj, OpenLayers.Filter.Spatial.INTERSECTS);
21450 "Within": function(node, obj) {
21451 this.readSpatial(node, obj, OpenLayers.Filter.Spatial.WITHIN);
21453 "Contains": function(node, obj) {
21454 this.readSpatial(node, obj, OpenLayers.Filter.Spatial.CONTAINS);
21456 "DWithin": function(node, obj) {
21457 this.readSpatial(node, obj, OpenLayers.Filter.Spatial.DWITHIN);
21459 "Distance": function(node, obj) {
21460 obj.distance = parseInt(this.getChildValue(node));
21461 obj.distanceUnits = node.getAttribute("units");
21463 "Function": function(node, obj) {
21464 //TODO write decoder for it
21467 "PropertyIsNull": function(node, obj) {
21468 var filter = new OpenLayers.Filter.Comparison({
21469 type: OpenLayers.Filter.Comparison.IS_NULL
21471 this.readChildNodes(node, filter);
21472 obj.filters.push(filter);
21478 * Method: readSpatial
21480 * Read a {<OpenLayers.Filter.Spatial>} filter.
21483 * node - {DOMElement} A DOM element that contains an ogc:expression.
21484 * obj - {Object} The target object.
21485 * type - {String} One of the OpenLayers.Filter.Spatial.* constants.
21488 * {<OpenLayers.Filter.Spatial>} The created filter.
21490 readSpatial: function(node, obj, type) {
21491 var filter = new OpenLayers.Filter.Spatial({
21494 this.readChildNodes(node, filter);
21495 filter.value = filter.components[0];
21496 delete filter.components;
21497 obj.filters.push(filter);
21501 * APIMethod: encodeLiteral
21502 * Generates the string representation of a value for use in <Literal>
21503 * elements. The default encoder writes Date values as ISO 8601
21507 * value - {Object} Literal value to encode
21510 * {String} String representation of the provided value.
21512 encodeLiteral: function(value) {
21513 if (value instanceof Date) {
21514 value = OpenLayers.Date.toISOString(value);
21520 * Method: writeOgcExpression
21521 * Limited support for writing OGC expressions. Currently it supports
21522 * (<OpenLayers.Filter.Function> || String || Number)
21525 * value - (<OpenLayers.Filter.Function> || String || Number)
21526 * node - {DOMElement} A parent DOM element
21529 * {DOMElement} Updated node element.
21531 writeOgcExpression: function(value, node) {
21532 if (value instanceof OpenLayers.Filter.Function){
21533 this.writeNode("Function", value, node);
21535 this.writeNode("Literal", value, node);
21544 * filter - {<OpenLayers.Filter>} A filter object.
21547 * {DOMElement} An ogc:Filter element.
21549 write: function(filter) {
21550 return this.writers.ogc["Filter"].apply(this, [filter]);
21554 * Property: writers
21555 * As a compliment to the readers property, this structure contains public
21556 * writing functions grouped by namespace alias and named like the
21557 * node names they produce.
21561 "Filter": function(filter) {
21562 var node = this.createElementNSPlus("ogc:Filter");
21563 this.writeNode(this.getFilterType(filter), filter, node);
21566 "_featureIds": function(filter) {
21567 var node = this.createDocumentFragment();
21568 for (var i=0, ii=filter.fids.length; i<ii; ++i) {
21569 this.writeNode("ogc:FeatureId", filter.fids[i], node);
21573 "FeatureId": function(fid) {
21574 return this.createElementNSPlus("ogc:FeatureId", {
21575 attributes: {fid: fid}
21578 "And": function(filter) {
21579 var node = this.createElementNSPlus("ogc:And");
21581 for (var i=0, ii=filter.filters.length; i<ii; ++i) {
21582 childFilter = filter.filters[i];
21584 this.getFilterType(childFilter), childFilter, node
21589 "Or": function(filter) {
21590 var node = this.createElementNSPlus("ogc:Or");
21592 for (var i=0, ii=filter.filters.length; i<ii; ++i) {
21593 childFilter = filter.filters[i];
21595 this.getFilterType(childFilter), childFilter, node
21600 "Not": function(filter) {
21601 var node = this.createElementNSPlus("ogc:Not");
21602 var childFilter = filter.filters[0];
21604 this.getFilterType(childFilter), childFilter, node
21608 "PropertyIsLessThan": function(filter) {
21609 var node = this.createElementNSPlus("ogc:PropertyIsLessThan");
21610 // no ogc:expression handling for PropertyName for now
21611 this.writeNode("PropertyName", filter, node);
21612 // handle Literals or Functions for now
21613 this.writeOgcExpression(filter.value, node);
21616 "PropertyIsGreaterThan": function(filter) {
21617 var node = this.createElementNSPlus("ogc:PropertyIsGreaterThan");
21618 // no ogc:expression handling for PropertyName for now
21619 this.writeNode("PropertyName", filter, node);
21620 // handle Literals or Functions for now
21621 this.writeOgcExpression(filter.value, node);
21624 "PropertyIsLessThanOrEqualTo": function(filter) {
21625 var node = this.createElementNSPlus("ogc:PropertyIsLessThanOrEqualTo");
21626 // no ogc:expression handling for PropertyName for now
21627 this.writeNode("PropertyName", filter, node);
21628 // handle Literals or Functions for now
21629 this.writeOgcExpression(filter.value, node);
21632 "PropertyIsGreaterThanOrEqualTo": function(filter) {
21633 var node = this.createElementNSPlus("ogc:PropertyIsGreaterThanOrEqualTo");
21634 // no ogc:expression handling for PropertyName for now
21635 this.writeNode("PropertyName", filter, node);
21636 // handle Literals or Functions for now
21637 this.writeOgcExpression(filter.value, node);
21640 "PropertyIsBetween": function(filter) {
21641 var node = this.createElementNSPlus("ogc:PropertyIsBetween");
21642 // no ogc:expression handling for PropertyName for now
21643 this.writeNode("PropertyName", filter, node);
21644 this.writeNode("LowerBoundary", filter, node);
21645 this.writeNode("UpperBoundary", filter, node);
21648 "PropertyName": function(filter) {
21649 // no ogc:expression handling for now
21650 return this.createElementNSPlus("ogc:PropertyName", {
21651 value: filter.property
21654 "Literal": function(value) {
21655 var encode = this.encodeLiteral ||
21656 OpenLayers.Format.Filter.v1.prototype.encodeLiteral;
21657 return this.createElementNSPlus("ogc:Literal", {
21658 value: encode(value)
21661 "LowerBoundary": function(filter) {
21662 // handle Literals or Functions for now
21663 var node = this.createElementNSPlus("ogc:LowerBoundary");
21664 this.writeOgcExpression(filter.lowerBoundary, node);
21667 "UpperBoundary": function(filter) {
21668 // handle Literals or Functions for now
21669 var node = this.createElementNSPlus("ogc:UpperBoundary");
21670 this.writeNode("Literal", filter.upperBoundary, node);
21673 "INTERSECTS": function(filter) {
21674 return this.writeSpatial(filter, "Intersects");
21676 "WITHIN": function(filter) {
21677 return this.writeSpatial(filter, "Within");
21679 "CONTAINS": function(filter) {
21680 return this.writeSpatial(filter, "Contains");
21682 "DWITHIN": function(filter) {
21683 var node = this.writeSpatial(filter, "DWithin");
21684 this.writeNode("Distance", filter, node);
21687 "Distance": function(filter) {
21688 return this.createElementNSPlus("ogc:Distance", {
21690 units: filter.distanceUnits
21692 value: filter.distance
21695 "Function": function(filter) {
21696 var node = this.createElementNSPlus("ogc:Function", {
21701 var params = filter.params;
21702 for(var i=0, len=params.length; i<len; i++){
21703 this.writeOgcExpression(params[i], node);
21707 "PropertyIsNull": function(filter) {
21708 var node = this.createElementNSPlus("ogc:PropertyIsNull");
21709 this.writeNode("PropertyName", filter, node);
21716 * Method: getFilterType
21718 getFilterType: function(filter) {
21719 var filterType = this.filterMap[filter.type];
21721 throw "Filter writing not supported for rule type: " + filter.type;
21727 * Property: filterMap
21728 * {Object} Contains a member for each filter type. Values are node names
21729 * for corresponding OGC Filter child elements.
21735 "==": "PropertyIsEqualTo",
21736 "!=": "PropertyIsNotEqualTo",
21737 "<": "PropertyIsLessThan",
21738 ">": "PropertyIsGreaterThan",
21739 "<=": "PropertyIsLessThanOrEqualTo",
21740 ">=": "PropertyIsGreaterThanOrEqualTo",
21741 "..": "PropertyIsBetween",
21742 "~": "PropertyIsLike",
21743 "NULL": "PropertyIsNull",
21745 "DWITHIN": "DWITHIN",
21746 "WITHIN": "WITHIN",
21747 "CONTAINS": "CONTAINS",
21748 "INTERSECTS": "INTERSECTS",
21749 "FID": "_featureIds"
21752 CLASS_NAME: "OpenLayers.Format.Filter.v1"
21755 /* ======================================================================
21756 OpenLayers/Format/Filter/v1_0_0.js
21757 ====================================================================== */
21759 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
21760 * full list of contributors). Published under the 2-clause BSD license.
21761 * See license.txt in the OpenLayers distribution or repository for the
21762 * full text of the license. */
21765 * @requires OpenLayers/Format/GML/v2.js
21766 * @requires OpenLayers/Format/Filter/v1.js
21770 * Class: OpenLayers.Format.Filter.v1_0_0
21771 * Write ogc:Filter version 1.0.0.
21774 * - <OpenLayers.Format.GML.v2>
21775 * - <OpenLayers.Format.Filter.v1>
21777 OpenLayers.Format.Filter.v1_0_0 = OpenLayers.Class(
21778 OpenLayers.Format.GML.v2, OpenLayers.Format.Filter.v1, {
21781 * Constant: VERSION
21787 * Property: schemaLocation
21788 * {String} http://www.opengis.net/ogc/filter/1.0.0/filter.xsd
21790 schemaLocation: "http://www.opengis.net/ogc/filter/1.0.0/filter.xsd",
21793 * Constructor: OpenLayers.Format.Filter.v1_0_0
21794 * Instances of this class are not created directly. Use the
21795 * <OpenLayers.Format.Filter> constructor instead.
21798 * options - {Object} An optional object whose properties will be set on
21801 initialize: function(options) {
21802 OpenLayers.Format.GML.v2.prototype.initialize.apply(
21808 * Property: readers
21809 * Contains public functions, grouped by namespace prefix, that will
21810 * be applied when a namespaced node is found matching the function
21811 * name. The function will be applied in the scope of this parser
21812 * with two arguments: the node being read and a context object passed
21816 "ogc": OpenLayers.Util.applyDefaults({
21817 "PropertyIsEqualTo": function(node, obj) {
21818 var filter = new OpenLayers.Filter.Comparison({
21819 type: OpenLayers.Filter.Comparison.EQUAL_TO
21821 this.readChildNodes(node, filter);
21822 obj.filters.push(filter);
21824 "PropertyIsNotEqualTo": function(node, obj) {
21825 var filter = new OpenLayers.Filter.Comparison({
21826 type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO
21828 this.readChildNodes(node, filter);
21829 obj.filters.push(filter);
21831 "PropertyIsLike": function(node, obj) {
21832 var filter = new OpenLayers.Filter.Comparison({
21833 type: OpenLayers.Filter.Comparison.LIKE
21835 this.readChildNodes(node, filter);
21836 var wildCard = node.getAttribute("wildCard");
21837 var singleChar = node.getAttribute("singleChar");
21838 var esc = node.getAttribute("escape");
21839 filter.value2regex(wildCard, singleChar, esc);
21840 obj.filters.push(filter);
21842 }, OpenLayers.Format.Filter.v1.prototype.readers["ogc"]),
21843 "gml": OpenLayers.Format.GML.v2.prototype.readers["gml"],
21844 "feature": OpenLayers.Format.GML.v2.prototype.readers["feature"]
21848 * Property: writers
21849 * As a compliment to the readers property, this structure contains public
21850 * writing functions grouped by namespace alias and named like the
21851 * node names they produce.
21854 "ogc": OpenLayers.Util.applyDefaults({
21855 "PropertyIsEqualTo": function(filter) {
21856 var node = this.createElementNSPlus("ogc:PropertyIsEqualTo");
21857 // no ogc:expression handling for PropertyName for now
21858 this.writeNode("PropertyName", filter, node);
21859 // handle Literals or Functions for now
21860 this.writeOgcExpression(filter.value, node);
21863 "PropertyIsNotEqualTo": function(filter) {
21864 var node = this.createElementNSPlus("ogc:PropertyIsNotEqualTo");
21865 // no ogc:expression handling for PropertyName for now
21866 this.writeNode("PropertyName", filter, node);
21867 // handle Literals or Functions for now
21868 this.writeOgcExpression(filter.value, node);
21871 "PropertyIsLike": function(filter) {
21872 var node = this.createElementNSPlus("ogc:PropertyIsLike", {
21874 wildCard: "*", singleChar: ".", escape: "!"
21877 // no ogc:expression handling for now
21878 this.writeNode("PropertyName", filter, node);
21879 // convert regex string to ogc string
21880 this.writeNode("Literal", filter.regex2value(), node);
21883 "BBOX": function(filter) {
21884 var node = this.createElementNSPlus("ogc:BBOX");
21885 // PropertyName is mandatory in 1.0.0, but e.g. GeoServer also
21886 // accepts filters without it. When this is used with
21887 // OpenLayers.Protocol.WFS, OpenLayers.Format.WFST will set a
21888 // missing filter.property to the geometryName that is
21889 // configured with the protocol, which defaults to "the_geom".
21890 // So the only way to omit this mandatory property is to not
21891 // set the property on the filter and to set the geometryName
21892 // on the WFS protocol to null. The latter also happens when
21893 // the protocol is configured without a geometryName and a
21895 filter.property && this.writeNode("PropertyName", filter, node);
21896 var box = this.writeNode("gml:Box", filter.value, node);
21897 if(filter.projection) {
21898 box.setAttribute("srsName", filter.projection);
21902 }, OpenLayers.Format.Filter.v1.prototype.writers["ogc"]),
21903 "gml": OpenLayers.Format.GML.v2.prototype.writers["gml"],
21904 "feature": OpenLayers.Format.GML.v2.prototype.writers["feature"]
21908 * Method: writeSpatial
21910 * Read a {<OpenLayers.Filter.Spatial>} filter and converts it into XML.
21913 * filter - {<OpenLayers.Filter.Spatial>} The filter.
21914 * name - {String} Name of the generated XML element.
21917 * {DOMElement} The created XML element.
21919 writeSpatial: function(filter, name) {
21920 var node = this.createElementNSPlus("ogc:"+name);
21921 this.writeNode("PropertyName", filter, node);
21922 if(filter.value instanceof OpenLayers.Filter.Function) {
21923 this.writeNode("Function", filter.value, node);
21926 if(filter.value instanceof OpenLayers.Geometry) {
21927 child = this.writeNode("feature:_geometry", filter.value).firstChild;
21929 child = this.writeNode("gml:Box", filter.value);
21931 if(filter.projection) {
21932 child.setAttribute("srsName", filter.projection);
21934 node.appendChild(child);
21940 CLASS_NAME: "OpenLayers.Format.Filter.v1_0_0"
21943 /* ======================================================================
21944 OpenLayers/Format/WFST/v1_0_0.js
21945 ====================================================================== */
21947 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
21948 * full list of contributors). Published under the 2-clause BSD license.
21949 * See license.txt in the OpenLayers distribution or repository for the
21950 * full text of the license. */
21953 * @requires OpenLayers/Format/WFST/v1.js
21954 * @requires OpenLayers/Format/Filter/v1_0_0.js
21958 * Class: OpenLayers.Format.WFST.v1_0_0
21959 * A format for creating WFS v1.0.0 transactions. Create a new instance with the
21960 * <OpenLayers.Format.WFST.v1_0_0> constructor.
21963 * - <OpenLayers.Format.Filter.v1_0_0>
21964 * - <OpenLayers.Format.WFST.v1>
21966 OpenLayers.Format.WFST.v1_0_0 = OpenLayers.Class(
21967 OpenLayers.Format.Filter.v1_0_0, OpenLayers.Format.WFST.v1, {
21970 * Property: version
21971 * {String} WFS version number.
21976 * APIProperty: srsNameInQuery
21977 * {Boolean} If true the reference system is passed in Query requests
21978 * via the "srsName" attribute to the "wfs:Query" element, this
21979 * property defaults to false as it isn't WFS 1.0.0 compliant.
21981 srsNameInQuery: false,
21984 * Property: schemaLocations
21985 * {Object} Properties are namespace aliases, values are schema locations.
21988 "wfs": "http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd"
21992 * Constructor: OpenLayers.Format.WFST.v1_0_0
21993 * A class for parsing and generating WFS v1.0.0 transactions.
21996 * options - {Object} Optional object whose properties will be set on the
21999 * Valid options properties:
22000 * featureType - {String} Local (without prefix) feature typeName (required).
22001 * featureNS - {String} Feature namespace (optional).
22002 * featurePrefix - {String} Feature namespace alias (optional - only used
22003 * if featureNS is provided). Default is 'feature'.
22004 * geometryName - {String} Name of geometry attribute. Default is 'the_geom'.
22006 initialize: function(options) {
22007 OpenLayers.Format.Filter.v1_0_0.prototype.initialize.apply(this, [options]);
22008 OpenLayers.Format.WFST.v1.prototype.initialize.apply(this, [options]);
22013 * Shorthand for applying one of the named readers given the node
22014 * namespace and local name. Readers take two args (node, obj) and
22015 * generally extend or modify the second.
22018 * node - {DOMElement} The node to be read (required).
22019 * obj - {Object} The object to be modified (optional).
22020 * first - {Boolean} Should be set to true for the first node read. This
22021 * is usually the readNode call in the read method. Without this being
22022 * set, auto-configured properties will stick on subsequent reads.
22025 * {Object} The input object, modified (or a new one if none was provided).
22027 readNode: function(node, obj, first) {
22028 // Not the superclass, only the mixin classes inherit from
22029 // Format.GML.v2. We need this because we don't want to get readNode
22030 // from the superclass's superclass, which is OpenLayers.Format.XML.
22031 return OpenLayers.Format.GML.v2.prototype.readNode.apply(this, arguments);
22035 * Property: readers
22036 * Contains public functions, grouped by namespace prefix, that will
22037 * be applied when a namespaced node is found matching the function
22038 * name. The function will be applied in the scope of this parser
22039 * with two arguments: the node being read and a context object passed
22043 "wfs": OpenLayers.Util.applyDefaults({
22044 "WFS_TransactionResponse": function(node, obj) {
22045 obj.insertIds = [];
22046 obj.success = false;
22047 this.readChildNodes(node, obj);
22049 "InsertResult": function(node, container) {
22050 var obj = {fids: []};
22051 this.readChildNodes(node, obj);
22052 container.insertIds = container.insertIds.concat(obj.fids);
22054 "TransactionResult": function(node, obj) {
22055 this.readChildNodes(node, obj);
22057 "Status": function(node, obj) {
22058 this.readChildNodes(node, obj);
22060 "SUCCESS": function(node, obj) {
22061 obj.success = true;
22063 }, OpenLayers.Format.WFST.v1.prototype.readers["wfs"]),
22064 "gml": OpenLayers.Format.GML.v2.prototype.readers["gml"],
22065 "feature": OpenLayers.Format.GML.v2.prototype.readers["feature"],
22066 "ogc": OpenLayers.Format.Filter.v1_0_0.prototype.readers["ogc"]
22070 * Property: writers
22071 * As a compliment to the readers property, this structure contains public
22072 * writing functions grouped by namespace alias and named like the
22073 * node names they produce.
22076 "wfs": OpenLayers.Util.applyDefaults({
22077 "Query": function(options) {
22078 options = OpenLayers.Util.extend({
22079 featureNS: this.featureNS,
22080 featurePrefix: this.featurePrefix,
22081 featureType: this.featureType,
22082 srsName: this.srsName,
22083 srsNameInQuery: this.srsNameInQuery
22085 var prefix = options.featurePrefix;
22086 var node = this.createElementNSPlus("wfs:Query", {
22088 typeName: (options.featureNS ? prefix + ":" : "") +
22089 options.featureType
22092 if(options.srsNameInQuery && options.srsName) {
22093 node.setAttribute("srsName", options.srsName);
22095 if(options.featureNS) {
22096 this.setAttributeNS(
22097 node, this.namespaces.xmlns,
22098 "xmlns:" + prefix, options.featureNS
22101 if(options.propertyNames) {
22102 for(var i=0,len = options.propertyNames.length; i<len; i++) {
22104 "ogc:PropertyName",
22105 {property: options.propertyNames[i]},
22110 if(options.filter) {
22111 this.setFilterProperty(options.filter);
22112 this.writeNode("ogc:Filter", options.filter, node);
22116 }, OpenLayers.Format.WFST.v1.prototype.writers["wfs"]),
22117 "gml": OpenLayers.Format.GML.v2.prototype.writers["gml"],
22118 "feature": OpenLayers.Format.GML.v2.prototype.writers["feature"],
22119 "ogc": OpenLayers.Format.Filter.v1_0_0.prototype.writers["ogc"]
22122 CLASS_NAME: "OpenLayers.Format.WFST.v1_0_0"
22124 /* ======================================================================
22125 OpenLayers/Protocol/WFS/v1_0_0.js
22126 ====================================================================== */
22128 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
22129 * full list of contributors). Published under the 2-clause BSD license.
22130 * See license.txt in the OpenLayers distribution or repository for the
22131 * full text of the license. */
22134 * @requires OpenLayers/Protocol/WFS/v1.js
22135 * @requires OpenLayers/Format/WFST/v1_0_0.js
22139 * Class: OpenLayers.Protocol.WFS.v1_0_0
22140 * A WFS v1.0.0 protocol for vector layers. Create a new instance with the
22141 * <OpenLayers.Protocol.WFS.v1_0_0> constructor.
22144 * - <OpenLayers.Protocol.WFS.v1>
22146 OpenLayers.Protocol.WFS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, {
22149 * Property: version
22150 * {String} WFS version number.
22155 * Constructor: OpenLayers.Protocol.WFS.v1_0_0
22156 * A class for giving layers WFS v1.0.0 protocol.
22159 * options - {Object} Optional object whose properties will be set on the
22162 * Valid options properties:
22163 * featureType - {String} Local (without prefix) feature typeName (required).
22164 * featureNS - {String} Feature namespace (optional).
22165 * featurePrefix - {String} Feature namespace alias (optional - only used
22166 * if featureNS is provided). Default is 'feature'.
22167 * geometryName - {String} Name of geometry attribute. Default is 'the_geom'.
22170 CLASS_NAME: "OpenLayers.Protocol.WFS.v1_0_0"
22172 /* ======================================================================
22173 OpenLayers/Control.js
22174 ====================================================================== */
22176 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
22177 * full list of contributors). Published under the 2-clause BSD license.
22178 * See license.txt in the OpenLayers distribution or repository for the
22179 * full text of the license. */
22182 * @requires OpenLayers/BaseTypes/Class.js
22186 * Class: OpenLayers.Control
22187 * Controls affect the display or behavior of the map. They allow everything
22188 * from panning and zooming to displaying a scale indicator. Controls by
22189 * default are added to the map they are contained within however it is
22190 * possible to add a control to an external div by passing the div in the
22191 * options parameter.
22194 * The following example shows how to add many of the common controls
22197 * > var map = new OpenLayers.Map('map', { controls: [] });
22199 * > map.addControl(new OpenLayers.Control.PanZoomBar());
22200 * > map.addControl(new OpenLayers.Control.LayerSwitcher({'ascending':false}));
22201 * > map.addControl(new OpenLayers.Control.Permalink());
22202 * > map.addControl(new OpenLayers.Control.Permalink('permalink'));
22203 * > map.addControl(new OpenLayers.Control.MousePosition());
22204 * > map.addControl(new OpenLayers.Control.OverviewMap());
22205 * > map.addControl(new OpenLayers.Control.KeyboardDefaults());
22207 * The next code fragment is a quick example of how to intercept
22208 * shift-mouse click to display the extent of the bounding box
22209 * dragged out by the user. Usually controls are not created
22210 * in exactly this manner. See the source for a more complete
22213 * > var control = new OpenLayers.Control();
22214 * > OpenLayers.Util.extend(control, {
22215 * > draw: function () {
22216 * > // this Handler.Box will intercept the shift-mousedown
22217 * > // before Control.MouseDefault gets to see it
22218 * > this.box = new OpenLayers.Handler.Box( control,
22219 * > {"done": this.notice},
22220 * > {keyMask: OpenLayers.Handler.MOD_SHIFT});
22221 * > this.box.activate();
22224 * > notice: function (bounds) {
22225 * > OpenLayers.Console.userError(bounds);
22228 * > map.addControl(control);
22231 OpenLayers.Control = OpenLayers.Class({
22241 * {<OpenLayers.Map>} this gets set in the addControl() function in
22248 * {DOMElement} The element that contains the control, if not present the
22249 * control is placed inside the map.
22254 * APIProperty: type
22255 * {Number} Controls can have a 'type'. The type determines the type of
22256 * interactions which are possible with them when they are placed in an
22257 * <OpenLayers.Control.Panel>.
22262 * Property: allowSelection
22263 * {Boolean} By default, controls do not allow selection, because
22264 * it may interfere with map dragging. If this is true, OpenLayers
22265 * will not prevent selection of the control.
22266 * Default is false.
22268 allowSelection: false,
22271 * Property: displayClass
22272 * {string} This property is used for CSS related to the drawing of the
22278 * APIProperty: title
22279 * {string} This property is used for showing a tooltip over the
22285 * APIProperty: autoActivate
22286 * {Boolean} Activate the control when it is added to a map. Default is
22289 autoActivate: false,
22292 * APIProperty: active
22293 * {Boolean} The control is active (read-only). Use <activate> and
22294 * <deactivate> to change control state.
22299 * Property: handlerOptions
22300 * {Object} Used to set non-default properties on the control's handler
22302 handlerOptions: null,
22305 * Property: handler
22306 * {<OpenLayers.Handler>} null
22311 * APIProperty: eventListeners
22312 * {Object} If set as an option at construction, the eventListeners
22313 * object will be registered with <OpenLayers.Events.on>. Object
22314 * structure must be a listeners object as shown in the example for
22315 * the events.on method.
22317 eventListeners: null,
22320 * APIProperty: events
22321 * {<OpenLayers.Events>} Events instance for listeners and triggering
22322 * control specific events.
22324 * Register a listener for a particular event with the following syntax:
22326 * control.events.register(type, obj, listener);
22329 * Listeners will be called with a reference to an event object. The
22330 * properties of this event depends on exactly what happened.
22332 * All event objects have at least the following properties:
22333 * object - {Object} A reference to control.events.object (a reference
22335 * element - {DOMElement} A reference to control.events.element (which
22336 * will be null unless documented otherwise).
22338 * Supported map event types:
22339 * activate - Triggered when activated.
22340 * deactivate - Triggered when deactivated.
22345 * Constructor: OpenLayers.Control
22346 * Create an OpenLayers Control. The options passed as a parameter
22347 * directly extend the control. For example passing the following:
22349 * > var control = new OpenLayers.Control({div: myDiv});
22351 * Overrides the default div attribute value of null.
22354 * options - {Object}
22356 initialize: function (options) {
22357 // We do this before the extend so that instances can override
22358 // className in options.
22359 this.displayClass =
22360 this.CLASS_NAME.replace("OpenLayers.", "ol").replace(/\./g, "");
22362 OpenLayers.Util.extend(this, options);
22364 this.events = new OpenLayers.Events(this);
22365 if(this.eventListeners instanceof Object) {
22366 this.events.on(this.eventListeners);
22368 if (this.id == null) {
22369 this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
22375 * The destroy method is used to perform any clean up before the control
22376 * is dereferenced. Typically this is where event listeners are removed
22377 * to prevent memory leaks.
22379 destroy: function () {
22381 if(this.eventListeners) {
22382 this.events.un(this.eventListeners);
22384 this.events.destroy();
22385 this.events = null;
22387 this.eventListeners = null;
22389 // eliminate circular references
22390 if (this.handler) {
22391 this.handler.destroy();
22392 this.handler = null;
22394 if(this.handlers) {
22395 for(var key in this.handlers) {
22396 if(this.handlers.hasOwnProperty(key) &&
22397 typeof this.handlers[key].destroy == "function") {
22398 this.handlers[key].destroy();
22401 this.handlers = null;
22404 this.map.removeControl(this);
22412 * Set the map property for the control. This is done through an accessor
22413 * so that subclasses can override this and take special action once
22414 * they have their map variable set.
22417 * map - {<OpenLayers.Map>}
22419 setMap: function(map) {
22421 if (this.handler) {
22422 this.handler.setMap(map);
22428 * The draw method is called when the control is ready to be displayed
22429 * on the page. If a div has not been created one is created. Controls
22430 * with a visual component will almost always want to override this method
22431 * to customize the look of control.
22434 * px - {<OpenLayers.Pixel>} The top-left pixel position of the control
22438 * {DOMElement} A reference to the DIV DOMElement containing the control
22440 draw: function (px) {
22441 if (this.div == null) {
22442 this.div = OpenLayers.Util.createDiv(this.id);
22443 this.div.className = this.displayClass;
22444 if (!this.allowSelection) {
22445 this.div.className += " olControlNoSelect";
22446 this.div.setAttribute("unselectable", "on", 0);
22447 this.div.onselectstart = OpenLayers.Function.False;
22449 if (this.title != "") {
22450 this.div.title = this.title;
22454 this.position = px.clone();
22456 this.moveTo(this.position);
22462 * Sets the left and top style attributes to the passed in pixel
22466 * px - {<OpenLayers.Pixel>}
22468 moveTo: function (px) {
22469 if ((px != null) && (this.div != null)) {
22470 this.div.style.left = px.x + "px";
22471 this.div.style.top = px.y + "px";
22476 * APIMethod: activate
22477 * Explicitly activates a control and its associated
22478 * handler if one has been set. Controls can be
22479 * deactivated by calling the deactivate() method.
22482 * {Boolean} True if the control was successfully activated or
22483 * false if the control was already active.
22485 activate: function () {
22489 if (this.handler) {
22490 this.handler.activate();
22492 this.active = true;
22494 OpenLayers.Element.addClass(
22495 this.map.viewPortDiv,
22496 this.displayClass.replace(/ /g, "") + "Active"
22499 this.events.triggerEvent("activate");
22504 * APIMethod: deactivate
22505 * Deactivates a control and its associated handler if any. The exact
22506 * effect of this depends on the control itself.
22509 * {Boolean} True if the control was effectively deactivated or false
22510 * if the control was already inactive.
22512 deactivate: function () {
22514 if (this.handler) {
22515 this.handler.deactivate();
22517 this.active = false;
22519 OpenLayers.Element.removeClass(
22520 this.map.viewPortDiv,
22521 this.displayClass.replace(/ /g, "") + "Active"
22524 this.events.triggerEvent("deactivate");
22530 CLASS_NAME: "OpenLayers.Control"
22534 * Constant: OpenLayers.Control.TYPE_BUTTON
22536 OpenLayers.Control.TYPE_BUTTON = 1;
22539 * Constant: OpenLayers.Control.TYPE_TOGGLE
22541 OpenLayers.Control.TYPE_TOGGLE = 2;
22544 * Constant: OpenLayers.Control.TYPE_TOOL
22546 OpenLayers.Control.TYPE_TOOL = 3;
22547 /* ======================================================================
22548 OpenLayers/Handler.js
22549 ====================================================================== */
22551 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
22552 * full list of contributors). Published under the 2-clause BSD license.
22553 * See license.txt in the OpenLayers distribution or repository for the
22554 * full text of the license. */
22557 * @requires OpenLayers/BaseTypes/Class.js
22558 * @requires OpenLayers/Events.js
22562 * Class: OpenLayers.Handler
22563 * Base class to construct a higher-level handler for event sequences. All
22564 * handlers have activate and deactivate methods. In addition, they have
22565 * methods named like browser events. When a handler is activated, any
22566 * additional methods named like a browser event is registered as a
22567 * listener for the corresponding event. When a handler is deactivated,
22568 * those same methods are unregistered as event listeners.
22570 * Handlers also typically have a callbacks object with keys named like
22571 * the abstracted events or event sequences that they are in charge of
22572 * handling. The controls that wrap handlers define the methods that
22573 * correspond to these abstract events - so instead of listening for
22574 * individual browser events, they only listen for the abstract events
22575 * defined by the handler.
22577 * Handlers are created by controls, which ultimately have the responsibility
22578 * of making changes to the the state of the application. Handlers
22579 * themselves may make temporary changes, but in general are expected to
22580 * return the application in the same state that they found it.
22582 OpenLayers.Handler = OpenLayers.Class({
22591 * APIProperty: control
22592 * {<OpenLayers.Control>}. The control that initialized this handler. The
22593 * control is assumed to have a valid map property - that map is used
22594 * in the handler's own setMap method.
22600 * {<OpenLayers.Map>}
22605 * APIProperty: keyMask
22606 * {Integer} Use bitwise operators and one or more of the OpenLayers.Handler
22607 * constants to construct a keyMask. The keyMask is used by
22608 * <checkModifiers>. If the keyMask matches the combination of keys
22609 * down on an event, checkModifiers returns true.
22613 * // handler only responds if the Shift key is down
22614 * handler.keyMask = OpenLayers.Handler.MOD_SHIFT;
22616 * // handler only responds if Ctrl-Shift is down
22617 * handler.keyMask = OpenLayers.Handler.MOD_SHIFT |
22618 * OpenLayers.Handler.MOD_CTRL;
22631 * {Event} This property references the last event handled by the handler.
22632 * Note that this property is not part of the stable API. Use of the
22633 * evt property should be restricted to controls in the library
22634 * or other applications that are willing to update with changes to
22635 * the OpenLayers code.
22641 * {Boolean} Indicates the support of touch events. When touch events are
22642 * started touch will be true and all mouse related listeners will do
22648 * Constructor: OpenLayers.Handler
22649 * Construct a handler.
22652 * control - {<OpenLayers.Control>} The control that initialized this
22653 * handler. The control is assumed to have a valid map property; that
22654 * map is used in the handler's own setMap method. If a map property
22655 * is present in the options argument it will be used instead.
22656 * callbacks - {Object} An object whose properties correspond to abstracted
22657 * events or sequences of browser events. The values for these
22658 * properties are functions defined by the control that get called by
22660 * options - {Object} An optional object whose properties will be set on
22663 initialize: function(control, callbacks, options) {
22664 OpenLayers.Util.extend(this, options);
22665 this.control = control;
22666 this.callbacks = callbacks;
22668 var map = this.map || control.map;
22673 this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
22679 setMap: function (map) {
22684 * Method: checkModifiers
22685 * Check the keyMask on the handler. If no <keyMask> is set, this always
22686 * returns true. If a <keyMask> is set and it matches the combination
22687 * of keys down on an event, this returns true.
22690 * {Boolean} The keyMask matches the keys down on an event.
22692 checkModifiers: function (evt) {
22693 if(this.keyMask == null) {
22696 /* calculate the keyboard modifier mask for this event */
22698 (evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) |
22699 (evt.ctrlKey ? OpenLayers.Handler.MOD_CTRL : 0) |
22700 (evt.altKey ? OpenLayers.Handler.MOD_ALT : 0) |
22701 (evt.metaKey ? OpenLayers.Handler.MOD_META : 0);
22703 /* if it differs from the handler object's key mask,
22704 bail out of the event handler */
22705 return (keyModifiers == this.keyMask);
22709 * APIMethod: activate
22710 * Turn on the handler. Returns false if the handler was already active.
22713 * {Boolean} The handler was activated.
22715 activate: function() {
22719 // register for event handlers defined on this class.
22720 var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
22721 for (var i=0, len=events.length; i<len; i++) {
22722 if (this[events[i]]) {
22723 this.register(events[i], this[events[i]]);
22726 this.active = true;
22731 * APIMethod: deactivate
22732 * Turn off the handler. Returns false if the handler was already inactive.
22735 * {Boolean} The handler was deactivated.
22737 deactivate: function() {
22741 // unregister event handlers defined on this class.
22742 var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
22743 for (var i=0, len=events.length; i<len; i++) {
22744 if (this[events[i]]) {
22745 this.unregister(events[i], this[events[i]]);
22748 this.touch = false;
22749 this.active = false;
22754 * Method: startTouch
22755 * Start touch events, this method must be called by subclasses in
22756 * "touchstart" method. When touch events are started <touch> will be
22757 * true and all mouse related listeners will do nothing.
22759 startTouch: function() {
22763 "mousedown", "mouseup", "mousemove", "click", "dblclick",
22766 for (var i=0, len=events.length; i<len; i++) {
22767 if (this[events[i]]) {
22768 this.unregister(events[i], this[events[i]]);
22776 * Trigger the control's named callback with the given arguments
22779 * name - {String} The key for the callback that is one of the properties
22780 * of the handler's callbacks object.
22781 * args - {Array(*)} An array of arguments (any type) with which to call
22782 * the callback (defined by the control).
22784 callback: function (name, args) {
22785 if (name && this.callbacks[name]) {
22786 this.callbacks[name].apply(this.control, args);
22792 * register an event on the map
22794 register: function (name, method) {
22795 // TODO: deal with registerPriority in 3.0
22796 this.map.events.registerPriority(name, this, method);
22797 this.map.events.registerPriority(name, this, this.setEvent);
22801 * Method: unregister
22802 * unregister an event from the map
22804 unregister: function (name, method) {
22805 this.map.events.unregister(name, this, method);
22806 this.map.events.unregister(name, this, this.setEvent);
22811 * With each registered browser event, the handler sets its own evt
22812 * property. This property can be accessed by controls if needed
22813 * to get more information about the event that the handler is
22816 * This allows modifier keys on the event to be checked (alt, shift, ctrl,
22817 * and meta cannot be checked with the keyboard handler). For a
22818 * control to determine which modifier keys are associated with the
22819 * event that a handler is currently processing, it should access
22820 * (code)handler.evt.altKey || handler.evt.shiftKey ||
22821 * handler.evt.ctrlKey || handler.evt.metaKey(end).
22824 * evt - {Event} The browser event.
22826 setEvent: function(evt) {
22833 * Deconstruct the handler.
22835 destroy: function () {
22836 // unregister event listeners
22838 // eliminate circular references
22839 this.control = this.map = null;
22842 CLASS_NAME: "OpenLayers.Handler"
22846 * Constant: OpenLayers.Handler.MOD_NONE
22847 * If set as the <keyMask>, <checkModifiers> returns false if any key is down.
22849 OpenLayers.Handler.MOD_NONE = 0;
22852 * Constant: OpenLayers.Handler.MOD_SHIFT
22853 * If set as the <keyMask>, <checkModifiers> returns false if Shift is down.
22855 OpenLayers.Handler.MOD_SHIFT = 1;
22858 * Constant: OpenLayers.Handler.MOD_CTRL
22859 * If set as the <keyMask>, <checkModifiers> returns false if Ctrl is down.
22861 OpenLayers.Handler.MOD_CTRL = 2;
22864 * Constant: OpenLayers.Handler.MOD_ALT
22865 * If set as the <keyMask>, <checkModifiers> returns false if Alt is down.
22867 OpenLayers.Handler.MOD_ALT = 4;
22870 * Constant: OpenLayers.Handler.MOD_META
22871 * If set as the <keyMask>, <checkModifiers> returns false if Cmd is down.
22873 OpenLayers.Handler.MOD_META = 8;
22876 /* ======================================================================
22877 OpenLayers/Handler/Drag.js
22878 ====================================================================== */
22880 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
22881 * full list of contributors). Published under the 2-clause BSD license.
22882 * See license.txt in the OpenLayers distribution or repository for the
22883 * full text of the license. */
22886 * @requires OpenLayers/Handler.js
22890 * Class: OpenLayers.Handler.Drag
22891 * The drag handler is used to deal with sequences of browser events related
22892 * to dragging. The handler is used by controls that want to know when
22893 * a drag sequence begins, when a drag is happening, and when it has
22896 * Controls that use the drag handler typically construct it with callbacks
22897 * for 'down', 'move', and 'done'. Callbacks for these keys are called
22898 * when the drag begins, with each move, and when the drag is done. In
22899 * addition, controls can have callbacks keyed to 'up' and 'out' if they
22900 * care to differentiate between the types of events that correspond with
22901 * the end of a drag sequence. If no drag actually occurs (no mouse move)
22902 * the 'down' and 'up' callbacks will be called, but not the 'done'
22905 * Create a new drag handler with the <OpenLayers.Handler.Drag> constructor.
22908 * - <OpenLayers.Handler>
22910 OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, {
22913 * Property: started
22914 * {Boolean} When a mousedown or touchstart event is received, we want to
22915 * record it, but not set 'dragging' until the mouse moves after starting.
22920 * Property: stopDown
22921 * {Boolean} Stop propagation of mousedown events from getting to listeners
22922 * on the same element. Default is true.
22927 * Property: dragging
22934 * {<OpenLayers.Pixel>} The last pixel location of the drag.
22940 * {<OpenLayers.Pixel>} The first pixel location of the drag.
22945 * Property: lastMoveEvt
22946 * {Object} The last mousemove event that occurred. Used to
22947 * position the map correctly when our "delay drag"
22953 * Property: oldOnselectstart
22956 oldOnselectstart: null,
22959 * Property: interval
22960 * {Integer} In order to increase performance, an interval (in
22961 * milliseconds) can be set to reduce the number of drag events
22962 * called. If set, a new drag event will not be set until the
22963 * interval has passed.
22964 * Defaults to 0, meaning no interval.
22969 * Property: timeoutId
22970 * {String} The id of the timeout used for the mousedown interval.
22971 * This is "private", and should be left alone.
22976 * APIProperty: documentDrag
22977 * {Boolean} If set to true, the handler will also handle mouse moves when
22978 * the cursor has moved out of the map viewport. Default is false.
22980 documentDrag: false,
22983 * Property: documentEvents
22984 * {Boolean} Are we currently observing document events?
22986 documentEvents: null,
22989 * Constructor: OpenLayers.Handler.Drag
22990 * Returns OpenLayers.Handler.Drag
22993 * control - {<OpenLayers.Control>} The control that is making use of
22994 * this handler. If a handler is being used without a control, the
22995 * handlers setMap method must be overridden to deal properly with
22997 * callbacks - {Object} An object containing a single function to be
22998 * called when the drag operation is finished. The callback should
22999 * expect to receive a single argument, the pixel location of the event.
23000 * Callbacks for 'move' and 'done' are supported. You can also speficy
23001 * callbacks for 'down', 'up', and 'out' to respond to those events.
23002 * options - {Object}
23004 initialize: function(control, callbacks, options) {
23005 OpenLayers.Handler.prototype.initialize.apply(this, arguments);
23007 if (this.documentDrag === true) {
23009 this._docMove = function(evt) {
23011 xy: {x: evt.clientX, y: evt.clientY},
23015 this._docUp = function(evt) {
23016 me.mouseup({xy: {x: evt.clientX, y: evt.clientY}});
23023 * Method: dragstart
23024 * This private method is factorized from mousedown and touchstart methods
23027 * evt - {Event} The event
23030 * {Boolean} Let the event propagate.
23032 dragstart: function (evt) {
23033 var propagate = true;
23034 this.dragging = false;
23035 if (this.checkModifiers(evt) &&
23036 this._pointerId == evt.pointerId &&
23037 (OpenLayers.Event.isLeftClick(evt) ||
23038 OpenLayers.Event.isSingleTouch(evt))) {
23039 this.started = true;
23040 this.start = evt.xy;
23041 this.last = evt.xy;
23042 OpenLayers.Element.addClass(
23043 this.map.viewPortDiv, "olDragDown"
23046 this.callback("down", [evt.xy]);
23048 // prevent document dragging
23049 OpenLayers.Event.preventDefault(evt);
23051 if(!this.oldOnselectstart) {
23052 this.oldOnselectstart = document.onselectstart ?
23053 document.onselectstart : OpenLayers.Function.True;
23055 document.onselectstart = OpenLayers.Function.False;
23057 propagate = !this.stopDown;
23059 delete this._pointerId;
23060 this.started = false;
23069 * This private method is factorized from mousemove and touchmove methods
23072 * evt - {Event} The event
23075 * {Boolean} Let the event propagate.
23077 dragmove: function (evt) {
23078 this.lastMoveEvt = evt;
23079 if (this.started && this._pointerId == evt.pointerId &&
23080 !this.timeoutId && (evt.xy.x != this.last.x ||
23081 evt.xy.y != this.last.y)) {
23082 if(this.documentDrag === true && this.documentEvents) {
23083 if(evt.element === document) {
23084 this.adjustXY(evt);
23085 // do setEvent manually because the documentEvents are not
23086 // registered with the map
23087 this.setEvent(evt);
23089 this.removeDocumentEvents();
23092 if (this.interval > 0) {
23093 this.timeoutId = setTimeout(
23094 OpenLayers.Function.bind(this.removeTimeout, this),
23097 this.dragging = true;
23100 this.callback("move", [evt.xy]);
23101 if(!this.oldOnselectstart) {
23102 this.oldOnselectstart = document.onselectstart;
23103 document.onselectstart = OpenLayers.Function.False;
23105 this.last = evt.xy;
23112 * This private method is factorized from mouseup and touchend methods
23115 * evt - {Event} The event
23118 * {Boolean} Let the event propagate.
23120 dragend: function (evt) {
23121 if (this.started && this._pointerId == evt.pointerId) {
23122 if(this.documentDrag === true && this.documentEvents) {
23123 this.adjustXY(evt);
23124 this.removeDocumentEvents();
23126 var dragged = (this.start != this.last);
23127 this.started = false;
23128 this.dragging = false;
23129 delete this._pointerId;
23130 OpenLayers.Element.removeClass(
23131 this.map.viewPortDiv, "olDragDown"
23134 this.callback("up", [evt.xy]);
23136 this.callback("done", [evt.xy]);
23138 document.onselectstart = this.oldOnselectstart;
23144 * The four methods below (down, move, up, and out) are used by subclasses
23145 * to do their own processing related to these mouse events.
23150 * This method is called during the handling of the mouse down event.
23151 * Subclasses can do their own processing here.
23154 * evt - {Event} The mouse down event
23156 down: function(evt) {
23161 * This method is called during the handling of the mouse move event.
23162 * Subclasses can do their own processing here.
23165 * evt - {Event} The mouse move event
23168 move: function(evt) {
23173 * This method is called during the handling of the mouse up event.
23174 * Subclasses can do their own processing here.
23177 * evt - {Event} The mouse up event
23179 up: function(evt) {
23184 * This method is called during the handling of the mouse out event.
23185 * Subclasses can do their own processing here.
23188 * evt - {Event} The mouse out event
23190 out: function(evt) {
23194 * The methods below are part of the magic of event handling. Because
23195 * they are named like browser events, they are registered as listeners
23196 * for the events they represent.
23200 * Method: mousedown
23201 * Handle mousedown events
23207 * {Boolean} Let the event propagate.
23209 mousedown: function(evt) {
23210 return this.dragstart(evt);
23214 * Method: touchstart
23215 * Handle touchstart events
23221 * {Boolean} Let the event propagate.
23223 touchstart: function(evt) {
23225 // only allow the first pointer event to be monitored by noting its pointerId
23226 // which is unique in the pointer model (and undefined in the touch model)
23227 if (!("_pointerId" in this)) {
23228 this._pointerId = evt.pointerId;
23230 return this.dragstart(evt);
23234 * Method: mousemove
23235 * Handle mousemove events
23241 * {Boolean} Let the event propagate.
23243 mousemove: function(evt) {
23244 return this.dragmove(evt);
23248 * Method: touchmove
23249 * Handle touchmove events
23255 * {Boolean} Let the event propagate.
23257 touchmove: function(evt) {
23258 return this.dragmove(evt);
23262 * Method: removeTimeout
23263 * Private. Called by mousemove() to remove the drag timeout.
23265 removeTimeout: function() {
23266 this.timeoutId = null;
23267 // if timeout expires while we're still dragging (mouseup
23268 // hasn't occurred) then call mousemove to move to the
23269 // correct position
23270 if(this.dragging) {
23271 this.mousemove(this.lastMoveEvt);
23277 * Handle mouseup events
23283 * {Boolean} Let the event propagate.
23285 mouseup: function(evt) {
23286 return this.dragend(evt);
23291 * Handle touchend events
23297 * {Boolean} Let the event propagate.
23299 touchend: function(evt) {
23300 // override evt.xy with last position since touchend does not have
23301 // any touch position
23302 evt.xy = this.last;
23303 return this.dragend(evt);
23308 * Handle mouseout events
23314 * {Boolean} Let the event propagate.
23316 mouseout: function (evt) {
23317 if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {
23318 if(this.documentDrag === true) {
23319 this.addDocumentEvents();
23321 var dragged = (this.start != this.last);
23322 this.started = false;
23323 this.dragging = false;
23324 OpenLayers.Element.removeClass(
23325 this.map.viewPortDiv, "olDragDown"
23328 this.callback("out", []);
23330 this.callback("done", [evt.xy]);
23332 if(document.onselectstart) {
23333 document.onselectstart = this.oldOnselectstart;
23342 * The drag handler captures the click event. If something else registers
23343 * for clicks on the same element, its listener will not be called
23350 * {Boolean} Let the event propagate.
23352 click: function (evt) {
23353 // let the click event propagate only if the mouse moved
23354 return (this.start == this.last);
23359 * Activate the handler.
23362 * {Boolean} The handler was successfully activated.
23364 activate: function() {
23365 var activated = false;
23366 if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
23367 this.dragging = false;
23374 * Method: deactivate
23375 * Deactivate the handler.
23378 * {Boolean} The handler was successfully deactivated.
23380 deactivate: function() {
23381 var deactivated = false;
23382 if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
23383 this.started = false;
23384 this.dragging = false;
23387 deactivated = true;
23388 OpenLayers.Element.removeClass(
23389 this.map.viewPortDiv, "olDragDown"
23392 return deactivated;
23397 * Converts event coordinates that are relative to the document body to
23398 * ones that are relative to the map viewport. The latter is the default in
23404 adjustXY: function(evt) {
23405 var pos = OpenLayers.Util.pagePosition(this.map.viewPortDiv);
23406 evt.xy.x -= pos[0];
23407 evt.xy.y -= pos[1];
23411 * Method: addDocumentEvents
23412 * Start observing document events when documentDrag is true and the mouse
23413 * cursor leaves the map viewport while dragging.
23415 addDocumentEvents: function() {
23416 OpenLayers.Element.addClass(document.body, "olDragDown");
23417 this.documentEvents = true;
23418 OpenLayers.Event.observe(document, "mousemove", this._docMove);
23419 OpenLayers.Event.observe(document, "mouseup", this._docUp);
23423 * Method: removeDocumentEvents
23424 * Stops observing document events when documentDrag is true and the mouse
23425 * cursor re-enters the map viewport while dragging.
23427 removeDocumentEvents: function() {
23428 OpenLayers.Element.removeClass(document.body, "olDragDown");
23429 this.documentEvents = false;
23430 OpenLayers.Event.stopObserving(document, "mousemove", this._docMove);
23431 OpenLayers.Event.stopObserving(document, "mouseup", this._docUp);
23434 CLASS_NAME: "OpenLayers.Handler.Drag"
23436 /* ======================================================================
23437 OpenLayers/Handler/Box.js
23438 ====================================================================== */
23440 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
23441 * full list of contributors). Published under the 2-clause BSD license.
23442 * See license.txt in the OpenLayers distribution or repository for the
23443 * full text of the license. */
23446 * @requires OpenLayers/Handler.js
23447 * @requires OpenLayers/Handler/Drag.js
23451 * Class: OpenLayers.Handler.Box
23452 * Handler for dragging a rectangle across the map. Box is displayed
23453 * on mouse down, moves on mouse move, and is finished on mouse up.
23456 * - <OpenLayers.Handler>
23458 OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, {
23461 * Property: dragHandler
23462 * {<OpenLayers.Handler.Drag>}
23467 * APIProperty: boxDivClassName
23468 * {String} The CSS class to use for drawing the box. Default is
23469 * olHandlerBoxZoomBox
23471 boxDivClassName: 'olHandlerBoxZoomBox',
23474 * Property: boxOffsets
23475 * {Object} Caches box offsets from css. This is used by the getBoxOffsets
23481 * Constructor: OpenLayers.Handler.Box
23484 * control - {<OpenLayers.Control>}
23485 * callbacks - {Object} An object with a properties whose values are
23486 * functions. Various callbacks described below.
23487 * options - {Object}
23490 * start - Called when the box drag operation starts.
23491 * done - Called when the box drag operation is finished.
23492 * The callback should expect to receive a single argument, the box
23493 * bounds or a pixel. If the box dragging didn't span more than a 5
23494 * pixel distance, a pixel will be returned instead of a bounds object.
23496 initialize: function(control, callbacks, options) {
23497 OpenLayers.Handler.prototype.initialize.apply(this, arguments);
23498 this.dragHandler = new OpenLayers.Handler.Drag(
23501 down: this.startBox,
23502 move: this.moveBox,
23503 out: this.removeBox,
23506 {keyMask: this.keyMask}
23513 destroy: function() {
23514 OpenLayers.Handler.prototype.destroy.apply(this, arguments);
23515 if (this.dragHandler) {
23516 this.dragHandler.destroy();
23517 this.dragHandler = null;
23524 setMap: function (map) {
23525 OpenLayers.Handler.prototype.setMap.apply(this, arguments);
23526 if (this.dragHandler) {
23527 this.dragHandler.setMap(map);
23535 * xy - {<OpenLayers.Pixel>}
23537 startBox: function (xy) {
23538 this.callback("start", []);
23539 this.zoomBox = OpenLayers.Util.createDiv('zoomBox', {
23542 this.zoomBox.className = this.boxDivClassName;
23543 this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
23545 this.map.viewPortDiv.appendChild(this.zoomBox);
23547 OpenLayers.Element.addClass(
23548 this.map.viewPortDiv, "olDrawBox"
23555 moveBox: function (xy) {
23556 var startX = this.dragHandler.start.x;
23557 var startY = this.dragHandler.start.y;
23558 var deltaX = Math.abs(startX - xy.x);
23559 var deltaY = Math.abs(startY - xy.y);
23561 var offset = this.getBoxOffsets();
23562 this.zoomBox.style.width = (deltaX + offset.width + 1) + "px";
23563 this.zoomBox.style.height = (deltaY + offset.height + 1) + "px";
23564 this.zoomBox.style.left = (xy.x < startX ?
23565 startX - deltaX - offset.left : startX - offset.left) + "px";
23566 this.zoomBox.style.top = (xy.y < startY ?
23567 startY - deltaY - offset.top : startY - offset.top) + "px";
23573 endBox: function(end) {
23575 if (Math.abs(this.dragHandler.start.x - end.x) > 5 ||
23576 Math.abs(this.dragHandler.start.y - end.y) > 5) {
23577 var start = this.dragHandler.start;
23578 var top = Math.min(start.y, end.y);
23579 var bottom = Math.max(start.y, end.y);
23580 var left = Math.min(start.x, end.x);
23581 var right = Math.max(start.x, end.x);
23582 result = new OpenLayers.Bounds(left, bottom, right, top);
23584 result = this.dragHandler.start.clone(); // i.e. OL.Pixel
23588 this.callback("done", [result]);
23592 * Method: removeBox
23593 * Remove the zoombox from the screen and nullify our reference to it.
23595 removeBox: function() {
23596 this.map.viewPortDiv.removeChild(this.zoomBox);
23597 this.zoomBox = null;
23598 this.boxOffsets = null;
23599 OpenLayers.Element.removeClass(
23600 this.map.viewPortDiv, "olDrawBox"
23608 activate: function () {
23609 if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
23610 this.dragHandler.activate();
23618 * Method: deactivate
23620 deactivate: function () {
23621 if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
23622 if (this.dragHandler.deactivate()) {
23623 if (this.zoomBox) {
23634 * Method: getBoxOffsets
23635 * Determines border offsets for a box, according to the box model.
23638 * {Object} an object with the following offsets:
23646 getBoxOffsets: function() {
23647 if (!this.boxOffsets) {
23648 // Determine the box model. If the testDiv's clientWidth is 3, then
23649 // the borders are outside and we are dealing with the w3c box
23650 // model. Otherwise, the browser uses the traditional box model and
23651 // the borders are inside the box bounds, leaving us with a
23652 // clientWidth of 1.
23653 var testDiv = document.createElement("div");
23654 //testDiv.style.visibility = "hidden";
23655 testDiv.style.position = "absolute";
23656 testDiv.style.border = "1px solid black";
23657 testDiv.style.width = "3px";
23658 document.body.appendChild(testDiv);
23659 var w3cBoxModel = testDiv.clientWidth == 3;
23660 document.body.removeChild(testDiv);
23662 var left = parseInt(OpenLayers.Element.getStyle(this.zoomBox,
23663 "border-left-width"));
23664 var right = parseInt(OpenLayers.Element.getStyle(
23665 this.zoomBox, "border-right-width"));
23666 var top = parseInt(OpenLayers.Element.getStyle(this.zoomBox,
23667 "border-top-width"));
23668 var bottom = parseInt(OpenLayers.Element.getStyle(
23669 this.zoomBox, "border-bottom-width"));
23670 this.boxOffsets = {
23675 width: w3cBoxModel === false ? left + right : 0,
23676 height: w3cBoxModel === false ? top + bottom : 0
23679 return this.boxOffsets;
23682 CLASS_NAME: "OpenLayers.Handler.Box"
23684 /* ======================================================================
23685 OpenLayers/Control/ZoomBox.js
23686 ====================================================================== */
23688 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
23689 * full list of contributors). Published under the 2-clause BSD license.
23690 * See license.txt in the OpenLayers distribution or repository for the
23691 * full text of the license. */
23694 * @requires OpenLayers/Control.js
23695 * @requires OpenLayers/Handler/Box.js
23699 * Class: OpenLayers.Control.ZoomBox
23700 * The ZoomBox control enables zooming directly to a given extent, by drawing
23701 * a box on the map. The box is drawn by holding down shift, whilst dragging
23705 * - <OpenLayers.Control>
23707 OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, {
23710 * {OpenLayers.Control.TYPE}
23712 type: OpenLayers.Control.TYPE_TOOL,
23716 * {Boolean} Should the control be used for zooming out?
23721 * APIProperty: keyMask
23722 * {Integer} Zoom only occurs if the keyMask matches the combination of
23723 * keys down. Use bitwise operators and one or more of the
23724 * <OpenLayers.Handler> constants to construct a keyMask. Leave null if
23725 * not used mask. Default is null.
23730 * APIProperty: alwaysZoom
23731 * {Boolean} Always zoom in/out when box drawn, even if the zoom level does
23737 * APIProperty: zoomOnClick
23738 * {Boolean} Should we zoom when no box was dragged, i.e. the user only
23739 * clicked? Default is true.
23747 this.handler = new OpenLayers.Handler.Box( this,
23748 {done: this.zoomBox}, {keyMask: this.keyMask} );
23755 * position - {<OpenLayers.Bounds>} or {<OpenLayers.Pixel>}
23757 zoomBox: function (position) {
23758 if (position instanceof OpenLayers.Bounds) {
23760 targetCenterPx = position.getCenterPixel();
23762 var minXY = this.map.getLonLatFromPixel({
23766 var maxXY = this.map.getLonLatFromPixel({
23770 bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat,
23771 maxXY.lon, maxXY.lat);
23773 var pixWidth = position.right - position.left;
23774 var pixHeight = position.bottom - position.top;
23775 var zoomFactor = Math.min((this.map.size.h / pixHeight),
23776 (this.map.size.w / pixWidth));
23777 var extent = this.map.getExtent();
23778 var center = this.map.getLonLatFromPixel(targetCenterPx);
23779 var xmin = center.lon - (extent.getWidth()/2)*zoomFactor;
23780 var xmax = center.lon + (extent.getWidth()/2)*zoomFactor;
23781 var ymin = center.lat - (extent.getHeight()/2)*zoomFactor;
23782 var ymax = center.lat + (extent.getHeight()/2)*zoomFactor;
23783 bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax);
23785 // always zoom in/out
23786 var lastZoom = this.map.getZoom(),
23787 size = this.map.getSize(),
23788 centerPx = {x: size.w / 2, y: size.h / 2},
23789 zoom = this.map.getZoomForExtent(bounds),
23790 oldRes = this.map.getResolution(),
23791 newRes = this.map.getResolutionForZoom(zoom);
23792 if (oldRes == newRes) {
23793 this.map.setCenter(this.map.getLonLatFromPixel(targetCenterPx));
23795 var zoomOriginPx = {
23796 x: (oldRes * targetCenterPx.x - newRes * centerPx.x) /
23798 y: (oldRes * targetCenterPx.y - newRes * centerPx.y) /
23801 this.map.zoomTo(zoom, zoomOriginPx);
23803 if (lastZoom == this.map.getZoom() && this.alwaysZoom == true){
23804 this.map.zoomTo(lastZoom + (this.out ? -1 : 1));
23806 } else if (this.zoomOnClick) { // it's a pixel
23808 this.map.zoomTo(this.map.getZoom() + 1, position);
23810 this.map.zoomTo(this.map.getZoom() - 1, position);
23815 CLASS_NAME: "OpenLayers.Control.ZoomBox"
23817 /* ======================================================================
23818 OpenLayers/Control/DragPan.js
23819 ====================================================================== */
23821 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
23822 * full list of contributors). Published under the 2-clause BSD license.
23823 * See license.txt in the OpenLayers distribution or repository for the
23824 * full text of the license. */
23827 * @requires OpenLayers/Control.js
23828 * @requires OpenLayers/Handler/Drag.js
23832 * Class: OpenLayers.Control.DragPan
23833 * The DragPan control pans the map with a drag of the mouse.
23836 * - <OpenLayers.Control>
23838 OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, {
23842 * {OpenLayers.Control.TYPES}
23844 type: OpenLayers.Control.TYPE_TOOL,
23848 * {Boolean} The map moved.
23853 * Property: interval
23854 * {Integer} The number of milliseconds that should ellapse before
23855 * panning the map again. Defaults to 0 milliseconds, which means that
23856 * no separate cycle is used for panning. In most cases you won't want
23857 * to change this value. For slow machines/devices larger values can be
23863 * APIProperty: documentDrag
23864 * {Boolean} If set to true, mouse dragging will continue even if the
23865 * mouse cursor leaves the map viewport. Default is false.
23867 documentDrag: false,
23870 * Property: kinetic
23871 * {<OpenLayers.Kinetic>} The OpenLayers.Kinetic object.
23876 * APIProperty: enableKinetic
23877 * {Boolean} Set this option to enable "kinetic dragging". Can be
23878 * set to true or to an object. If set to an object this
23879 * object will be passed to the {<OpenLayers.Kinetic>}
23880 * constructor. Defaults to true.
23881 * To get kinetic dragging, ensure that OpenLayers/Kinetic.js is
23882 * included in your build config.
23884 enableKinetic: true,
23887 * APIProperty: kineticInterval
23888 * {Integer} Interval in milliseconds between 2 steps in the "kinetic
23889 * scrolling". Applies only if enableKinetic is set. Defaults
23890 * to 10 milliseconds.
23892 kineticInterval: 10,
23897 * Creates a Drag handler, using <panMap> and
23898 * <panMapDone> as callbacks.
23901 if (this.enableKinetic && OpenLayers.Kinetic) {
23902 var config = {interval: this.kineticInterval};
23903 if(typeof this.enableKinetic === "object") {
23904 config = OpenLayers.Util.extend(config, this.enableKinetic);
23906 this.kinetic = new OpenLayers.Kinetic(config);
23908 this.handler = new OpenLayers.Handler.Drag(this, {
23909 "move": this.panMap,
23910 "done": this.panMapDone,
23911 "down": this.panMapStart
23913 interval: this.interval,
23914 documentDrag: this.documentDrag
23920 * Method: panMapStart
23922 panMapStart: function() {
23924 this.kinetic.begin();
23932 * xy - {<OpenLayers.Pixel>} Pixel of the mouse position
23934 panMap: function(xy) {
23936 this.kinetic.update(xy);
23938 this.panned = true;
23940 this.handler.last.x - xy.x,
23941 this.handler.last.y - xy.y,
23942 {dragging: true, animate: false}
23947 * Method: panMapDone
23948 * Finish the panning operation. Only call setCenter (through <panMap>)
23949 * if the map has actually been moved.
23952 * xy - {<OpenLayers.Pixel>} Pixel of the mouse position
23954 panMapDone: function(xy) {
23957 if (this.kinetic) {
23958 res = this.kinetic.end(xy);
23961 this.handler.last.x - xy.x,
23962 this.handler.last.y - xy.y,
23963 {dragging: !!res, animate: false}
23967 this.kinetic.move(res, function(x, y, end) {
23968 self.map.pan(x, y, {dragging: !end, animate: false});
23971 this.panned = false;
23975 CLASS_NAME: "OpenLayers.Control.DragPan"
23977 /* ======================================================================
23978 OpenLayers/Handler/MouseWheel.js
23979 ====================================================================== */
23981 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
23982 * full list of contributors). Published under the 2-clause BSD license.
23983 * See license.txt in the OpenLayers distribution or repository for the
23984 * full text of the license. */
23987 * @requires OpenLayers/Handler.js
23991 * Class: OpenLayers.Handler.MouseWheel
23992 * Handler for wheel up/down events.
23995 * - <OpenLayers.Handler>
23997 OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, {
23999 * Property: wheelListener
24002 wheelListener: null,
24005 * Property: interval
24006 * {Integer} In order to increase server performance, an interval (in
24007 * milliseconds) can be set to reduce the number of up/down events
24008 * called. If set, a new up/down event will not be set until the
24009 * interval has passed.
24010 * Defaults to 0, meaning no interval.
24015 * Property: maxDelta
24016 * {Integer} Maximum delta to collect before breaking from the current
24017 * interval. In cumulative mode, this also limits the maximum delta
24018 * returned from the handler. Default is Number.POSITIVE_INFINITY.
24020 maxDelta: Number.POSITIVE_INFINITY,
24024 * {Integer} When interval is set, delta collects the mousewheel z-deltas
24025 * of the events that occur within the interval.
24026 * See also the cumulative option
24031 * Property: cumulative
24032 * {Boolean} When interval is set: true to collect all the mousewheel
24033 * z-deltas, false to only record the delta direction (positive or
24039 * Constructor: OpenLayers.Handler.MouseWheel
24042 * control - {<OpenLayers.Control>}
24043 * callbacks - {Object} An object containing a single function to be
24044 * called when the drag operation is finished.
24045 * The callback should expect to receive a single
24046 * argument, the point geometry.
24047 * options - {Object}
24049 initialize: function(control, callbacks, options) {
24050 OpenLayers.Handler.prototype.initialize.apply(this, arguments);
24051 this.wheelListener = OpenLayers.Function.bindAsEventListener(
24052 this.onWheelEvent, this
24059 destroy: function() {
24060 OpenLayers.Handler.prototype.destroy.apply(this, arguments);
24061 this.wheelListener = null;
24065 * Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/
24069 * Method: onWheelEvent
24070 * Catch the wheel event and handle it xbrowserly
24075 onWheelEvent: function(e){
24077 // make sure we have a map and check keyboard modifiers
24078 if (!this.map || !this.checkModifiers(e)) {
24082 // Ride up the element's DOM hierarchy to determine if it or any of
24083 // its ancestors was:
24084 // * specifically marked as scrollable (CSS overflow property)
24085 // * one of our layer divs or a div marked as scrollable
24086 // ('olScrollable' CSS class)
24089 var overScrollableDiv = false;
24090 var allowScroll = false;
24091 var overMapDiv = false;
24093 var elem = OpenLayers.Event.element(e);
24094 while((elem != null) && !overMapDiv && !overScrollableDiv) {
24096 if (!overScrollableDiv) {
24099 if (elem.currentStyle) {
24100 overflow = elem.currentStyle["overflow"];
24103 document.defaultView.getComputedStyle(elem, null);
24104 overflow = style.getPropertyValue("overflow");
24106 overScrollableDiv = ( overflow &&
24107 (overflow == "auto") || (overflow == "scroll") );
24109 //sometimes when scrolling in a popup, this causes
24110 // obscure browser error
24114 if (!allowScroll) {
24115 allowScroll = OpenLayers.Element.hasClass(elem, 'olScrollable');
24116 if (!allowScroll) {
24117 for (var i = 0, len = this.map.layers.length; i < len; i++) {
24118 // Are we in the layer div? Note that we have two cases
24119 // here: one is to catch EventPane layers, which have a
24120 // pane above the layer (layer.pane)
24121 var layer = this.map.layers[i];
24122 if (elem == layer.div || elem == layer.pane) {
24123 allowScroll = true;
24129 overMapDiv = (elem == this.map.div);
24131 elem = elem.parentNode;
24134 // Logic below is the following:
24136 // If we are over a scrollable div or not over the map div:
24137 // * do nothing (let the browser handle scrolling)
24141 // If we are over the layer div or a 'olScrollable' div:
24144 // * kill event (so as not to also scroll the page after zooming)
24148 // Kill the event (dont scroll the page if we wheel over the
24149 // layerswitcher or the pan/zoom control)
24151 if (!overScrollableDiv && overMapDiv) {
24155 if (e.wheelDelta) {
24156 delta = e.wheelDelta;
24157 if (delta % 160 === 0) {
24158 // opera have steps of 160 instead of 120
24159 delta = delta * 0.75;
24161 delta = delta / 120;
24162 } else if (e.detail) {
24163 // detail in Firefox on OS X is 1/3 of Windows
24164 // so force delta 1 / -1
24165 delta = - (e.detail / Math.abs(e.detail));
24167 this.delta += delta;
24169 window.clearTimeout(this._timeoutId);
24170 if(this.interval && Math.abs(this.delta) < this.maxDelta) {
24171 // store e because window.event might change during delay
24172 var evt = OpenLayers.Util.extend({}, e);
24173 this._timeoutId = window.setTimeout(
24174 OpenLayers.Function.bind(function(){
24175 this.wheelZoom(evt);
24183 OpenLayers.Event.stop(e);
24188 * Method: wheelZoom
24189 * Given the wheel event, we carry out the appropriate zooming in or out,
24190 * based on the 'wheelDelta' or 'detail' property of the event.
24195 wheelZoom: function(e) {
24196 var delta = this.delta;
24200 e.xy = this.map.events.getMousePosition(e);
24202 this.callback("down",
24203 [e, this.cumulative ? Math.max(-this.maxDelta, delta) : -1]);
24205 this.callback("up",
24206 [e, this.cumulative ? Math.min(this.maxDelta, delta) : 1]);
24214 activate: function (evt) {
24215 if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
24216 //register mousewheel events specifically on the window and document
24217 var wheelListener = this.wheelListener;
24218 OpenLayers.Event.observe(window, "DOMMouseScroll", wheelListener);
24219 OpenLayers.Event.observe(window, "mousewheel", wheelListener);
24220 OpenLayers.Event.observe(document, "mousewheel", wheelListener);
24228 * Method: deactivate
24230 deactivate: function (evt) {
24231 if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
24232 // unregister mousewheel events specifically on the window and document
24233 var wheelListener = this.wheelListener;
24234 OpenLayers.Event.stopObserving(window, "DOMMouseScroll", wheelListener);
24235 OpenLayers.Event.stopObserving(window, "mousewheel", wheelListener);
24236 OpenLayers.Event.stopObserving(document, "mousewheel", wheelListener);
24243 CLASS_NAME: "OpenLayers.Handler.MouseWheel"
24245 /* ======================================================================
24246 OpenLayers/Handler/Click.js
24247 ====================================================================== */
24249 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
24250 * full list of contributors). Published under the 2-clause BSD license.
24251 * See license.txt in the OpenLayers distribution or repository for the
24252 * full text of the license. */
24255 * @requires OpenLayers/Handler.js
24259 * Class: OpenLayers.Handler.Click
24260 * A handler for mouse clicks. The intention of this handler is to give
24261 * controls more flexibility with handling clicks. Browsers trigger
24262 * click events twice for a double-click. In addition, the mousedown,
24263 * mousemove, mouseup sequence fires a click event. With this handler,
24264 * controls can decide whether to ignore clicks associated with a double
24265 * click. By setting a <pixelTolerance>, controls can also ignore clicks
24266 * that include a drag. Create a new instance with the
24267 * <OpenLayers.Handler.Click> constructor.
24270 * - <OpenLayers.Handler>
24272 OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, {
24274 * APIProperty: delay
24275 * {Number} Number of milliseconds between clicks before the event is
24276 * considered a double-click.
24281 * APIProperty: single
24282 * {Boolean} Handle single clicks. Default is true. If false, clicks
24283 * will not be reported. If true, single-clicks will be reported.
24288 * APIProperty: double
24289 * {Boolean} Handle double-clicks. Default is false.
24294 * APIProperty: pixelTolerance
24295 * {Number} Maximum number of pixels between mouseup and mousedown for an
24296 * event to be considered a click. Default is 0. If set to an
24297 * integer value, clicks with a drag greater than the value will be
24298 * ignored. This property can only be set when the handler is
24304 * APIProperty: dblclickTolerance
24305 * {Number} Maximum distance in pixels between clicks for a sequence of
24306 * events to be considered a double click. Default is 13. If the
24307 * distance between two clicks is greater than this value, a double-
24308 * click will not be fired.
24310 dblclickTolerance: 13,
24313 * APIProperty: stopSingle
24314 * {Boolean} Stop other listeners from being notified of clicks. Default
24315 * is false. If true, any listeners registered before this one for
24316 * click or rightclick events will not be notified.
24321 * APIProperty: stopDouble
24322 * {Boolean} Stop other listeners from being notified of double-clicks.
24323 * Default is false. If true, any click listeners registered before
24324 * this one will not be notified of *any* double-click events.
24326 * The one caveat with stopDouble is that given a map with two click
24327 * handlers, one with stopDouble true and the other with stopSingle
24328 * true, the stopSingle handler should be activated last to get
24329 * uniform cross-browser performance. Since IE triggers one click
24330 * with a dblclick and FF triggers two, if a stopSingle handler is
24331 * activated first, all it gets in IE is a single click when the
24332 * second handler stops propagation on the dblclick.
24337 * Property: timerId
24338 * {Number} The id of the timeout waiting to clear the <delayedCall>.
24344 * {Object} Object that store relevant information about the last
24345 * mousedown or touchstart. Its 'xy' OpenLayers.Pixel property gives
24346 * the average location of the mouse/touch event. Its 'touches'
24347 * property records clientX/clientY of each touches.
24353 * {Object} Object that store relevant information about the last
24354 * mousemove or touchmove. Its 'xy' OpenLayers.Pixel property gives
24355 * the average location of the mouse/touch event. Its 'touches'
24356 * property records clientX/clientY of each touches.
24362 * {Object} When waiting for double clicks, this object will store
24363 * information about the first click in a two click sequence.
24368 * Property: rightclickTimerId
24369 * {Number} The id of the right mouse timeout waiting to clear the
24372 rightclickTimerId: null,
24375 * Constructor: OpenLayers.Handler.Click
24376 * Create a new click handler.
24379 * control - {<OpenLayers.Control>} The control that is making use of
24380 * this handler. If a handler is being used without a control, the
24381 * handler's setMap method must be overridden to deal properly with
24383 * callbacks - {Object} An object with keys corresponding to callbacks
24384 * that will be called by the handler. The callbacks should
24385 * expect to receive a single argument, the click event.
24386 * Callbacks for 'click' and 'dblclick' are supported.
24387 * options - {Object} Optional object whose properties will be set on the
24392 * Method: touchstart
24393 * Handle touchstart.
24396 * {Boolean} Continue propagating this event.
24398 touchstart: function(evt) {
24400 this.down = this.getEventInfo(evt);
24401 this.last = this.getEventInfo(evt);
24406 * Method: touchmove
24407 * Store position of last move, because touchend event can have
24408 * an empty "touches" property.
24411 * {Boolean} Continue propagating this event.
24413 touchmove: function(evt) {
24414 this.last = this.getEventInfo(evt);
24420 * Correctly set event xy property, and add lastTouches to have
24421 * touches property from last touchstart or touchmove
24424 * {Boolean} Continue propagating this event.
24426 touchend: function(evt) {
24427 // touchstart may not have been allowed to propagate
24429 evt.xy = this.last.xy;
24430 evt.lastTouches = this.last.touches;
24431 this.handleSingle(evt);
24438 * Method: mousedown
24439 * Handle mousedown.
24442 * {Boolean} Continue propagating this event.
24444 mousedown: function(evt) {
24445 this.down = this.getEventInfo(evt);
24446 this.last = this.getEventInfo(evt);
24452 * Handle mouseup. Installed to support collection of right mouse events.
24455 * {Boolean} Continue propagating this event.
24457 mouseup: function (evt) {
24458 var propagate = true;
24460 // Collect right mouse clicks from the mouseup
24461 // IE - ignores the second right click in mousedown so using
24463 if (this.checkModifiers(evt) && this.control.handleRightClicks &&
24464 OpenLayers.Event.isRightClick(evt)) {
24465 propagate = this.rightclick(evt);
24472 * Method: rightclick
24473 * Handle rightclick. For a dblrightclick, we get two clicks so we need
24474 * to always register for dblrightclick to properly handle single
24478 * {Boolean} Continue propagating this event.
24480 rightclick: function(evt) {
24481 if(this.passesTolerance(evt)) {
24482 if(this.rightclickTimerId != null) {
24483 //Second click received before timeout this must be
24486 this.callback('dblrightclick', [evt]);
24487 return !this.stopDouble;
24489 //Set the rightclickTimerId, send evt only if double is
24490 // true else trigger single
24491 var clickEvent = this['double'] ?
24492 OpenLayers.Util.extend({}, evt) :
24493 this.callback('rightclick', [evt]);
24495 var delayedRightCall = OpenLayers.Function.bind(
24496 this.delayedRightCall,
24500 this.rightclickTimerId = window.setTimeout(
24501 delayedRightCall, this.delay
24505 return !this.stopSingle;
24509 * Method: delayedRightCall
24510 * Sets <rightclickTimerId> to null. And optionally triggers the
24511 * rightclick callback if evt is set.
24513 delayedRightCall: function(evt) {
24514 this.rightclickTimerId = null;
24516 this.callback('rightclick', [evt]);
24522 * Handle click events from the browser. This is registered as a listener
24523 * for click events and should not be called from other events in this
24527 * {Boolean} Continue propagating this event.
24529 click: function(evt) {
24531 this.last = this.getEventInfo(evt);
24533 this.handleSingle(evt);
24534 return !this.stopSingle;
24539 * Handle dblclick. For a dblclick, we get two clicks in some browsers
24540 * (FF) and one in others (IE). So we need to always register for
24541 * dblclick to properly handle single clicks. This method is registered
24542 * as a listener for the dblclick browser event. It should *not* be
24543 * called by other methods in this handler.
24546 * {Boolean} Continue propagating this event.
24548 dblclick: function(evt) {
24549 this.handleDouble(evt);
24550 return !this.stopDouble;
24554 * Method: handleDouble
24555 * Handle double-click sequence.
24557 handleDouble: function(evt) {
24558 if (this.passesDblclickTolerance(evt)) {
24559 if (this["double"]) {
24560 this.callback("dblclick", [evt]);
24562 // to prevent a dblclick from firing the click callback in IE
24568 * Method: handleSingle
24569 * Handle single click sequence.
24571 handleSingle: function(evt) {
24572 if (this.passesTolerance(evt)) {
24573 if (this.timerId != null) {
24574 // already received a click
24575 if (this.last.touches && this.last.touches.length === 1) {
24576 // touch device, no dblclick event - this may be a double
24577 if (this["double"]) {
24578 // on Android don't let the browser zoom on the page
24579 OpenLayers.Event.preventDefault(evt);
24581 this.handleDouble(evt);
24583 // if we're not in a touch environment we clear the click timer
24584 // if we've got a second touch, we'll get two touchend events
24585 if (!this.last.touches || this.last.touches.length !== 2) {
24589 // remember the first click info so we can compare to the second
24590 this.first = this.getEventInfo(evt);
24591 // set the timer, send evt only if single is true
24592 //use a clone of the event object because it will no longer
24593 //be a valid event object in IE in the timer callback
24594 var clickEvent = this.single ?
24595 OpenLayers.Util.extend({}, evt) : null;
24596 this.queuePotentialClick(clickEvent);
24602 * Method: queuePotentialClick
24603 * This method is separated out largely to make testing easier (so we
24604 * don't have to override window.setTimeout)
24606 queuePotentialClick: function(evt) {
24607 this.timerId = window.setTimeout(
24608 OpenLayers.Function.bind(this.delayedCall, this, evt),
24614 * Method: passesTolerance
24615 * Determine whether the event is within the optional pixel tolerance. Note
24616 * that the pixel tolerance check only works if mousedown events get to
24617 * the listeners registered here. If they are stopped by other elements,
24618 * the <pixelTolerance> will have no effect here (this method will always
24622 * {Boolean} The click is within the pixel tolerance (if specified).
24624 passesTolerance: function(evt) {
24626 if (this.pixelTolerance != null && this.down && this.down.xy) {
24627 passes = this.pixelTolerance >= this.down.xy.distanceTo(evt.xy);
24628 // for touch environments, we also enforce that all touches
24629 // start and end within the given tolerance to be considered a click
24630 if (passes && this.touch &&
24631 this.down.touches.length === this.last.touches.length) {
24632 // the touchend event doesn't come with touches, so we check
24634 for (var i=0, ii=this.down.touches.length; i<ii; ++i) {
24635 if (this.getTouchDistance(
24636 this.down.touches[i],
24637 this.last.touches[i]
24638 ) > this.pixelTolerance) {
24649 * Method: getTouchDistance
24652 * {Boolean} The pixel displacement between two touches.
24654 getTouchDistance: function(from, to) {
24656 Math.pow(from.clientX - to.clientX, 2) +
24657 Math.pow(from.clientY - to.clientY, 2)
24662 * Method: passesDblclickTolerance
24663 * Determine whether the event is within the optional double-cick pixel
24667 * {Boolean} The click is within the double-click pixel tolerance.
24669 passesDblclickTolerance: function(evt) {
24671 if (this.down && this.first) {
24672 passes = this.down.xy.distanceTo(this.first.xy) <= this.dblclickTolerance;
24678 * Method: clearTimer
24679 * Clear the timer and set <timerId> to null.
24681 clearTimer: function() {
24682 if (this.timerId != null) {
24683 window.clearTimeout(this.timerId);
24684 this.timerId = null;
24686 if (this.rightclickTimerId != null) {
24687 window.clearTimeout(this.rightclickTimerId);
24688 this.rightclickTimerId = null;
24693 * Method: delayedCall
24694 * Sets <timerId> to null. And optionally triggers the click callback if
24697 delayedCall: function(evt) {
24698 this.timerId = null;
24700 this.callback("click", [evt]);
24705 * Method: getEventInfo
24706 * This method allows us to store event information without storing the
24707 * actual event. In touch devices (at least), the same event is
24708 * modified between touchstart, touchmove, and touchend.
24711 * {Object} An object with event related info.
24713 getEventInfo: function(evt) {
24716 var len = evt.touches.length;
24717 touches = new Array(len);
24719 for (var i=0; i<len; i++) {
24720 touch = evt.touches[i];
24722 clientX: touch.olClientX,
24723 clientY: touch.olClientY
24734 * APIMethod: deactivate
24735 * Deactivate the handler.
24738 * {Boolean} The handler was successfully deactivated.
24740 deactivate: function() {
24741 var deactivated = false;
24742 if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
24747 deactivated = true;
24749 return deactivated;
24752 CLASS_NAME: "OpenLayers.Handler.Click"
24754 /* ======================================================================
24755 OpenLayers/Control/Navigation.js
24756 ====================================================================== */
24758 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
24759 * full list of contributors). Published under the 2-clause BSD license.
24760 * See license.txt in the OpenLayers distribution or repository for the
24761 * full text of the license. */
24764 * @requires OpenLayers/Control/ZoomBox.js
24765 * @requires OpenLayers/Control/DragPan.js
24766 * @requires OpenLayers/Handler/MouseWheel.js
24767 * @requires OpenLayers/Handler/Click.js
24771 * Class: OpenLayers.Control.Navigation
24772 * The navigation control handles map browsing with mouse events (dragging,
24773 * double-clicking, and scrolling the wheel). Create a new navigation
24774 * control with the <OpenLayers.Control.Navigation> control.
24776 * Note that this control is added to the map by default (if no controls
24777 * array is sent in the options object to the <OpenLayers.Map>
24781 * - <OpenLayers.Control>
24783 OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, {
24786 * Property: dragPan
24787 * {<OpenLayers.Control.DragPan>}
24792 * APIProperty: dragPanOptions
24793 * {Object} Options passed to the DragPan control.
24795 dragPanOptions: null,
24798 * Property: pinchZoom
24799 * {<OpenLayers.Control.PinchZoom>}
24804 * APIProperty: pinchZoomOptions
24805 * {Object} Options passed to the PinchZoom control.
24807 pinchZoomOptions: null,
24810 * APIProperty: documentDrag
24811 * {Boolean} Allow panning of the map by dragging outside map viewport.
24812 * Default is false.
24814 documentDrag: false,
24817 * Property: zoomBox
24818 * {<OpenLayers.Control.ZoomBox>}
24823 * APIProperty: zoomBoxEnabled
24824 * {Boolean} Whether the user can draw a box to zoom
24826 zoomBoxEnabled: true,
24829 * APIProperty: zoomWheelEnabled
24830 * {Boolean} Whether the mousewheel should zoom the map
24832 zoomWheelEnabled: true,
24835 * Property: mouseWheelOptions
24836 * {Object} Options passed to the MouseWheel control (only useful if
24837 * <zoomWheelEnabled> is set to true). Default is no options for maps
24838 * with fractionalZoom set to true, otherwise
24839 * {cumulative: false, interval: 50, maxDelta: 6}
24841 mouseWheelOptions: null,
24844 * APIProperty: handleRightClicks
24845 * {Boolean} Whether or not to handle right clicks. Default is false.
24847 handleRightClicks: false,
24850 * APIProperty: zoomBoxKeyMask
24851 * {Integer} <OpenLayers.Handler> key code of the key, which has to be
24852 * pressed, while drawing the zoom box with the mouse on the screen.
24853 * You should probably set handleRightClicks to true if you use this
24854 * with MOD_CTRL, to disable the context menu for machines which use
24855 * CTRL-Click as a right click.
24856 * Default: <OpenLayers.Handler.MOD_SHIFT>
24858 zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT,
24861 * APIProperty: autoActivate
24862 * {Boolean} Activate the control when it is added to a map. Default is
24865 autoActivate: true,
24868 * Constructor: OpenLayers.Control.Navigation
24869 * Create a new navigation control
24872 * options - {Object} An optional object whose properties will be set on
24875 initialize: function(options) {
24876 this.handlers = {};
24877 OpenLayers.Control.prototype.initialize.apply(this, arguments);
24882 * The destroy method is used to perform any clean up before the control
24883 * is dereferenced. Typically this is where event listeners are removed
24884 * to prevent memory leaks.
24886 destroy: function() {
24889 if (this.dragPan) {
24890 this.dragPan.destroy();
24892 this.dragPan = null;
24894 if (this.zoomBox) {
24895 this.zoomBox.destroy();
24897 this.zoomBox = null;
24899 if (this.pinchZoom) {
24900 this.pinchZoom.destroy();
24902 this.pinchZoom = null;
24904 OpenLayers.Control.prototype.destroy.apply(this,arguments);
24910 activate: function() {
24911 this.dragPan.activate();
24912 if (this.zoomWheelEnabled) {
24913 this.handlers.wheel.activate();
24915 this.handlers.click.activate();
24916 if (this.zoomBoxEnabled) {
24917 this.zoomBox.activate();
24919 if (this.pinchZoom) {
24920 this.pinchZoom.activate();
24922 return OpenLayers.Control.prototype.activate.apply(this,arguments);
24926 * Method: deactivate
24928 deactivate: function() {
24929 if (this.pinchZoom) {
24930 this.pinchZoom.deactivate();
24932 this.zoomBox.deactivate();
24933 this.dragPan.deactivate();
24934 this.handlers.click.deactivate();
24935 this.handlers.wheel.deactivate();
24936 return OpenLayers.Control.prototype.deactivate.apply(this,arguments);
24943 // disable right mouse context menu for support of right click events
24944 if (this.handleRightClicks) {
24945 this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False;
24948 var clickCallbacks = {
24949 'click': this.defaultClick,
24950 'dblclick': this.defaultDblClick,
24951 'dblrightclick': this.defaultDblRightClick
24953 var clickOptions = {
24957 this.handlers.click = new OpenLayers.Handler.Click(
24958 this, clickCallbacks, clickOptions
24960 this.dragPan = new OpenLayers.Control.DragPan(
24961 OpenLayers.Util.extend({
24963 documentDrag: this.documentDrag
24964 }, this.dragPanOptions)
24966 this.zoomBox = new OpenLayers.Control.ZoomBox(
24967 {map: this.map, keyMask: this.zoomBoxKeyMask});
24968 this.dragPan.draw();
24969 this.zoomBox.draw();
24970 var wheelOptions = this.map.fractionalZoom ? {} : {
24975 this.handlers.wheel = new OpenLayers.Handler.MouseWheel(
24976 this, {up : this.wheelUp, down: this.wheelDown},
24977 OpenLayers.Util.extend(wheelOptions, this.mouseWheelOptions)
24979 if (OpenLayers.Control.PinchZoom) {
24980 this.pinchZoom = new OpenLayers.Control.PinchZoom(
24981 OpenLayers.Util.extend(
24982 {map: this.map}, this.pinchZoomOptions));
24987 * Method: defaultClick
24992 defaultClick: function (evt) {
24993 if (evt.lastTouches && evt.lastTouches.length == 2) {
24994 this.map.zoomOut();
24999 * Method: defaultDblClick
25004 defaultDblClick: function (evt) {
25005 this.map.zoomTo(this.map.zoom + 1, evt.xy);
25009 * Method: defaultDblRightClick
25014 defaultDblRightClick: function (evt) {
25015 this.map.zoomTo(this.map.zoom - 1, evt.xy);
25019 * Method: wheelChange
25023 * deltaZ - {Integer}
25025 wheelChange: function(evt, deltaZ) {
25026 if (!this.map.fractionalZoom) {
25027 deltaZ = Math.round(deltaZ);
25029 var currentZoom = this.map.getZoom(),
25030 newZoom = currentZoom + deltaZ;
25031 newZoom = Math.max(newZoom, 0);
25032 newZoom = Math.min(newZoom, this.map.getNumZoomLevels());
25033 if (newZoom === currentZoom) {
25036 this.map.zoomTo(newZoom, evt.xy);
25041 * User spun scroll wheel up
25045 * delta - {Integer}
25047 wheelUp: function(evt, delta) {
25048 this.wheelChange(evt, delta || 1);
25052 * Method: wheelDown
25053 * User spun scroll wheel down
25057 * delta - {Integer}
25059 wheelDown: function(evt, delta) {
25060 this.wheelChange(evt, delta || -1);
25064 * Method: disableZoomBox
25066 disableZoomBox : function() {
25067 this.zoomBoxEnabled = false;
25068 this.zoomBox.deactivate();
25072 * Method: enableZoomBox
25074 enableZoomBox : function() {
25075 this.zoomBoxEnabled = true;
25077 this.zoomBox.activate();
25082 * Method: disableZoomWheel
25085 disableZoomWheel : function() {
25086 this.zoomWheelEnabled = false;
25087 this.handlers.wheel.deactivate();
25091 * Method: enableZoomWheel
25094 enableZoomWheel : function() {
25095 this.zoomWheelEnabled = true;
25097 this.handlers.wheel.activate();
25101 CLASS_NAME: "OpenLayers.Control.Navigation"
25103 /* ======================================================================
25104 OpenLayers/Events/buttonclick.js
25105 ====================================================================== */
25107 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
25108 * full list of contributors). Published under the 2-clause BSD license.
25109 * See license.txt in the OpenLayers distribution or repository for the
25110 * full text of the license. */
25113 * @requires OpenLayers/Events.js
25117 * Class: OpenLayers.Events.buttonclick
25118 * Extension event type for handling buttons on top of a dom element. This
25119 * event type fires "buttonclick" on its <target> when a button was
25120 * clicked. Buttons are detected by the "olButton" class.
25122 * This event type makes sure that button clicks do not interfere with other
25123 * events that are registered on the same <element>.
25125 * Event types provided by this extension:
25126 * - *buttonclick* Triggered when a button is clicked. Listeners receive an
25127 * object with a *buttonElement* property referencing the dom element of
25128 * the clicked button, and an *buttonXY* property with the click position
25129 * relative to the button.
25131 OpenLayers.Events.buttonclick = OpenLayers.Class({
25135 * {<OpenLayers.Events>} The events instance that the buttonclick event will
25142 * {Array} Events to observe and conditionally stop from propagating when
25143 * an element with the olButton class (or its olAlphaImg child) is
25147 'mousedown', 'mouseup', 'click', 'dblclick',
25148 'touchstart', 'touchmove', 'touchend', 'keydown'
25152 * Property: startRegEx
25153 * {RegExp} Regular expression to test Event.type for events that start
25154 * a buttonclick sequence.
25156 startRegEx: /^mousedown|touchstart$/,
25159 * Property: cancelRegEx
25160 * {RegExp} Regular expression to test Event.type for events that cancel
25161 * a buttonclick sequence.
25163 cancelRegEx: /^touchmove$/,
25166 * Property: completeRegEx
25167 * {RegExp} Regular expression to test Event.type for events that complete
25168 * a buttonclick sequence.
25170 completeRegEx: /^mouseup|touchend$/,
25173 * Property: isDeviceTouchCapable
25174 * {Boolean} Tells whether the browser detects touch events.
25176 isDeviceTouchCapable: 'ontouchstart' in window ||
25177 window.DocumentTouch && document instanceof window.DocumentTouch,
25180 * Property: startEvt
25181 * {Event} The event that started the click sequence
25185 * Constructor: OpenLayers.Events.buttonclick
25186 * Construct a buttonclick event type. Applications are not supposed to
25187 * create instances of this class - they are created on demand by
25188 * <OpenLayers.Events> instances.
25191 * target - {<OpenLayers.Events>} The events instance that the buttonclick
25192 * event will be triggered on.
25194 initialize: function(target) {
25195 this.target = target;
25196 for (var i=this.events.length-1; i>=0; --i) {
25197 this.target.register(this.events[i], this, this.buttonClick, {
25206 destroy: function() {
25207 for (var i=this.events.length-1; i>=0; --i) {
25208 this.target.unregister(this.events[i], this, this.buttonClick);
25210 delete this.target;
25214 * Method: getPressedButton
25215 * Get the pressed button, if any. Returns undefined if no button
25219 * element - {DOMElement} The event target.
25222 * {DOMElement} The button element, or undefined.
25224 getPressedButton: function(element) {
25225 var depth = 3, // limit the search depth
25228 if(OpenLayers.Element.hasClass(element, "olButton")) {
25233 element = element.parentNode;
25234 } while(--depth > 0 && element);
25240 * Check for event target elements that should be ignored by OpenLayers.
25243 * element - {DOMElement} The event target.
25245 ignore: function(element) {
25249 if (element.nodeName.toLowerCase() === 'a') {
25253 element = element.parentNode;
25254 } while (--depth > 0 && element);
25259 * Method: buttonClick
25260 * Check if a button was clicked, and fire the buttonclick event
25265 buttonClick: function(evt) {
25266 var propagate = true,
25267 element = OpenLayers.Event.element(evt);
25270 (OpenLayers.Event.isLeftClick(evt) &&
25271 !this.isDeviceTouchCapable ||
25272 !~evt.type.indexOf("mouse"))) {
25273 // was a button pressed?
25274 var button = this.getPressedButton(element);
25276 if (evt.type === "keydown") {
25277 switch (evt.keyCode) {
25278 case OpenLayers.Event.KEY_RETURN:
25279 case OpenLayers.Event.KEY_SPACE:
25280 this.target.triggerEvent("buttonclick", {
25281 buttonElement: button
25283 OpenLayers.Event.stop(evt);
25287 } else if (this.startEvt) {
25288 if (this.completeRegEx.test(evt.type)) {
25289 var pos = OpenLayers.Util.pagePosition(button);
25290 var viewportElement = OpenLayers.Util.getViewportElement();
25291 var scrollTop = window.pageYOffset || viewportElement.scrollTop;
25292 var scrollLeft = window.pageXOffset || viewportElement.scrollLeft;
25293 pos[0] = pos[0] - scrollLeft;
25294 pos[1] = pos[1] - scrollTop;
25296 this.target.triggerEvent("buttonclick", {
25297 buttonElement: button,
25299 x: this.startEvt.clientX - pos[0],
25300 y: this.startEvt.clientY - pos[1]
25304 if (this.cancelRegEx.test(evt.type)) {
25305 if (evt.touches && this.startEvt.touches &&
25306 (Math.abs(evt.touches[0].olClientX - this.startEvt.touches[0].olClientX) > 4 ||
25307 Math.abs(evt.touches[0].olClientY - this.startEvt.touches[0].olClientY)) > 4) {
25308 delete this.startEvt;
25311 OpenLayers.Event.stop(evt);
25314 if (this.startRegEx.test(evt.type)) {
25315 this.startEvt = evt;
25316 OpenLayers.Event.stop(evt);
25320 propagate = !this.ignore(OpenLayers.Event.element(evt));
25321 delete this.startEvt;
25328 /* ======================================================================
25329 OpenLayers/Control/PanZoom.js
25330 ====================================================================== */
25332 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
25333 * full list of contributors). Published under the 2-clause BSD license.
25334 * See license.txt in the OpenLayers distribution or repository for the
25335 * full text of the license. */
25339 * @requires OpenLayers/Control.js
25340 * @requires OpenLayers/Events/buttonclick.js
25344 * Class: OpenLayers.Control.PanZoom
25345 * The PanZoom is a visible control, composed of a
25346 * <OpenLayers.Control.PanPanel> and a <OpenLayers.Control.ZoomPanel>. By
25347 * default it is drawn in the upper left corner of the map.
25350 * - <OpenLayers.Control>
25352 OpenLayers.Control.PanZoom = OpenLayers.Class(OpenLayers.Control, {
25355 * APIProperty: slideFactor
25356 * {Integer} Number of pixels by which we'll pan the map in any direction
25357 * on clicking the arrow buttons. If you want to pan by some ratio
25358 * of the map dimensions, use <slideRatio> instead.
25363 * APIProperty: slideRatio
25364 * {Number} The fraction of map width/height by which we'll pan the map
25365 * on clicking the arrow buttons. Default is null. If set, will
25366 * override <slideFactor>. E.g. if slideRatio is .5, then the Pan Up
25367 * button will pan up half the map height.
25372 * Property: buttons
25373 * {Array(DOMElement)} Array of Button Divs
25378 * Property: position
25379 * {<OpenLayers.Pixel>}
25384 * Constructor: OpenLayers.Control.PanZoom
25387 * options - {Object}
25389 initialize: function(options) {
25390 this.position = new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X,
25391 OpenLayers.Control.PanZoom.Y);
25392 OpenLayers.Control.prototype.initialize.apply(this, arguments);
25396 * APIMethod: destroy
25398 destroy: function() {
25399 if (this.map && !this.outsideViewport) {
25400 this.map.events.unregister("buttonclick", this, this.onButtonClick);
25402 this.removeButtons();
25403 this.buttons = null;
25404 this.position = null;
25405 OpenLayers.Control.prototype.destroy.apply(this, arguments);
25412 * map - {<OpenLayers.Map>}
25414 setMap: function(map) {
25415 OpenLayers.Control.prototype.setMap.apply(this, arguments);
25417 if (this.outsideViewport) {
25418 this.events.attachToElement(this.div);
25423 target.events.register('buttonclick', this, this.onButtonClick);
25430 * px - {<OpenLayers.Pixel>}
25433 * {DOMElement} A reference to the container div for the PanZoom control.
25435 draw: function(px) {
25436 // initialize our internal div
25437 OpenLayers.Control.prototype.draw.apply(this, arguments);
25438 px = this.position;
25440 // place the controls
25443 var sz = {w: 18, h: 18};
25444 var centered = new OpenLayers.Pixel(px.x+sz.w/2, px.y);
25446 this._addButton("panup", "north-mini.png", centered, sz);
25447 px.y = centered.y+sz.h;
25448 this._addButton("panleft", "west-mini.png", px, sz);
25449 this._addButton("panright", "east-mini.png", px.add(sz.w, 0), sz);
25450 this._addButton("pandown", "south-mini.png",
25451 centered.add(0, sz.h*2), sz);
25452 this._addButton("zoomin", "zoom-plus-mini.png",
25453 centered.add(0, sz.h*3+5), sz);
25454 this._addButton("zoomworld", "zoom-world-mini.png",
25455 centered.add(0, sz.h*4+5), sz);
25456 this._addButton("zoomout", "zoom-minus-mini.png",
25457 centered.add(0, sz.h*5+5), sz);
25462 * Method: _addButton
25467 * xy - {<OpenLayers.Pixel>}
25468 * sz - {<OpenLayers.Size>}
25471 * {DOMElement} A Div (an alphaImageDiv, to be precise) that contains the
25472 * image of the button, and has all the proper event handlers set.
25474 _addButton:function(id, img, xy, sz) {
25475 var imgLocation = OpenLayers.Util.getImageLocation(img);
25476 var btn = OpenLayers.Util.createAlphaImageDiv(
25477 this.id + "_" + id,
25478 xy, sz, imgLocation, "absolute");
25479 btn.style.cursor = "pointer";
25480 //we want to add the outer div
25481 this.div.appendChild(btn);
25483 btn.className = "olButton";
25485 //we want to remember/reference the outer div
25486 this.buttons.push(btn);
25491 * Method: _removeButton
25496 _removeButton: function(btn) {
25497 this.div.removeChild(btn);
25498 OpenLayers.Util.removeItem(this.buttons, btn);
25502 * Method: removeButtons
25504 removeButtons: function() {
25505 for(var i=this.buttons.length-1; i>=0; --i) {
25506 this._removeButton(this.buttons[i]);
25511 * Method: onButtonClick
25516 onButtonClick: function(evt) {
25517 var btn = evt.buttonElement;
25518 switch (btn.action) {
25520 this.map.pan(0, -this.getSlideFactor("h"));
25523 this.map.pan(0, this.getSlideFactor("h"));
25526 this.map.pan(-this.getSlideFactor("w"), 0);
25529 this.map.pan(this.getSlideFactor("w"), 0);
25535 this.map.zoomOut();
25538 this.map.zoomToMaxExtent();
25544 * Method: getSlideFactor
25547 * dim - {String} "w" or "h" (for width or height).
25550 * {Number} The slide factor for panning in the requested direction.
25552 getSlideFactor: function(dim) {
25553 return this.slideRatio ?
25554 this.map.getSize()[dim] * this.slideRatio :
25558 CLASS_NAME: "OpenLayers.Control.PanZoom"
25565 OpenLayers.Control.PanZoom.X = 4;
25571 OpenLayers.Control.PanZoom.Y = 4;
25572 /* ======================================================================
25573 OpenLayers/Handler/Pinch.js
25574 ====================================================================== */
25576 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
25577 * full list of contributors). Published under the 2-clause BSD license.
25578 * See license.txt in the OpenLayers distribution or repository for the
25579 * full text of the license. */
25582 * @requires OpenLayers/Handler.js
25586 * Class: OpenLayers.Handler.Pinch
25587 * The pinch handler is used to deal with sequences of browser events related
25588 * to pinch gestures. The handler is used by controls that want to know
25589 * when a pinch sequence begins, when a pinch is happening, and when it has
25592 * Controls that use the pinch handler typically construct it with callbacks
25593 * for 'start', 'move', and 'done'. Callbacks for these keys are
25594 * called when the pinch begins, with each change, and when the pinch is
25597 * Create a new pinch handler with the <OpenLayers.Handler.Pinch> constructor.
25600 * - <OpenLayers.Handler>
25602 OpenLayers.Handler.Pinch = OpenLayers.Class(OpenLayers.Handler, {
25605 * Property: started
25606 * {Boolean} When a touchstart event is received, we want to record it,
25607 * but not set 'pinching' until the touchmove get started after
25613 * Property: stopDown
25614 * {Boolean} Stop propagation of touchstart events from getting to
25615 * listeners on the same element. Default is false.
25620 * Property: pinching
25627 * {Object} Object that store informations related to pinch last touch.
25633 * {Object} Object that store informations related to pinch touchstart.
25638 * Constructor: OpenLayers.Handler.Pinch
25639 * Returns OpenLayers.Handler.Pinch
25642 * control - {<OpenLayers.Control>} The control that is making use of
25643 * this handler. If a handler is being used without a control, the
25644 * handlers setMap method must be overridden to deal properly with
25646 * callbacks - {Object} An object containing functions to be called when
25647 * the pinch operation start, change, or is finished. The callbacks
25648 * should expect to receive an object argument, which contains
25649 * information about scale, distance, and position of touch points.
25650 * options - {Object}
25654 * Method: touchstart
25655 * Handle touchstart events
25661 * {Boolean} Let the event propagate.
25663 touchstart: function(evt) {
25664 var propagate = true;
25665 this.pinching = false;
25666 if (OpenLayers.Event.isMultiTouch(evt)) {
25667 this.started = true;
25668 this.last = this.start = {
25669 distance: this.getDistance(evt.touches),
25673 this.callback("start", [evt, this.start]);
25674 propagate = !this.stopDown;
25675 } else if (this.started) {
25676 // Some webkit versions send fake single-touch events during
25677 // multitouch, which cause the drag handler to trigger
25680 this.started = false;
25684 // prevent document dragging
25685 OpenLayers.Event.preventDefault(evt);
25690 * Method: touchmove
25691 * Handle touchmove events
25697 * {Boolean} Let the event propagate.
25699 touchmove: function(evt) {
25700 if (this.started && OpenLayers.Event.isMultiTouch(evt)) {
25701 this.pinching = true;
25702 var current = this.getPinchData(evt);
25703 this.callback("move", [evt, current]);
25704 this.last = current;
25705 // prevent document dragging
25706 OpenLayers.Event.stop(evt);
25707 } else if (this.started) {
25708 // Some webkit versions send fake single-touch events during
25709 // multitouch, which cause the drag handler to trigger
25717 * Handle touchend events
25723 * {Boolean} Let the event propagate.
25725 touchend: function(evt) {
25726 if (this.started && !OpenLayers.Event.isMultiTouch(evt)) {
25727 this.started = false;
25728 this.pinching = false;
25729 this.callback("done", [evt, this.start, this.last]);
25739 * Activate the handler.
25742 * {Boolean} The handler was successfully activated.
25744 activate: function() {
25745 var activated = false;
25746 if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
25747 this.pinching = false;
25754 * Method: deactivate
25755 * Deactivate the handler.
25758 * {Boolean} The handler was successfully deactivated.
25760 deactivate: function() {
25761 var deactivated = false;
25762 if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
25763 this.started = false;
25764 this.pinching = false;
25767 deactivated = true;
25769 return deactivated;
25773 * Method: getDistance
25774 * Get the distance in pixels between two touches.
25777 * touches - {Array(Object)}
25780 * {Number} The distance in pixels.
25782 getDistance: function(touches) {
25783 var t0 = touches[0];
25784 var t1 = touches[1];
25786 Math.pow(t0.olClientX - t1.olClientX, 2) +
25787 Math.pow(t0.olClientY - t1.olClientY, 2)
25793 * Method: getPinchData
25794 * Get informations about the pinch event.
25800 * {Object} Object that contains data about the current pinch.
25802 getPinchData: function(evt) {
25803 var distance = this.getDistance(evt.touches);
25804 var scale = distance / this.start.distance;
25806 distance: distance,
25807 delta: this.last.distance - distance,
25812 CLASS_NAME: "OpenLayers.Handler.Pinch"
25815 /* ======================================================================
25816 OpenLayers/Control/PinchZoom.js
25817 ====================================================================== */
25819 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
25820 * full list of contributors). Published under the 2-clause BSD license.
25821 * See license.txt in the OpenLayers distribution or repository for the
25822 * full text of the license. */
25825 * @requires OpenLayers/Handler/Pinch.js
25829 * Class: OpenLayers.Control.PinchZoom
25832 * - <OpenLayers.Control>
25834 OpenLayers.Control.PinchZoom = OpenLayers.Class(OpenLayers.Control, {
25838 * {OpenLayers.Control.TYPES}
25840 type: OpenLayers.Control.TYPE_TOOL,
25843 * Property: pinchOrigin
25844 * {Object} Cached object representing the pinch start (in pixels).
25849 * Property: currentCenter
25850 * {Object} Cached object representing the latest pinch center (in pixels).
25852 currentCenter: null,
25855 * APIProperty: autoActivate
25856 * {Boolean} Activate the control when it is added to a map. Default is
25859 autoActivate: true,
25862 * APIProperty: preserveCenter
25863 * {Boolean} Set this to true if you don't want the map center to change
25864 * while pinching. For example you may want to set preserveCenter to
25865 * true when the user location is being watched and you want to preserve
25866 * the user location at the center of the map even if he zooms in or
25867 * out using pinch. This property's value can be changed any time on an
25868 * existing instance. Default is false.
25870 preserveCenter: false,
25873 * APIProperty: handlerOptions
25874 * {Object} Used to set non-default properties on the pinch handler
25878 * Constructor: OpenLayers.Control.PinchZoom
25879 * Create a control for zooming with pinch gestures. This works on devices
25880 * with multi-touch support.
25883 * options - {Object} An optional object whose properties will be set on
25886 initialize: function(options) {
25887 OpenLayers.Control.prototype.initialize.apply(this, arguments);
25888 this.handler = new OpenLayers.Handler.Pinch(this, {
25889 start: this.pinchStart,
25890 move: this.pinchMove,
25891 done: this.pinchDone
25892 }, this.handlerOptions);
25896 * Method: pinchStart
25900 * pinchData - {Object} pinch data object related to the current touchmove
25901 * of the pinch gesture. This give us the current scale of the pinch.
25903 pinchStart: function(evt, pinchData) {
25904 var xy = (this.preserveCenter) ?
25905 this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy;
25906 this.pinchOrigin = xy;
25907 this.currentCenter = xy;
25911 * Method: pinchMove
25915 * pinchData - {Object} pinch data object related to the current touchmove
25916 * of the pinch gesture. This give us the current scale of the pinch.
25918 pinchMove: function(evt, pinchData) {
25919 var scale = pinchData.scale;
25920 var containerOrigin = this.map.layerContainerOriginPx;
25921 var pinchOrigin = this.pinchOrigin;
25922 var current = (this.preserveCenter) ?
25923 this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy;
25925 var dx = Math.round((containerOrigin.x + current.x - pinchOrigin.x) + (scale - 1) * (containerOrigin.x - pinchOrigin.x));
25926 var dy = Math.round((containerOrigin.y + current.y - pinchOrigin.y) + (scale - 1) * (containerOrigin.y - pinchOrigin.y));
25928 this.map.applyTransform(dx, dy, scale);
25929 this.currentCenter = current;
25933 * Method: pinchDone
25937 * start - {Object} pinch data object related to the touchstart event that
25938 * started the pinch gesture.
25939 * last - {Object} pinch data object related to the last touchmove event
25940 * of the pinch gesture. This give us the final scale of the pinch.
25942 pinchDone: function(evt, start, last) {
25943 this.map.applyTransform();
25944 var zoom = this.map.getZoomForResolution(this.map.getResolution() / last.scale, true);
25945 if (zoom !== this.map.getZoom() || !this.currentCenter.equals(this.pinchOrigin)) {
25946 var resolution = this.map.getResolutionForZoom(zoom);
25948 var location = this.map.getLonLatFromPixel(this.pinchOrigin);
25949 var zoomPixel = this.currentCenter;
25950 var size = this.map.getSize();
25952 location.lon += resolution * ((size.w / 2) - zoomPixel.x);
25953 location.lat -= resolution * ((size.h / 2) - zoomPixel.y);
25955 // Force a reflow before calling setCenter. This is to work
25956 // around an issue occurring in iOS.
25958 // See https://github.com/openlayers/ol2/pull/351.
25960 // Without a reflow setting the layer container div's top left
25961 // style properties to "0px" - as done in Map.moveTo when zoom
25962 // is changed - won't actually correctly reposition the layer
25965 // Also, we need to use a statement that the Google Closure
25966 // compiler won't optimize away.
25967 this.map.div.clientWidth = this.map.div.clientWidth;
25969 this.map.setCenter(location, zoom);
25973 CLASS_NAME: "OpenLayers.Control.PinchZoom"
25976 /* ======================================================================
25977 OpenLayers/Control/TouchNavigation.js
25978 ====================================================================== */
25980 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
25981 * full list of contributors). Published under the 2-clause BSD license.
25982 * See license.txt in the OpenLayers distribution or repository for the
25983 * full text of the license. */
25986 * @requires OpenLayers/Control/DragPan.js
25987 * @requires OpenLayers/Control/PinchZoom.js
25988 * @requires OpenLayers/Handler/Click.js
25992 * Class: OpenLayers.Control.TouchNavigation
25993 * The navigation control handles map browsing with touch events (dragging,
25994 * double-tapping, tap with two fingers, and pinch zoom). Create a new
25995 * control with the <OpenLayers.Control.TouchNavigation> constructor.
25997 * If you’re only targeting touch enabled devices with your mapping application,
25998 * you can create a map with only a TouchNavigation control. The
25999 * <OpenLayers.Control.Navigation> control is mobile ready by default, but
26000 * you can generate a smaller build of the library by only including this
26001 * touch navigation control if you aren't concerned about mouse interaction.
26004 * - <OpenLayers.Control>
26006 OpenLayers.Control.TouchNavigation = OpenLayers.Class(OpenLayers.Control, {
26009 * Property: dragPan
26010 * {<OpenLayers.Control.DragPan>}
26015 * APIProperty: dragPanOptions
26016 * {Object} Options passed to the DragPan control.
26018 dragPanOptions: null,
26021 * Property: pinchZoom
26022 * {<OpenLayers.Control.PinchZoom>}
26027 * APIProperty: pinchZoomOptions
26028 * {Object} Options passed to the PinchZoom control.
26030 pinchZoomOptions: null,
26033 * APIProperty: clickHandlerOptions
26034 * {Object} Options passed to the Click handler.
26036 clickHandlerOptions: null,
26039 * APIProperty: documentDrag
26040 * {Boolean} Allow panning of the map by dragging outside map viewport.
26041 * Default is false.
26043 documentDrag: false,
26046 * APIProperty: autoActivate
26047 * {Boolean} Activate the control when it is added to a map. Default is
26050 autoActivate: true,
26053 * Constructor: OpenLayers.Control.TouchNavigation
26054 * Create a new navigation control
26057 * options - {Object} An optional object whose properties will be set on
26060 initialize: function(options) {
26061 this.handlers = {};
26062 OpenLayers.Control.prototype.initialize.apply(this, arguments);
26067 * The destroy method is used to perform any clean up before the control
26068 * is dereferenced. Typically this is where event listeners are removed
26069 * to prevent memory leaks.
26071 destroy: function() {
26074 this.dragPan.destroy();
26076 this.dragPan = null;
26077 if (this.pinchZoom) {
26078 this.pinchZoom.destroy();
26079 delete this.pinchZoom;
26081 OpenLayers.Control.prototype.destroy.apply(this,arguments);
26087 activate: function() {
26088 if(OpenLayers.Control.prototype.activate.apply(this,arguments)) {
26089 this.dragPan.activate();
26090 this.handlers.click.activate();
26091 this.pinchZoom.activate();
26098 * Method: deactivate
26100 deactivate: function() {
26101 if(OpenLayers.Control.prototype.deactivate.apply(this,arguments)) {
26102 this.dragPan.deactivate();
26103 this.handlers.click.deactivate();
26104 this.pinchZoom.deactivate();
26114 var clickCallbacks = {
26115 click: this.defaultClick,
26116 dblclick: this.defaultDblClick
26118 var clickOptions = OpenLayers.Util.extend({
26122 }, this.clickHandlerOptions);
26123 this.handlers.click = new OpenLayers.Handler.Click(
26124 this, clickCallbacks, clickOptions
26126 this.dragPan = new OpenLayers.Control.DragPan(
26127 OpenLayers.Util.extend({
26129 documentDrag: this.documentDrag
26130 }, this.dragPanOptions)
26132 this.dragPan.draw();
26133 this.pinchZoom = new OpenLayers.Control.PinchZoom(
26134 OpenLayers.Util.extend({map: this.map}, this.pinchZoomOptions)
26139 * Method: defaultClick
26144 defaultClick: function (evt) {
26145 if(evt.lastTouches && evt.lastTouches.length == 2) {
26146 this.map.zoomOut();
26151 * Method: defaultDblClick
26156 defaultDblClick: function (evt) {
26157 this.map.zoomTo(this.map.zoom + 1, evt.xy);
26160 CLASS_NAME: "OpenLayers.Control.TouchNavigation"
26162 /* ======================================================================
26163 OpenLayers/Control/Attribution.js
26164 ====================================================================== */
26166 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
26167 * full list of contributors). Published under the 2-clause BSD license.
26168 * See license.txt in the OpenLayers distribution or repository for the
26169 * full text of the license. */
26172 * @requires OpenLayers/Control.js
26176 * Class: OpenLayers.Control.Attribution
26177 * The attribution control adds attribution from layers to the map display.
26178 * It uses 'attribution' property of each layer.
26181 * - <OpenLayers.Control>
26183 OpenLayers.Control.Attribution =
26184 OpenLayers.Class(OpenLayers.Control, {
26187 * APIProperty: separator
26188 * {String} String used to separate layers.
26193 * APIProperty: template
26194 * {String} Template for the global attribution markup. This has to include the
26195 * substring "${layers}", which will be replaced by the layer specific
26196 * attributions, separated by <separator>. The default is "${layers}".
26198 template: "${layers}",
26201 * APIProperty: layerTemplate
26202 * {String} Template for the layer specific attribution. This has to include
26203 * the substrings "${href}" and "${title}", which will be replaced by
26204 * the layer specific attribution object properties.
26205 * The default is '<a href="${href}" target="_blank">${title}</a>'.
26207 layerTemplate: '<a href="${href}" target="_blank">${title}</a>',
26210 * Constructor: OpenLayers.Control.Attribution
26213 * options - {Object} Options for control.
26220 destroy: function() {
26221 this.map.events.un({
26222 "removelayer": this.updateAttribution,
26223 "addlayer": this.updateAttribution,
26224 "changelayer": this.updateAttribution,
26225 "changebaselayer": this.updateAttribution,
26229 OpenLayers.Control.prototype.destroy.apply(this, arguments);
26234 * Initialize control.
26237 * {DOMElement} A reference to the DIV DOMElement containing the control
26240 OpenLayers.Control.prototype.draw.apply(this, arguments);
26242 this.map.events.on({
26243 'changebaselayer': this.updateAttribution,
26244 'changelayer': this.updateAttribution,
26245 'addlayer': this.updateAttribution,
26246 'removelayer': this.updateAttribution,
26249 this.updateAttribution();
26255 * Method: updateAttribution
26256 * Update attribution string.
26258 updateAttribution: function() {
26259 var attributions = [], attribution;
26260 if (this.map && this.map.layers) {
26261 for(var i=0, len=this.map.layers.length; i<len; i++) {
26262 var layer = this.map.layers[i];
26263 if (layer.attribution && layer.getVisibility()) {
26264 attribution = (typeof layer.attribution == "object") ?
26265 OpenLayers.String.format(
26266 this.layerTemplate, layer.attribution) :
26268 // add attribution only if attribution text is unique
26269 if (OpenLayers.Util.indexOf(
26270 attributions, attribution) === -1) {
26271 attributions.push( attribution );
26275 this.div.innerHTML = OpenLayers.String.format(this.template, {
26276 layers: attributions.join(this.separator)
26281 CLASS_NAME: "OpenLayers.Control.Attribution"
26283 /* ======================================================================
26284 OpenLayers/Control/Zoom.js
26285 ====================================================================== */
26287 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
26288 * full list of contributors). Published under the 2-clause BSD license.
26289 * See license.txt in the OpenLayers distribution or repository for the
26290 * full text of the license. */
26293 * @requires OpenLayers/Control.js
26294 * @requires OpenLayers/Events/buttonclick.js
26298 * Class: OpenLayers.Control.Zoom
26299 * The Zoom control is a pair of +/- links for zooming in and out.
26302 * - <OpenLayers.Control>
26304 OpenLayers.Control.Zoom = OpenLayers.Class(OpenLayers.Control, {
26307 * APIProperty: zoomInText
26309 * Text for zoom-in link. Default is "+".
26314 * APIProperty: zoomInId
26316 * Instead of having the control create a zoom in link, you can provide
26317 * the identifier for an anchor element already added to the document.
26318 * By default, an element with id "olZoomInLink" will be searched for
26319 * and used if it exists.
26321 zoomInId: "olZoomInLink",
26324 * APIProperty: zoomOutText
26326 * Text for zoom-out link. Default is "\u2212".
26328 zoomOutText: "\u2212",
26331 * APIProperty: zoomOutId
26333 * Instead of having the control create a zoom out link, you can provide
26334 * the identifier for an anchor element already added to the document.
26335 * By default, an element with id "olZoomOutLink" will be searched for
26336 * and used if it exists.
26338 zoomOutId: "olZoomOutLink",
26344 * {DOMElement} A reference to the DOMElement containing the zoom links.
26347 var div = OpenLayers.Control.prototype.draw.apply(this),
26348 links = this.getOrCreateLinks(div),
26349 zoomIn = links.zoomIn,
26350 zoomOut = links.zoomOut,
26351 eventsInstance = this.map.events;
26353 if (zoomOut.parentNode !== div) {
26354 eventsInstance = this.events;
26355 eventsInstance.attachToElement(zoomOut.parentNode);
26357 eventsInstance.register("buttonclick", this, this.onZoomClick);
26359 this.zoomInLink = zoomIn;
26360 this.zoomOutLink = zoomOut;
26365 * Method: getOrCreateLinks
26368 * el - {DOMElement}
26371 * {Object} Object with zoomIn and zoomOut properties referencing links.
26373 getOrCreateLinks: function(el) {
26374 var zoomIn = document.getElementById(this.zoomInId),
26375 zoomOut = document.getElementById(this.zoomOutId);
26377 zoomIn = document.createElement("a");
26378 zoomIn.href = "#zoomIn";
26379 zoomIn.appendChild(document.createTextNode(this.zoomInText));
26380 zoomIn.className = "olControlZoomIn";
26381 el.appendChild(zoomIn);
26383 OpenLayers.Element.addClass(zoomIn, "olButton");
26385 zoomOut = document.createElement("a");
26386 zoomOut.href = "#zoomOut";
26387 zoomOut.appendChild(document.createTextNode(this.zoomOutText));
26388 zoomOut.className = "olControlZoomOut";
26389 el.appendChild(zoomOut);
26391 OpenLayers.Element.addClass(zoomOut, "olButton");
26393 zoomIn: zoomIn, zoomOut: zoomOut
26398 * Method: onZoomClick
26399 * Called when zoomin/out link is clicked.
26401 onZoomClick: function(evt) {
26402 var button = evt.buttonElement;
26403 if (button === this.zoomInLink) {
26405 } else if (button === this.zoomOutLink) {
26406 this.map.zoomOut();
26414 destroy: function() {
26416 this.map.events.unregister("buttonclick", this, this.onZoomClick);
26418 delete this.zoomInLink;
26419 delete this.zoomOutLink;
26420 OpenLayers.Control.prototype.destroy.apply(this);
26423 CLASS_NAME: "OpenLayers.Control.Zoom"
26425 /* ======================================================================
26426 OpenLayers/Handler/Feature.js
26427 ====================================================================== */
26429 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
26430 * full list of contributors). Published under the 2-clause BSD license.
26431 * See license.txt in the OpenLayers distribution or repository for the
26432 * full text of the license. */
26436 * @requires OpenLayers/Handler.js
26440 * Class: OpenLayers.Handler.Feature
26441 * Handler to respond to mouse events related to a drawn feature. Callbacks
26442 * with the following keys will be notified of the following events
26443 * associated with features: click, clickout, over, out, and dblclick.
26445 * This handler stops event propagation for mousedown and mouseup if those
26446 * browser events target features that can be selected.
26449 * - <OpenLayers.Handler>
26451 OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, {
26454 * Property: EVENTMAP
26455 * {Object} A object mapping the browser events to objects with callback
26456 * keys for in and out.
26459 'click': {'in': 'click', 'out': 'clickout'},
26460 'mousemove': {'in': 'over', 'out': 'out'},
26461 'dblclick': {'in': 'dblclick', 'out': null},
26462 'mousedown': {'in': null, 'out': null},
26463 'mouseup': {'in': null, 'out': null},
26464 'touchstart': {'in': 'click', 'out': 'clickout'}
26468 * Property: feature
26469 * {<OpenLayers.Feature.Vector>} The last feature that was hovered.
26474 * Property: lastFeature
26475 * {<OpenLayers.Feature.Vector>} The last feature that was handled.
26481 * {<OpenLayers.Pixel>} The location of the last mousedown.
26487 * {<OpenLayers.Pixel>} The location of the last mouseup.
26492 * Property: clickTolerance
26493 * {Number} The number of pixels the mouse can move between mousedown
26494 * and mouseup for the event to still be considered a click.
26495 * Dragging the map should not trigger the click and clickout callbacks
26496 * unless the map is moved by less than this tolerance. Defaults to 4.
26501 * Property: geometryTypes
26502 * To restrict dragging to a limited set of geometry types, send a list
26503 * of strings corresponding to the geometry class names.
26505 * @type Array(String)
26507 geometryTypes: null,
26510 * Property: stopClick
26511 * {Boolean} If stopClick is set to true, handled clicks do not
26512 * propagate to other click listeners. Otherwise, handled clicks
26513 * do propagate. Unhandled clicks always propagate, whatever the
26514 * value of stopClick. Defaults to true.
26519 * Property: stopDown
26520 * {Boolean} If stopDown is set to true, handled mousedowns do not
26521 * propagate to other mousedown listeners. Otherwise, handled
26522 * mousedowns do propagate. Unhandled mousedowns always propagate,
26523 * whatever the value of stopDown. Defaults to true.
26529 * {Boolean} If stopUp is set to true, handled mouseups do not
26530 * propagate to other mouseup listeners. Otherwise, handled mouseups
26531 * do propagate. Unhandled mouseups always propagate, whatever the
26532 * value of stopUp. Defaults to false.
26537 * Constructor: OpenLayers.Handler.Feature
26540 * control - {<OpenLayers.Control>}
26541 * layer - {<OpenLayers.Layer.Vector>}
26542 * callbacks - {Object} An object with a 'over' property whos value is
26543 * a function to be called when the mouse is over a feature. The
26544 * callback should expect to receive a single argument, the feature.
26545 * options - {Object}
26547 initialize: function(control, layer, callbacks, options) {
26548 OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]);
26549 this.layer = layer;
26553 * Method: touchstart
26554 * Handle touchstart events
26560 * {Boolean} Let the event propagate.
26562 touchstart: function(evt) {
26564 return OpenLayers.Event.isMultiTouch(evt) ?
26565 true : this.mousedown(evt);
26569 * Method: touchmove
26570 * Handle touchmove events. We just prevent the browser default behavior,
26571 * for Android Webkit not to select text when moving the finger after
26572 * selecting a feature.
26577 touchmove: function(evt) {
26578 OpenLayers.Event.preventDefault(evt);
26582 * Method: mousedown
26583 * Handle mouse down. Stop propagation if a feature is targeted by this
26584 * event (stops map dragging during feature selection).
26589 mousedown: function(evt) {
26590 // Feature selection is only done with a left click. Other handlers may stop the
26591 // propagation of left-click mousedown events but not right-click mousedown events.
26592 // This mismatch causes problems when comparing the location of the down and up
26593 // events in the click function so it is important ignore right-clicks.
26594 if (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt)) {
26595 this.down = evt.xy;
26597 return this.handle(evt) ? !this.stopDown : true;
26602 * Handle mouse up. Stop propagation if a feature is targeted by this
26608 mouseup: function(evt) {
26610 return this.handle(evt) ? !this.stopUp : true;
26615 * Handle click. Call the "click" callback if click on a feature,
26616 * or the "clickout" callback if click outside any feature.
26624 click: function(evt) {
26625 return this.handle(evt) ? !this.stopClick : true;
26629 * Method: mousemove
26630 * Handle mouse moves. Call the "over" callback if moving in to a feature,
26631 * or the "out" callback if moving out of a feature.
26639 mousemove: function(evt) {
26640 if (!this.callbacks['over'] && !this.callbacks['out']) {
26649 * Handle dblclick. Call the "dblclick" callback if dblclick on a feature.
26657 dblclick: function(evt) {
26658 return !this.handle(evt);
26662 * Method: geometryTypeMatches
26663 * Return true if the geometry type of the passed feature matches
26664 * one of the geometry types in the geometryTypes array.
26667 * feature - {<OpenLayers.Vector.Feature>}
26672 geometryTypeMatches: function(feature) {
26673 return this.geometryTypes == null ||
26674 OpenLayers.Util.indexOf(this.geometryTypes,
26675 feature.geometry.CLASS_NAME) > -1;
26685 * {Boolean} The event occurred over a relevant feature.
26687 handle: function(evt) {
26688 if(this.feature && !this.feature.layer) {
26689 // feature has been destroyed
26690 this.feature = null;
26692 var type = evt.type;
26693 var handled = false;
26694 var previouslyIn = !!(this.feature); // previously in a feature
26695 var click = (type == "click" || type == "dblclick" || type == "touchstart");
26696 this.feature = this.layer.getFeatureFromEvent(evt);
26697 if(this.feature && !this.feature.layer) {
26698 // feature has been destroyed
26699 this.feature = null;
26701 if(this.lastFeature && !this.lastFeature.layer) {
26702 // last feature has been destroyed
26703 this.lastFeature = null;
26706 if(type === "touchstart") {
26707 // stop the event to prevent Android Webkit from
26708 // "flashing" the map div
26709 OpenLayers.Event.preventDefault(evt);
26711 var inNew = (this.feature != this.lastFeature);
26712 if(this.geometryTypeMatches(this.feature)) {
26714 if(previouslyIn && inNew) {
26715 // out of last feature and in to another
26716 if(this.lastFeature) {
26717 this.triggerCallback(type, 'out', [this.lastFeature]);
26719 this.triggerCallback(type, 'in', [this.feature]);
26720 } else if(!previouslyIn || click) {
26721 // in feature for the first time
26722 this.triggerCallback(type, 'in', [this.feature]);
26724 this.lastFeature = this.feature;
26727 // not in to a feature
26728 if(this.lastFeature && (previouslyIn && inNew || click)) {
26729 // out of last feature for the first time
26730 this.triggerCallback(type, 'out', [this.lastFeature]);
26732 // next time the mouse goes in a feature whose geometry type
26733 // doesn't match we don't want to call the 'out' callback
26734 // again, so let's set this.feature to null so that
26735 // previouslyIn will evaluate to false the next time
26736 // we enter handle. Yes, a bit hackish...
26737 this.feature = null;
26739 } else if(this.lastFeature && (previouslyIn || click)) {
26740 this.triggerCallback(type, 'out', [this.lastFeature]);
26746 * Method: triggerCallback
26747 * Call the callback keyed in the event map with the supplied arguments.
26748 * For click and clickout, the <clickTolerance> is checked first.
26753 triggerCallback: function(type, mode, args) {
26754 var key = this.EVENTMAP[type][mode];
26756 if(type == 'click' && this.up && this.down) {
26757 // for click/clickout, only trigger callback if tolerance is met
26758 var dpx = Math.sqrt(
26759 Math.pow(this.up.x - this.down.x, 2) +
26760 Math.pow(this.up.y - this.down.y, 2)
26762 if(dpx <= this.clickTolerance) {
26763 this.callback(key, args);
26765 // we're done with this set of events now: clear the cached
26766 // positions so we can't trip over them later (this can occur
26767 // if one of the up/down events gets eaten before it gets to us
26768 // but we still get the click)
26769 this.up = this.down = null;
26771 this.callback(key, args);
26778 * Turn on the handler. Returns false if the handler was already active.
26783 activate: function() {
26784 var activated = false;
26785 if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
26786 this.moveLayerToTop();
26787 this.map.events.on({
26788 "removelayer": this.handleMapEvents,
26789 "changelayer": this.handleMapEvents,
26798 * Method: deactivate
26799 * Turn off the handler. Returns false if the handler was already active.
26804 deactivate: function() {
26805 var deactivated = false;
26806 if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
26807 this.moveLayerBack();
26808 this.feature = null;
26809 this.lastFeature = null;
26812 this.map.events.un({
26813 "removelayer": this.handleMapEvents,
26814 "changelayer": this.handleMapEvents,
26817 deactivated = true;
26819 return deactivated;
26823 * Method: handleMapEvents
26828 handleMapEvents: function(evt) {
26829 if (evt.type == "removelayer" || evt.property == "order") {
26830 this.moveLayerToTop();
26835 * Method: moveLayerToTop
26836 * Moves the layer for this handler to the top, so mouse events can reach
26839 moveLayerToTop: function() {
26840 var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1,
26841 this.layer.getZIndex()) + 1;
26842 this.layer.setZIndex(index);
26847 * Method: moveLayerBack
26848 * Moves the layer back to the position determined by the map's layers
26851 moveLayerBack: function() {
26852 var index = this.layer.getZIndex() - 1;
26853 if (index >= this.map.Z_INDEX_BASE['Feature']) {
26854 this.layer.setZIndex(index);
26856 this.map.setLayerZIndex(this.layer,
26857 this.map.getLayerIndex(this.layer));
26861 CLASS_NAME: "OpenLayers.Handler.Feature"
26863 /* ======================================================================
26864 OpenLayers/Layer.js
26865 ====================================================================== */
26867 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
26868 * full list of contributors). Published under the 2-clause BSD license.
26869 * See license.txt in the OpenLayers distribution or repository for the
26870 * full text of the license. */
26874 * @requires OpenLayers/BaseTypes/Class.js
26875 * @requires OpenLayers/Map.js
26876 * @requires OpenLayers/Projection.js
26880 * Class: OpenLayers.Layer
26882 OpenLayers.Layer = OpenLayers.Class({
26891 * APIProperty: name
26903 * APIProperty: opacity
26904 * {Float} The layer's opacity. Float number between 0.0 and 1.0. Default
26910 * APIProperty: alwaysInRange
26911 * {Boolean} If a layer's display should not be scale-based, this should
26912 * be set to true. This will cause the layer, as an overlay, to always
26913 * be 'active', by always returning true from the calculateInRange()
26916 * If not explicitly specified for a layer, its value will be
26917 * determined on startup in initResolutions() based on whether or not
26918 * any scale-specific properties have been set as options on the
26919 * layer. If no scale-specific options have been set on the layer, we
26920 * assume that it should always be in range.
26922 * See #987 for more info.
26924 alwaysInRange: null,
26927 * Constant: RESOLUTION_PROPERTIES
26928 * {Array} The properties that are used for calculating resolutions
26931 RESOLUTION_PROPERTIES: [
26932 'scales', 'resolutions',
26933 'maxScale', 'minScale',
26934 'maxResolution', 'minResolution',
26935 'numZoomLevels', 'maxZoomLevel'
26939 * APIProperty: events
26940 * {<OpenLayers.Events>}
26942 * Register a listener for a particular event with the following syntax:
26944 * layer.events.register(type, obj, listener);
26947 * Listeners will be called with a reference to an event object. The
26948 * properties of this event depends on exactly what happened.
26950 * All event objects have at least the following properties:
26951 * object - {Object} A reference to layer.events.object.
26952 * element - {DOMElement} A reference to layer.events.element.
26954 * Supported map event types:
26955 * loadstart - Triggered when layer loading starts. When using a Vector
26956 * layer with a Fixed or BBOX strategy, the event object includes
26957 * a *filter* property holding the OpenLayers.Filter used when
26958 * calling read on the protocol.
26959 * loadend - Triggered when layer loading ends. When using a Vector layer
26960 * with a Fixed or BBOX strategy, the event object includes a
26961 * *response* property holding an OpenLayers.Protocol.Response object.
26962 * visibilitychanged - Triggered when the layer's visibility property is
26963 * changed, e.g. by turning the layer on or off in the layer switcher.
26964 * Note that the actual visibility of the layer can also change if it
26965 * gets out of range (see <calculateInRange>). If you also want to catch
26966 * these cases, register for the map's 'changelayer' event instead.
26967 * move - Triggered when layer moves (triggered with every mousemove
26969 * moveend - Triggered when layer is done moving, object passed as
26970 * argument has a zoomChanged boolean property which tells that the
26971 * zoom has changed.
26972 * added - Triggered after the layer is added to a map. Listeners will
26973 * receive an object with a *map* property referencing the map and a
26974 * *layer* property referencing the layer.
26975 * removed - Triggered after the layer is removed from the map. Listeners
26976 * will receive an object with a *map* property referencing the map and
26977 * a *layer* property referencing the layer.
26983 * {<OpenLayers.Map>} This variable is set when the layer is added to
26984 * the map, via the accessor function setMap().
26989 * APIProperty: isBaseLayer
26990 * {Boolean} Whether or not the layer is a base layer. This should be set
26991 * individually by all subclasses. Default is false
26993 isBaseLayer: false,
26997 * {Boolean} The layer's images have an alpha channel. Default is false.
27002 * APIProperty: displayInLayerSwitcher
27003 * {Boolean} Display the layer's name in the layer switcher. Default is
27006 displayInLayerSwitcher: true,
27009 * APIProperty: visibility
27010 * {Boolean} The layer should be displayed in the map. Default is true.
27015 * APIProperty: attribution
27016 * {<Object>} or {<String>} Attribution information, displayed when an
27017 * <OpenLayers.Control.Attribution> has been added to the map.
27019 * An object is required to store the full attribution information
27020 * from a WMS capabilities response. Example attribution object:
27021 * {title:"",href:"",logo:{format:"",width:10,height:10,href:""}}
27026 * Property: inRange
27027 * {Boolean} The current map resolution is within the layer's min/max
27028 * range. This is set in <OpenLayers.Map.setCenter> whenever the zoom
27034 * Propery: imageSize
27035 * {<OpenLayers.Size>} For layers with a gutter, the image is larger than
27036 * the tile by twice the gutter in each dimension.
27043 * Property: options
27044 * {Object} An optional object whose properties will be set on the layer.
27045 * Any of the layer properties can be set as a property of the options
27046 * object and sent to the constructor when the layer is created.
27051 * APIProperty: eventListeners
27052 * {Object} If set as an option at construction, the eventListeners
27053 * object will be registered with <OpenLayers.Events.on>. Object
27054 * structure must be a listeners object as shown in the example for
27055 * the events.on method.
27057 eventListeners: null,
27060 * APIProperty: gutter
27061 * {Integer} Determines the width (in pixels) of the gutter around image
27062 * tiles to ignore. By setting this property to a non-zero value,
27063 * images will be requested that are wider and taller than the tile
27064 * size by a value of 2 x gutter. This allows artifacts of rendering
27065 * at tile edges to be ignored. Set a gutter value that is equal to
27066 * half the size of the widest symbol that needs to be displayed.
27067 * Defaults to zero. Non-tiled layers always have zero gutter.
27072 * APIProperty: projection
27073 * {<OpenLayers.Projection>} or {<String>} Specifies the projection of the layer.
27074 * Can be set in the layer options. If not specified in the layer options,
27075 * it is set to the default projection specified in the map,
27076 * when the layer is added to the map.
27077 * Projection along with default maxExtent and resolutions
27078 * are set automatically with commercial baselayers in EPSG:3857,
27079 * such as Google, Bing and OpenStreetMap, and do not need to be specified.
27080 * Otherwise, if specifying projection, also set maxExtent,
27081 * maxResolution or resolutions as appropriate.
27082 * When using vector layers with strategies, layer projection should be set
27083 * to the projection of the source data if that is different from the map default.
27085 * Can be either a string or an <OpenLayers.Projection> object;
27086 * if a string is passed, will be converted to an object when
27087 * the layer is added to the map.
27093 * APIProperty: units
27094 * {String} The layer map units. Defaults to null. Possible values
27095 * are 'degrees' (or 'dd'), 'm', 'ft', 'km', 'mi', 'inches'.
27096 * Normally taken from the projection.
27097 * Only required if both map and layers do not define a projection,
27098 * or if they define a projection which does not define units.
27103 * APIProperty: scales
27104 * {Array} An array of map scales in descending order. The values in the
27105 * array correspond to the map scale denominator. Note that these
27106 * values only make sense if the display (monitor) resolution of the
27107 * client is correctly guessed by whomever is configuring the
27108 * application. In addition, the units property must also be set.
27109 * Use <resolutions> instead wherever possible.
27114 * APIProperty: resolutions
27115 * {Array} A list of map resolutions (map units per pixel) in descending
27116 * order. If this is not set in the layer constructor, it will be set
27117 * based on other resolution related properties (maxExtent,
27118 * maxResolution, maxScale, etc.).
27123 * APIProperty: maxExtent
27124 * {<OpenLayers.Bounds>|Array} If provided as an array, the array
27125 * should consist of four values (left, bottom, right, top).
27126 * The maximum extent for the layer. Defaults to null.
27128 * The center of these bounds will not stray outside
27129 * of the viewport extent during panning. In addition, if
27130 * <displayOutsideMaxExtent> is set to false, data will not be
27131 * requested that falls completely outside of these bounds.
27136 * APIProperty: minExtent
27137 * {<OpenLayers.Bounds>|Array} If provided as an array, the array
27138 * should consist of four values (left, bottom, right, top).
27139 * The minimum extent for the layer. Defaults to null.
27144 * APIProperty: maxResolution
27145 * {Float} Default max is 360 deg / 256 px, which corresponds to
27146 * zoom level 0 on gmaps. Specify a different value in the layer
27147 * options if you are not using the default <OpenLayers.Map.tileSize>
27148 * and displaying the whole world.
27150 maxResolution: null,
27153 * APIProperty: minResolution
27156 minResolution: null,
27159 * APIProperty: numZoomLevels
27162 numZoomLevels: null,
27165 * APIProperty: minScale
27171 * APIProperty: maxScale
27177 * APIProperty: displayOutsideMaxExtent
27178 * {Boolean} Request map tiles that are completely outside of the max
27179 * extent for this layer. Defaults to false.
27181 displayOutsideMaxExtent: false,
27184 * APIProperty: wrapDateLine
27185 * {Boolean} Wraps the world at the international dateline, so the map can
27186 * be panned infinitely in longitudinal direction. Only use this on the
27187 * base layer, and only if the layer's maxExtent equals the world bounds.
27188 * #487 for more info.
27190 wrapDateLine: false,
27193 * Property: metadata
27194 * {Object} This object can be used to store additional information on a
27200 * Constructor: OpenLayers.Layer
27203 * name - {String} The layer name
27204 * options - {Object} Hashtable of extra options to tag onto the layer
27206 initialize: function(name, options) {
27208 this.metadata = {};
27210 options = OpenLayers.Util.extend({}, options);
27211 // make sure we respect alwaysInRange if set on the prototype
27212 if (this.alwaysInRange != null) {
27213 options.alwaysInRange = this.alwaysInRange;
27215 this.addOptions(options);
27219 if (this.id == null) {
27221 this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
27223 this.div = OpenLayers.Util.createDiv(this.id);
27224 this.div.style.width = "100%";
27225 this.div.style.height = "100%";
27226 this.div.dir = "ltr";
27228 this.events = new OpenLayers.Events(this, this.div);
27229 if(this.eventListeners instanceof Object) {
27230 this.events.on(this.eventListeners);
27238 * Destroy is a destructor: this is to alleviate cyclic references which
27239 * the Javascript garbage cleaner can not take care of on its own.
27242 * setNewBaseLayer - {Boolean} Set a new base layer when this layer has
27243 * been destroyed. Default is true.
27245 destroy: function(setNewBaseLayer) {
27246 if (setNewBaseLayer == null) {
27247 setNewBaseLayer = true;
27249 if (this.map != null) {
27250 this.map.removeLayer(this, setNewBaseLayer);
27252 this.projection = null;
27256 this.options = null;
27259 if(this.eventListeners) {
27260 this.events.un(this.eventListeners);
27262 this.events.destroy();
27264 this.eventListeners = null;
27265 this.events = null;
27272 * obj - {<OpenLayers.Layer>} The layer to be cloned
27275 * {<OpenLayers.Layer>} An exact clone of this <OpenLayers.Layer>
27277 clone: function (obj) {
27280 obj = new OpenLayers.Layer(this.name, this.getOptions());
27283 // catch any randomly tagged-on properties
27284 OpenLayers.Util.applyDefaults(obj, this);
27286 // a cloned layer should never have its map property set
27287 // because it has not been added to a map yet.
27294 * Method: getOptions
27295 * Extracts an object from the layer with the properties that were set as
27296 * options, but updates them with the values currently set on the
27300 * {Object} the <options> of the layer, representing the current state.
27302 getOptions: function() {
27304 for(var o in this.options) {
27305 options[o] = this[o];
27311 * APIMethod: setName
27312 * Sets the new layer name for this layer. Can trigger a changelayer event
27316 * newName - {String} The new name.
27318 setName: function(newName) {
27319 if (newName != this.name) {
27320 this.name = newName;
27321 if (this.map != null) {
27322 this.map.events.triggerEvent("changelayer", {
27331 * APIMethod: addOptions
27334 * newOptions - {Object}
27335 * reinitialize - {Boolean} If set to true, and if resolution options of the
27336 * current baseLayer were changed, the map will be recentered to make
27337 * sure that it is displayed with a valid resolution, and a
27338 * changebaselayer event will be triggered.
27340 addOptions: function (newOptions, reinitialize) {
27342 if (this.options == null) {
27347 // make sure this.projection references a projection object
27348 if(typeof newOptions.projection == "string") {
27349 newOptions.projection = new OpenLayers.Projection(newOptions.projection);
27351 if (newOptions.projection) {
27352 // get maxResolution, units and maxExtent from projection defaults if
27353 // they are not defined already
27354 OpenLayers.Util.applyDefaults(newOptions,
27355 OpenLayers.Projection.defaults[newOptions.projection.getCode()]);
27357 // allow array for extents
27358 if (newOptions.maxExtent && !(newOptions.maxExtent instanceof OpenLayers.Bounds)) {
27359 newOptions.maxExtent = new OpenLayers.Bounds(newOptions.maxExtent);
27361 if (newOptions.minExtent && !(newOptions.minExtent instanceof OpenLayers.Bounds)) {
27362 newOptions.minExtent = new OpenLayers.Bounds(newOptions.minExtent);
27366 // update our copy for clone
27367 OpenLayers.Util.extend(this.options, newOptions);
27369 // add new options to this
27370 OpenLayers.Util.extend(this, newOptions);
27372 // get the units from the projection, if we have a projection
27373 // and it it has units
27374 if(this.projection && this.projection.getUnits()) {
27375 this.units = this.projection.getUnits();
27378 // re-initialize resolutions if necessary, i.e. if any of the
27379 // properties of the "properties" array defined below is set
27380 // in the new options
27382 // store current resolution so we can try to restore it later
27383 var resolution = this.map.getResolution();
27384 var properties = this.RESOLUTION_PROPERTIES.concat(
27385 ["projection", "units", "minExtent", "maxExtent"]
27387 for(var o in newOptions) {
27388 if(newOptions.hasOwnProperty(o) &&
27389 OpenLayers.Util.indexOf(properties, o) >= 0) {
27391 this.initResolutions();
27392 if (reinitialize && this.map.baseLayer === this) {
27393 // update map position, and restore previous resolution
27394 this.map.setCenter(this.map.getCenter(),
27395 this.map.getZoomForResolution(resolution),
27398 // trigger a changebaselayer event to make sure that
27399 // all controls (especially
27400 // OpenLayers.Control.PanZoomBar) get notified of the
27402 this.map.events.triggerEvent("changebaselayer", {
27413 * APIMethod: onMapResize
27414 * This function can be implemented by subclasses
27416 onMapResize: function() {
27417 //this function can be implemented by subclasses
27421 * APIMethod: redraw
27422 * Redraws the layer. Returns true if the layer was redrawn, false if not.
27425 * {Boolean} The layer was redrawn.
27427 redraw: function() {
27428 var redrawn = false;
27431 // min/max Range may have changed
27432 this.inRange = this.calculateInRange();
27434 // map's center might not yet be set
27435 var extent = this.getExtent();
27437 if (extent && this.inRange && this.visibility) {
27438 var zoomChanged = true;
27439 this.moveTo(extent, zoomChanged, false);
27440 this.events.triggerEvent("moveend",
27441 {"zoomChanged": zoomChanged});
27452 * bounds - {<OpenLayers.Bounds>}
27453 * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to
27454 * do some init work in that case.
27455 * dragging - {Boolean}
27457 moveTo:function(bounds, zoomChanged, dragging) {
27458 var display = this.visibility;
27459 if (!this.isBaseLayer) {
27460 display = display && this.inRange;
27462 this.display(display);
27467 * Move the layer based on pixel vector. To be implemented by subclasses.
27470 * dx - {Number} The x coord of the displacement vector.
27471 * dy - {Number} The y coord of the displacement vector.
27473 moveByPx: function(dx, dy) {
27478 * Set the map property for the layer. This is done through an accessor
27479 * so that subclasses can override this and take special action once
27480 * they have their map variable set.
27482 * Here we take care to bring over any of the necessary default
27483 * properties from the map.
27486 * map - {<OpenLayers.Map>}
27488 setMap: function(map) {
27489 if (this.map == null) {
27493 // grab some essential layer data from the map if it hasn't already
27495 this.maxExtent = this.maxExtent || this.map.maxExtent;
27496 this.minExtent = this.minExtent || this.map.minExtent;
27498 this.projection = this.projection || this.map.projection;
27499 if (typeof this.projection == "string") {
27500 this.projection = new OpenLayers.Projection(this.projection);
27503 // Check the projection to see if we can get units -- if not, refer
27505 if (this.projection && this.projection.getUnits()) {
27506 this.units = this.projection.getUnits();
27509 this.units = this.units || this.map.units;
27512 this.initResolutions();
27514 if (!this.isBaseLayer) {
27515 this.inRange = this.calculateInRange();
27516 var show = ((this.visibility) && (this.inRange));
27517 this.div.style.display = show ? "" : "none";
27520 // deal with gutters
27521 this.setTileSize();
27527 * Called at the end of the map.addLayer sequence. At this point, the map
27528 * will have a base layer. To be overridden by subclasses.
27530 afterAdd: function() {
27534 * APIMethod: removeMap
27535 * Just as setMap() allows each layer the possibility to take a
27536 * personalized action on being added to the map, removeMap() allows
27537 * each layer to take a personalized action on being removed from it.
27538 * For now, this will be mostly unused, except for the EventPane layer,
27539 * which needs this hook so that it can remove the special invisible
27543 * map - {<OpenLayers.Map>}
27545 removeMap: function(map) {
27546 //to be overridden by subclasses
27550 * APIMethod: getImageSize
27553 * bounds - {<OpenLayers.Bounds>} optional tile bounds, can be used
27554 * by subclasses that have to deal with different tile sizes at the
27555 * layer extent edges (e.g. Zoomify)
27558 * {<OpenLayers.Size>} The size that the image should be, taking into
27561 getImageSize: function(bounds) {
27562 return (this.imageSize || this.tileSize);
27566 * APIMethod: setTileSize
27567 * Set the tile size based on the map size. This also sets layer.imageSize
27568 * or use by Tile.Image.
27571 * size - {<OpenLayers.Size>}
27573 setTileSize: function(size) {
27574 var tileSize = (size) ? size :
27575 ((this.tileSize) ? this.tileSize :
27576 this.map.getTileSize());
27577 this.tileSize = tileSize;
27579 // layers with gutters need non-null tile sizes
27580 //if(tileSize == null) {
27581 // OpenLayers.console.error("Error in layer.setMap() for " +
27582 // this.name + ": layers with " +
27583 // "gutters need non-null tile sizes");
27585 this.imageSize = new OpenLayers.Size(tileSize.w + (2*this.gutter),
27586 tileSize.h + (2*this.gutter));
27591 * APIMethod: getVisibility
27594 * {Boolean} The layer should be displayed (if in range).
27596 getVisibility: function() {
27597 return this.visibility;
27601 * APIMethod: setVisibility
27602 * Set the visibility flag for the layer and hide/show & redraw
27603 * accordingly. Fire event unless otherwise specified
27605 * Note that visibility is no longer simply whether or not the layer's
27606 * style.display is set to "block". Now we store a 'visibility' state
27607 * property on the layer class, this allows us to remember whether or
27608 * not we *desire* for a layer to be visible. In the case where the
27609 * map's resolution is out of the layer's range, this desire may be
27613 * visibility - {Boolean} Whether or not to display the layer (if in range)
27615 setVisibility: function(visibility) {
27616 if (visibility != this.visibility) {
27617 this.visibility = visibility;
27618 this.display(visibility);
27620 if (this.map != null) {
27621 this.map.events.triggerEvent("changelayer", {
27623 property: "visibility"
27626 this.events.triggerEvent("visibilitychanged");
27631 * APIMethod: display
27632 * Hide or show the Layer. This is designed to be used internally, and
27633 * is not generally the way to enable or disable the layer. For that,
27634 * use the setVisibility function instead..
27637 * display - {Boolean}
27639 display: function(display) {
27640 if (display != (this.div.style.display != "none")) {
27641 this.div.style.display = (display && this.calculateInRange()) ? "block" : "none";
27646 * APIMethod: calculateInRange
27649 * {Boolean} The layer is displayable at the current map's current
27650 * resolution. Note that if 'alwaysInRange' is true for the layer,
27651 * this function will always return true.
27653 calculateInRange: function() {
27654 var inRange = false;
27656 if (this.alwaysInRange) {
27660 var resolution = this.map.getResolution();
27661 inRange = ( (resolution >= this.minResolution) &&
27662 (resolution <= this.maxResolution) );
27669 * APIMethod: setIsBaseLayer
27672 * isBaseLayer - {Boolean}
27674 setIsBaseLayer: function(isBaseLayer) {
27675 if (isBaseLayer != this.isBaseLayer) {
27676 this.isBaseLayer = isBaseLayer;
27677 if (this.map != null) {
27678 this.map.events.triggerEvent("changebaselayer", {
27685 /********************************************************/
27687 /* Baselayer Functions */
27689 /********************************************************/
27692 * Method: initResolutions
27693 * This method's responsibility is to set up the 'resolutions' array
27694 * for the layer -- this array is what the layer will use to interface
27695 * between the zoom levels of the map and the resolution display
27698 * The user has several options that determine how the array is set up.
27700 * For a detailed explanation, see the following wiki from the
27701 * openlayers.org homepage:
27702 * http://trac.openlayers.org/wiki/SettingZoomLevels
27704 initResolutions: function() {
27706 // ok we want resolutions, here's our strategy:
27708 // 1. if resolutions are defined in the layer config, use them
27709 // 2. else, if scales are defined in the layer config then derive
27710 // resolutions from these scales
27711 // 3. else, attempt to calculate resolutions from maxResolution,
27712 // minResolution, numZoomLevels, maxZoomLevel set in the
27714 // 4. if we still don't have resolutions, and if resolutions
27715 // are defined in the same, use them
27716 // 5. else, if scales are defined in the map then derive
27717 // resolutions from these scales
27718 // 6. else, attempt to calculate resolutions from maxResolution,
27719 // minResolution, numZoomLevels, maxZoomLevel set in the
27721 // 7. hope for the best!
27724 var props = {}, alwaysInRange = true;
27726 // get resolution data from layer config
27727 // (we also set alwaysInRange in the layer as appropriate)
27728 for(i=0, len=this.RESOLUTION_PROPERTIES.length; i<len; i++) {
27729 p = this.RESOLUTION_PROPERTIES[i];
27730 props[p] = this.options[p];
27731 if(alwaysInRange && this.options[p]) {
27732 alwaysInRange = false;
27735 if(this.options.alwaysInRange == null) {
27736 this.alwaysInRange = alwaysInRange;
27739 // if we don't have resolutions then attempt to derive them from scales
27740 if(props.resolutions == null) {
27741 props.resolutions = this.resolutionsFromScales(props.scales);
27744 // if we still don't have resolutions then attempt to calculate them
27745 if(props.resolutions == null) {
27746 props.resolutions = this.calculateResolutions(props);
27749 // if we couldn't calculate resolutions then we look at we have
27751 if(props.resolutions == null) {
27752 for(i=0, len=this.RESOLUTION_PROPERTIES.length; i<len; i++) {
27753 p = this.RESOLUTION_PROPERTIES[i];
27754 props[p] = this.options[p] != null ?
27755 this.options[p] : this.map[p];
27757 if(props.resolutions == null) {
27758 props.resolutions = this.resolutionsFromScales(props.scales);
27760 if(props.resolutions == null) {
27761 props.resolutions = this.calculateResolutions(props);
27765 // ok, we new need to set properties in the instance
27767 // get maxResolution from the config if it's defined there
27769 if(this.options.maxResolution &&
27770 this.options.maxResolution !== "auto") {
27771 maxResolution = this.options.maxResolution;
27773 if(this.options.minScale) {
27774 maxResolution = OpenLayers.Util.getResolutionFromScale(
27775 this.options.minScale, this.units);
27778 // get minResolution from the config if it's defined there
27780 if(this.options.minResolution &&
27781 this.options.minResolution !== "auto") {
27782 minResolution = this.options.minResolution;
27784 if(this.options.maxScale) {
27785 minResolution = OpenLayers.Util.getResolutionFromScale(
27786 this.options.maxScale, this.units);
27789 if(props.resolutions) {
27791 //sort resolutions array descendingly
27792 props.resolutions.sort(function(a, b) {
27796 // if we still don't have a maxResolution get it from the
27797 // resolutions array
27798 if(!maxResolution) {
27799 maxResolution = props.resolutions[0];
27802 // if we still don't have a minResolution get it from the
27803 // resolutions array
27804 if(!minResolution) {
27805 var lastIdx = props.resolutions.length - 1;
27806 minResolution = props.resolutions[lastIdx];
27810 this.resolutions = props.resolutions;
27811 if(this.resolutions) {
27812 len = this.resolutions.length;
27813 this.scales = new Array(len);
27814 for(i=0; i<len; i++) {
27815 this.scales[i] = OpenLayers.Util.getScaleFromResolution(
27816 this.resolutions[i], this.units);
27818 this.numZoomLevels = len;
27820 this.minResolution = minResolution;
27821 if(minResolution) {
27822 this.maxScale = OpenLayers.Util.getScaleFromResolution(
27823 minResolution, this.units);
27825 this.maxResolution = maxResolution;
27826 if(maxResolution) {
27827 this.minScale = OpenLayers.Util.getScaleFromResolution(
27828 maxResolution, this.units);
27833 * Method: resolutionsFromScales
27834 * Derive resolutions from scales.
27837 * scales - {Array(Number)} Scales
27840 * {Array(Number)} Resolutions
27842 resolutionsFromScales: function(scales) {
27843 if(scales == null) {
27846 var resolutions, i, len;
27847 len = scales.length;
27848 resolutions = new Array(len);
27849 for(i=0; i<len; i++) {
27850 resolutions[i] = OpenLayers.Util.getResolutionFromScale(
27851 scales[i], this.units);
27853 return resolutions;
27857 * Method: calculateResolutions
27858 * Calculate resolutions based on the provided properties.
27861 * props - {Object} Properties
27864 * {Array({Number})} Array of resolutions.
27866 calculateResolutions: function(props) {
27868 var viewSize, wRes, hRes;
27870 // determine maxResolution
27871 var maxResolution = props.maxResolution;
27872 if(props.minScale != null) {
27874 OpenLayers.Util.getResolutionFromScale(props.minScale,
27876 } else if(maxResolution == "auto" && this.maxExtent != null) {
27877 viewSize = this.map.getSize();
27878 wRes = this.maxExtent.getWidth() / viewSize.w;
27879 hRes = this.maxExtent.getHeight() / viewSize.h;
27880 maxResolution = Math.max(wRes, hRes);
27883 // determine minResolution
27884 var minResolution = props.minResolution;
27885 if(props.maxScale != null) {
27887 OpenLayers.Util.getResolutionFromScale(props.maxScale,
27889 } else if(props.minResolution == "auto" && this.minExtent != null) {
27890 viewSize = this.map.getSize();
27891 wRes = this.minExtent.getWidth() / viewSize.w;
27892 hRes = this.minExtent.getHeight()/ viewSize.h;
27893 minResolution = Math.max(wRes, hRes);
27896 if(typeof maxResolution !== "number" &&
27897 typeof minResolution !== "number" &&
27898 this.maxExtent != null) {
27899 // maxResolution for default grid sets assumes that at zoom
27900 // level zero, the whole world fits on one tile.
27901 var tileSize = this.map.getTileSize();
27902 maxResolution = Math.max(
27903 this.maxExtent.getWidth() / tileSize.w,
27904 this.maxExtent.getHeight() / tileSize.h
27908 // determine numZoomLevels
27909 var maxZoomLevel = props.maxZoomLevel;
27910 var numZoomLevels = props.numZoomLevels;
27911 if(typeof minResolution === "number" &&
27912 typeof maxResolution === "number" && numZoomLevels === undefined) {
27913 var ratio = maxResolution / minResolution;
27914 numZoomLevels = Math.floor(Math.log(ratio) / Math.log(2)) + 1;
27915 } else if(numZoomLevels === undefined && maxZoomLevel != null) {
27916 numZoomLevels = maxZoomLevel + 1;
27919 // are we able to calculate resolutions?
27920 if(typeof numZoomLevels !== "number" || numZoomLevels <= 0 ||
27921 (typeof maxResolution !== "number" &&
27922 typeof minResolution !== "number")) {
27926 // now we have numZoomLevels and at least one of maxResolution
27927 // or minResolution, we can populate the resolutions array
27929 var resolutions = new Array(numZoomLevels);
27931 if(typeof minResolution == "number" &&
27932 typeof maxResolution == "number") {
27933 // if maxResolution and minResolution are set, we calculate
27934 // the base for exponential scaling that starts at
27935 // maxResolution and ends at minResolution in numZoomLevels
27938 (maxResolution / minResolution),
27939 (1 / (numZoomLevels - 1))
27944 if(typeof maxResolution === "number") {
27945 for(i=0; i<numZoomLevels; i++) {
27946 resolutions[i] = maxResolution / Math.pow(base, i);
27949 for(i=0; i<numZoomLevels; i++) {
27950 resolutions[numZoomLevels - 1 - i] =
27951 minResolution * Math.pow(base, i);
27955 return resolutions;
27959 * APIMethod: getResolution
27962 * {Float} The currently selected resolution of the map, taken from the
27963 * resolutions array, indexed by current zoom level.
27965 getResolution: function() {
27966 var zoom = this.map.getZoom();
27967 return this.getResolutionForZoom(zoom);
27971 * APIMethod: getExtent
27974 * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat
27975 * bounds of the current viewPort.
27977 getExtent: function() {
27978 // just use stock map calculateBounds function -- passing no arguments
27979 // means it will user map's current center & resolution
27981 return this.map.calculateBounds();
27985 * APIMethod: getZoomForExtent
27988 * extent - {<OpenLayers.Bounds>}
27989 * closest - {Boolean} Find the zoom level that most closely fits the
27990 * specified bounds. Note that this may result in a zoom that does
27991 * not exactly contain the entire extent.
27992 * Default is false.
27995 * {Integer} The index of the zoomLevel (entry in the resolutions array)
27996 * for the passed-in extent. We do this by calculating the ideal
27997 * resolution for the given extent (based on the map size) and then
27998 * calling getZoomForResolution(), passing along the 'closest'
28001 getZoomForExtent: function(extent, closest) {
28002 var viewSize = this.map.getSize();
28003 var idealResolution = Math.max( extent.getWidth() / viewSize.w,
28004 extent.getHeight() / viewSize.h );
28006 return this.getZoomForResolution(idealResolution, closest);
28010 * Method: getDataExtent
28011 * Calculates the max extent which includes all of the data for the layer.
28012 * This function is to be implemented by subclasses.
28015 * {<OpenLayers.Bounds>}
28017 getDataExtent: function () {
28018 //to be implemented by subclasses
28022 * APIMethod: getResolutionForZoom
28028 * {Float} A suitable resolution for the specified zoom.
28030 getResolutionForZoom: function(zoom) {
28031 zoom = Math.max(0, Math.min(zoom, this.resolutions.length - 1));
28033 if(this.map.fractionalZoom) {
28034 var low = Math.floor(zoom);
28035 var high = Math.ceil(zoom);
28036 resolution = this.resolutions[low] -
28037 ((zoom-low) * (this.resolutions[low]-this.resolutions[high]));
28039 resolution = this.resolutions[Math.round(zoom)];
28045 * APIMethod: getZoomForResolution
28048 * resolution - {Float}
28049 * closest - {Boolean} Find the zoom level that corresponds to the absolute
28050 * closest resolution, which may result in a zoom whose corresponding
28051 * resolution is actually smaller than we would have desired (if this
28052 * is being called from a getZoomForExtent() call, then this means that
28053 * the returned zoom index might not actually contain the entire
28054 * extent specified... but it'll be close).
28055 * Default is false.
28058 * {Integer} The index of the zoomLevel (entry in the resolutions array)
28059 * that corresponds to the best fit resolution given the passed in
28060 * value and the 'closest' specification.
28062 getZoomForResolution: function(resolution, closest) {
28064 if(this.map.fractionalZoom) {
28066 var highZoom = this.resolutions.length - 1;
28067 var highRes = this.resolutions[lowZoom];
28068 var lowRes = this.resolutions[highZoom];
28070 for(i=0, len=this.resolutions.length; i<len; ++i) {
28071 res = this.resolutions[i];
28072 if(res >= resolution) {
28076 if(res <= resolution) {
28082 var dRes = highRes - lowRes;
28084 zoom = lowZoom + ((highRes - resolution) / dRes);
28090 var minDiff = Number.POSITIVE_INFINITY;
28091 for(i=0, len=this.resolutions.length; i<len; i++) {
28093 diff = Math.abs(this.resolutions[i] - resolution);
28094 if (diff > minDiff) {
28099 if (this.resolutions[i] < resolution) {
28104 zoom = Math.max(0, i-1);
28110 * APIMethod: getLonLatFromViewPortPx
28113 * viewPortPx - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or
28114 * an object with a 'x'
28115 * and 'y' properties.
28118 * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in
28119 * view port <OpenLayers.Pixel>, translated into lon/lat by the layer.
28121 getLonLatFromViewPortPx: function (viewPortPx) {
28123 var map = this.map;
28124 if (viewPortPx != null && map.minPx) {
28125 var res = map.getResolution();
28126 var maxExtent = map.getMaxExtent({restricted: true});
28127 var lon = (viewPortPx.x - map.minPx.x) * res + maxExtent.left;
28128 var lat = (map.minPx.y - viewPortPx.y) * res + maxExtent.top;
28129 lonlat = new OpenLayers.LonLat(lon, lat);
28131 if (this.wrapDateLine) {
28132 lonlat = lonlat.wrapDateLine(this.maxExtent);
28139 * APIMethod: getViewPortPxFromLonLat
28140 * Returns a pixel location given a map location. This method will return
28141 * fractional pixel values.
28144 * lonlat - {<OpenLayers.LonLat>|Object} An OpenLayers.LonLat or
28145 * an object with a 'lon'
28146 * and 'lat' properties.
28149 * {<OpenLayers.Pixel>} An <OpenLayers.Pixel> which is the passed-in
28150 * lonlat translated into view port pixels.
28152 getViewPortPxFromLonLat: function (lonlat, resolution) {
28154 if (lonlat != null) {
28155 resolution = resolution || this.map.getResolution();
28156 var extent = this.map.calculateBounds(null, resolution);
28157 px = new OpenLayers.Pixel(
28158 (1/resolution * (lonlat.lon - extent.left)),
28159 (1/resolution * (extent.top - lonlat.lat))
28166 * APIMethod: setOpacity
28167 * Sets the opacity for the entire layer (all images)
28170 * opacity - {Float}
28172 setOpacity: function(opacity) {
28173 if (opacity != this.opacity) {
28174 this.opacity = opacity;
28175 var childNodes = this.div.childNodes;
28176 for(var i = 0, len = childNodes.length; i < len; ++i) {
28177 var element = childNodes[i].firstChild || childNodes[i];
28178 var lastChild = childNodes[i].lastChild;
28179 //TODO de-uglify this
28180 if (lastChild && lastChild.nodeName.toLowerCase() === "iframe") {
28181 element = lastChild.parentNode;
28183 OpenLayers.Util.modifyDOMElement(element, null, null, null,
28184 null, null, null, opacity);
28186 if (this.map != null) {
28187 this.map.events.triggerEvent("changelayer", {
28189 property: "opacity"
28196 * Method: getZIndex
28199 * {Integer} the z-index of this layer
28201 getZIndex: function () {
28202 return this.div.style.zIndex;
28206 * Method: setZIndex
28209 * zIndex - {Integer}
28211 setZIndex: function (zIndex) {
28212 this.div.style.zIndex = zIndex;
28216 * Method: adjustBounds
28217 * This function will take a bounds, and if wrapDateLine option is set
28218 * on the layer, it will return a bounds which is wrapped around the
28219 * world. We do not wrap for bounds which *cross* the
28220 * maxExtent.left/right, only bounds which are entirely to the left
28221 * or entirely to the right.
28224 * bounds - {<OpenLayers.Bounds>}
28226 adjustBounds: function (bounds) {
28229 // Adjust the extent of a bounds in map units by the
28230 // layer's gutter in pixels.
28231 var mapGutter = this.gutter * this.map.getResolution();
28232 bounds = new OpenLayers.Bounds(bounds.left - mapGutter,
28233 bounds.bottom - mapGutter,
28234 bounds.right + mapGutter,
28235 bounds.top + mapGutter);
28238 if (this.wrapDateLine) {
28239 // wrap around the date line, within the limits of rounding error
28240 var wrappingOptions = {
28241 'rightTolerance':this.getResolution(),
28242 'leftTolerance':this.getResolution()
28244 bounds = bounds.wrapDateLine(this.maxExtent, wrappingOptions);
28250 CLASS_NAME: "OpenLayers.Layer"
28252 /* ======================================================================
28253 OpenLayers/Renderer.js
28254 ====================================================================== */
28256 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
28257 * full list of contributors). Published under the 2-clause BSD license.
28258 * See license.txt in the OpenLayers distribution or repository for the
28259 * full text of the license. */
28262 * @requires OpenLayers/BaseTypes/Class.js
28266 * Class: OpenLayers.Renderer
28267 * This is the base class for all renderers.
28269 * This is based on a merger code written by Paul Spencer and Bertil Chapuis.
28270 * It is largely composed of virtual functions that are to be implemented
28271 * in technology-specific subclasses, but there is some generic code too.
28273 * The functions that *are* implemented here merely deal with the maintenance
28274 * of the size and extent variables, as well as the cached 'resolution'
28277 * A note to the user that all subclasses should use getResolution() instead
28278 * of directly accessing this.resolution in order to correctly use the
28282 OpenLayers.Renderer = OpenLayers.Class({
28285 * Property: container
28298 * {<OpenLayers.Bounds>}
28304 * {Boolean} If the renderer is currently in a state where many things
28305 * are changing, the 'locked' property is set to true. This means
28306 * that renderers can expect at least one more drawFeature event to be
28307 * called with the 'locked' property set to 'true': In some renderers,
28308 * this might make sense to use as a 'only update local information'
28315 * {<OpenLayers.Size>}
28320 * Property: resolution
28321 * {Float} cache of current map resolution
28327 * {<OpenLayers.Map>} Reference to the map -- this is set in Vector's setMap()
28332 * Property: featureDx
28333 * {Number} Feature offset in x direction. Will be calculated for and
28334 * applied to the current feature while rendering (see
28335 * <calculateFeatureDx>).
28340 * Constructor: OpenLayers.Renderer
28343 * containerID - {<String>}
28344 * options - {Object} options for this renderer. See sublcasses for
28345 * supported options.
28347 initialize: function(containerID, options) {
28348 this.container = OpenLayers.Util.getElement(containerID);
28349 OpenLayers.Util.extend(this, options);
28353 * APIMethod: destroy
28355 destroy: function() {
28356 this.container = null;
28357 this.extent = null;
28359 this.resolution = null;
28364 * APIMethod: supported
28365 * This should be overridden by specific subclasses
28368 * {Boolean} Whether or not the browser supports the renderer class
28370 supported: function() {
28375 * Method: setExtent
28376 * Set the visible part of the layer.
28378 * Resolution has probably changed, so we nullify the resolution
28379 * cache (this.resolution) -- this way it will be re-computed when
28380 * next it is needed.
28381 * We nullify the resolution cache (this.resolution) if resolutionChanged
28382 * is set to true - this way it will be re-computed on the next
28383 * getResolution() request.
28386 * extent - {<OpenLayers.Bounds>}
28387 * resolutionChanged - {Boolean}
28390 * {Boolean} true to notify the layer that the new extent does not exceed
28391 * the coordinate range, and the features will not need to be redrawn.
28394 setExtent: function(extent, resolutionChanged) {
28395 this.extent = extent.clone();
28396 if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
28397 var ratio = extent.getWidth() / this.map.getExtent().getWidth(),
28398 extent = extent.scale(1 / ratio);
28399 this.extent = extent.wrapDateLine(this.map.getMaxExtent()).scale(ratio);
28401 if (resolutionChanged) {
28402 this.resolution = null;
28409 * Sets the size of the drawing surface.
28411 * Resolution has probably changed, so we nullify the resolution
28412 * cache (this.resolution) -- this way it will be re-computed when
28413 * next it is needed.
28416 * size - {<OpenLayers.Size>}
28418 setSize: function(size) {
28419 this.size = size.clone();
28420 this.resolution = null;
28424 * Method: getResolution
28425 * Uses cached copy of resolution if available to minimize computing
28428 * {Float} The current map's resolution
28430 getResolution: function() {
28431 this.resolution = this.resolution || this.map.getResolution();
28432 return this.resolution;
28436 * Method: drawFeature
28437 * Draw the feature. The optional style argument can be used
28438 * to override the feature's own style. This method should only
28439 * be called from layer.drawFeature().
28442 * feature - {<OpenLayers.Feature.Vector>}
28443 * style - {<Object>}
28446 * {Boolean} true if the feature has been drawn completely, false if not,
28447 * undefined if the feature had no geometry
28449 drawFeature: function(feature, style) {
28450 if(style == null) {
28451 style = feature.style;
28453 if (feature.geometry) {
28454 var bounds = feature.geometry.getBounds();
28457 if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
28458 worldBounds = this.map.getMaxExtent();
28460 if (!bounds.intersectsBounds(this.extent, {worldBounds: worldBounds})) {
28461 style = {display: "none"};
28463 this.calculateFeatureDx(bounds, worldBounds);
28465 var rendered = this.drawGeometry(feature.geometry, style, feature.id);
28466 if(style.display != "none" && style.label && rendered !== false) {
28468 var location = feature.geometry.getCentroid();
28469 if(style.labelXOffset || style.labelYOffset) {
28470 var xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset;
28471 var yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset;
28472 var res = this.getResolution();
28473 location.move(xOffset*res, yOffset*res);
28475 this.drawText(feature.id, style, location);
28477 this.removeText(feature.id);
28485 * Method: calculateFeatureDx
28486 * {Number} Calculates the feature offset in x direction. Looking at the
28487 * center of the feature bounds and the renderer extent, we calculate how
28488 * many world widths the two are away from each other. This distance is
28489 * used to shift the feature as close as possible to the center of the
28490 * current enderer extent, which ensures that the feature is visible in the
28491 * current viewport.
28494 * bounds - {<OpenLayers.Bounds>} Bounds of the feature
28495 * worldBounds - {<OpenLayers.Bounds>} Bounds of the world
28497 calculateFeatureDx: function(bounds, worldBounds) {
28498 this.featureDx = 0;
28500 var worldWidth = worldBounds.getWidth(),
28501 rendererCenterX = (this.extent.left + this.extent.right) / 2,
28502 featureCenterX = (bounds.left + bounds.right) / 2,
28503 worldsAway = Math.round((featureCenterX - rendererCenterX) / worldWidth);
28504 this.featureDx = worldsAway * worldWidth;
28509 * Method: drawGeometry
28511 * Draw a geometry. This should only be called from the renderer itself.
28512 * Use layer.drawFeature() from outside the renderer.
28516 * geometry - {<OpenLayers.Geometry>}
28518 * featureId - {<String>}
28520 drawGeometry: function(geometry, style, featureId) {},
28524 * Function for drawing text labels.
28525 * This method is only called by the renderer itself.
28528 * featureId - {String}
28530 * location - {<OpenLayers.Geometry.Point>}
28532 drawText: function(featureId, style, location) {},
28535 * Method: removeText
28536 * Function for removing text labels.
28537 * This method is only called by the renderer itself.
28540 * featureId - {String}
28542 removeText: function(featureId) {},
28546 * Clear all vectors from the renderer.
28547 * virtual function.
28549 clear: function() {},
28552 * Method: getFeatureIdFromEvent
28553 * Returns a feature id from an event on the renderer.
28554 * How this happens is specific to the renderer. This should be
28555 * called from layer.getFeatureFromEvent().
28556 * Virtual function.
28559 * evt - {<OpenLayers.Event>}
28562 * {String} A feature id or undefined.
28564 getFeatureIdFromEvent: function(evt) {},
28567 * Method: eraseFeatures
28568 * This is called by the layer to erase features
28571 * features - {Array(<OpenLayers.Feature.Vector>)}
28573 eraseFeatures: function(features) {
28574 if(!(OpenLayers.Util.isArray(features))) {
28575 features = [features];
28577 for(var i=0, len=features.length; i<len; ++i) {
28578 var feature = features[i];
28579 this.eraseGeometry(feature.geometry, feature.id);
28580 this.removeText(feature.id);
28585 * Method: eraseGeometry
28586 * Remove a geometry from the renderer (by id).
28587 * virtual function.
28590 * geometry - {<OpenLayers.Geometry>}
28591 * featureId - {String}
28593 eraseGeometry: function(geometry, featureId) {},
28597 * moves this renderer's root to a (different) renderer.
28598 * To be implemented by subclasses that require a common renderer root for
28599 * feature selection.
28602 * renderer - {<OpenLayers.Renderer>} target renderer for the moved root
28604 moveRoot: function(renderer) {},
28607 * Method: getRenderLayerId
28608 * Gets the layer that this renderer's output appears on. If moveRoot was
28609 * used, this will be different from the id of the layer containing the
28610 * features rendered by this renderer.
28613 * {String} the id of the output layer.
28615 getRenderLayerId: function() {
28616 return this.container.id;
28620 * Method: applyDefaultSymbolizer
28623 * symbolizer - {Object}
28628 applyDefaultSymbolizer: function(symbolizer) {
28629 var result = OpenLayers.Util.extend({},
28630 OpenLayers.Renderer.defaultSymbolizer);
28631 if(symbolizer.stroke === false) {
28632 delete result.strokeWidth;
28633 delete result.strokeColor;
28635 if(symbolizer.fill === false) {
28636 delete result.fillColor;
28638 OpenLayers.Util.extend(result, symbolizer);
28642 CLASS_NAME: "OpenLayers.Renderer"
28646 * Constant: OpenLayers.Renderer.defaultSymbolizer
28647 * {Object} Properties from this symbolizer will be applied to symbolizers
28648 * with missing properties. This can also be used to set a global
28649 * symbolizer default in OpenLayers. To be SLD 1.x compliant, add the
28650 * following code before rendering any vector features:
28652 * OpenLayers.Renderer.defaultSymbolizer = {
28653 * fillColor: "#808080",
28655 * strokeColor: "#000000",
28656 * strokeOpacity: 1,
28659 * graphicName: "square"
28663 OpenLayers.Renderer.defaultSymbolizer = {
28664 fillColor: "#000000",
28665 strokeColor: "#000000",
28676 * Constant: OpenLayers.Renderer.symbol
28677 * Coordinate arrays for well known (named) symbols.
28679 OpenLayers.Renderer.symbol = {
28680 "star": [350,75, 379,161, 469,161, 397,215, 423,301, 350,250, 277,301,
28681 303,215, 231,161, 321,161, 350,75],
28682 "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,
28684 "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],
28685 "square": [0,0, 0,1, 1,1, 1,0, 0,0],
28686 "triangle": [0,10, 10,10, 5,0, 0,10]
28688 /* ======================================================================
28689 OpenLayers/Layer/Vector.js
28690 ====================================================================== */
28692 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
28693 * full list of contributors). Published under the 2-clause BSD license.
28694 * See license.txt in the OpenLayers distribution or repository for the
28695 * full text of the license. */
28698 * @requires OpenLayers/Layer.js
28699 * @requires OpenLayers/Renderer.js
28700 * @requires OpenLayers/StyleMap.js
28701 * @requires OpenLayers/Feature/Vector.js
28702 * @requires OpenLayers/Console.js
28703 * @requires OpenLayers/Lang.js
28707 * Class: OpenLayers.Layer.Vector
28708 * Instances of OpenLayers.Layer.Vector are used to render vector data from
28709 * a variety of sources. Create a new vector layer with the
28710 * <OpenLayers.Layer.Vector> constructor.
28713 * - <OpenLayers.Layer>
28715 OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
28718 * APIProperty: events
28719 * {<OpenLayers.Events>}
28721 * Register a listener for a particular event with the following syntax:
28723 * layer.events.register(type, obj, listener);
28726 * Listeners will be called with a reference to an event object. The
28727 * properties of this event depends on exactly what happened.
28729 * All event objects have at least the following properties:
28730 * object - {Object} A reference to layer.events.object.
28731 * element - {DOMElement} A reference to layer.events.element.
28733 * Supported map event types (in addition to those from <OpenLayers.Layer.events>):
28734 * beforefeatureadded - Triggered before a feature is added. Listeners
28735 * will receive an object with a *feature* property referencing the
28736 * feature to be added. To stop the feature from being added, a
28737 * listener should return false.
28738 * beforefeaturesadded - Triggered before an array of features is added.
28739 * Listeners will receive an object with a *features* property
28740 * referencing the feature to be added. To stop the features from
28741 * being added, a listener should return false.
28742 * featureadded - Triggered after a feature is added. The event
28743 * object passed to listeners will have a *feature* property with a
28744 * reference to the added feature.
28745 * featuresadded - Triggered after features are added. The event
28746 * object passed to listeners will have a *features* property with a
28747 * reference to an array of added features.
28748 * beforefeatureremoved - Triggered before a feature is removed. Listeners
28749 * will receive an object with a *feature* property referencing the
28750 * feature to be removed.
28751 * beforefeaturesremoved - Triggered before multiple features are removed.
28752 * Listeners will receive an object with a *features* property
28753 * referencing the features to be removed.
28754 * featureremoved - Triggered after a feature is removed. The event
28755 * object passed to listeners will have a *feature* property with a
28756 * reference to the removed feature.
28757 * featuresremoved - Triggered after features are removed. The event
28758 * object passed to listeners will have a *features* property with a
28759 * reference to an array of removed features.
28760 * beforefeatureselected - Triggered before a feature is selected. Listeners
28761 * will receive an object with a *feature* property referencing the
28762 * feature to be selected. To stop the feature from being selectd, a
28763 * listener should return false.
28764 * featureselected - Triggered after a feature is selected. Listeners
28765 * will receive an object with a *feature* property referencing the
28766 * selected feature.
28767 * featureunselected - Triggered after a feature is unselected.
28768 * Listeners will receive an object with a *feature* property
28769 * referencing the unselected feature.
28770 * beforefeaturemodified - Triggered when a feature is selected to
28771 * be modified. Listeners will receive an object with a *feature*
28772 * property referencing the selected feature.
28773 * featuremodified - Triggered when a feature has been modified.
28774 * Listeners will receive an object with a *feature* property referencing
28775 * the modified feature.
28776 * afterfeaturemodified - Triggered when a feature is finished being modified.
28777 * Listeners will receive an object with a *feature* property referencing
28778 * the modified feature.
28779 * vertexmodified - Triggered when a vertex within any feature geometry
28780 * has been modified. Listeners will receive an object with a
28781 * *feature* property referencing the modified feature, a *vertex*
28782 * property referencing the vertex modified (always a point geometry),
28783 * and a *pixel* property referencing the pixel location of the
28785 * vertexremoved - Triggered when a vertex within any feature geometry
28786 * has been deleted. Listeners will receive an object with a
28787 * *feature* property referencing the modified feature, a *vertex*
28788 * property referencing the vertex modified (always a point geometry),
28789 * and a *pixel* property referencing the pixel location of the
28791 * sketchstarted - Triggered when a feature sketch bound for this layer
28792 * is started. Listeners will receive an object with a *feature*
28793 * property referencing the new sketch feature and a *vertex* property
28794 * referencing the creation point.
28795 * sketchmodified - Triggered when a feature sketch bound for this layer
28796 * is modified. Listeners will receive an object with a *vertex*
28797 * property referencing the modified vertex and a *feature* property
28798 * referencing the sketch feature.
28799 * sketchcomplete - Triggered when a feature sketch bound for this layer
28800 * is complete. Listeners will receive an object with a *feature*
28801 * property referencing the sketch feature. By returning false, a
28802 * listener can stop the sketch feature from being added to the layer.
28803 * refresh - Triggered when something wants a strategy to ask the protocol
28804 * for a new set of features.
28808 * APIProperty: isBaseLayer
28809 * {Boolean} The layer is a base layer. Default is false. Set this property
28810 * in the layer options.
28812 isBaseLayer: false,
28815 * APIProperty: isFixed
28816 * {Boolean} Whether the layer remains in one place while dragging the
28817 * map. Note that setting this to true will move the layer to the bottom
28818 * of the layer stack.
28823 * APIProperty: features
28824 * {Array(<OpenLayers.Feature.Vector>)}
28830 * {<OpenLayers.Filter>} The filter set in this layer,
28831 * a strategy launching read requests can combined
28832 * this filter with its own filter.
28837 * Property: selectedFeatures
28838 * {Array(<OpenLayers.Feature.Vector>)}
28840 selectedFeatures: null,
28843 * Property: unrenderedFeatures
28844 * {Object} hash of features, keyed by feature.id, that the renderer
28847 unrenderedFeatures: null,
28850 * APIProperty: reportError
28851 * {Boolean} report friendly error message when loading of renderer
28857 * APIProperty: style
28858 * {Object} Default style for the layer
28863 * Property: styleMap
28864 * {<OpenLayers.StyleMap>}
28869 * Property: strategies
28870 * {Array(<OpenLayers.Strategy>})} Optional list of strategies for the layer.
28875 * Property: protocol
28876 * {<OpenLayers.Protocol>} Optional protocol for the layer.
28881 * Property: renderers
28882 * {Array(String)} List of supported Renderer classes. Add to this list to
28883 * add support for additional renderers. This list is ordered:
28884 * the first renderer which returns true for the 'supported()'
28885 * method will be used, if not defined in the 'renderer' option.
28887 renderers: ['SVG', 'VML', 'Canvas'],
28890 * Property: renderer
28891 * {<OpenLayers.Renderer>}
28896 * APIProperty: rendererOptions
28897 * {Object} Options for the renderer. See {<OpenLayers.Renderer>} for
28898 * supported options.
28900 rendererOptions: null,
28903 * APIProperty: geometryType
28904 * {String} geometryType allows you to limit the types of geometries this
28905 * layer supports. This should be set to something like
28906 * "OpenLayers.Geometry.Point" to limit types.
28908 geometryType: null,
28912 * {Boolean} Whether the Vector Layer features have been drawn yet.
28917 * APIProperty: ratio
28918 * {Float} This specifies the ratio of the size of the visiblity of the Vector Layer features to the size of the map.
28923 * Constructor: OpenLayers.Layer.Vector
28924 * Create a new vector layer
28927 * name - {String} A name for the layer
28928 * options - {Object} Optional object with non-default properties to set on
28932 * {<OpenLayers.Layer.Vector>} A new vector layer
28934 initialize: function(name, options) {
28935 OpenLayers.Layer.prototype.initialize.apply(this, arguments);
28937 // allow user-set renderer, otherwise assign one
28938 if (!this.renderer || !this.renderer.supported()) {
28939 this.assignRenderer();
28942 // if no valid renderer found, display error
28943 if (!this.renderer || !this.renderer.supported()) {
28944 this.renderer = null;
28945 this.displayError();
28948 if (!this.styleMap) {
28949 this.styleMap = new OpenLayers.StyleMap();
28952 this.features = [];
28953 this.selectedFeatures = [];
28954 this.unrenderedFeatures = {};
28956 // Allow for custom layer behavior
28957 if(this.strategies){
28958 for(var i=0, len=this.strategies.length; i<len; i++) {
28959 this.strategies[i].setLayer(this);
28966 * APIMethod: destroy
28967 * Destroy this layer
28969 destroy: function() {
28970 if (this.strategies) {
28971 var strategy, i, len;
28972 for(i=0, len=this.strategies.length; i<len; i++) {
28973 strategy = this.strategies[i];
28974 if(strategy.autoDestroy) {
28975 strategy.destroy();
28978 this.strategies = null;
28980 if (this.protocol) {
28981 if(this.protocol.autoDestroy) {
28982 this.protocol.destroy();
28984 this.protocol = null;
28986 this.destroyFeatures();
28987 this.features = null;
28988 this.selectedFeatures = null;
28989 this.unrenderedFeatures = null;
28990 if (this.renderer) {
28991 this.renderer.destroy();
28993 this.renderer = null;
28994 this.geometryType = null;
28996 OpenLayers.Layer.prototype.destroy.apply(this, arguments);
29001 * Create a clone of this layer.
29003 * Note: Features of the layer are also cloned.
29006 * {<OpenLayers.Layer.Vector>} An exact clone of this layer
29008 clone: function (obj) {
29011 obj = new OpenLayers.Layer.Vector(this.name, this.getOptions());
29014 //get all additions from superclasses
29015 obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
29017 // copy/set any non-init, non-simple values here
29018 var features = this.features;
29019 var len = features.length;
29020 var clonedFeatures = new Array(len);
29021 for(var i=0; i<len; ++i) {
29022 clonedFeatures[i] = features[i].clone();
29024 obj.features = clonedFeatures;
29031 * Ask the layer to request features again and redraw them. Triggers
29032 * the refresh event if the layer is in range and visible.
29035 * obj - {Object} Optional object with properties for any listener of
29036 * the refresh event.
29038 refresh: function(obj) {
29039 if(this.calculateInRange() && this.visibility) {
29040 this.events.triggerEvent("refresh", obj);
29045 * Method: assignRenderer
29046 * Iterates through the available renderer implementations and selects
29047 * and assigns the first one whose "supported()" function returns true.
29049 assignRenderer: function() {
29050 for (var i=0, len=this.renderers.length; i<len; i++) {
29051 var rendererClass = this.renderers[i];
29052 var renderer = (typeof rendererClass == "function") ?
29054 OpenLayers.Renderer[rendererClass];
29055 if (renderer && renderer.prototype.supported()) {
29056 this.renderer = new renderer(this.div, this.rendererOptions);
29063 * Method: displayError
29064 * Let the user know their browser isn't supported.
29066 displayError: function() {
29067 if (this.reportError) {
29068 OpenLayers.Console.userError(OpenLayers.i18n("browserNotSupported",
29069 {renderers: this. renderers.join('\n')}));
29075 * The layer has been added to the map.
29077 * If there is no renderer set, the layer can't be used. Remove it.
29078 * Otherwise, give the renderer a reference to the map and set its size.
29081 * map - {<OpenLayers.Map>}
29083 setMap: function(map) {
29084 OpenLayers.Layer.prototype.setMap.apply(this, arguments);
29086 if (!this.renderer) {
29087 this.map.removeLayer(this);
29089 this.renderer.map = this.map;
29091 var newSize = this.map.getSize();
29092 newSize.w = newSize.w * this.ratio;
29093 newSize.h = newSize.h * this.ratio;
29094 this.renderer.setSize(newSize);
29100 * Called at the end of the map.addLayer sequence. At this point, the map
29101 * will have a base layer. Any autoActivate strategies will be
29104 afterAdd: function() {
29105 if(this.strategies) {
29106 var strategy, i, len;
29107 for(i=0, len=this.strategies.length; i<len; i++) {
29108 strategy = this.strategies[i];
29109 if(strategy.autoActivate) {
29110 strategy.activate();
29117 * Method: removeMap
29118 * The layer has been removed from the map.
29121 * map - {<OpenLayers.Map>}
29123 removeMap: function(map) {
29124 this.drawn = false;
29125 if(this.strategies) {
29126 var strategy, i, len;
29127 for(i=0, len=this.strategies.length; i<len; i++) {
29128 strategy = this.strategies[i];
29129 if(strategy.autoActivate) {
29130 strategy.deactivate();
29137 * Method: onMapResize
29138 * Notify the renderer of the change in size.
29141 onMapResize: function() {
29142 OpenLayers.Layer.prototype.onMapResize.apply(this, arguments);
29144 var newSize = this.map.getSize();
29145 newSize.w = newSize.w * this.ratio;
29146 newSize.h = newSize.h * this.ratio;
29147 this.renderer.setSize(newSize);
29152 * Reset the vector layer's div so that it once again is lined up with
29153 * the map. Notify the renderer of the change of extent, and in the
29154 * case of a change of zoom level (resolution), have the
29155 * renderer redraw features.
29157 * If the layer has not yet been drawn, cycle through the layer's
29158 * features and draw each one.
29161 * bounds - {<OpenLayers.Bounds>}
29162 * zoomChanged - {Boolean}
29163 * dragging - {Boolean}
29165 moveTo: function(bounds, zoomChanged, dragging) {
29166 OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
29168 var coordSysUnchanged = true;
29170 this.renderer.root.style.visibility = 'hidden';
29172 var viewSize = this.map.getSize(),
29173 viewWidth = viewSize.w,
29174 viewHeight = viewSize.h,
29175 offsetLeft = (viewWidth / 2 * this.ratio) - viewWidth / 2,
29176 offsetTop = (viewHeight / 2 * this.ratio) - viewHeight / 2;
29177 offsetLeft += this.map.layerContainerOriginPx.x;
29178 offsetLeft = -Math.round(offsetLeft);
29179 offsetTop += this.map.layerContainerOriginPx.y;
29180 offsetTop = -Math.round(offsetTop);
29182 this.div.style.left = offsetLeft + 'px';
29183 this.div.style.top = offsetTop + 'px';
29185 var extent = this.map.getExtent().scale(this.ratio);
29186 coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged);
29188 this.renderer.root.style.visibility = 'visible';
29190 // Force a reflow on gecko based browsers to prevent jump/flicker.
29191 // This seems to happen on only certain configurations; it was originally
29192 // noticed in FF 2.0 and Linux.
29193 if (OpenLayers.IS_GECKO === true) {
29194 this.div.scrollLeft = this.div.scrollLeft;
29197 if (!zoomChanged && coordSysUnchanged) {
29198 for (var i in this.unrenderedFeatures) {
29199 var feature = this.unrenderedFeatures[i];
29200 this.drawFeature(feature);
29204 if (!this.drawn || zoomChanged || !coordSysUnchanged) {
29207 for(var i=0, len=this.features.length; i<len; i++) {
29208 this.renderer.locked = (i !== (len - 1));
29209 feature = this.features[i];
29210 this.drawFeature(feature);
29216 * APIMethod: display
29217 * Hide or show the Layer
29220 * display - {Boolean}
29222 display: function(display) {
29223 OpenLayers.Layer.prototype.display.apply(this, arguments);
29224 // we need to set the display style of the root in case it is attached
29225 // to a foreign layer
29226 var currentDisplay = this.div.style.display;
29227 if(currentDisplay != this.renderer.root.style.display) {
29228 this.renderer.root.style.display = currentDisplay;
29233 * APIMethod: addFeatures
29234 * Add Features to the layer.
29237 * features - {Array(<OpenLayers.Feature.Vector>)}
29238 * options - {Object}
29240 addFeatures: function(features, options) {
29241 if (!(OpenLayers.Util.isArray(features))) {
29242 features = [features];
29245 var notify = !options || !options.silent;
29247 var event = {features: features};
29248 var ret = this.events.triggerEvent("beforefeaturesadded", event);
29249 if(ret === false) {
29252 features = event.features;
29255 // Track successfully added features for featuresadded event, since
29256 // beforefeatureadded can veto single features.
29257 var featuresAdded = [];
29258 for (var i=0, len=features.length; i<len; i++) {
29259 if (i != (features.length - 1)) {
29260 this.renderer.locked = true;
29262 this.renderer.locked = false;
29264 var feature = features[i];
29266 if (this.geometryType &&
29267 !(feature.geometry instanceof this.geometryType)) {
29268 throw new TypeError('addFeatures: component should be an ' +
29269 this.geometryType.prototype.CLASS_NAME);
29272 //give feature reference to its layer
29273 feature.layer = this;
29275 if (!feature.style && this.style) {
29276 feature.style = OpenLayers.Util.extend({}, this.style);
29280 if(this.events.triggerEvent("beforefeatureadded",
29281 {feature: feature}) === false) {
29284 this.preFeatureInsert(feature);
29287 featuresAdded.push(feature);
29288 this.features.push(feature);
29289 this.drawFeature(feature);
29292 this.events.triggerEvent("featureadded", {
29295 this.onFeatureInsert(feature);
29300 this.events.triggerEvent("featuresadded", {features: featuresAdded});
29306 * APIMethod: removeFeatures
29307 * Remove features from the layer. This erases any drawn features and
29308 * removes them from the layer's control. The beforefeatureremoved
29309 * and featureremoved events will be triggered for each feature. The
29310 * featuresremoved event will be triggered after all features have
29311 * been removed. To suppress event triggering, use the silent option.
29314 * features - {Array(<OpenLayers.Feature.Vector>)} List of features to be
29316 * options - {Object} Optional properties for changing behavior of the
29320 * silent - {Boolean} Suppress event triggering. Default is false.
29322 removeFeatures: function(features, options) {
29323 if(!features || features.length === 0) {
29326 if (features === this.features) {
29327 return this.removeAllFeatures(options);
29329 if (!(OpenLayers.Util.isArray(features))) {
29330 features = [features];
29332 if (features === this.selectedFeatures) {
29333 features = features.slice();
29336 var notify = !options || !options.silent;
29339 this.events.triggerEvent(
29340 "beforefeaturesremoved", {features: features}
29344 for (var i = features.length - 1; i >= 0; i--) {
29345 // We remain locked so long as we're not at 0
29346 // and the 'next' feature has a geometry. We do the geometry check
29347 // because if all the features after the current one are 'null', we
29348 // won't call eraseGeometry, so we break the 'renderer functions
29349 // will always be called with locked=false *last*' rule. The end result
29350 // is a possible gratiutious unlocking to save a loop through the rest
29351 // of the list checking the remaining features every time. So long as
29352 // null geoms are rare, this is probably okay.
29353 if (i != 0 && features[i-1].geometry) {
29354 this.renderer.locked = true;
29356 this.renderer.locked = false;
29359 var feature = features[i];
29360 delete this.unrenderedFeatures[feature.id];
29363 this.events.triggerEvent("beforefeatureremoved", {
29368 this.features = OpenLayers.Util.removeItem(this.features, feature);
29369 // feature has no layer at this point
29370 feature.layer = null;
29372 if (feature.geometry) {
29373 this.renderer.eraseFeatures(feature);
29376 //in the case that this feature is one of the selected features,
29377 // remove it from that array as well.
29378 if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1){
29379 OpenLayers.Util.removeItem(this.selectedFeatures, feature);
29383 this.events.triggerEvent("featureremoved", {
29390 this.events.triggerEvent("featuresremoved", {features: features});
29395 * APIMethod: removeAllFeatures
29396 * Remove all features from the layer.
29399 * options - {Object} Optional properties for changing behavior of the
29403 * silent - {Boolean} Suppress event triggering. Default is false.
29405 removeAllFeatures: function(options) {
29406 var notify = !options || !options.silent;
29407 var features = this.features;
29409 this.events.triggerEvent(
29410 "beforefeaturesremoved", {features: features}
29414 for (var i = features.length-1; i >= 0; i--) {
29415 feature = features[i];
29417 this.events.triggerEvent("beforefeatureremoved", {
29421 feature.layer = null;
29423 this.events.triggerEvent("featureremoved", {
29428 this.renderer.clear();
29429 this.features = [];
29430 this.unrenderedFeatures = {};
29431 this.selectedFeatures = [];
29433 this.events.triggerEvent("featuresremoved", {features: features});
29438 * APIMethod: destroyFeatures
29439 * Erase and destroy features on the layer.
29442 * features - {Array(<OpenLayers.Feature.Vector>)} An optional array of
29443 * features to destroy. If not supplied, all features on the layer
29444 * will be destroyed.
29445 * options - {Object}
29447 destroyFeatures: function(features, options) {
29448 var all = (features == undefined); // evaluates to true if
29449 // features is null
29451 features = this.features;
29454 this.removeFeatures(features, options);
29455 for(var i=features.length-1; i>=0; i--) {
29456 features[i].destroy();
29462 * APIMethod: drawFeature
29463 * Draw (or redraw) a feature on the layer. If the optional style argument
29464 * is included, this style will be used. If no style is included, the
29465 * feature's style will be used. If the feature doesn't have a style,
29466 * the layer's style will be used.
29468 * This function is not designed to be used when adding features to
29469 * the layer (use addFeatures instead). It is meant to be used when
29470 * the style of a feature has changed, or in some other way needs to
29471 * visually updated *after* it has already been added to a layer. You
29472 * must add the feature to the layer for most layer-related events to
29476 * feature - {<OpenLayers.Feature.Vector>}
29477 * style - {String | Object} Named render intent or full symbolizer object.
29479 drawFeature: function(feature, style) {
29480 // don't try to draw the feature with the renderer if the layer is not
29485 if (typeof style != "object") {
29486 if(!style && feature.state === OpenLayers.State.DELETE) {
29489 var renderIntent = style || feature.renderIntent;
29490 style = feature.style || this.style;
29492 style = this.styleMap.createSymbolizer(feature, renderIntent);
29496 var drawn = this.renderer.drawFeature(feature, style);
29497 //TODO remove the check for null when we get rid of Renderer.SVG
29498 if (drawn === false || drawn === null) {
29499 this.unrenderedFeatures[feature.id] = feature;
29501 delete this.unrenderedFeatures[feature.id];
29506 * Method: eraseFeatures
29507 * Erase features from the layer.
29510 * features - {Array(<OpenLayers.Feature.Vector>)}
29512 eraseFeatures: function(features) {
29513 this.renderer.eraseFeatures(features);
29517 * Method: getFeatureFromEvent
29518 * Given an event, return a feature if the event occurred over one.
29519 * Otherwise, return null.
29525 * {<OpenLayers.Feature.Vector>} A feature if one was under the event.
29527 getFeatureFromEvent: function(evt) {
29528 if (!this.renderer) {
29529 throw new Error('getFeatureFromEvent called on layer with no ' +
29530 'renderer. This usually means you destroyed a ' +
29531 'layer, but not some handler which is associated ' +
29534 var feature = null;
29535 var featureId = this.renderer.getFeatureIdFromEvent(evt);
29537 if (typeof featureId === "string") {
29538 feature = this.getFeatureById(featureId);
29540 feature = featureId;
29547 * APIMethod: getFeatureBy
29548 * Given a property value, return the feature if it exists in the features array
29551 * property - {String}
29555 * {<OpenLayers.Feature.Vector>} A feature corresponding to the given
29556 * property value or null if there is no such feature.
29558 getFeatureBy: function(property, value) {
29559 //TBD - would it be more efficient to use a hash for this.features?
29560 var feature = null;
29561 for(var i=0, len=this.features.length; i<len; ++i) {
29562 if(this.features[i][property] == value) {
29563 feature = this.features[i];
29571 * APIMethod: getFeatureById
29572 * Given a feature id, return the feature if it exists in the features array
29575 * featureId - {String}
29578 * {<OpenLayers.Feature.Vector>} A feature corresponding to the given
29579 * featureId or null if there is no such feature.
29581 getFeatureById: function(featureId) {
29582 return this.getFeatureBy('id', featureId);
29586 * APIMethod: getFeatureByFid
29587 * Given a feature fid, return the feature if it exists in the features array
29590 * featureFid - {String}
29593 * {<OpenLayers.Feature.Vector>} A feature corresponding to the given
29594 * featureFid or null if there is no such feature.
29596 getFeatureByFid: function(featureFid) {
29597 return this.getFeatureBy('fid', featureFid);
29601 * APIMethod: getFeaturesByAttribute
29602 * Returns an array of features that have the given attribute key set to the
29603 * given value. Comparison of attribute values takes care of datatypes, e.g.
29604 * the string '1234' is not equal to the number 1234.
29607 * attrName - {String}
29608 * attrValue - {Mixed}
29611 * Array({<OpenLayers.Feature.Vector>}) An array of features that have the
29612 * passed named attribute set to the given value.
29614 getFeaturesByAttribute: function(attrName, attrValue) {
29617 len = this.features.length,
29618 foundFeatures = [];
29619 for(i = 0; i < len; i++) {
29620 feature = this.features[i];
29621 if(feature && feature.attributes) {
29622 if (feature.attributes[attrName] === attrValue) {
29623 foundFeatures.push(feature);
29627 return foundFeatures;
29631 * Unselect the selected features
29632 * i.e. clears the featureSelection array
29633 * change the style back
29634 clearSelection: function() {
29636 var vectorLayer = this.map.vectorLayer;
29637 for (var i = 0; i < this.map.featureSelection.length; i++) {
29638 var featureSelection = this.map.featureSelection[i];
29639 vectorLayer.drawFeature(featureSelection, vectorLayer.style);
29641 this.map.featureSelection = [];
29647 * APIMethod: onFeatureInsert
29648 * method called after a feature is inserted.
29649 * Does nothing by default. Override this if you
29650 * need to do something on feature updates.
29653 * feature - {<OpenLayers.Feature.Vector>}
29655 onFeatureInsert: function(feature) {
29659 * APIMethod: preFeatureInsert
29660 * method called before a feature is inserted.
29661 * Does nothing by default. Override this if you
29662 * need to do something when features are first added to the
29663 * layer, but before they are drawn, such as adjust the style.
29666 * feature - {<OpenLayers.Feature.Vector>}
29668 preFeatureInsert: function(feature) {
29672 * APIMethod: getDataExtent
29673 * Calculates the max extent which includes all of the features.
29676 * {<OpenLayers.Bounds>} or null if the layer has no features with
29679 getDataExtent: function () {
29680 var maxExtent = null;
29681 var features = this.features;
29682 if(features && (features.length > 0)) {
29683 var geometry = null;
29684 for(var i=0, len=features.length; i<len; i++) {
29685 geometry = features[i].geometry;
29687 if (maxExtent === null) {
29688 maxExtent = new OpenLayers.Bounds();
29690 maxExtent.extend(geometry.getBounds());
29697 CLASS_NAME: "OpenLayers.Layer.Vector"
29699 /* ======================================================================
29700 OpenLayers/Layer/Vector/RootContainer.js
29701 ====================================================================== */
29703 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
29704 * full list of contributors). Published under the 2-clause BSD license.
29705 * See license.txt in the OpenLayers distribution or repository for the
29706 * full text of the license. */
29709 * @requires OpenLayers/Layer/Vector.js
29713 * Class: OpenLayers.Layer.Vector.RootContainer
29714 * A special layer type to combine multiple vector layers inside a single
29715 * renderer root container. This class is not supposed to be instantiated
29716 * from user space, it is a helper class for controls that require event
29717 * processing for multiple vector layers.
29720 * - <OpenLayers.Layer.Vector>
29722 OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, {
29725 * Property: displayInLayerSwitcher
29726 * Set to false for this layer type
29728 displayInLayerSwitcher: false,
29731 * APIProperty: layers
29732 * Layers that are attached to this container. Required config option.
29737 * Constructor: OpenLayers.Layer.Vector.RootContainer
29738 * Create a new root container for multiple vector layer. This constructor
29739 * is not supposed to be used from user space, it is only to be used by
29740 * controls that need feature selection across multiple vector layers.
29743 * name - {String} A name for the layer
29744 * options - {Object} Optional object with non-default properties to set on
29747 * Required options properties:
29748 * layers - {Array(<OpenLayers.Layer.Vector>)} The layers managed by this
29752 * {<OpenLayers.Layer.Vector.RootContainer>} A new vector layer root
29759 display: function() {},
29762 * Method: getFeatureFromEvent
29763 * walk through the layers to find the feature returned by the event
29766 * evt - {Object} event object with a feature property
29769 * {<OpenLayers.Feature.Vector>}
29771 getFeatureFromEvent: function(evt) {
29772 var layers = this.layers;
29774 for(var i=0; i<layers.length; i++) {
29775 feature = layers[i].getFeatureFromEvent(evt);
29786 * map - {<OpenLayers.Map>}
29788 setMap: function(map) {
29789 OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);
29790 this.collectRoots();
29791 map.events.register("changelayer", this, this.handleChangeLayer);
29795 * Method: removeMap
29798 * map - {<OpenLayers.Map>}
29800 removeMap: function(map) {
29801 map.events.unregister("changelayer", this, this.handleChangeLayer);
29803 OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments);
29807 * Method: collectRoots
29808 * Collects the root nodes of all layers this control is configured with
29809 * and moveswien the nodes to this control's layer
29811 collectRoots: function() {
29813 // walk through all map layers, because we want to keep the order
29814 for(var i=0; i<this.map.layers.length; ++i) {
29815 layer = this.map.layers[i];
29816 if(OpenLayers.Util.indexOf(this.layers, layer) != -1) {
29817 layer.renderer.moveRoot(this.renderer);
29823 * Method: resetRoots
29824 * Resets the root nodes back into the layers they belong to.
29826 resetRoots: function() {
29828 for(var i=0; i<this.layers.length; ++i) {
29829 layer = this.layers[i];
29830 if(this.renderer && layer.renderer.getRenderLayerId() == this.id) {
29831 this.renderer.moveRoot(layer.renderer);
29837 * Method: handleChangeLayer
29838 * Event handler for the map's changelayer event. We need to rebuild
29839 * this container's layer dom if order of one of its layers changes.
29840 * This handler is added with the setMap method, and removed with the
29841 * removeMap method.
29846 handleChangeLayer: function(evt) {
29847 var layer = evt.layer;
29848 if(evt.property == "order" &&
29849 OpenLayers.Util.indexOf(this.layers, layer) != -1) {
29851 this.collectRoots();
29855 CLASS_NAME: "OpenLayers.Layer.Vector.RootContainer"
29857 /* ======================================================================
29858 OpenLayers/Control/SelectFeature.js
29859 ====================================================================== */
29861 /* Copyright (c) 2006-2015 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. */
29868 * @requires OpenLayers/Control.js
29869 * @requires OpenLayers/Feature/Vector.js
29870 * @requires OpenLayers/Handler/Feature.js
29871 * @requires OpenLayers/Layer/Vector/RootContainer.js
29875 * Class: OpenLayers.Control.SelectFeature
29876 * The SelectFeature control selects vector features from a given layer on
29880 * - <OpenLayers.Control>
29882 OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {
29885 * APIProperty: events
29886 * {<OpenLayers.Events>} Events instance for listeners and triggering
29887 * control specific events.
29889 * Register a listener for a particular event with the following syntax:
29891 * control.events.register(type, obj, listener);
29894 * Supported event types (in addition to those from <OpenLayers.Control.events>):
29895 * beforefeaturehighlighted - Triggered before a feature is highlighted
29896 * featurehighlighted - Triggered when a feature is highlighted
29897 * featureunhighlighted - Triggered when a feature is unhighlighted
29898 * boxselectionstart - Triggered before box selection starts
29899 * boxselectionend - Triggered after box selection ends
29903 * APIProperty: multipleKey
29904 * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets
29905 * the <multiple> property to true. Default is null.
29910 * APIProperty: toggleKey
29911 * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets
29912 * the <toggle> property to true. Default is null.
29917 * APIProperty: multiple
29918 * {Boolean} Allow selection of multiple geometries. Default is false.
29923 * APIProperty: clickout
29924 * {Boolean} Unselect features when clicking outside any feature.
29930 * APIProperty: toggle
29931 * {Boolean} Unselect a selected feature on click. Default is false. Only
29932 * has meaning if hover is false.
29937 * APIProperty: hover
29938 * {Boolean} Select on mouse over and deselect on mouse out. If true, this
29939 * ignores clicks and only listens to mouse moves.
29944 * APIProperty: highlightOnly
29945 * {Boolean} If true do not actually select features (that is place them in
29946 * the layer's selected features array), just highlight them. This property
29947 * has no effect if hover is false. Defaults to false.
29949 highlightOnly: false,
29953 * {Boolean} Allow feature selection by drawing a box.
29958 * APIProperty: onBeforeSelect
29959 * {Function} Optional function to be called before a feature is selected.
29960 * The function should expect to be called with a feature.
29962 onBeforeSelect: function() {},
29965 * APIProperty: onSelect
29966 * {Function} Optional function to be called when a feature is selected.
29967 * The function should expect to be called with a feature.
29969 onSelect: function() {},
29972 * APIProperty: onUnselect
29973 * {Function} Optional function to be called when a feature is unselected.
29974 * The function should expect to be called with a feature.
29976 onUnselect: function() {},
29979 * APIProperty: scope
29980 * {Object} The scope to use with the onBeforeSelect, onSelect, onUnselect
29981 * callbacks. If null the scope will be this control.
29986 * APIProperty: geometryTypes
29987 * {Array(String)} To restrict selecting to a limited set of geometry types,
29988 * send a list of strings corresponding to the geometry class names.
29990 geometryTypes: null,
29994 * {<OpenLayers.Layer.Vector>} The vector layer with a common renderer
29995 * root for all layers this control is configured with (if an array of
29996 * layers was passed to the constructor), or the vector layer the control
29997 * was configured with (if a single layer was passed to the constructor).
30003 * {Array(<OpenLayers.Layer.Vector>)} The layers this control will work on,
30004 * or null if the control was configured with a single layer
30009 * APIProperty: callbacks
30010 * {Object} The functions that are sent to the handlers.feature for callback
30015 * APIProperty: selectStyle
30016 * {Object} Hash of styles
30021 * APIProperty: renderIntent
30022 * {String} key used to retrieve the select style from the layer's
30025 renderIntent: "select",
30028 * Property: handlers
30029 * {Object} Object with references to multiple <OpenLayers.Handler>
30035 * Constructor: OpenLayers.Control.SelectFeature
30036 * Create a new control for selecting features.
30039 * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers. The
30040 * layer(s) this control will select features from.
30041 * options - {Object}
30043 initialize: function(layers, options) {
30044 OpenLayers.Control.prototype.initialize.apply(this, [options]);
30046 if(this.scope === null) {
30049 this.initLayer(layers);
30051 click: this.clickFeature,
30052 clickout: this.clickoutFeature
30055 callbacks.over = this.overFeature;
30056 callbacks.out = this.outFeature;
30059 this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);
30061 feature: new OpenLayers.Handler.Feature(
30062 this, this.layer, this.callbacks,
30063 {geometryTypes: this.geometryTypes}
30068 this.handlers.box = new OpenLayers.Handler.Box(
30069 this, {done: this.selectBox},
30070 {boxDivClassName: "olHandlerBoxSelectFeature"}
30076 * Method: initLayer
30077 * Assign the layer property. If layers is an array, we need to use
30081 * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers.
30083 initLayer: function(layers) {
30084 if(OpenLayers.Util.isArray(layers)) {
30085 this.layers = layers;
30086 this.layer = new OpenLayers.Layer.Vector.RootContainer(
30087 this.id + "_container", {
30092 this.layer = layers;
30099 destroy: function() {
30100 if(this.active && this.layers) {
30101 this.map.removeLayer(this.layer);
30103 OpenLayers.Control.prototype.destroy.apply(this, arguments);
30105 this.layer.destroy();
30111 * Activates the control.
30114 * {Boolean} The control was effectively activated.
30116 activate: function () {
30117 if (!this.active) {
30119 this.map.addLayer(this.layer);
30121 this.handlers.feature.activate();
30122 if(this.box && this.handlers.box) {
30123 this.handlers.box.activate();
30126 return OpenLayers.Control.prototype.activate.apply(
30132 * Method: deactivate
30133 * Deactivates the control.
30136 * {Boolean} The control was effectively deactivated.
30138 deactivate: function () {
30140 this.handlers.feature.deactivate();
30141 if(this.handlers.box) {
30142 this.handlers.box.deactivate();
30145 this.map.removeLayer(this.layer);
30148 return OpenLayers.Control.prototype.deactivate.apply(
30154 * Method: unselectAll
30155 * Unselect all selected features. To unselect all except for a single
30156 * feature, set the options.except property to the feature.
30159 * options - {Object} Optional configuration object.
30161 unselectAll: function(options) {
30162 // we'll want an option to suppress notification here
30163 var layers = this.layers || [this.layer],
30164 layer, feature, l, numExcept;
30165 for(l=0; l<layers.length; ++l) {
30168 //layer.selectedFeatures is null when layer is destroyed and
30169 //one of it's preremovelayer listener calls setLayer
30170 //with another layer on this control
30171 if(layer.selectedFeatures != null) {
30172 while(layer.selectedFeatures.length > numExcept) {
30173 feature = layer.selectedFeatures[numExcept];
30174 if(!options || options.except != feature) {
30175 this.unselect(feature);
30185 * Method: clickFeature
30186 * Called on click in a feature
30187 * Only responds if this.hover is false.
30190 * feature - {<OpenLayers.Feature.Vector>}
30192 clickFeature: function(feature) {
30194 var selected = (OpenLayers.Util.indexOf(
30195 feature.layer.selectedFeatures, feature) > -1);
30197 if(this.toggleSelect()) {
30198 this.unselect(feature);
30199 } else if(!this.multipleSelect()) {
30200 this.unselectAll({except: feature});
30203 if(!this.multipleSelect()) {
30204 this.unselectAll({except: feature});
30206 this.select(feature);
30212 * Method: multipleSelect
30213 * Allow for multiple selected features based on <multiple> property and
30214 * <multipleKey> event modifier.
30217 * {Boolean} Allow for multiple selected features.
30219 multipleSelect: function() {
30220 return this.multiple || (this.handlers.feature.evt &&
30221 this.handlers.feature.evt[this.multipleKey]);
30225 * Method: toggleSelect
30226 * Event should toggle the selected state of a feature based on <toggle>
30227 * property and <toggleKey> event modifier.
30230 * {Boolean} Toggle the selected state of a feature.
30232 toggleSelect: function() {
30233 return this.toggle || (this.handlers.feature.evt &&
30234 this.handlers.feature.evt[this.toggleKey]);
30238 * Method: clickoutFeature
30239 * Called on click outside a previously clicked (selected) feature.
30240 * Only responds if this.hover is false.
30243 * feature - {<OpenLayers.Vector.Feature>}
30245 clickoutFeature: function(feature) {
30246 if(!this.hover && this.clickout) {
30247 this.unselectAll();
30252 * Method: overFeature
30253 * Called on over a feature.
30254 * Only responds if this.hover is true.
30257 * feature - {<OpenLayers.Feature.Vector>}
30259 overFeature: function(feature) {
30260 var layer = feature.layer;
30262 if(this.highlightOnly) {
30263 this.highlight(feature);
30264 } else if(OpenLayers.Util.indexOf(
30265 layer.selectedFeatures, feature) == -1) {
30266 this.select(feature);
30272 * Method: outFeature
30273 * Called on out of a selected feature.
30274 * Only responds if this.hover is true.
30277 * feature - {<OpenLayers.Feature.Vector>}
30279 outFeature: function(feature) {
30281 if(this.highlightOnly) {
30282 // we do nothing if we're not the last highlighter of the
30284 if(feature._lastHighlighter == this.id) {
30285 // if another select control had highlighted the feature before
30286 // we did it ourself then we use that control to highlight the
30287 // feature as it was before we highlighted it, else we just
30289 if(feature._prevHighlighter &&
30290 feature._prevHighlighter != this.id) {
30291 delete feature._lastHighlighter;
30292 var control = this.map.getControl(
30293 feature._prevHighlighter);
30295 control.highlight(feature);
30298 this.unhighlight(feature);
30302 this.unselect(feature);
30308 * Method: highlight
30309 * Redraw feature with the select style.
30312 * feature - {<OpenLayers.Feature.Vector>}
30314 highlight: function(feature) {
30315 var layer = feature.layer;
30316 var cont = this.events.triggerEvent("beforefeaturehighlighted", {
30319 if(cont !== false) {
30320 feature._prevHighlighter = feature._lastHighlighter;
30321 feature._lastHighlighter = this.id;
30322 var style = this.selectStyle || this.renderIntent;
30323 layer.drawFeature(feature, style);
30324 this.events.triggerEvent("featurehighlighted", {feature : feature});
30329 * Method: unhighlight
30330 * Redraw feature with the "default" style
30333 * feature - {<OpenLayers.Feature.Vector>}
30335 unhighlight: function(feature) {
30336 var layer = feature.layer;
30338 // 1. there's no other highlighter, in that case _prev is undefined,
30339 // and we just need to undef _last
30340 // 2. another control highlighted the feature after we did it, in
30341 // that case _last references this other control, and we just
30342 // need to undef _prev
30343 // 3. another control highlighted the feature before we did it, in
30344 // that case _prev references this other control, and we need to
30345 // set _last to _prev and undef _prev
30346 if(feature._prevHighlighter == undefined) {
30347 delete feature._lastHighlighter;
30348 } else if(feature._prevHighlighter == this.id) {
30349 delete feature._prevHighlighter;
30351 feature._lastHighlighter = feature._prevHighlighter;
30352 delete feature._prevHighlighter;
30354 layer.drawFeature(feature, feature.style || feature.layer.style ||
30356 this.events.triggerEvent("featureunhighlighted", {feature : feature});
30361 * Add feature to the layer's selectedFeature array, render the feature as
30362 * selected, and call the onSelect function.
30365 * feature - {<OpenLayers.Feature.Vector>}
30367 select: function(feature) {
30368 var cont = this.onBeforeSelect.call(this.scope, feature);
30369 var layer = feature.layer;
30370 if(cont !== false) {
30371 cont = layer.events.triggerEvent("beforefeatureselected", {
30374 if(cont !== false) {
30375 layer.selectedFeatures.push(feature);
30376 this.highlight(feature);
30377 // if the feature handler isn't involved in the feature
30378 // selection (because the box handler is used or the
30379 // feature is selected programatically) we fake the
30380 // feature handler to allow unselecting on click
30381 if(!this.handlers.feature.lastFeature) {
30382 this.handlers.feature.lastFeature = layer.selectedFeatures[0];
30384 layer.events.triggerEvent("featureselected", {feature: feature});
30385 this.onSelect.call(this.scope, feature);
30392 * Remove feature from the layer's selectedFeature array, render the feature as
30393 * normal, and call the onUnselect function.
30396 * feature - {<OpenLayers.Feature.Vector>}
30398 unselect: function(feature) {
30399 var layer = feature.layer;
30400 // Store feature style for restoration later
30401 this.unhighlight(feature);
30402 OpenLayers.Util.removeItem(layer.selectedFeatures, feature);
30403 layer.events.triggerEvent("featureunselected", {feature: feature});
30404 this.onUnselect.call(this.scope, feature);
30408 * Method: selectBox
30409 * Callback from the handlers.box set up when <box> selection is true
30413 * position - {<OpenLayers.Bounds> || <OpenLayers.Pixel> }
30415 selectBox: function(position) {
30416 if (position instanceof OpenLayers.Bounds) {
30417 var minXY = this.map.getLonLatFromPixel({
30421 var maxXY = this.map.getLonLatFromPixel({
30425 var bounds = new OpenLayers.Bounds(
30426 minXY.lon, minXY.lat, maxXY.lon, maxXY.lat
30429 // if multiple is false, first deselect currently selected features
30430 if (!this.multipleSelect()) {
30431 this.unselectAll();
30434 // because we're using a box, we consider we want multiple selection
30435 var prevMultiple = this.multiple;
30436 this.multiple = true;
30437 var layers = this.layers || [this.layer];
30438 this.events.triggerEvent("boxselectionstart", {layers: layers});
30440 for(var l=0; l<layers.length; ++l) {
30442 for(var i=0, len = layer.features.length; i<len; ++i) {
30443 var feature = layer.features[i];
30444 // check if the feature is displayed
30445 if (!feature.getVisibility()) {
30449 if (this.geometryTypes == null || OpenLayers.Util.indexOf(
30450 this.geometryTypes, feature.geometry.CLASS_NAME) > -1) {
30451 if (bounds.toGeometry().intersects(feature.geometry)) {
30452 if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) {
30453 this.select(feature);
30459 this.multiple = prevMultiple;
30460 this.events.triggerEvent("boxselectionend", {layers: layers});
30466 * Set the map property for the control.
30469 * map - {<OpenLayers.Map>}
30471 setMap: function(map) {
30472 this.handlers.feature.setMap(map);
30474 this.handlers.box.setMap(map);
30476 OpenLayers.Control.prototype.setMap.apply(this, arguments);
30480 * APIMethod: setLayer
30481 * Attach a new layer to the control, overriding any existing layers.
30484 * layers - Array of {<OpenLayers.Layer.Vector>} or a single
30485 * {<OpenLayers.Layer.Vector>}
30487 setLayer: function(layers) {
30488 var isActive = this.active;
30489 this.unselectAll();
30492 this.layer.destroy();
30493 this.layers = null;
30495 this.initLayer(layers);
30496 this.handlers.feature.layer = this.layer;
30503 * APIMethod: addLayer
30504 * Add a layer to the control, making the existing layers still selectable
30507 * layer - element <OpenLayers.Layer.Vector>
30509 addLayer: function( layer ) {
30510 var isActive = this.active;
30512 if (this.layers == null) {
30513 if (this.layer != null) {
30514 this.layers = [this.layer];
30515 this.layers.push(layer);
30517 this.layers = [layer];
30520 this.layers.push(layer);
30522 this.initLayer(this.layers);
30523 this.handlers.feature.layer = this.layer;
30529 CLASS_NAME: "OpenLayers.Control.SelectFeature"
30531 /* ======================================================================
30532 OpenLayers/Format/JSON.js
30533 ====================================================================== */
30535 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
30536 * full list of contributors). Published under the 2-clause BSD license.
30537 * See license.txt in the OpenLayers distribution or repository for the
30538 * full text of the license. */
30542 * This work draws heavily from the public domain JSON serializer/deserializer
30543 * at http://www.json.org/json.js. Rewritten so that it doesn't modify
30544 * basic data prototypes.
30548 * @requires OpenLayers/Format.js
30552 * Class: OpenLayers.Format.JSON
30553 * A parser to read/write JSON safely. Create a new instance with the
30554 * <OpenLayers.Format.JSON> constructor.
30557 * - <OpenLayers.Format>
30559 OpenLayers.Format.JSON = OpenLayers.Class(OpenLayers.Format, {
30562 * APIProperty: indent
30563 * {String} For "pretty" printing, the indent string will be used once for
30564 * each indentation level.
30569 * APIProperty: space
30570 * {String} For "pretty" printing, the space string will be used after
30571 * the ":" separating a name/value pair.
30576 * APIProperty: newline
30577 * {String} For "pretty" printing, the newline string will be used at the
30578 * end of each name/value pair or array item.
30584 * {Integer} For "pretty" printing, this is incremented/decremented during
30591 * {Boolean} Serialize with extra whitespace for structure. This is set
30592 * by the <write> method.
30597 * Property: nativeJSON
30598 * {Boolean} Does the browser support native json?
30600 nativeJSON: (function() {
30601 return !!(window.JSON && typeof JSON.parse == "function" && typeof JSON.stringify == "function");
30605 * Constructor: OpenLayers.Format.JSON
30606 * Create a new parser for JSON.
30609 * options - {Object} An optional object whose properties will be set on
30615 * Deserialize a json string.
30618 * json - {String} A JSON string
30619 * filter - {Function} A function which will be called for every key and
30620 * value at every level of the final result. Each value will be
30621 * replaced by the result of the filter function. This can be used to
30622 * reform generic objects into instances of classes, or to transform
30623 * date strings into Date objects.
30626 * {Object} An object, array, string, or number .
30628 read: function(json, filter) {
30630 if (this.nativeJSON) {
30631 object = JSON.parse(json, filter);
30634 * Parsing happens in three stages. In the first stage, we run the
30635 * text against a regular expression which looks for non-JSON
30636 * characters. We are especially concerned with '()' and 'new'
30637 * because they can cause invocation, and '=' because it can
30638 * cause mutation. But just to be safe, we will reject all
30639 * unexpected characters.
30641 if (/^[\],:{}\s]*$/.test(json.replace(/\\["\\\/bfnrtu]/g, '@').
30642 replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
30643 replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
30646 * In the second stage we use the eval function to compile the
30647 * text into a JavaScript structure. The '{' operator is
30648 * subject to a syntactic ambiguity in JavaScript - it can
30649 * begin a block or an object literal. We wrap the text in
30650 * parens to eliminate the ambiguity.
30652 object = eval('(' + json + ')');
30655 * In the optional third stage, we recursively walk the new
30656 * structure, passing each name/value pair to a filter
30657 * function for possible transformation.
30659 if(typeof filter === 'function') {
30660 function walk(k, v) {
30661 if(v && typeof v === 'object') {
30663 if(v.hasOwnProperty(i)) {
30664 v[i] = walk(i, v[i]);
30668 return filter(k, v);
30670 object = walk('', object);
30674 // Fall through if the regexp test fails.
30677 if(this.keepData) {
30678 this.data = object;
30686 * Serialize an object into a JSON string.
30689 * value - {String} The object, array, string, number, boolean or date
30690 * to be serialized.
30691 * pretty - {Boolean} Structure the output with newlines and indentation.
30692 * Default is false.
30695 * {String} The JSON string representation of the input value.
30697 write: function(value, pretty) {
30698 this.pretty = !!pretty;
30700 var type = typeof value;
30701 if(this.serialize[type]) {
30703 json = (!this.pretty && this.nativeJSON) ?
30704 JSON.stringify(value) :
30705 this.serialize[type].apply(this, [value]);
30707 OpenLayers.Console.error("Trouble serializing: " + err);
30714 * Method: writeIndent
30715 * Output an indentation string depending on the indentation level.
30718 * {String} An appropriate indentation string.
30720 writeIndent: function() {
30723 for(var i=0; i<this.level; ++i) {
30724 pieces.push(this.indent);
30727 return pieces.join('');
30731 * Method: writeNewline
30732 * Output a string representing a newline if in pretty printing mode.
30735 * {String} A string representing a new line.
30737 writeNewline: function() {
30738 return (this.pretty) ? this.newline : '';
30742 * Method: writeSpace
30743 * Output a string representing a space if in pretty printing mode.
30746 * {String} A space.
30748 writeSpace: function() {
30749 return (this.pretty) ? this.space : '';
30753 * Property: serialize
30754 * Object with properties corresponding to the serializable data types.
30755 * Property values are functions that do the actual serializing.
30759 * Method: serialize.object
30760 * Transform an object into a JSON string.
30763 * object - {Object} The object to be serialized.
30766 * {String} A JSON string representing the object.
30768 'object': function(object) {
30769 // three special objects that we want to treat differently
30770 if(object == null) {
30773 if(object.constructor == Date) {
30774 return this.serialize.date.apply(this, [object]);
30776 if(object.constructor == Array) {
30777 return this.serialize.array.apply(this, [object]);
30779 var pieces = ['{'];
30781 var key, keyJSON, valueJSON;
30783 var addComma = false;
30784 for(key in object) {
30785 if(object.hasOwnProperty(key)) {
30786 // recursive calls need to allow for sub-classing
30787 keyJSON = OpenLayers.Format.JSON.prototype.write.apply(this,
30788 [key, this.pretty]);
30789 valueJSON = OpenLayers.Format.JSON.prototype.write.apply(this,
30790 [object[key], this.pretty]);
30791 if(keyJSON != null && valueJSON != null) {
30795 pieces.push(this.writeNewline(), this.writeIndent(),
30796 keyJSON, ':', this.writeSpace(), valueJSON);
30803 pieces.push(this.writeNewline(), this.writeIndent(), '}');
30804 return pieces.join('');
30808 * Method: serialize.array
30809 * Transform an array into a JSON string.
30812 * array - {Array} The array to be serialized
30815 * {String} A JSON string representing the array.
30817 'array': function(array) {
30819 var pieces = ['['];
30822 for(var i=0, len=array.length; i<len; ++i) {
30823 // recursive calls need to allow for sub-classing
30824 json = OpenLayers.Format.JSON.prototype.write.apply(this,
30825 [array[i], this.pretty]);
30830 pieces.push(this.writeNewline(), this.writeIndent(), json);
30835 pieces.push(this.writeNewline(), this.writeIndent(), ']');
30836 return pieces.join('');
30840 * Method: serialize.string
30841 * Transform a string into a JSON string.
30844 * string - {String} The string to be serialized
30847 * {String} A JSON string representing the string.
30849 'string': function(string) {
30850 // If the string contains no control characters, no quote characters, and no
30851 // backslash characters, then we can simply slap some quotes around it.
30852 // Otherwise we must also replace the offending characters with safe
30863 if(/["\\\x00-\x1f]/.test(string)) {
30864 return '"' + string.replace(/([\x00-\x1f\\"])/g, function(a, b) {
30869 c = b.charCodeAt();
30871 Math.floor(c / 16).toString(16) +
30872 (c % 16).toString(16);
30875 return '"' + string + '"';
30879 * Method: serialize.number
30880 * Transform a number into a JSON string.
30883 * number - {Number} The number to be serialized.
30886 * {String} A JSON string representing the number.
30888 'number': function(number) {
30889 return isFinite(number) ? String(number) : "null";
30893 * Method: serialize.boolean
30894 * Transform a boolean into a JSON string.
30897 * bool - {Boolean} The boolean to be serialized.
30900 * {String} A JSON string representing the boolean.
30902 'boolean': function(bool) {
30903 return String(bool);
30907 * Method: serialize.object
30908 * Transform a date into a JSON string.
30911 * date - {Date} The date to be serialized.
30914 * {String} A JSON string representing the date.
30916 'date': function(date) {
30917 function format(number) {
30918 // Format integers to have at least two digits.
30919 return (number < 10) ? '0' + number : number;
30921 return '"' + date.getFullYear() + '-' +
30922 format(date.getMonth() + 1) + '-' +
30923 format(date.getDate()) + 'T' +
30924 format(date.getHours()) + ':' +
30925 format(date.getMinutes()) + ':' +
30926 format(date.getSeconds()) + '"';
30930 CLASS_NAME: "OpenLayers.Format.JSON"
30933 /* ======================================================================
30934 OpenLayers/Format/GeoJSON.js
30935 ====================================================================== */
30937 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
30938 * full list of contributors). Published under the 2-clause BSD license.
30939 * See license.txt in the OpenLayers distribution or repository for the
30940 * full text of the license. */
30943 * @requires OpenLayers/Format/JSON.js
30944 * @requires OpenLayers/Feature/Vector.js
30945 * @requires OpenLayers/Geometry/Point.js
30946 * @requires OpenLayers/Geometry/MultiPoint.js
30947 * @requires OpenLayers/Geometry/LineString.js
30948 * @requires OpenLayers/Geometry/MultiLineString.js
30949 * @requires OpenLayers/Geometry/Polygon.js
30950 * @requires OpenLayers/Geometry/MultiPolygon.js
30951 * @requires OpenLayers/Console.js
30955 * Class: OpenLayers.Format.GeoJSON
30956 * Read and write GeoJSON. Create a new parser with the
30957 * <OpenLayers.Format.GeoJSON> constructor.
30960 * - <OpenLayers.Format.JSON>
30962 OpenLayers.Format.GeoJSON = OpenLayers.Class(OpenLayers.Format.JSON, {
30965 * APIProperty: ignoreExtraDims
30966 * {Boolean} Ignore dimensions higher than 2 when reading geometry
30969 ignoreExtraDims: false,
30972 * Constructor: OpenLayers.Format.GeoJSON
30973 * Create a new parser for GeoJSON.
30976 * options - {Object} An optional object whose properties will be set on
30982 * Deserialize a GeoJSON string.
30985 * json - {String} A GeoJSON string
30986 * type - {String} Optional string that determines the structure of
30987 * the output. Supported values are "Geometry", "Feature", and
30988 * "FeatureCollection". If absent or null, a default of
30989 * "FeatureCollection" is assumed.
30990 * filter - {Function} A function which will be called for every key and
30991 * value at every level of the final result. Each value will be
30992 * replaced by the result of the filter function. This can be used to
30993 * reform generic objects into instances of classes, or to transform
30994 * date strings into Date objects.
30997 * {Object} The return depends on the value of the type argument. If type
30998 * is "FeatureCollection" (the default), the return will be an array
30999 * of <OpenLayers.Feature.Vector>. If type is "Geometry", the input json
31000 * must represent a single geometry, and the return will be an
31001 * <OpenLayers.Geometry>. If type is "Feature", the input json must
31002 * represent a single feature, and the return will be an
31003 * <OpenLayers.Feature.Vector>.
31005 read: function(json, type, filter) {
31006 type = (type) ? type : "FeatureCollection";
31007 var results = null;
31009 if (typeof json == "string") {
31010 obj = OpenLayers.Format.JSON.prototype.read.apply(this,
31016 OpenLayers.Console.error("Bad JSON: " + json);
31017 } else if(typeof(obj.type) != "string") {
31018 OpenLayers.Console.error("Bad GeoJSON - no type: " + json);
31019 } else if(this.isValidType(obj, type)) {
31023 results = this.parseGeometry(obj);
31025 OpenLayers.Console.error(err);
31030 results = this.parseFeature(obj);
31031 results.type = "Feature";
31033 OpenLayers.Console.error(err);
31036 case "FeatureCollection":
31037 // for type FeatureCollection, we allow input to be any type
31042 results.push(this.parseFeature(obj));
31045 OpenLayers.Console.error(err);
31048 case "FeatureCollection":
31049 for(var i=0, len=obj.features.length; i<len; ++i) {
31051 results.push(this.parseFeature(obj.features[i]));
31054 OpenLayers.Console.error(err);
31060 var geom = this.parseGeometry(obj);
31061 results.push(new OpenLayers.Feature.Vector(geom));
31064 OpenLayers.Console.error(err);
31074 * Method: isValidType
31075 * Check if a GeoJSON object is a valid representative of the given type.
31078 * {Boolean} The object is valid GeoJSON object of the given type.
31080 isValidType: function(obj, type) {
31084 if(OpenLayers.Util.indexOf(
31085 ["Point", "MultiPoint", "LineString", "MultiLineString",
31086 "Polygon", "MultiPolygon", "Box", "GeometryCollection"],
31088 // unsupported geometry type
31089 OpenLayers.Console.error("Unsupported geometry type: " +
31095 case "FeatureCollection":
31096 // allow for any type to be converted to a feature collection
31100 // for Feature types must match
31101 if(obj.type == type) {
31104 OpenLayers.Console.error("Cannot convert types from " +
31105 obj.type + " to " + type);
31112 * Method: parseFeature
31113 * Convert a feature object from GeoJSON into an
31114 * <OpenLayers.Feature.Vector>.
31117 * obj - {Object} An object created from a GeoJSON object
31120 * {<OpenLayers.Feature.Vector>} A feature.
31122 parseFeature: function(obj) {
31123 var feature, geometry, attributes, bbox;
31124 attributes = (obj.properties) ? obj.properties : {};
31125 bbox = (obj.geometry && obj.geometry.bbox) || obj.bbox;
31127 geometry = this.parseGeometry(obj.geometry);
31129 // deal with bad geometries
31132 feature = new OpenLayers.Feature.Vector(geometry, attributes);
31134 feature.bounds = OpenLayers.Bounds.fromArray(bbox);
31137 feature.fid = obj.id;
31143 * Method: parseGeometry
31144 * Convert a geometry object from GeoJSON into an <OpenLayers.Geometry>.
31147 * obj - {Object} An object created from a GeoJSON object
31150 * {<OpenLayers.Geometry>} A geometry.
31152 parseGeometry: function(obj) {
31156 var geometry, collection = false;
31157 if(obj.type == "GeometryCollection") {
31158 if(!(OpenLayers.Util.isArray(obj.geometries))) {
31159 throw "GeometryCollection must have geometries array: " + obj;
31161 var numGeom = obj.geometries.length;
31162 var components = new Array(numGeom);
31163 for(var i=0; i<numGeom; ++i) {
31164 components[i] = this.parseGeometry.apply(
31165 this, [obj.geometries[i]]
31168 geometry = new OpenLayers.Geometry.Collection(components);
31171 if(!(OpenLayers.Util.isArray(obj.coordinates))) {
31172 throw "Geometry must have coordinates array: " + obj;
31174 if(!this.parseCoords[obj.type.toLowerCase()]) {
31175 throw "Unsupported geometry type: " + obj.type;
31178 geometry = this.parseCoords[obj.type.toLowerCase()].apply(
31179 this, [obj.coordinates]
31182 // deal with bad coordinates
31186 // We don't reproject collections because the children are reprojected
31187 // for us when they are created.
31188 if (this.internalProjection && this.externalProjection && !collection) {
31189 geometry.transform(this.externalProjection,
31190 this.internalProjection);
31196 * Property: parseCoords
31197 * Object with properties corresponding to the GeoJSON geometry types.
31198 * Property values are functions that do the actual parsing.
31202 * Method: parseCoords.point
31203 * Convert a coordinate array from GeoJSON into an
31204 * <OpenLayers.Geometry>.
31207 * array - {Object} The coordinates array from the GeoJSON fragment.
31210 * {<OpenLayers.Geometry>} A geometry.
31212 "point": function(array) {
31213 if (this.ignoreExtraDims == false &&
31214 array.length != 2) {
31215 throw "Only 2D points are supported: " + array;
31217 return new OpenLayers.Geometry.Point(array[0], array[1]);
31221 * Method: parseCoords.multipoint
31222 * Convert a coordinate array from GeoJSON into an
31223 * <OpenLayers.Geometry>.
31226 * array - {Object} The coordinates array from the GeoJSON fragment.
31229 * {<OpenLayers.Geometry>} A geometry.
31231 "multipoint": function(array) {
31234 for(var i=0, len=array.length; i<len; ++i) {
31236 p = this.parseCoords["point"].apply(this, [array[i]]);
31242 return new OpenLayers.Geometry.MultiPoint(points);
31246 * Method: parseCoords.linestring
31247 * Convert a coordinate array from GeoJSON into an
31248 * <OpenLayers.Geometry>.
31251 * array - {Object} The coordinates array from the GeoJSON fragment.
31254 * {<OpenLayers.Geometry>} A geometry.
31256 "linestring": function(array) {
31259 for(var i=0, len=array.length; i<len; ++i) {
31261 p = this.parseCoords["point"].apply(this, [array[i]]);
31267 return new OpenLayers.Geometry.LineString(points);
31271 * Method: parseCoords.multilinestring
31272 * Convert a coordinate array from GeoJSON into an
31273 * <OpenLayers.Geometry>.
31276 * array - {Object} The coordinates array from the GeoJSON fragment.
31279 * {<OpenLayers.Geometry>} A geometry.
31281 "multilinestring": function(array) {
31284 for(var i=0, len=array.length; i<len; ++i) {
31286 l = this.parseCoords["linestring"].apply(this, [array[i]]);
31292 return new OpenLayers.Geometry.MultiLineString(lines);
31296 * Method: parseCoords.polygon
31297 * Convert a coordinate array from GeoJSON into an
31298 * <OpenLayers.Geometry>.
31301 * {<OpenLayers.Geometry>} A geometry.
31303 "polygon": function(array) {
31306 for(var i=0, len=array.length; i<len; ++i) {
31308 l = this.parseCoords["linestring"].apply(this, [array[i]]);
31312 r = new OpenLayers.Geometry.LinearRing(l.components);
31315 return new OpenLayers.Geometry.Polygon(rings);
31319 * Method: parseCoords.multipolygon
31320 * Convert a coordinate array from GeoJSON into an
31321 * <OpenLayers.Geometry>.
31324 * array - {Object} The coordinates array from the GeoJSON fragment.
31327 * {<OpenLayers.Geometry>} A geometry.
31329 "multipolygon": function(array) {
31332 for(var i=0, len=array.length; i<len; ++i) {
31334 p = this.parseCoords["polygon"].apply(this, [array[i]]);
31340 return new OpenLayers.Geometry.MultiPolygon(polys);
31344 * Method: parseCoords.box
31345 * Convert a coordinate array from GeoJSON into an
31346 * <OpenLayers.Geometry>.
31349 * array - {Object} The coordinates array from the GeoJSON fragment.
31352 * {<OpenLayers.Geometry>} A geometry.
31354 "box": function(array) {
31355 if(array.length != 2) {
31356 throw "GeoJSON box coordinates must have 2 elements";
31358 return new OpenLayers.Geometry.Polygon([
31359 new OpenLayers.Geometry.LinearRing([
31360 new OpenLayers.Geometry.Point(array[0][0], array[0][1]),
31361 new OpenLayers.Geometry.Point(array[1][0], array[0][1]),
31362 new OpenLayers.Geometry.Point(array[1][0], array[1][1]),
31363 new OpenLayers.Geometry.Point(array[0][0], array[1][1]),
31364 new OpenLayers.Geometry.Point(array[0][0], array[0][1])
31373 * Serialize a feature, geometry, array of features into a GeoJSON string.
31376 * obj - {Object} An <OpenLayers.Feature.Vector>, <OpenLayers.Geometry>,
31377 * or an array of features.
31378 * pretty - {Boolean} Structure the output with newlines and indentation.
31379 * Default is false.
31382 * {String} The GeoJSON string representation of the input geometry,
31383 * features, or array of features.
31385 write: function(obj, pretty) {
31389 if(OpenLayers.Util.isArray(obj)) {
31390 geojson.type = "FeatureCollection";
31391 var numFeatures = obj.length;
31392 geojson.features = new Array(numFeatures);
31393 for(var i=0; i<numFeatures; ++i) {
31394 var element = obj[i];
31395 if(!element instanceof OpenLayers.Feature.Vector) {
31396 var msg = "FeatureCollection only supports collections " +
31397 "of features: " + element;
31400 geojson.features[i] = this.extract.feature.apply(
31404 } else if (obj.CLASS_NAME.indexOf("OpenLayers.Geometry") == 0) {
31405 geojson = this.extract.geometry.apply(this, [obj]);
31406 } else if (obj instanceof OpenLayers.Feature.Vector) {
31407 geojson = this.extract.feature.apply(this, [obj]);
31408 if(obj.layer && obj.layer.projection) {
31409 geojson.crs = this.createCRSObject(obj);
31412 return OpenLayers.Format.JSON.prototype.write.apply(this,
31413 [geojson, pretty]);
31417 * Method: createCRSObject
31418 * Create the CRS object for an object.
31421 * object - {<OpenLayers.Feature.Vector>}
31424 * {Object} An object which can be assigned to the crs property
31425 * of a GeoJSON object.
31427 createCRSObject: function(object) {
31428 var proj = object.layer.projection.toString();
31430 if (proj.match(/epsg:/i)) {
31431 var code = parseInt(proj.substring(proj.indexOf(":") + 1));
31432 if (code == 4326) {
31436 "name": "urn:ogc:def:crs:OGC:1.3:CRS84"
31443 "name": "EPSG:" + code
31452 * Property: extract
31453 * Object with properties corresponding to the GeoJSON types.
31454 * Property values are functions that do the actual value extraction.
31458 * Method: extract.feature
31459 * Return a partial GeoJSON object representing a single feature.
31462 * feature - {<OpenLayers.Feature.Vector>}
31465 * {Object} An object representing the point.
31467 'feature': function(feature) {
31468 var geom = this.extract.geometry.apply(this, [feature.geometry]);
31471 "properties": feature.attributes,
31474 if (feature.fid != null) {
31475 json.id = feature.fid;
31481 * Method: extract.geometry
31482 * Return a GeoJSON object representing a single geometry.
31485 * geometry - {<OpenLayers.Geometry>}
31488 * {Object} An object representing the geometry.
31490 'geometry': function(geometry) {
31491 if (geometry == null) {
31494 if (this.internalProjection && this.externalProjection) {
31495 geometry = geometry.clone();
31496 geometry.transform(this.internalProjection,
31497 this.externalProjection);
31499 var geometryType = geometry.CLASS_NAME.split('.')[2];
31500 var data = this.extract[geometryType.toLowerCase()].apply(this, [geometry]);
31502 if(geometryType == "Collection") {
31504 "type": "GeometryCollection",
31509 "type": geometryType,
31510 "coordinates": data
31518 * Method: extract.point
31519 * Return an array of coordinates from a point.
31522 * point - {<OpenLayers.Geometry.Point>}
31525 * {Array} An array of coordinates representing the point.
31527 'point': function(point) {
31528 return [point.x, point.y];
31532 * Method: extract.multipoint
31533 * Return an array of point coordinates from a multipoint.
31536 * multipoint - {<OpenLayers.Geometry.MultiPoint>}
31539 * {Array} An array of point coordinate arrays representing
31542 'multipoint': function(multipoint) {
31544 for(var i=0, len=multipoint.components.length; i<len; ++i) {
31545 array.push(this.extract.point.apply(this, [multipoint.components[i]]));
31551 * Method: extract.linestring
31552 * Return an array of coordinate arrays from a linestring.
31555 * linestring - {<OpenLayers.Geometry.LineString>}
31558 * {Array} An array of coordinate arrays representing
31561 'linestring': function(linestring) {
31563 for(var i=0, len=linestring.components.length; i<len; ++i) {
31564 array.push(this.extract.point.apply(this, [linestring.components[i]]));
31570 * Method: extract.multilinestring
31571 * Return an array of linestring arrays from a linestring.
31574 * multilinestring - {<OpenLayers.Geometry.MultiLineString>}
31577 * {Array} An array of linestring arrays representing
31578 * the multilinestring.
31580 'multilinestring': function(multilinestring) {
31582 for(var i=0, len=multilinestring.components.length; i<len; ++i) {
31583 array.push(this.extract.linestring.apply(this, [multilinestring.components[i]]));
31589 * Method: extract.polygon
31590 * Return an array of linear ring arrays from a polygon.
31593 * polygon - {<OpenLayers.Geometry.Polygon>}
31596 * {Array} An array of linear ring arrays representing the polygon.
31598 'polygon': function(polygon) {
31600 for(var i=0, len=polygon.components.length; i<len; ++i) {
31601 array.push(this.extract.linestring.apply(this, [polygon.components[i]]));
31607 * Method: extract.multipolygon
31608 * Return an array of polygon arrays from a multipolygon.
31611 * multipolygon - {<OpenLayers.Geometry.MultiPolygon>}
31614 * {Array} An array of polygon arrays representing
31617 'multipolygon': function(multipolygon) {
31619 for(var i=0, len=multipolygon.components.length; i<len; ++i) {
31620 array.push(this.extract.polygon.apply(this, [multipolygon.components[i]]));
31626 * Method: extract.collection
31627 * Return an array of geometries from a geometry collection.
31630 * collection - {<OpenLayers.Geometry.Collection>}
31633 * {Array} An array of geometry objects representing the geometry
31636 'collection': function(collection) {
31637 var len = collection.components.length;
31638 var array = new Array(len);
31639 for(var i=0; i<len; ++i) {
31640 array[i] = this.extract.geometry.apply(
31641 this, [collection.components[i]]
31650 CLASS_NAME: "OpenLayers.Format.GeoJSON"
31653 /* ======================================================================
31654 OpenLayers/Lang/de.js
31655 ====================================================================== */
31657 /* Translators (2009 onwards):
31664 * @requires OpenLayers/Lang.js
31668 * Namespace: OpenLayers.Lang["de"]
31669 * Dictionary for Deutsch. Keys for entries are used in calls to
31670 * <OpenLayers.Lang.translate>. Entry bodies are normal strings or
31671 * strings formatted for use with <OpenLayers.String.format> calls.
31673 OpenLayers.Lang["de"] = OpenLayers.Util.applyDefaults({
31675 'unhandledRequest': "Unbehandelte Anfragerückmeldung ${statusText}",
31677 'Permalink': "Permalink",
31679 'Overlays': "Overlays",
31681 'Base Layer': "Grundkarte",
31683 'noFID': "Ein Feature, für das keine FID existiert, kann nicht aktualisiert werden.",
31685 'browserNotSupported': "Ihr Browser unterstützt keine Vektordarstellung. Aktuell unterstützte Renderer:\n${renderers}",
31687 '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.",
31689 'commitSuccess': "WFS-Transaktion: Erfolgreich ${response}",
31691 'commitFailed': "WFS-Transaktion: Fehlgeschlagen ${response}",
31693 '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",
31695 '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",
31697 'Scale = 1 : ${scaleDenom}': "Maßstab = 1 : ${scaleDenom}",
31707 '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.",
31709 'methodDeprecated': "Die Methode ist veraltet und wird in 3.0 entfernt. Bitte verwende stattdessen ${newMethod}."
31712 /* ======================================================================
31713 OpenLayers/Lang/en.js
31714 ====================================================================== */
31717 * @requires OpenLayers/Lang.js
31721 * Namespace: OpenLayers.Lang["en"]
31722 * Dictionary for English. Keys for entries are used in calls to
31723 * <OpenLayers.Lang.translate>. Entry bodies are normal strings or
31724 * strings formatted for use with <OpenLayers.String.format> calls.
31726 OpenLayers.Lang.en = {
31728 'unhandledRequest': "Unhandled request return ${statusText}",
31730 'Permalink': "Permalink",
31732 'Overlays': "Overlays",
31734 'Base Layer': "Base Layer",
31736 'noFID': "Can't update a feature for which there is no FID.",
31738 'browserNotSupported':
31739 "Your browser does not support vector rendering. Currently supported renderers are:\n${renderers}",
31742 'minZoomLevelError':
31743 "The minZoomLevel property is only intended for use " +
31744 "with the FixedZoomLevels-descendent layers. That this " +
31745 "wfs layer checks for minZoomLevel is a relic of the" +
31746 "past. We cannot, however, remove it without possibly " +
31747 "breaking OL based applications that may depend on it." +
31748 " Therefore we are deprecating it -- the minZoomLevel " +
31749 "check below will be removed at 3.0. Please instead " +
31750 "use min/max resolution setting as described here: " +
31751 "http://trac.openlayers.org/wiki/SettingZoomLevels",
31753 'commitSuccess': "WFS Transaction: SUCCESS ${response}",
31755 'commitFailed': "WFS Transaction: FAILED ${response}",
31758 "The Google Layer was unable to load correctly.<br><br>" +
31759 "To get rid of this message, select a new BaseLayer " +
31760 "in the layer switcher in the upper-right corner.<br><br>" +
31761 "Most likely, this is because the Google Maps library " +
31762 "script was either not included, or does not contain the " +
31763 "correct API key for your site.<br><br>" +
31764 "Developers: For help getting this working correctly, " +
31765 "<a href='http://trac.openlayers.org/wiki/Google' " +
31766 "target='_blank'>click here</a>",
31769 "The ${layerType} Layer was unable to load correctly.<br><br>" +
31770 "To get rid of this message, select a new BaseLayer " +
31771 "in the layer switcher in the upper-right corner.<br><br>" +
31772 "Most likely, this is because the ${layerLib} library " +
31773 "script was not correctly included.<br><br>" +
31774 "Developers: For help getting this working correctly, " +
31775 "<a href='http://trac.openlayers.org/wiki/${layerLib}' " +
31776 "target='_blank'>click here</a>",
31778 'Scale = 1 : ${scaleDenom}': "Scale = 1 : ${scaleDenom}",
31780 //labels for the graticule control
31785 'Graticule': 'Graticule',
31788 'reprojectDeprecated':
31789 "You are using the 'reproject' option " +
31790 "on the ${layerName} layer. This option is deprecated: " +
31791 "its use was designed to support displaying data over commercial " +
31792 "basemaps, but that functionality should now be achieved by using " +
31793 "Spherical Mercator support. More information is available from " +
31794 "http://trac.openlayers.org/wiki/SphericalMercator.",
31797 'methodDeprecated':
31798 "This method has been deprecated and will be removed in 3.0. " +
31799 "Please use ${newMethod} instead.",
31805 /* ======================================================================
31806 OpenLayers/Popup.js
31807 ====================================================================== */
31809 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
31810 * full list of contributors). Published under the 2-clause BSD license.
31811 * See license.txt in the OpenLayers distribution or repository for the
31812 * full text of the license. */
31815 * @requires OpenLayers/BaseTypes/Class.js
31820 * Class: OpenLayers.Popup
31821 * A popup is a small div that can opened and closed on the map.
31822 * Typically opened in response to clicking on a marker.
31823 * See <OpenLayers.Marker>. Popup's don't require their own
31824 * layer and are added the the map using the <OpenLayers.Map.addPopup>
31829 * popup = new OpenLayers.Popup("chicken",
31830 * new OpenLayers.LonLat(5,40),
31831 * new OpenLayers.Size(200,200),
31835 * map.addPopup(popup);
31838 OpenLayers.Popup = OpenLayers.Class({
31842 * {<OpenLayers.Events>} custom event manager
31847 * {String} the unique identifier assigned to this popup.
31853 * {<OpenLayers.LonLat>} the position of this popup on the map
31859 * {DOMElement} the div that contains this popup.
31864 * Property: contentSize
31865 * {<OpenLayers.Size>} the width and height of the content.
31871 * {<OpenLayers.Size>} the width and height of the popup.
31876 * Property: contentHTML
31877 * {String} An HTML string for this popup to display.
31882 * Property: backgroundColor
31883 * {String} the background color used by the popup.
31885 backgroundColor: "",
31888 * Property: opacity
31889 * {float} the opacity of this popup (between 0.0 and 1.0)
31895 * {String} the border size of the popup. (eg 2px)
31900 * Property: contentDiv
31901 * {DOMElement} a reference to the element that holds the content of
31907 * Property: groupDiv
31908 * {DOMElement} First and only child of 'div'. The group Div contains the
31909 * 'contentDiv' and the 'closeDiv'.
31914 * Property: closeDiv
31915 * {DOMElement} the optional closer image
31920 * APIProperty: autoSize
31921 * {Boolean} Resize the popup to auto-fit the contents.
31922 * Default is false.
31927 * APIProperty: minSize
31928 * {<OpenLayers.Size>} Minimum size allowed for the popup's contents.
31933 * APIProperty: maxSize
31934 * {<OpenLayers.Size>} Maximum size allowed for the popup's contents.
31939 * Property: displayClass
31940 * {String} The CSS class of the popup.
31942 displayClass: "olPopup",
31945 * Property: contentDisplayClass
31946 * {String} The CSS class of the popup content div.
31948 contentDisplayClass: "olPopupContent",
31951 * Property: padding
31952 * {int or <OpenLayers.Bounds>} An extra opportunity to specify internal
31953 * padding of the content div inside the popup. This was originally
31954 * confused with the css padding as specified in style.css's
31955 * 'olPopupContent' class. We would like to get rid of this altogether,
31956 * except that it does come in handy for the framed and anchoredbubble
31957 * popups, who need to maintain yet another barrier between their
31958 * content and the outer border of the popup itself.
31960 * Note that in order to not break API, we must continue to support
31961 * this property being set as an integer. Really, though, we'd like to
31962 * have this specified as a Bounds object so that user can specify
31963 * distinct left, top, right, bottom paddings. With the 3.0 release
31964 * we can make this only a bounds.
31969 * Property: disableFirefoxOverflowHack
31970 * {Boolean} The hack for overflow in Firefox causes all elements
31971 * to be re-drawn, which causes Flash elements to be
31972 * re-initialized, which is troublesome.
31973 * With this property the hack can be disabled.
31975 disableFirefoxOverflowHack: false,
31978 * Method: fixPadding
31979 * To be removed in 3.0, this function merely helps us to deal with the
31980 * case where the user may have set an integer value for padding,
31981 * instead of an <OpenLayers.Bounds> object.
31983 fixPadding: function() {
31984 if (typeof this.padding == "number") {
31985 this.padding = new OpenLayers.Bounds(
31986 this.padding, this.padding, this.padding, this.padding
31992 * APIProperty: panMapIfOutOfView
31993 * {Boolean} When drawn, pan map such that the entire popup is visible in
31994 * the current viewport (if necessary).
31995 * Default is false.
31997 panMapIfOutOfView: false,
32000 * APIProperty: keepInMap
32001 * {Boolean} If panMapIfOutOfView is false, and this property is true,
32002 * contrain the popup such that it always fits in the available map
32003 * space. By default, this is not set on the base class. If you are
32004 * creating popups that are near map edges and not allowing pannning,
32005 * and especially if you have a popup which has a
32006 * fixedRelativePosition, setting this to false may be a smart thing to
32007 * do. Subclasses may want to override this setting.
32009 * Default is false.
32014 * APIProperty: closeOnMove
32015 * {Boolean} When map pans, close the popup.
32016 * Default is false.
32018 closeOnMove: false,
32022 * {<OpenLayers.Map>} this gets set in Map.js when the popup is added to the map
32027 * Constructor: OpenLayers.Popup
32031 * id - {String} a unqiue identifier for this popup. If null is passed
32032 * an identifier will be automatically generated.
32033 * lonlat - {<OpenLayers.LonLat>} The position on the map the popup will
32035 * contentSize - {<OpenLayers.Size>} The size of the content.
32036 * contentHTML - {String} An HTML string to display inside the
32038 * closeBox - {Boolean} Whether to display a close box inside
32040 * closeBoxCallback - {Function} Function to be called on closeBox click.
32042 initialize:function(id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback) {
32044 id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
32048 this.lonlat = lonlat;
32050 this.contentSize = (contentSize != null) ? contentSize
32051 : new OpenLayers.Size(
32052 OpenLayers.Popup.WIDTH,
32053 OpenLayers.Popup.HEIGHT);
32054 if (contentHTML != null) {
32055 this.contentHTML = contentHTML;
32057 this.backgroundColor = OpenLayers.Popup.COLOR;
32058 this.opacity = OpenLayers.Popup.OPACITY;
32059 this.border = OpenLayers.Popup.BORDER;
32061 this.div = OpenLayers.Util.createDiv(this.id, null, null,
32062 null, null, null, "hidden");
32063 this.div.className = this.displayClass;
32065 var groupDivId = this.id + "_GroupDiv";
32066 this.groupDiv = OpenLayers.Util.createDiv(groupDivId, null, null,
32067 null, "relative", null,
32070 var id = this.div.id + "_contentDiv";
32071 this.contentDiv = OpenLayers.Util.createDiv(id, null, this.contentSize.clone(),
32073 this.contentDiv.className = this.contentDisplayClass;
32074 this.groupDiv.appendChild(this.contentDiv);
32075 this.div.appendChild(this.groupDiv);
32078 this.addCloseBox(closeBoxCallback);
32081 this.registerEvents();
32086 * nullify references to prevent circular references and memory leaks
32088 destroy: function() {
32091 this.lonlat = null;
32093 this.contentHTML = null;
32095 this.backgroundColor = null;
32096 this.opacity = null;
32097 this.border = null;
32099 if (this.closeOnMove && this.map) {
32100 this.map.events.unregister("movestart", this, this.hide);
32103 this.events.destroy();
32104 this.events = null;
32106 if (this.closeDiv) {
32107 OpenLayers.Event.stopObservingElement(this.closeDiv);
32108 this.groupDiv.removeChild(this.closeDiv);
32110 this.closeDiv = null;
32112 this.div.removeChild(this.groupDiv);
32113 this.groupDiv = null;
32115 if (this.map != null) {
32116 this.map.removePopup(this);
32121 this.autoSize = null;
32122 this.minSize = null;
32123 this.maxSize = null;
32124 this.padding = null;
32125 this.panMapIfOutOfView = null;
32130 * Constructs the elements that make up the popup.
32133 * px - {<OpenLayers.Pixel>} the position the popup in pixels.
32136 * {DOMElement} Reference to a div that contains the drawn popup
32138 draw: function(px) {
32140 if ((this.lonlat != null) && (this.map != null)) {
32141 px = this.map.getLayerPxFromLonLat(this.lonlat);
32145 // this assumes that this.map already exists, which is okay because
32146 // this.draw is only called once the popup has been added to the map.
32147 if (this.closeOnMove) {
32148 this.map.events.register("movestart", this, this.hide);
32151 //listen to movestart, moveend to disable overflow (FF bug)
32152 if (!this.disableFirefoxOverflowHack && OpenLayers.BROWSER_NAME == 'firefox') {
32153 this.map.events.register("movestart", this, function() {
32154 var style = document.defaultView.getComputedStyle(
32155 this.contentDiv, null
32157 var currentOverflow = style.getPropertyValue("overflow");
32158 if (currentOverflow != "hidden") {
32159 this.contentDiv._oldOverflow = currentOverflow;
32160 this.contentDiv.style.overflow = "hidden";
32163 this.map.events.register("moveend", this, function() {
32164 var oldOverflow = this.contentDiv._oldOverflow;
32166 this.contentDiv.style.overflow = oldOverflow;
32167 this.contentDiv._oldOverflow = null;
32173 if (!this.autoSize && !this.size) {
32174 this.setSize(this.contentSize);
32176 this.setBackgroundColor();
32179 this.setContentHTML();
32181 if (this.panMapIfOutOfView) {
32182 this.panIntoView();
32189 * Method: updatePosition
32190 * if the popup has a lonlat and its map members set,
32191 * then have it move itself to its proper position
32193 updatePosition: function() {
32194 if ((this.lonlat) && (this.map)) {
32195 var px = this.map.getLayerPxFromLonLat(this.lonlat);
32206 * px - {<OpenLayers.Pixel>} the top and left position of the popup div.
32208 moveTo: function(px) {
32209 if ((px != null) && (this.div != null)) {
32210 this.div.style.left = px.x + "px";
32211 this.div.style.top = px.y + "px";
32219 * {Boolean} Boolean indicating whether or not the popup is visible
32221 visible: function() {
32222 return OpenLayers.Element.visible(this.div);
32227 * Toggles visibility of the popup.
32229 toggle: function() {
32230 if (this.visible()) {
32239 * Makes the popup visible.
32242 this.div.style.display = '';
32244 if (this.panMapIfOutOfView) {
32245 this.panIntoView();
32251 * Makes the popup invisible.
32254 this.div.style.display = 'none';
32259 * Used to adjust the size of the popup.
32262 * contentSize - {<OpenLayers.Size>} the new size for the popup's
32263 * contents div (in pixels).
32265 setSize:function(contentSize) {
32266 this.size = contentSize.clone();
32268 // if our contentDiv has a css 'padding' set on it by a stylesheet, we
32269 // must add that to the desired "size".
32270 var contentDivPadding = this.getContentDivPadding();
32271 var wPadding = contentDivPadding.left + contentDivPadding.right;
32272 var hPadding = contentDivPadding.top + contentDivPadding.bottom;
32274 // take into account the popup's 'padding' property
32276 wPadding += this.padding.left + this.padding.right;
32277 hPadding += this.padding.top + this.padding.bottom;
32279 // make extra space for the close div
32280 if (this.closeDiv) {
32281 var closeDivWidth = parseInt(this.closeDiv.style.width);
32282 wPadding += closeDivWidth + contentDivPadding.right;
32285 //increase size of the main popup div to take into account the
32286 // users's desired padding and close div.
32287 this.size.w += wPadding;
32288 this.size.h += hPadding;
32290 //now if our browser is IE, we need to actually make the contents
32291 // div itself bigger to take its own padding into effect. this makes
32292 // me want to shoot someone, but so it goes.
32293 if (OpenLayers.BROWSER_NAME == "msie") {
32294 this.contentSize.w +=
32295 contentDivPadding.left + contentDivPadding.right;
32296 this.contentSize.h +=
32297 contentDivPadding.bottom + contentDivPadding.top;
32300 if (this.div != null) {
32301 this.div.style.width = this.size.w + "px";
32302 this.div.style.height = this.size.h + "px";
32304 if (this.contentDiv != null){
32305 this.contentDiv.style.width = contentSize.w + "px";
32306 this.contentDiv.style.height = contentSize.h + "px";
32311 * APIMethod: updateSize
32312 * Auto size the popup so that it precisely fits its contents (as
32313 * determined by this.contentDiv.innerHTML). Popup size will, of
32314 * course, be limited by the available space on the current map
32316 updateSize: function() {
32318 // determine actual render dimensions of the contents by putting its
32319 // contents into a fake contentDiv (for the CSS) and then measuring it
32320 var preparedHTML = "<div class='" + this.contentDisplayClass+ "'>" +
32321 this.contentDiv.innerHTML +
32324 var containerElement = (this.map) ? this.map.div : document.body;
32325 var realSize = OpenLayers.Util.getRenderedDimensions(
32326 preparedHTML, null, {
32327 displayClass: this.displayClass,
32328 containerElement: containerElement
32332 // is the "real" size of the div is safe to display in our map?
32333 var safeSize = this.getSafeContentSize(realSize);
32335 var newSize = null;
32336 if (safeSize.equals(realSize)) {
32337 //real size of content is small enough to fit on the map,
32338 // so we use real size.
32339 newSize = realSize;
32343 // make a new 'size' object with the clipped dimensions
32344 // set or null if not clipped.
32346 w: (safeSize.w < realSize.w) ? safeSize.w : null,
32347 h: (safeSize.h < realSize.h) ? safeSize.h : null
32350 if (fixedSize.w && fixedSize.h) {
32351 //content is too big in both directions, so we will use
32352 // max popup size (safeSize), knowing well that it will
32353 // overflow both ways.
32354 newSize = safeSize;
32356 //content is clipped in only one direction, so we need to
32357 // run getRenderedDimensions() again with a fixed dimension
32358 var clippedSize = OpenLayers.Util.getRenderedDimensions(
32359 preparedHTML, fixedSize, {
32360 displayClass: this.contentDisplayClass,
32361 containerElement: containerElement
32365 //if the clipped size is still the same as the safeSize,
32366 // that means that our content must be fixed in the
32367 // offending direction. If overflow is 'auto', this means
32368 // we are going to have a scrollbar for sure, so we must
32369 // adjust for that.
32371 var currentOverflow = OpenLayers.Element.getStyle(
32372 this.contentDiv, "overflow"
32374 if ( (currentOverflow != "hidden") &&
32375 (clippedSize.equals(safeSize)) ) {
32376 var scrollBar = OpenLayers.Util.getScrollbarWidth();
32378 clippedSize.h += scrollBar;
32380 clippedSize.w += scrollBar;
32384 newSize = this.getSafeContentSize(clippedSize);
32387 this.setSize(newSize);
32391 * Method: setBackgroundColor
32392 * Sets the background color of the popup.
32395 * color - {String} the background color. eg "#FFBBBB"
32397 setBackgroundColor:function(color) {
32398 if (color != undefined) {
32399 this.backgroundColor = color;
32402 if (this.div != null) {
32403 this.div.style.backgroundColor = this.backgroundColor;
32408 * Method: setOpacity
32409 * Sets the opacity of the popup.
32412 * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid).
32414 setOpacity:function(opacity) {
32415 if (opacity != undefined) {
32416 this.opacity = opacity;
32419 if (this.div != null) {
32420 // for Mozilla and Safari
32421 this.div.style.opacity = this.opacity;
32424 this.div.style.filter = 'alpha(opacity=' + this.opacity*100 + ')';
32429 * Method: setBorder
32430 * Sets the border style of the popup.
32433 * border - {String} The border style value. eg 2px
32435 setBorder:function(border) {
32436 if (border != undefined) {
32437 this.border = border;
32440 if (this.div != null) {
32441 this.div.style.border = this.border;
32446 * Method: setContentHTML
32447 * Allows the user to set the HTML content of the popup.
32450 * contentHTML - {String} HTML for the div.
32452 setContentHTML:function(contentHTML) {
32454 if (contentHTML != null) {
32455 this.contentHTML = contentHTML;
32458 if ((this.contentDiv != null) &&
32459 (this.contentHTML != null) &&
32460 (this.contentHTML != this.contentDiv.innerHTML)) {
32462 this.contentDiv.innerHTML = this.contentHTML;
32464 if (this.autoSize) {
32466 //if popup has images, listen for when they finish
32467 // loading and resize accordingly
32468 this.registerImageListeners();
32470 //auto size the popup to its current contents
32478 * Method: registerImageListeners
32479 * Called when an image contained by the popup loaded. this function
32480 * updates the popup size, then unregisters the image load listener.
32482 registerImageListeners: function() {
32484 // As the images load, this function will call updateSize() to
32485 // resize the popup to fit the content div (which presumably is now
32486 // bigger than when the image was not loaded).
32488 // If the 'panMapIfOutOfView' property is set, we will pan the newly
32489 // resized popup back into view.
32491 // Note that this function, when called, will have 'popup' and
32492 // 'img' properties in the context.
32494 var onImgLoad = function() {
32495 if (this.popup.id === null) { // this.popup has been destroyed!
32498 this.popup.updateSize();
32500 if ( this.popup.visible() && this.popup.panMapIfOutOfView ) {
32501 this.popup.panIntoView();
32504 OpenLayers.Event.stopObserving(
32505 this.img, "load", this.img._onImgLoad
32510 //cycle through the images and if their size is 0x0, that means that
32511 // they haven't been loaded yet, so we attach the listener, which
32512 // will fire when the images finish loading and will resize the
32513 // popup accordingly to its new size.
32514 var images = this.contentDiv.getElementsByTagName("img");
32515 for (var i = 0, len = images.length; i < len; i++) {
32516 var img = images[i];
32517 if (img.width == 0 || img.height == 0) {
32524 //expando this function to the image itself before registering
32525 // it. This way we can easily and properly unregister it.
32526 img._onImgLoad = OpenLayers.Function.bind(onImgLoad, context);
32528 OpenLayers.Event.observe(img, 'load', img._onImgLoad);
32534 * APIMethod: getSafeContentSize
32537 * size - {<OpenLayers.Size>} Desired size to make the popup.
32540 * {<OpenLayers.Size>} A size to make the popup which is neither smaller
32541 * than the specified minimum size, nor bigger than the maximum
32542 * size (which is calculated relative to the size of the viewport).
32544 getSafeContentSize: function(size) {
32546 var safeContentSize = size.clone();
32548 // if our contentDiv has a css 'padding' set on it by a stylesheet, we
32549 // must add that to the desired "size".
32550 var contentDivPadding = this.getContentDivPadding();
32551 var wPadding = contentDivPadding.left + contentDivPadding.right;
32552 var hPadding = contentDivPadding.top + contentDivPadding.bottom;
32554 // take into account the popup's 'padding' property
32556 wPadding += this.padding.left + this.padding.right;
32557 hPadding += this.padding.top + this.padding.bottom;
32559 if (this.closeDiv) {
32560 var closeDivWidth = parseInt(this.closeDiv.style.width);
32561 wPadding += closeDivWidth + contentDivPadding.right;
32564 // prevent the popup from being smaller than a specified minimal size
32565 if (this.minSize) {
32566 safeContentSize.w = Math.max(safeContentSize.w,
32567 (this.minSize.w - wPadding));
32568 safeContentSize.h = Math.max(safeContentSize.h,
32569 (this.minSize.h - hPadding));
32572 // prevent the popup from being bigger than a specified maximum size
32573 if (this.maxSize) {
32574 safeContentSize.w = Math.min(safeContentSize.w,
32575 (this.maxSize.w - wPadding));
32576 safeContentSize.h = Math.min(safeContentSize.h,
32577 (this.maxSize.h - hPadding));
32580 //make sure the desired size to set doesn't result in a popup that
32581 // is bigger than the map's viewport.
32583 if (this.map && this.map.size) {
32585 var extraX = 0, extraY = 0;
32586 if (this.keepInMap && !this.panMapIfOutOfView) {
32587 var px = this.map.getPixelFromLonLat(this.lonlat);
32588 switch (this.relativePosition) {
32591 extraY = this.map.size.h - px.y;
32594 extraX = this.map.size.w - px.x;
32595 extraY = this.map.size.h - px.y;
32598 extraX = this.map.size.w - px.x;
32607 extraY = this.map.size.h - px.y;
32612 var maxY = this.map.size.h -
32613 this.map.paddingForPopups.top -
32614 this.map.paddingForPopups.bottom -
32617 var maxX = this.map.size.w -
32618 this.map.paddingForPopups.left -
32619 this.map.paddingForPopups.right -
32622 safeContentSize.w = Math.min(safeContentSize.w, maxX);
32623 safeContentSize.h = Math.min(safeContentSize.h, maxY);
32626 return safeContentSize;
32630 * Method: getContentDivPadding
32631 * Glorious, oh glorious hack in order to determine the css 'padding' of
32632 * the contentDiv. IE/Opera return null here unless we actually add the
32633 * popup's main 'div' element (which contains contentDiv) to the DOM.
32634 * So we make it invisible and then add it to the document temporarily.
32636 * Once we've taken the padding readings we need, we then remove it
32637 * from the DOM (it will actually get added to the DOM in
32638 * Map.js's addPopup)
32641 * {<OpenLayers.Bounds>}
32643 getContentDivPadding: function() {
32645 //use cached value if we have it
32646 var contentDivPadding = this._contentDivPadding;
32647 if (!contentDivPadding) {
32649 if (this.div.parentNode == null) {
32650 //make the div invisible and add it to the page
32651 this.div.style.display = "none";
32652 document.body.appendChild(this.div);
32655 //read the padding settings from css, put them in an OL.Bounds
32656 contentDivPadding = new OpenLayers.Bounds(
32657 OpenLayers.Element.getStyle(this.contentDiv, "padding-left"),
32658 OpenLayers.Element.getStyle(this.contentDiv, "padding-bottom"),
32659 OpenLayers.Element.getStyle(this.contentDiv, "padding-right"),
32660 OpenLayers.Element.getStyle(this.contentDiv, "padding-top")
32664 this._contentDivPadding = contentDivPadding;
32666 if (this.div.parentNode == document.body) {
32667 //remove the div from the page and make it visible again
32668 document.body.removeChild(this.div);
32669 this.div.style.display = "";
32672 return contentDivPadding;
32676 * Method: addCloseBox
32679 * callback - {Function} The callback to be called when the close button
32682 addCloseBox: function(callback) {
32684 this.closeDiv = OpenLayers.Util.createDiv(
32685 this.id + "_close", null, {w: 17, h: 17}
32687 this.closeDiv.className = "olPopupCloseBox";
32689 // use the content div's css padding to determine if we should
32690 // padd the close div
32691 var contentDivPadding = this.getContentDivPadding();
32693 this.closeDiv.style.right = contentDivPadding.right + "px";
32694 this.closeDiv.style.top = contentDivPadding.top + "px";
32695 this.groupDiv.appendChild(this.closeDiv);
32697 var closePopup = callback || function(e) {
32699 OpenLayers.Event.stop(e);
32701 OpenLayers.Event.observe(this.closeDiv, "touchend",
32702 OpenLayers.Function.bindAsEventListener(closePopup, this));
32703 OpenLayers.Event.observe(this.closeDiv, "click",
32704 OpenLayers.Function.bindAsEventListener(closePopup, this));
32708 * Method: panIntoView
32709 * Pans the map such that the popup is totaly viewable (if necessary)
32711 panIntoView: function() {
32713 var mapSize = this.map.getSize();
32715 //start with the top left corner of the popup, in px,
32716 // relative to the viewport
32717 var origTL = this.map.getViewPortPxFromLayerPx( new OpenLayers.Pixel(
32718 parseInt(this.div.style.left),
32719 parseInt(this.div.style.top)
32721 var newTL = origTL.clone();
32723 //new left (compare to margins, using this.size to calculate right)
32724 if (origTL.x < this.map.paddingForPopups.left) {
32725 newTL.x = this.map.paddingForPopups.left;
32727 if ( (origTL.x + this.size.w) > (mapSize.w - this.map.paddingForPopups.right)) {
32728 newTL.x = mapSize.w - this.map.paddingForPopups.right - this.size.w;
32731 //new top (compare to margins, using this.size to calculate bottom)
32732 if (origTL.y < this.map.paddingForPopups.top) {
32733 newTL.y = this.map.paddingForPopups.top;
32735 if ( (origTL.y + this.size.h) > (mapSize.h - this.map.paddingForPopups.bottom)) {
32736 newTL.y = mapSize.h - this.map.paddingForPopups.bottom - this.size.h;
32739 var dx = origTL.x - newTL.x;
32740 var dy = origTL.y - newTL.y;
32742 this.map.pan(dx, dy);
32746 * Method: registerEvents
32747 * Registers events on the popup.
32749 * Do this in a separate function so that subclasses can
32750 * choose to override it if they wish to deal differently
32751 * with mouse events
32753 * Note in the following handler functions that some special
32754 * care is needed to deal correctly with mousing and popups.
32756 * Because the user might select the zoom-rectangle option and
32757 * then drag it over a popup, we need a safe way to allow the
32758 * mousemove and mouseup events to pass through the popup when
32759 * they are initiated from outside. The same procedure is needed for
32760 * touchmove and touchend events.
32762 * Otherwise, we want to essentially kill the event propagation
32763 * for all other events, though we have to do so carefully,
32764 * without disabling basic html functionality, like clicking on
32765 * hyperlinks or drag-selecting text.
32767 registerEvents:function() {
32768 this.events = new OpenLayers.Events(this, this.div, null, true);
32770 function onTouchstart(evt) {
32771 OpenLayers.Event.stop(evt, true);
32774 "mousedown": this.onmousedown,
32775 "mousemove": this.onmousemove,
32776 "mouseup": this.onmouseup,
32777 "click": this.onclick,
32778 "mouseout": this.onmouseout,
32779 "dblclick": this.ondblclick,
32780 "touchstart": onTouchstart,
32787 * Method: onmousedown
32788 * When mouse goes down within the popup, make a note of
32789 * it locally, and then do not propagate the mousedown
32790 * (but do so safely so that user can select text inside)
32795 onmousedown: function (evt) {
32796 this.mousedown = true;
32797 OpenLayers.Event.stop(evt, true);
32801 * Method: onmousemove
32802 * If the drag was started within the popup, then
32803 * do not propagate the mousemove (but do so safely
32804 * so that user can select text inside)
32809 onmousemove: function (evt) {
32810 if (this.mousedown) {
32811 OpenLayers.Event.stop(evt, true);
32816 * Method: onmouseup
32817 * When mouse comes up within the popup, after going down
32818 * in it, reset the flag, and then (once again) do not
32819 * propagate the event, but do so safely so that user can
32820 * select text inside
32825 onmouseup: function (evt) {
32826 if (this.mousedown) {
32827 this.mousedown = false;
32828 OpenLayers.Event.stop(evt, true);
32834 * Ignore clicks, but allowing default browser handling
32839 onclick: function (evt) {
32840 OpenLayers.Event.stop(evt, true);
32844 * Method: onmouseout
32845 * When mouse goes out of the popup set the flag to false so that
32846 * if they let go and then drag back in, we won't be confused.
32851 onmouseout: function (evt) {
32852 this.mousedown = false;
32856 * Method: ondblclick
32857 * Ignore double-clicks, but allowing default browser handling
32862 ondblclick: function (evt) {
32863 OpenLayers.Event.stop(evt, true);
32866 CLASS_NAME: "OpenLayers.Popup"
32869 OpenLayers.Popup.WIDTH = 200;
32870 OpenLayers.Popup.HEIGHT = 200;
32871 OpenLayers.Popup.COLOR = "white";
32872 OpenLayers.Popup.OPACITY = 1;
32873 OpenLayers.Popup.BORDER = "0px";
32874 /* ======================================================================
32875 OpenLayers/Popup/Anchored.js
32876 ====================================================================== */
32878 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
32879 * full list of contributors). Published under the 2-clause BSD license.
32880 * See license.txt in the OpenLayers distribution or repository for the
32881 * full text of the license. */
32885 * @requires OpenLayers/Popup.js
32889 * Class: OpenLayers.Popup.Anchored
32892 * - <OpenLayers.Popup>
32894 OpenLayers.Popup.Anchored =
32895 OpenLayers.Class(OpenLayers.Popup, {
32898 * Property: relativePosition
32899 * {String} Relative position of the popup ("br", "tr", "tl" or "bl").
32901 relativePosition: null,
32904 * APIProperty: keepInMap
32905 * {Boolean} If panMapIfOutOfView is false, and this property is true,
32906 * contrain the popup such that it always fits in the available map
32907 * space. By default, this is set. If you are creating popups that are
32908 * near map edges and not allowing pannning, and especially if you have
32909 * a popup which has a fixedRelativePosition, setting this to false may
32910 * be a smart thing to do.
32912 * For anchored popups, default is true, since subclasses will
32913 * usually want this functionality.
32919 * {Object} Object to which we'll anchor the popup. Must expose a
32920 * 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>).
32925 * Constructor: OpenLayers.Popup.Anchored
32929 * lonlat - {<OpenLayers.LonLat>}
32930 * contentSize - {<OpenLayers.Size>}
32931 * contentHTML - {String}
32932 * anchor - {Object} Object which must expose a 'size' <OpenLayers.Size>
32933 * and 'offset' <OpenLayers.Pixel> (generally an <OpenLayers.Icon>).
32934 * closeBox - {Boolean}
32935 * closeBoxCallback - {Function} Function to be called on closeBox click.
32937 initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
32938 closeBoxCallback) {
32939 var newArguments = [
32940 id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback
32942 OpenLayers.Popup.prototype.initialize.apply(this, newArguments);
32944 this.anchor = (anchor != null) ? anchor
32945 : { size: new OpenLayers.Size(0,0),
32946 offset: new OpenLayers.Pixel(0,0)};
32950 * APIMethod: destroy
32952 destroy: function() {
32953 this.anchor = null;
32954 this.relativePosition = null;
32956 OpenLayers.Popup.prototype.destroy.apply(this, arguments);
32961 * Overridden from Popup since user might hide popup and then show() it
32962 * in a new location (meaning we might want to update the relative
32963 * position on the show)
32966 this.updatePosition();
32967 OpenLayers.Popup.prototype.show.apply(this, arguments);
32972 * Since the popup is moving to a new px, it might need also to be moved
32973 * relative to where the marker is. We first calculate the new
32974 * relativePosition, and then we calculate the new px where we will
32975 * put the popup, based on the new relative position.
32977 * If the relativePosition has changed, we must also call
32978 * updateRelativePosition() to make any visual changes to the popup
32979 * which are associated with putting it in a new relativePosition.
32982 * px - {<OpenLayers.Pixel>}
32984 moveTo: function(px) {
32985 var oldRelativePosition = this.relativePosition;
32986 this.relativePosition = this.calculateRelativePosition(px);
32988 OpenLayers.Popup.prototype.moveTo.call(this, this.calculateNewPx(px));
32990 //if this move has caused the popup to change its relative position,
32991 // we need to make the appropriate cosmetic changes.
32992 if (this.relativePosition != oldRelativePosition) {
32993 this.updateRelativePosition();
32998 * APIMethod: setSize
33001 * contentSize - {<OpenLayers.Size>} the new size for the popup's
33002 * contents div (in pixels).
33004 setSize:function(contentSize) {
33005 OpenLayers.Popup.prototype.setSize.apply(this, arguments);
33007 if ((this.lonlat) && (this.map)) {
33008 var px = this.map.getLayerPxFromLonLat(this.lonlat);
33014 * Method: calculateRelativePosition
33017 * px - {<OpenLayers.Pixel>}
33020 * {String} The relative position ("br" "tr" "tl" "bl") at which the popup
33021 * should be placed.
33023 calculateRelativePosition:function(px) {
33024 var lonlat = this.map.getLonLatFromLayerPx(px);
33026 var extent = this.map.getExtent();
33027 var quadrant = extent.determineQuadrant(lonlat);
33029 return OpenLayers.Bounds.oppositeQuadrant(quadrant);
33033 * Method: updateRelativePosition
33034 * The popup has been moved to a new relative location, so we may want to
33035 * make some cosmetic adjustments to it.
33037 * Note that in the classic Anchored popup, there is nothing to do
33038 * here, since the popup looks exactly the same in all four positions.
33039 * Subclasses such as Framed, however, will want to do something
33042 updateRelativePosition: function() {
33043 //to be overridden by subclasses
33047 * Method: calculateNewPx
33050 * px - {<OpenLayers.Pixel>}
33053 * {<OpenLayers.Pixel>} The the new px position of the popup on the screen
33054 * relative to the passed-in px.
33056 calculateNewPx:function(px) {
33057 var newPx = px.offset(this.anchor.offset);
33059 //use contentSize if size is not already set
33060 var size = this.size || this.contentSize;
33062 var top = (this.relativePosition.charAt(0) == 't');
33063 newPx.y += (top) ? -size.h : this.anchor.size.h;
33065 var left = (this.relativePosition.charAt(1) == 'l');
33066 newPx.x += (left) ? -size.w : this.anchor.size.w;
33071 CLASS_NAME: "OpenLayers.Popup.Anchored"
33073 /* ======================================================================
33074 OpenLayers/Popup/Framed.js
33075 ====================================================================== */
33077 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
33078 * full list of contributors). Published under the 2-clause BSD license.
33079 * See license.txt in the OpenLayers distribution or repository for the
33080 * full text of the license. */
33083 * @requires OpenLayers/Popup/Anchored.js
33087 * Class: OpenLayers.Popup.Framed
33090 * - <OpenLayers.Popup.Anchored>
33092 OpenLayers.Popup.Framed =
33093 OpenLayers.Class(OpenLayers.Popup.Anchored, {
33096 * Property: imageSrc
33097 * {String} location of the image to be used as the popup frame
33102 * Property: imageSize
33103 * {<OpenLayers.Size>} Size (measured in pixels) of the image located
33104 * by the 'imageSrc' property.
33109 * APIProperty: isAlphaImage
33110 * {Boolean} The image has some alpha and thus needs to use the alpha
33111 * image hack. Note that setting this to true will have no noticeable
33112 * effect in FF or IE7 browsers, but will all but crush the ie6
33114 * Default is false.
33116 isAlphaImage: false,
33119 * Property: positionBlocks
33120 * {Object} Hash of different position blocks (Object/Hashs). Each block
33121 * will be keyed by a two-character 'relativePosition'
33122 * code string (ie "tl", "tr", "bl", "br"). Block properties are
33123 * 'offset', 'padding' (self-explanatory), and finally the 'blocks'
33124 * parameter, which is an array of the block objects.
33126 * Each block object must have 'size', 'anchor', and 'position'
33129 * Note that positionBlocks should never be modified at runtime.
33131 positionBlocks: null,
33135 * {Array[Object]} Array of objects, each of which is one "block" of the
33136 * popup. Each block has a 'div' and an 'image' property, both of
33137 * which are DOMElements, and the latter of which is appended to the
33138 * former. These are reused as the popup goes changing positions for
33139 * great economy and elegance.
33144 * APIProperty: fixedRelativePosition
33145 * {Boolean} We want the framed popup to work dynamically placed relative
33146 * to its anchor but also in just one fixed position. A well designed
33147 * framed popup will have the pixels and logic to display itself in
33148 * any of the four relative positions, but (understandably), this will
33149 * not be the case for all of them. By setting this property to 'true',
33150 * framed popup will not recalculate for the best placement each time
33151 * it's open, but will always open the same way.
33152 * Note that if this is set to true, it is generally advisable to also
33153 * set the 'panIntoView' property to true so that the popup can be
33154 * scrolled into view (since it will often be offscreen on open)
33155 * Default is false.
33157 fixedRelativePosition: false,
33160 * Constructor: OpenLayers.Popup.Framed
33164 * lonlat - {<OpenLayers.LonLat>}
33165 * contentSize - {<OpenLayers.Size>}
33166 * contentHTML - {String}
33167 * anchor - {Object} Object to which we'll anchor the popup. Must expose
33168 * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>)
33169 * (Note that this is generally an <OpenLayers.Icon>).
33170 * closeBox - {Boolean}
33171 * closeBoxCallback - {Function} Function to be called on closeBox click.
33173 initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
33174 closeBoxCallback) {
33176 OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments);
33178 if (this.fixedRelativePosition) {
33179 //based on our decided relativePostion, set the current padding
33180 // this keeps us from getting into trouble
33181 this.updateRelativePosition();
33183 //make calculateRelativePosition always return the specified
33185 this.calculateRelativePosition = function(px) {
33186 return this.relativePosition;
33190 this.contentDiv.style.position = "absolute";
33191 this.contentDiv.style.zIndex = 1;
33194 this.closeDiv.style.zIndex = 1;
33197 this.groupDiv.style.position = "absolute";
33198 this.groupDiv.style.top = "0px";
33199 this.groupDiv.style.left = "0px";
33200 this.groupDiv.style.height = "100%";
33201 this.groupDiv.style.width = "100%";
33205 * APIMethod: destroy
33207 destroy: function() {
33208 this.imageSrc = null;
33209 this.imageSize = null;
33210 this.isAlphaImage = null;
33212 this.fixedRelativePosition = false;
33213 this.positionBlocks = null;
33215 //remove our blocks
33216 for(var i = 0; i < this.blocks.length; i++) {
33217 var block = this.blocks[i];
33220 block.div.removeChild(block.image);
33222 block.image = null;
33225 this.groupDiv.removeChild(block.div);
33229 this.blocks = null;
33231 OpenLayers.Popup.Anchored.prototype.destroy.apply(this, arguments);
33235 * APIMethod: setBackgroundColor
33237 setBackgroundColor:function(color) {
33238 //does nothing since the framed popup's entire scheme is based on a
33239 // an image -- changing the background color makes no sense.
33243 * APIMethod: setBorder
33245 setBorder:function() {
33246 //does nothing since the framed popup's entire scheme is based on a
33247 // an image -- changing the popup's border makes no sense.
33251 * Method: setOpacity
33252 * Sets the opacity of the popup.
33255 * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid).
33257 setOpacity:function(opacity) {
33258 //does nothing since we suppose that we'll never apply an opacity
33259 // to a framed popup
33263 * APIMethod: setSize
33264 * Overridden here, because we need to update the blocks whenever the size
33265 * of the popup has changed.
33268 * contentSize - {<OpenLayers.Size>} the new size for the popup's
33269 * contents div (in pixels).
33271 setSize:function(contentSize) {
33272 OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments);
33274 this.updateBlocks();
33278 * Method: updateRelativePosition
33279 * When the relative position changes, we need to set the new padding
33280 * BBOX on the popup, reposition the close div, and update the blocks.
33282 updateRelativePosition: function() {
33284 //update the padding
33285 this.padding = this.positionBlocks[this.relativePosition].padding;
33287 //update the position of our close box to new padding
33288 if (this.closeDiv) {
33289 // use the content div's css padding to determine if we should
33290 // padd the close div
33291 var contentDivPadding = this.getContentDivPadding();
33293 this.closeDiv.style.right = contentDivPadding.right +
33294 this.padding.right + "px";
33295 this.closeDiv.style.top = contentDivPadding.top +
33296 this.padding.top + "px";
33299 this.updateBlocks();
33303 * Method: calculateNewPx
33304 * Besides the standard offset as determined by the Anchored class, our
33305 * Framed popups have a special 'offset' property for each of their
33306 * positions, which is used to offset the popup relative to its anchor.
33309 * px - {<OpenLayers.Pixel>}
33312 * {<OpenLayers.Pixel>} The the new px position of the popup on the screen
33313 * relative to the passed-in px.
33315 calculateNewPx:function(px) {
33316 var newPx = OpenLayers.Popup.Anchored.prototype.calculateNewPx.apply(
33320 newPx = newPx.offset(this.positionBlocks[this.relativePosition].offset);
33326 * Method: createBlocks
33328 createBlocks: function() {
33331 //since all positions contain the same number of blocks, we can
33332 // just pick the first position and use its blocks array to create
33333 // our blocks array
33334 var firstPosition = null;
33335 for(var key in this.positionBlocks) {
33336 firstPosition = key;
33340 var position = this.positionBlocks[firstPosition];
33341 for (var i = 0; i < position.blocks.length; i++) {
33344 this.blocks.push(block);
33346 var divId = this.id + '_FrameDecorationDiv_' + i;
33347 block.div = OpenLayers.Util.createDiv(divId,
33348 null, null, null, "absolute", null, "hidden", null
33351 var imgId = this.id + '_FrameDecorationImg_' + i;
33353 (this.isAlphaImage) ? OpenLayers.Util.createAlphaImageDiv
33354 : OpenLayers.Util.createImage;
33356 block.image = imageCreator(imgId,
33357 null, this.imageSize, this.imageSrc,
33358 "absolute", null, null, null
33361 block.div.appendChild(block.image);
33362 this.groupDiv.appendChild(block.div);
33367 * Method: updateBlocks
33368 * Internal method, called on initialize and when the popup's relative
33369 * position has changed. This function takes care of re-positioning
33370 * the popup's blocks in their appropropriate places.
33372 updateBlocks: function() {
33373 if (!this.blocks) {
33374 this.createBlocks();
33377 if (this.size && this.relativePosition) {
33378 var position = this.positionBlocks[this.relativePosition];
33379 for (var i = 0; i < position.blocks.length; i++) {
33381 var positionBlock = position.blocks[i];
33382 var block = this.blocks[i];
33385 var l = positionBlock.anchor.left;
33386 var b = positionBlock.anchor.bottom;
33387 var r = positionBlock.anchor.right;
33388 var t = positionBlock.anchor.top;
33390 //note that we use the isNaN() test here because if the
33391 // size object is initialized with a "auto" parameter, the
33392 // size constructor calls parseFloat() on the string,
33393 // which will turn it into NaN
33395 var w = (isNaN(positionBlock.size.w)) ? this.size.w - (r + l)
33396 : positionBlock.size.w;
33398 var h = (isNaN(positionBlock.size.h)) ? this.size.h - (b + t)
33399 : positionBlock.size.h;
33401 block.div.style.width = (w < 0 ? 0 : w) + 'px';
33402 block.div.style.height = (h < 0 ? 0 : h) + 'px';
33404 block.div.style.left = (l != null) ? l + 'px' : '';
33405 block.div.style.bottom = (b != null) ? b + 'px' : '';
33406 block.div.style.right = (r != null) ? r + 'px' : '';
33407 block.div.style.top = (t != null) ? t + 'px' : '';
33409 block.image.style.left = positionBlock.position.x + 'px';
33410 block.image.style.top = positionBlock.position.y + 'px';
33413 this.contentDiv.style.left = this.padding.left + "px";
33414 this.contentDiv.style.top = this.padding.top + "px";
33418 CLASS_NAME: "OpenLayers.Popup.Framed"
33420 /* ======================================================================
33421 OpenLayers/Popup/FramedCloud.js
33422 ====================================================================== */
33424 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
33425 * full list of contributors). Published under the 2-clause BSD license.
33426 * See license.txt in the OpenLayers distribution or repository for the
33427 * full text of the license. */
33430 * @requires OpenLayers/Popup/Framed.js
33431 * @requires OpenLayers/Util.js
33432 * @requires OpenLayers/BaseTypes/Bounds.js
33433 * @requires OpenLayers/BaseTypes/Pixel.js
33434 * @requires OpenLayers/BaseTypes/Size.js
33438 * Class: OpenLayers.Popup.FramedCloud
33441 * - <OpenLayers.Popup.Framed>
33443 OpenLayers.Popup.FramedCloud =
33444 OpenLayers.Class(OpenLayers.Popup.Framed, {
33447 * Property: contentDisplayClass
33448 * {String} The CSS class of the popup content div.
33450 contentDisplayClass: "olFramedCloudPopupContent",
33453 * APIProperty: autoSize
33454 * {Boolean} Framed Cloud is autosizing by default.
33459 * APIProperty: panMapIfOutOfView
33460 * {Boolean} Framed Cloud does pan into view by default.
33462 panMapIfOutOfView: true,
33465 * APIProperty: imageSize
33466 * {<OpenLayers.Size>}
33468 imageSize: new OpenLayers.Size(1276, 736),
33471 * APIProperty: isAlphaImage
33472 * {Boolean} The FramedCloud does not use an alpha image (in honor of the
33473 * good ie6 folk out there)
33475 isAlphaImage: false,
33478 * APIProperty: fixedRelativePosition
33479 * {Boolean} The Framed Cloud popup works in just one fixed position.
33481 fixedRelativePosition: false,
33484 * Property: positionBlocks
33485 * {Object} Hash of differen position blocks, keyed by relativePosition
33486 * two-character code string (ie "tl", "tr", "bl", "br")
33490 'offset': new OpenLayers.Pixel(44, 0),
33491 'padding': new OpenLayers.Bounds(8, 40, 8, 9),
33494 size: new OpenLayers.Size('auto', 'auto'),
33495 anchor: new OpenLayers.Bounds(0, 51, 22, 0),
33496 position: new OpenLayers.Pixel(0, 0)
33499 size: new OpenLayers.Size(22, 'auto'),
33500 anchor: new OpenLayers.Bounds(null, 50, 0, 0),
33501 position: new OpenLayers.Pixel(-1238, 0)
33504 size: new OpenLayers.Size('auto', 19),
33505 anchor: new OpenLayers.Bounds(0, 32, 22, null),
33506 position: new OpenLayers.Pixel(0, -631)
33509 size: new OpenLayers.Size(22, 18),
33510 anchor: new OpenLayers.Bounds(null, 32, 0, null),
33511 position: new OpenLayers.Pixel(-1238, -632)
33514 size: new OpenLayers.Size(81, 35),
33515 anchor: new OpenLayers.Bounds(null, 0, 0, null),
33516 position: new OpenLayers.Pixel(0, -688)
33521 'offset': new OpenLayers.Pixel(-45, 0),
33522 'padding': new OpenLayers.Bounds(8, 40, 8, 9),
33525 size: new OpenLayers.Size('auto', 'auto'),
33526 anchor: new OpenLayers.Bounds(0, 51, 22, 0),
33527 position: new OpenLayers.Pixel(0, 0)
33530 size: new OpenLayers.Size(22, 'auto'),
33531 anchor: new OpenLayers.Bounds(null, 50, 0, 0),
33532 position: new OpenLayers.Pixel(-1238, 0)
33535 size: new OpenLayers.Size('auto', 19),
33536 anchor: new OpenLayers.Bounds(0, 32, 22, null),
33537 position: new OpenLayers.Pixel(0, -631)
33540 size: new OpenLayers.Size(22, 19),
33541 anchor: new OpenLayers.Bounds(null, 32, 0, null),
33542 position: new OpenLayers.Pixel(-1238, -631)
33545 size: new OpenLayers.Size(81, 35),
33546 anchor: new OpenLayers.Bounds(0, 0, null, null),
33547 position: new OpenLayers.Pixel(-215, -687)
33552 'offset': new OpenLayers.Pixel(45, 0),
33553 'padding': new OpenLayers.Bounds(8, 9, 8, 40),
33556 size: new OpenLayers.Size('auto', 'auto'),
33557 anchor: new OpenLayers.Bounds(0, 21, 22, 32),
33558 position: new OpenLayers.Pixel(0, 0)
33561 size: new OpenLayers.Size(22, 'auto'),
33562 anchor: new OpenLayers.Bounds(null, 21, 0, 32),
33563 position: new OpenLayers.Pixel(-1238, 0)
33566 size: new OpenLayers.Size('auto', 21),
33567 anchor: new OpenLayers.Bounds(0, 0, 22, null),
33568 position: new OpenLayers.Pixel(0, -629)
33571 size: new OpenLayers.Size(22, 21),
33572 anchor: new OpenLayers.Bounds(null, 0, 0, null),
33573 position: new OpenLayers.Pixel(-1238, -629)
33576 size: new OpenLayers.Size(81, 33),
33577 anchor: new OpenLayers.Bounds(null, null, 0, 0),
33578 position: new OpenLayers.Pixel(-101, -674)
33583 'offset': new OpenLayers.Pixel(-44, 0),
33584 'padding': new OpenLayers.Bounds(8, 9, 8, 40),
33587 size: new OpenLayers.Size('auto', 'auto'),
33588 anchor: new OpenLayers.Bounds(0, 21, 22, 32),
33589 position: new OpenLayers.Pixel(0, 0)
33592 size: new OpenLayers.Size(22, 'auto'),
33593 anchor: new OpenLayers.Bounds(null, 21, 0, 32),
33594 position: new OpenLayers.Pixel(-1238, 0)
33597 size: new OpenLayers.Size('auto', 21),
33598 anchor: new OpenLayers.Bounds(0, 0, 22, null),
33599 position: new OpenLayers.Pixel(0, -629)
33602 size: new OpenLayers.Size(22, 21),
33603 anchor: new OpenLayers.Bounds(null, 0, 0, null),
33604 position: new OpenLayers.Pixel(-1238, -629)
33607 size: new OpenLayers.Size(81, 33),
33608 anchor: new OpenLayers.Bounds(0, null, null, 0),
33609 position: new OpenLayers.Pixel(-311, -674)
33616 * APIProperty: minSize
33617 * {<OpenLayers.Size>}
33619 minSize: new OpenLayers.Size(105, 10),
33622 * APIProperty: maxSize
33623 * {<OpenLayers.Size>}
33625 maxSize: new OpenLayers.Size(1200, 660),
33628 * Constructor: OpenLayers.Popup.FramedCloud
33632 * lonlat - {<OpenLayers.LonLat>}
33633 * contentSize - {<OpenLayers.Size>}
33634 * contentHTML - {String}
33635 * anchor - {Object} Object to which we'll anchor the popup. Must expose
33636 * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>)
33637 * (Note that this is generally an <OpenLayers.Icon>).
33638 * closeBox - {Boolean}
33639 * closeBoxCallback - {Function} Function to be called on closeBox click.
33641 initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
33642 closeBoxCallback) {
33644 this.imageSrc = OpenLayers.Util.getImageLocation('cloud-popup-relative.png');
33645 OpenLayers.Popup.Framed.prototype.initialize.apply(this, arguments);
33646 this.contentDiv.className = this.contentDisplayClass;
33649 CLASS_NAME: "OpenLayers.Popup.FramedCloud"
33651 /* ======================================================================
33652 OpenLayers/Renderer/Canvas.js
33653 ====================================================================== */
33655 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
33656 * full list of contributors). Published under the 2-clause BSD license.
33657 * See license.txt in the OpenLayers distribution or repository for the
33658 * full text of the license. */
33661 * @requires OpenLayers/Renderer.js
33665 * Class: OpenLayers.Renderer.Canvas
33666 * A renderer based on the 2D 'canvas' drawing element.
33669 * - <OpenLayers.Renderer>
33671 OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {
33674 * APIProperty: hitDetection
33675 * {Boolean} Allow for hit detection of features. Default is true.
33677 hitDetection: true,
33680 * Property: hitOverflow
33681 * {Number} The method for converting feature identifiers to color values
33682 * supports 16777215 sequential values. Two features cannot be
33683 * predictably detected if their identifiers differ by more than this
33684 * value. The hitOverflow allows for bigger numbers (but the
33685 * difference in values is still limited).
33691 * {Canvas} The canvas context object.
33696 * Property: features
33697 * {Object} Internal object of feature/style pairs for use in redrawing the layer.
33702 * Property: pendingRedraw
33703 * {Boolean} The renderer needs a redraw call to render features added while
33704 * the renderer was locked.
33706 pendingRedraw: false,
33709 * Property: cachedSymbolBounds
33710 * {Object} Internal cache of calculated symbol extents.
33712 cachedSymbolBounds: {},
33715 * Constructor: OpenLayers.Renderer.Canvas
33718 * containerID - {<String>}
33719 * options - {Object} Optional properties to be set on the renderer.
33721 initialize: function(containerID, options) {
33722 OpenLayers.Renderer.prototype.initialize.apply(this, arguments);
33723 this.root = document.createElement("canvas");
33724 this.container.appendChild(this.root);
33725 this.canvas = this.root.getContext("2d");
33726 this._clearRectId = OpenLayers.Util.createUniqueID();
33727 this.features = {};
33728 if (this.hitDetection) {
33729 this.hitCanvas = document.createElement("canvas");
33730 this.hitContext = this.hitCanvas.getContext("2d");
33735 * Method: setExtent
33736 * Set the visible part of the layer.
33739 * extent - {<OpenLayers.Bounds>}
33740 * resolutionChanged - {Boolean}
33743 * {Boolean} true to notify the layer that the new extent does not exceed
33744 * the coordinate range, and the features will not need to be redrawn.
33747 setExtent: function() {
33748 OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);
33749 // always redraw features
33754 * Method: eraseGeometry
33755 * Erase a geometry from the renderer. Because the Canvas renderer has
33756 * 'memory' of the features that it has drawn, we have to remove the
33757 * feature so it doesn't redraw.
33760 * geometry - {<OpenLayers.Geometry>}
33761 * featureId - {String}
33763 eraseGeometry: function(geometry, featureId) {
33764 this.eraseFeatures(this.features[featureId][0]);
33768 * APIMethod: supported
33771 * {Boolean} Whether or not the browser supports the renderer class
33773 supported: function() {
33774 return OpenLayers.CANVAS_SUPPORTED;
33779 * Sets the size of the drawing surface.
33781 * Once the size is updated, redraw the canvas.
33784 * size - {<OpenLayers.Size>}
33786 setSize: function(size) {
33787 this.size = size.clone();
33788 var root = this.root;
33789 root.style.width = size.w + "px";
33790 root.style.height = size.h + "px";
33791 root.width = size.w;
33792 root.height = size.h;
33793 this.resolution = null;
33794 if (this.hitDetection) {
33795 var hitCanvas = this.hitCanvas;
33796 hitCanvas.style.width = size.w + "px";
33797 hitCanvas.style.height = size.h + "px";
33798 hitCanvas.width = size.w;
33799 hitCanvas.height = size.h;
33804 * Method: drawFeature
33805 * Draw the feature. Stores the feature in the features list,
33806 * then redraws the layer.
33809 * feature - {<OpenLayers.Feature.Vector>}
33810 * style - {<Object>}
33813 * {Boolean} The feature has been drawn completely. If the feature has no
33814 * geometry, undefined will be returned. If the feature is not rendered
33815 * for other reasons, false will be returned.
33817 drawFeature: function(feature, style) {
33819 if (feature.geometry) {
33820 style = this.applyDefaultSymbolizer(style || feature.style);
33821 // don't render if display none or feature outside extent
33822 var bounds = feature.geometry.getBounds();
33825 if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
33826 worldBounds = this.map.getMaxExtent();
33829 var intersects = bounds && bounds.intersectsBounds(this.extent, {worldBounds: worldBounds});
33831 rendered = (style.display !== "none") && !!bounds && intersects;
33833 // keep track of what we have rendered for redraw
33834 this.features[feature.id] = [feature, style];
33837 // remove from features tracked for redraw
33838 delete(this.features[feature.id]);
33840 this.pendingRedraw = true;
33842 if (this.pendingRedraw && !this.locked) {
33844 this.pendingRedraw = false;
33850 * Method: drawGeometry
33851 * Used when looping (in redraw) over the features; draws
33855 * geometry - {<OpenLayers.Geometry>}
33858 drawGeometry: function(geometry, style, featureId) {
33859 var className = geometry.CLASS_NAME;
33860 if ((className == "OpenLayers.Geometry.Collection") ||
33861 (className == "OpenLayers.Geometry.MultiPoint") ||
33862 (className == "OpenLayers.Geometry.MultiLineString") ||
33863 (className == "OpenLayers.Geometry.MultiPolygon")) {
33864 var worldBounds = (this.map.baseLayer && this.map.baseLayer.wrapDateLine) && this.map.getMaxExtent();
33865 for (var i = 0; i < geometry.components.length; i++) {
33866 this.calculateFeatureDx(geometry.components[i].getBounds(), worldBounds);
33867 this.drawGeometry(geometry.components[i], style, featureId);
33871 switch (geometry.CLASS_NAME) {
33872 case "OpenLayers.Geometry.Point":
33873 this.drawPoint(geometry, style, featureId);
33875 case "OpenLayers.Geometry.LineString":
33876 this.drawLineString(geometry, style, featureId);
33878 case "OpenLayers.Geometry.LinearRing":
33879 this.drawLinearRing(geometry, style, featureId);
33881 case "OpenLayers.Geometry.Polygon":
33882 this.drawPolygon(geometry, style, featureId);
33890 * Method: drawExternalGraphic
33891 * Called to draw External graphics.
33894 * geometry - {<OpenLayers.Geometry>}
33896 * featureId - {String}
33898 drawExternalGraphic: function(geometry, style, featureId) {
33899 var img = new Image();
33901 var title = style.title || style.graphicTitle;
33906 var width = style.graphicWidth || style.graphicHeight;
33907 var height = style.graphicHeight || style.graphicWidth;
33908 width = width ? width : style.pointRadius * 2;
33909 height = height ? height : style.pointRadius * 2;
33910 var xOffset = (style.graphicXOffset != undefined) ?
33911 style.graphicXOffset : -(0.5 * width);
33912 var yOffset = (style.graphicYOffset != undefined) ?
33913 style.graphicYOffset : -(0.5 * height);
33914 var _clearRectId = this._clearRectId;
33916 var opacity = style.graphicOpacity || style.fillOpacity;
33918 var onLoad = function() {
33919 if(!this.features[featureId] ||
33920 _clearRectId !== this._clearRectId) {
33923 var pt = this.getLocalXY(geometry);
33926 if(!isNaN(p0) && !isNaN(p1)) {
33927 var x = (p0 + xOffset) | 0;
33928 var y = (p1 + yOffset) | 0;
33929 var canvas = this.canvas;
33930 canvas.globalAlpha = opacity;
33931 var factor = OpenLayers.Renderer.Canvas.drawImageScaleFactor ||
33932 (OpenLayers.Renderer.Canvas.drawImageScaleFactor =
33933 /android 2.1/.test(navigator.userAgent.toLowerCase()) ?
33934 // 320 is the screen width of the G1 phone, for
33935 // which drawImage works out of the box.
33936 320 / window.screen.width : 1
33939 img, x*factor, y*factor, width*factor, height*factor
33941 if (this.hitDetection) {
33942 this.setHitContextStyle("fill", featureId);
33943 this.hitContext.fillRect(x, y, width, height);
33947 img.onload = OpenLayers.Function.bind(onLoad, this);
33948 img.src = style.externalGraphic;
33949 if (img.complete) {
33956 * Method: drawNamedSymbol
33957 * Called to draw Well Known Graphic Symbol Name.
33958 * This method is only called by the renderer itself.
33961 * geometry - {<OpenLayers.Geometry>}
33963 * featureId - {String}
33965 drawNamedSymbol: function(geometry, style, featureId) {
33966 var x, y, cx, cy, i, symbolBounds, scaling, angle;
33967 var unscaledStrokeWidth;
33968 var deg2rad = Math.PI / 180.0;
33970 var symbol = OpenLayers.Renderer.symbol[style.graphicName];
33973 throw new Error(style.graphicName + ' is not a valid symbol name');
33976 if (!symbol.length || symbol.length < 2) return;
33978 var pt = this.getLocalXY(geometry);
33982 if (isNaN(p0) || isNaN(p1)) return;
33984 // Use rounded line caps
33985 this.canvas.lineCap = "round";
33986 this.canvas.lineJoin = "round";
33988 if (this.hitDetection) {
33989 this.hitContext.lineCap = "round";
33990 this.hitContext.lineJoin = "round";
33993 // Scale and rotate symbols, using precalculated bounds whenever possible.
33994 if (style.graphicName in this.cachedSymbolBounds) {
33995 symbolBounds = this.cachedSymbolBounds[style.graphicName];
33997 symbolBounds = new OpenLayers.Bounds();
33998 for(i = 0; i < symbol.length; i+=2) {
33999 symbolBounds.extend(new OpenLayers.LonLat(symbol[i], symbol[i+1]));
34001 this.cachedSymbolBounds[style.graphicName] = symbolBounds;
34004 // Push symbol scaling, translation and rotation onto the transformation stack in reverse order.
34005 // Don't forget to apply all canvas transformations to the hitContext canvas as well(!)
34006 this.canvas.save();
34007 if (this.hitDetection) { this.hitContext.save(); }
34009 // Step 3: place symbol at the desired location
34010 this.canvas.translate(p0,p1);
34011 if (this.hitDetection) { this.hitContext.translate(p0,p1); }
34013 // Step 2a. rotate the symbol if necessary
34014 angle = deg2rad * style.rotation; // will be NaN when style.rotation is undefined.
34015 if (!isNaN(angle)) {
34016 this.canvas.rotate(angle);
34017 if (this.hitDetection) { this.hitContext.rotate(angle); }
34020 // // Step 2: scale symbol such that pointRadius equals half the maximum symbol dimension.
34021 scaling = 2.0 * style.pointRadius / Math.max(symbolBounds.getWidth(), symbolBounds.getHeight());
34022 this.canvas.scale(scaling,scaling);
34023 if (this.hitDetection) { this.hitContext.scale(scaling,scaling); }
34025 // Step 1: center the symbol at the origin
34026 cx = symbolBounds.getCenterLonLat().lon;
34027 cy = symbolBounds.getCenterLonLat().lat;
34028 this.canvas.translate(-cx,-cy);
34029 if (this.hitDetection) { this.hitContext.translate(-cx,-cy); }
34031 // Don't forget to scale stroke widths, because they are affected by canvas scale transformations as well(!)
34032 // Alternative: scale symbol coordinates manually, so stroke width scaling is not needed anymore.
34033 unscaledStrokeWidth = style.strokeWidth;
34034 style.strokeWidth = unscaledStrokeWidth / scaling;
34036 if (style.fill !== false) {
34037 this.setCanvasStyle("fill", style);
34038 this.canvas.beginPath();
34039 for (i=0; i<symbol.length; i=i+2) {
34042 if (i == 0) this.canvas.moveTo(x,y);
34043 this.canvas.lineTo(x,y);
34045 this.canvas.closePath();
34046 this.canvas.fill();
34048 if (this.hitDetection) {
34049 this.setHitContextStyle("fill", featureId, style);
34050 this.hitContext.beginPath();
34051 for (i=0; i<symbol.length; i=i+2) {
34054 if (i == 0) this.canvas.moveTo(x,y);
34055 this.hitContext.lineTo(x,y);
34057 this.hitContext.closePath();
34058 this.hitContext.fill();
34062 if (style.stroke !== false) {
34063 this.setCanvasStyle("stroke", style);
34064 this.canvas.beginPath();
34065 for (i=0; i<symbol.length; i=i+2) {
34068 if (i == 0) this.canvas.moveTo(x,y);
34069 this.canvas.lineTo(x,y);
34071 this.canvas.closePath();
34072 this.canvas.stroke();
34075 if (this.hitDetection) {
34076 this.setHitContextStyle("stroke", featureId, style, scaling);
34077 this.hitContext.beginPath();
34078 for (i=0; i<symbol.length; i=i+2) {
34081 if (i == 0) this.hitContext.moveTo(x,y);
34082 this.hitContext.lineTo(x,y);
34084 this.hitContext.closePath();
34085 this.hitContext.stroke();
34090 style.strokeWidth = unscaledStrokeWidth;
34091 this.canvas.restore();
34092 if (this.hitDetection) { this.hitContext.restore(); }
34093 this.setCanvasStyle("reset");
34097 * Method: setCanvasStyle
34098 * Prepare the canvas for drawing by setting various global settings.
34101 * type - {String} one of 'stroke', 'fill', or 'reset'
34102 * style - {Object} Symbolizer hash
34104 setCanvasStyle: function(type, style) {
34105 if (type === "fill") {
34106 this.canvas.globalAlpha = style['fillOpacity'];
34107 this.canvas.fillStyle = style['fillColor'];
34108 } else if (type === "stroke") {
34109 this.canvas.globalAlpha = style['strokeOpacity'];
34110 this.canvas.strokeStyle = style['strokeColor'];
34111 this.canvas.lineWidth = style['strokeWidth'];
34113 this.canvas.globalAlpha = 0;
34114 this.canvas.lineWidth = 1;
34119 * Method: featureIdToHex
34120 * Convert a feature ID string into an RGB hex string.
34123 * featureId - {String} Feature id
34126 * {String} RGB hex string.
34128 featureIdToHex: function(featureId) {
34129 var id = Number(featureId.split("_").pop()) + 1; // zero for no feature
34130 if (id >= 16777216) {
34131 this.hitOverflow = id - 16777215;
34132 id = id % 16777216 + 1;
34134 var hex = "000000" + id.toString(16);
34135 var len = hex.length;
34136 hex = "#" + hex.substring(len-6, len);
34141 * Method: setHitContextStyle
34142 * Prepare the hit canvas for drawing by setting various global settings.
34145 * type - {String} one of 'stroke', 'fill', or 'reset'
34146 * featureId - {String} The feature id.
34147 * symbolizer - {<OpenLayers.Symbolizer>} The symbolizer.
34149 setHitContextStyle: function(type, featureId, symbolizer, strokeScaling) {
34150 var hex = this.featureIdToHex(featureId);
34151 if (type == "fill") {
34152 this.hitContext.globalAlpha = 1.0;
34153 this.hitContext.fillStyle = hex;
34154 } else if (type == "stroke") {
34155 this.hitContext.globalAlpha = 1.0;
34156 this.hitContext.strokeStyle = hex;
34157 // bump up stroke width to deal with antialiasing. If strokeScaling is defined, we're rendering a symbol
34158 // on a transformed canvas, so the antialias width bump has to scale as well.
34159 if (typeof strokeScaling === "undefined") {
34160 this.hitContext.lineWidth = symbolizer.strokeWidth + 2;
34162 if (!isNaN(strokeScaling)) { this.hitContext.lineWidth = symbolizer.strokeWidth + 2.0 / strokeScaling; }
34165 this.hitContext.globalAlpha = 0;
34166 this.hitContext.lineWidth = 1;
34171 * Method: drawPoint
34172 * This method is only called by the renderer itself.
34175 * geometry - {<OpenLayers.Geometry>}
34177 * featureId - {String}
34179 drawPoint: function(geometry, style, featureId) {
34180 if(style.graphic !== false) {
34181 if(style.externalGraphic) {
34182 this.drawExternalGraphic(geometry, style, featureId);
34183 } else if (style.graphicName && (style.graphicName != "circle")) {
34184 this.drawNamedSymbol(geometry, style, featureId);
34186 var pt = this.getLocalXY(geometry);
34189 if(!isNaN(p0) && !isNaN(p1)) {
34190 var twoPi = Math.PI*2;
34191 var radius = style.pointRadius;
34192 if(style.fill !== false) {
34193 this.setCanvasStyle("fill", style);
34194 this.canvas.beginPath();
34195 this.canvas.arc(p0, p1, radius, 0, twoPi, true);
34196 this.canvas.fill();
34197 if (this.hitDetection) {
34198 this.setHitContextStyle("fill", featureId, style);
34199 this.hitContext.beginPath();
34200 this.hitContext.arc(p0, p1, radius, 0, twoPi, true);
34201 this.hitContext.fill();
34205 if(style.stroke !== false) {
34206 this.setCanvasStyle("stroke", style);
34207 this.canvas.beginPath();
34208 this.canvas.arc(p0, p1, radius, 0, twoPi, true);
34209 this.canvas.stroke();
34210 if (this.hitDetection) {
34211 this.setHitContextStyle("stroke", featureId, style);
34212 this.hitContext.beginPath();
34213 this.hitContext.arc(p0, p1, radius, 0, twoPi, true);
34214 this.hitContext.stroke();
34216 this.setCanvasStyle("reset");
34224 * Method: drawLineString
34225 * This method is only called by the renderer itself.
34228 * geometry - {<OpenLayers.Geometry>}
34230 * featureId - {String}
34232 drawLineString: function(geometry, style, featureId) {
34233 style = OpenLayers.Util.applyDefaults({fill: false}, style);
34234 this.drawLinearRing(geometry, style, featureId);
34238 * Method: drawLinearRing
34239 * This method is only called by the renderer itself.
34242 * geometry - {<OpenLayers.Geometry>}
34244 * featureId - {String}
34246 drawLinearRing: function(geometry, style, featureId) {
34247 if (style.fill !== false) {
34248 this.setCanvasStyle("fill", style);
34249 this.renderPath(this.canvas, geometry, style, featureId, "fill");
34250 if (this.hitDetection) {
34251 this.setHitContextStyle("fill", featureId, style);
34252 this.renderPath(this.hitContext, geometry, style, featureId, "fill");
34255 if (style.stroke !== false) {
34256 this.setCanvasStyle("stroke", style);
34257 this.renderPath(this.canvas, geometry, style, featureId, "stroke");
34258 if (this.hitDetection) {
34259 this.setHitContextStyle("stroke", featureId, style);
34260 this.renderPath(this.hitContext, geometry, style, featureId, "stroke");
34263 this.setCanvasStyle("reset");
34267 * Method: renderPath
34268 * Render a path with stroke and optional fill.
34270 renderPath: function(context, geometry, style, featureId, type) {
34271 var components = geometry.components;
34272 var len = components.length;
34273 context.beginPath();
34274 var start = this.getLocalXY(components[0]);
34277 if (!isNaN(x) && !isNaN(y)) {
34278 context.moveTo(start[0], start[1]);
34279 for (var i=1; i<len; ++i) {
34280 var pt = this.getLocalXY(components[i]);
34281 context.lineTo(pt[0], pt[1]);
34283 if (type === "fill") {
34292 * Method: drawPolygon
34293 * This method is only called by the renderer itself.
34296 * geometry - {<OpenLayers.Geometry>}
34298 * featureId - {String}
34300 drawPolygon: function(geometry, style, featureId) {
34301 var components = geometry.components;
34302 var len = components.length;
34303 this.drawLinearRing(components[0], style, featureId);
34304 // erase inner rings
34305 for (var i=1; i<len; ++i) {
34307 * Note that this is overly aggressive. Here we punch holes through
34308 * all previously rendered features on the same canvas. A better
34309 * solution for polygons with interior rings would be to draw the
34310 * polygon on a sketch canvas first. We could erase all holes
34311 * there and then copy the drawing to the layer canvas.
34312 * TODO: http://trac.osgeo.org/openlayers/ticket/3130
34314 this.canvas.globalCompositeOperation = "destination-out";
34315 if (this.hitDetection) {
34316 this.hitContext.globalCompositeOperation = "destination-out";
34318 this.drawLinearRing(
34320 OpenLayers.Util.applyDefaults({stroke: false, fillOpacity: 1.0}, style),
34323 this.canvas.globalCompositeOperation = "source-over";
34324 if (this.hitDetection) {
34325 this.hitContext.globalCompositeOperation = "source-over";
34327 this.drawLinearRing(
34329 OpenLayers.Util.applyDefaults({fill: false}, style),
34337 * This method is only called by the renderer itself.
34340 * location - {<OpenLayers.Point>}
34343 drawText: function(location, style) {
34344 var pt = this.getLocalXY(location);
34346 this.setCanvasStyle("reset");
34347 this.canvas.fillStyle = style.fontColor;
34348 this.canvas.globalAlpha = style.fontOpacity || 1.0;
34349 var fontStyle = [style.fontStyle ? style.fontStyle : "normal",
34350 "normal", // "font-variant" not supported
34351 style.fontWeight ? style.fontWeight : "normal",
34352 style.fontSize ? style.fontSize : "1em",
34353 style.fontFamily ? style.fontFamily : "sans-serif"].join(" ");
34354 var labelRows = style.label.split('\n');
34355 var numRows = labelRows.length;
34356 if (this.canvas.fillText) {
34358 this.canvas.font = fontStyle;
34359 this.canvas.textAlign =
34360 OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[0]] ||
34362 this.canvas.textBaseline =
34363 OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[1]] ||
34366 OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];
34367 if (vfactor == null) {
34371 this.canvas.measureText('Mg').height ||
34372 this.canvas.measureText('xx').width;
34373 pt[1] += lineHeight*vfactor*(numRows-1);
34374 for (var i = 0; i < numRows; i++) {
34375 if (style.labelOutlineWidth) {
34376 this.canvas.save();
34377 this.canvas.globalAlpha = style.labelOutlineOpacity || style.fontOpacity || 1.0;
34378 this.canvas.strokeStyle = style.labelOutlineColor;
34379 this.canvas.lineWidth = style.labelOutlineWidth;
34380 this.canvas.strokeText(labelRows[i], pt[0], pt[1] + (lineHeight*i) + 1);
34381 this.canvas.restore();
34383 this.canvas.fillText(labelRows[i], pt[0], pt[1] + (lineHeight*i));
34385 } else if (this.canvas.mozDrawText) {
34386 // Mozilla pre-Gecko1.9.1 (<FF3.1)
34387 this.canvas.mozTextStyle = fontStyle;
34388 // No built-in text alignment, so we measure and adjust the position
34390 OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[0]];
34391 if (hfactor == null) {
34395 OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];
34396 if (vfactor == null) {
34399 var lineHeight = this.canvas.mozMeasureText('xx');
34400 pt[1] += lineHeight*(1 + (vfactor*numRows));
34401 for (var i = 0; i < numRows; i++) {
34402 var x = pt[0] + (hfactor*this.canvas.mozMeasureText(labelRows[i]));
34403 var y = pt[1] + (i*lineHeight);
34404 this.canvas.translate(x, y);
34405 this.canvas.mozDrawText(labelRows[i]);
34406 this.canvas.translate(-x, -y);
34409 this.setCanvasStyle("reset");
34413 * Method: getLocalXY
34414 * transform geographic xy into pixel xy
34417 * point - {<OpenLayers.Geometry.Point>}
34419 getLocalXY: function(point) {
34420 var resolution = this.getResolution();
34421 var extent = this.extent;
34422 var x = ((point.x - this.featureDx) / resolution + (-extent.left / resolution));
34423 var y = ((extent.top / resolution) - point.y / resolution);
34429 * Clear all vectors from the renderer.
34431 clear: function() {
34432 this.clearCanvas();
34433 this.features = {};
34437 * Method: clearCanvas
34438 * Clear the canvas element of the renderer.
34440 clearCanvas: function() {
34441 var height = this.root.height;
34442 var width = this.root.width;
34443 this.canvas.clearRect(0, 0, width, height);
34444 this._clearRectId = OpenLayers.Util.createUniqueID();
34445 if (this.hitDetection) {
34446 this.hitContext.clearRect(0, 0, width, height);
34451 * Method: getFeatureIdFromEvent
34452 * Returns a feature id from an event on the renderer.
34455 * evt - {<OpenLayers.Event>}
34458 * {<OpenLayers.Feature.Vector} A feature or undefined. This method returns a
34459 * feature instead of a feature id to avoid an unnecessary lookup on the
34462 getFeatureIdFromEvent: function(evt) {
34463 var featureId, feature;
34465 if (this.hitDetection && this.root.style.display !== "none") {
34466 // this dragging check should go in the feature handler
34467 if (!this.map.dragging) {
34471 var data = this.hitContext.getImageData(x, y, 1, 1).data;
34472 if (data[3] === 255) { // antialiased
34473 var id = data[2] + (256 * (data[1] + (256 * data[0])));
34475 featureId = "OpenLayers_Feature_Vector_" + (id - 1 + this.hitOverflow);
34477 feature = this.features[featureId][0];
34479 // Because of antialiasing on the canvas, when the hit location is at a point where the edge of
34480 // one symbol intersects the interior of another symbol, a wrong hit color (and therefore id) results.
34481 // todo: set Antialiasing = 'off' on the hitContext as soon as browsers allow it.
34491 * Method: eraseFeatures
34492 * This is called by the layer to erase features; removes the feature from
34493 * the list, then redraws the layer.
34496 * features - {Array(<OpenLayers.Feature.Vector>)}
34498 eraseFeatures: function(features) {
34499 if(!(OpenLayers.Util.isArray(features))) {
34500 features = [features];
34502 for(var i=0; i<features.length; ++i) {
34503 delete this.features[features[i].id];
34510 * The real 'meat' of the function: any time things have changed,
34511 * redraw() can be called to loop over all the data and (you guessed
34512 * it) redraw it. Unlike Elements-based Renderers, we can't interact
34513 * with things once they're drawn, to remove them, for example, so
34514 * instead we have to just clear everything and draw from scratch.
34516 redraw: function() {
34517 if (!this.locked) {
34518 this.clearCanvas();
34520 var feature, geometry, style;
34521 var worldBounds = (this.map.baseLayer && this.map.baseLayer.wrapDateLine) && this.map.getMaxExtent();
34522 for (var id in this.features) {
34523 if (!this.features.hasOwnProperty(id)) { continue; }
34524 feature = this.features[id][0];
34525 geometry = feature.geometry;
34526 this.calculateFeatureDx(geometry.getBounds(), worldBounds);
34527 style = this.features[id][1];
34528 this.drawGeometry(geometry, style, feature.id);
34530 labelMap.push([feature, style]);
34534 for (var i=0, len=labelMap.length; i<len; ++i) {
34535 item = labelMap[i];
34536 this.drawText(item[0].geometry.getCentroid(), item[1]);
34541 CLASS_NAME: "OpenLayers.Renderer.Canvas"
34545 * Constant: OpenLayers.Renderer.Canvas.LABEL_ALIGN
34548 OpenLayers.Renderer.Canvas.LABEL_ALIGN = {
34556 * Constant: OpenLayers.Renderer.Canvas.LABEL_FACTOR
34559 OpenLayers.Renderer.Canvas.LABEL_FACTOR = {
34567 * Constant: OpenLayers.Renderer.Canvas.drawImageScaleFactor
34568 * {Number} Scale factor to apply to the canvas drawImage arguments. This
34569 * is always 1 except for Android 2.1 devices, to work around
34570 * http://code.google.com/p/android/issues/detail?id=5141.
34572 OpenLayers.Renderer.Canvas.drawImageScaleFactor = null;
34573 /* ======================================================================
34574 OpenLayers/Renderer/Elements.js
34575 ====================================================================== */
34577 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
34578 * full list of contributors). Published under the 2-clause BSD license.
34579 * See license.txt in the OpenLayers distribution or repository for the
34580 * full text of the license. */
34583 * @requires OpenLayers/Renderer.js
34587 * Class: OpenLayers.ElementsIndexer
34588 * This class takes care of figuring out which order elements should be
34589 * placed in the DOM based on given indexing methods.
34591 OpenLayers.ElementsIndexer = OpenLayers.Class({
34594 * Property: maxZIndex
34595 * {Integer} This is the largest-most z-index value for a node
34596 * contained within the indexer.
34602 * {Array<String>} This is an array of node id's stored in the
34603 * order that they should show up on screen. Id's higher up in the
34604 * array (higher array index) represent nodes with higher z-indeces.
34609 * Property: indices
34610 * {Object} This is a hash that maps node ids to their z-index value
34611 * stored in the indexer. This is done to make finding a nodes z-index
34617 * Property: compare
34618 * {Function} This is the function used to determine placement of
34619 * of a new node within the indexer. If null, this defaults to to
34620 * the Z_ORDER_DRAWING_ORDER comparison method.
34625 * APIMethod: initialize
34626 * Create a new indexer with
34629 * yOrdering - {Boolean} Whether to use y-ordering.
34631 initialize: function(yOrdering) {
34633 this.compare = yOrdering ?
34634 OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER :
34635 OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER;
34641 * APIMethod: insert
34642 * Insert a new node into the indexer. In order to find the correct
34643 * positioning for the node to be inserted, this method uses a binary
34644 * search. This makes inserting O(log(n)).
34647 * newNode - {DOMElement} The new node to be inserted.
34650 * {DOMElement} the node before which we should insert our newNode, or
34651 * null if newNode can just be appended.
34653 insert: function(newNode) {
34654 // If the node is known to the indexer, remove it so we can
34655 // recalculate where it should go.
34656 if (this.exists(newNode)) {
34657 this.remove(newNode);
34660 var nodeId = newNode.id;
34662 this.determineZIndex(newNode);
34664 var leftIndex = -1;
34665 var rightIndex = this.order.length;
34668 while (rightIndex - leftIndex > 1) {
34669 middle = parseInt((leftIndex + rightIndex) / 2);
34671 var placement = this.compare(this, newNode,
34672 OpenLayers.Util.getElement(this.order[middle]));
34674 if (placement > 0) {
34675 leftIndex = middle;
34677 rightIndex = middle;
34681 this.order.splice(rightIndex, 0, nodeId);
34682 this.indices[nodeId] = this.getZIndex(newNode);
34684 // If the new node should be before another in the index
34685 // order, return the node before which we have to insert the new one;
34686 // else, return null to indicate that the new node can be appended.
34687 return this.getNextElement(rightIndex);
34691 * APIMethod: remove
34694 * node - {DOMElement} The node to be removed.
34696 remove: function(node) {
34697 var nodeId = node.id;
34698 var arrayIndex = OpenLayers.Util.indexOf(this.order, nodeId);
34699 if (arrayIndex >= 0) {
34700 // Remove it from the order array, as well as deleting the node
34701 // from the indeces hash.
34702 this.order.splice(arrayIndex, 1);
34703 delete this.indices[nodeId];
34705 // Reset the maxium z-index based on the last item in the
34707 if (this.order.length > 0) {
34708 var lastId = this.order[this.order.length - 1];
34709 this.maxZIndex = this.indices[lastId];
34711 this.maxZIndex = 0;
34719 clear: function() {
34722 this.maxZIndex = 0;
34726 * APIMethod: exists
34729 * node - {DOMElement} The node to test for existence.
34732 * {Boolean} Whether or not the node exists in the indexer?
34734 exists: function(node) {
34735 return (this.indices[node.id] != null);
34739 * APIMethod: getZIndex
34740 * Get the z-index value for the current node from the node data itself.
34743 * node - {DOMElement} The node whose z-index to get.
34746 * {Integer} The z-index value for the specified node (from the node
34749 getZIndex: function(node) {
34750 return node._style.graphicZIndex;
34754 * Method: determineZIndex
34755 * Determine the z-index for the current node if there isn't one,
34756 * and set the maximum value if we've found a new maximum.
34759 * node - {DOMElement}
34761 determineZIndex: function(node) {
34762 var zIndex = node._style.graphicZIndex;
34764 // Everything must have a zIndex. If none is specified,
34765 // this means the user *must* (hint: assumption) want this
34766 // node to succomb to drawing order. To enforce drawing order
34767 // over all indexing methods, we'll create a new z-index that's
34768 // greater than any currently in the indexer.
34769 if (zIndex == null) {
34770 zIndex = this.maxZIndex;
34771 node._style.graphicZIndex = zIndex;
34772 } else if (zIndex > this.maxZIndex) {
34773 this.maxZIndex = zIndex;
34778 * APIMethod: getNextElement
34779 * Get the next element in the order stack.
34782 * index - {Integer} The index of the current node in this.order.
34785 * {DOMElement} the node following the index passed in, or
34788 getNextElement: function(index) {
34789 for (var nextIndex = index + 1, nextElement = undefined;
34790 (nextIndex < this.order.length) && (nextElement == undefined);
34792 nextElement = OpenLayers.Util.getElement(this.order[nextIndex]);
34795 return nextElement || null;
34798 CLASS_NAME: "OpenLayers.ElementsIndexer"
34802 * Namespace: OpenLayers.ElementsIndexer.IndexingMethods
34803 * These are the compare methods for figuring out where a new node should be
34804 * placed within the indexer. These methods are very similar to general
34805 * sorting methods in that they return -1, 0, and 1 to specify the
34806 * direction in which new nodes fall in the ordering.
34808 OpenLayers.ElementsIndexer.IndexingMethods = {
34812 * This compare method is used by other comparison methods.
34813 * It can be used individually for ordering, but is not recommended,
34814 * because it doesn't subscribe to drawing order.
34817 * indexer - {<OpenLayers.ElementsIndexer>}
34818 * newNode - {DOMElement}
34819 * nextNode - {DOMElement}
34824 Z_ORDER: function(indexer, newNode, nextNode) {
34825 var newZIndex = indexer.getZIndex(newNode);
34829 var nextZIndex = indexer.getZIndex(nextNode);
34830 returnVal = newZIndex - nextZIndex;
34837 * APIMethod: Z_ORDER_DRAWING_ORDER
34838 * This method orders nodes by their z-index, but does so in a way
34839 * that, if there are other nodes with the same z-index, the newest
34840 * drawn will be the front most within that z-index. This is the
34841 * default indexing method.
34844 * indexer - {<OpenLayers.ElementsIndexer>}
34845 * newNode - {DOMElement}
34846 * nextNode - {DOMElement}
34851 Z_ORDER_DRAWING_ORDER: function(indexer, newNode, nextNode) {
34852 var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(
34858 // Make Z_ORDER subscribe to drawing order by pushing it above
34859 // all of the other nodes with the same z-index.
34860 if (nextNode && returnVal == 0) {
34868 * APIMethod: Z_ORDER_Y_ORDER
34869 * This one should really be called Z_ORDER_Y_ORDER_DRAWING_ORDER, as it
34870 * best describes which ordering methods have precedence (though, the
34871 * name would be too long). This method orders nodes by their z-index,
34872 * but does so in a way that, if there are other nodes with the same
34873 * z-index, the nodes with the lower y position will be "closer" than
34874 * those with a higher y position. If two nodes have the exact same y
34875 * position, however, then this method will revert to using drawing
34876 * order to decide placement.
34879 * indexer - {<OpenLayers.ElementsIndexer>}
34880 * newNode - {DOMElement}
34881 * nextNode - {DOMElement}
34886 Z_ORDER_Y_ORDER: function(indexer, newNode, nextNode) {
34887 var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(
34893 if (nextNode && returnVal === 0) {
34894 var result = nextNode._boundsBottom - newNode._boundsBottom;
34895 returnVal = (result === 0) ? 1 : result;
34903 * Class: OpenLayers.Renderer.Elements
34904 * This is another virtual class in that it should never be instantiated by
34905 * itself as a Renderer. It exists because there is *tons* of shared
34906 * functionality between different vector libraries which use nodes/elements
34907 * as a base for rendering vectors.
34909 * The highlevel bits of code that are implemented here are the adding and
34910 * removing of geometries, which is essentially the same for any
34911 * element-based renderer. The details of creating each node and drawing the
34912 * paths are of course different, but the machinery is the same.
34915 * - <OpenLayers.Renderer>
34917 OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
34920 * Property: rendererRoot
34923 rendererRoot: null,
34932 * Property: vectorRoot
34938 * Property: textRoot
34950 * Property: xOffset
34951 * {Number} Offset to apply to the renderer viewport translation in x
34952 * direction. If the renderer extent's center is on the right of the
34953 * dateline (i.e. exceeds the world bounds), we shift the viewport to the
34954 * left by one world width. This avoids that features disappear from the
34955 * map viewport. Because our dateline handling logic in other places
34956 * ensures that extents crossing the dateline always have a center
34957 * exceeding the world bounds on the left, we need this offset to make sure
34958 * that the same is true for the renderer extent in pixel space as well.
34963 * Property: rightOfDateLine
34964 * {Boolean} Keeps track of the location of the map extent relative to the
34965 * date line. The <setExtent> method compares this value (which is the one
34966 * from the previous <setExtent> call) with the current position of the map
34967 * extent relative to the date line and updates the xOffset when the extent
34968 * has moved from one side of the date line to the other.
34972 * Property: Indexer
34973 * {<OpenLayers.ElementIndexer>} An instance of OpenLayers.ElementsIndexer
34974 * created upon initialization if the zIndexing or yOrdering options
34975 * passed to this renderer's constructor are set to true.
34980 * Constant: BACKGROUND_ID_SUFFIX
34983 BACKGROUND_ID_SUFFIX: "_background",
34986 * Constant: LABEL_ID_SUFFIX
34989 LABEL_ID_SUFFIX: "_label",
34992 * Constant: LABEL_OUTLINE_SUFFIX
34995 LABEL_OUTLINE_SUFFIX: "_outline",
34998 * Constructor: OpenLayers.Renderer.Elements
35001 * containerID - {String}
35002 * options - {Object} options for this renderer.
35004 * Supported options are:
35005 * yOrdering - {Boolean} Whether to use y-ordering
35006 * zIndexing - {Boolean} Whether to use z-indexing. Will be ignored
35007 * if yOrdering is set to true.
35009 initialize: function(containerID, options) {
35010 OpenLayers.Renderer.prototype.initialize.apply(this, arguments);
35012 this.rendererRoot = this.createRenderRoot();
35013 this.root = this.createRoot("_root");
35014 this.vectorRoot = this.createRoot("_vroot");
35015 this.textRoot = this.createRoot("_troot");
35017 this.root.appendChild(this.vectorRoot);
35018 this.root.appendChild(this.textRoot);
35020 this.rendererRoot.appendChild(this.root);
35021 this.container.appendChild(this.rendererRoot);
35023 if(options && (options.zIndexing || options.yOrdering)) {
35024 this.indexer = new OpenLayers.ElementsIndexer(options.yOrdering);
35031 destroy: function() {
35035 this.rendererRoot = null;
35039 OpenLayers.Renderer.prototype.destroy.apply(this, arguments);
35044 * Remove all the elements from the root
35046 clear: function() {
35048 var root = this.vectorRoot;
35050 while (child = root.firstChild) {
35051 root.removeChild(child);
35054 root = this.textRoot;
35056 while (child = root.firstChild) {
35057 root.removeChild(child);
35060 if (this.indexer) {
35061 this.indexer.clear();
35066 * Method: setExtent
35067 * Set the visible part of the layer.
35070 * extent - {<OpenLayers.Bounds>}
35071 * resolutionChanged - {Boolean}
35074 * {Boolean} true to notify the layer that the new extent does not exceed
35075 * the coordinate range, and the features will not need to be redrawn.
35078 setExtent: function(extent, resolutionChanged) {
35079 var coordSysUnchanged = OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);
35080 var resolution = this.getResolution();
35081 if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
35082 var rightOfDateLine,
35083 ratio = extent.getWidth() / this.map.getExtent().getWidth(),
35084 extent = extent.scale(1 / ratio),
35085 world = this.map.getMaxExtent();
35086 if (world.right > extent.left && world.right < extent.right) {
35087 rightOfDateLine = true;
35088 } else if (world.left > extent.left && world.left < extent.right) {
35089 rightOfDateLine = false;
35091 if (rightOfDateLine !== this.rightOfDateLine || resolutionChanged) {
35092 coordSysUnchanged = false;
35093 this.xOffset = rightOfDateLine === true ?
35094 world.getWidth() / resolution : 0;
35096 this.rightOfDateLine = rightOfDateLine;
35098 return coordSysUnchanged;
35102 * Method: getNodeType
35103 * This function is in charge of asking the specific renderer which type
35104 * of node to create for the given geometry and style. All geometries
35105 * in an Elements-based renderer consist of one node and some
35106 * attributes. We have the nodeFactory() function which creates a node
35107 * for us, but it takes a 'type' as input, and that is precisely what
35108 * this function tells us.
35111 * geometry - {<OpenLayers.Geometry>}
35115 * {String} The corresponding node type for the specified geometry
35117 getNodeType: function(geometry, style) { },
35120 * Method: drawGeometry
35121 * Draw the geometry, creating new nodes, setting paths, setting style,
35122 * setting featureId on the node. This method should only be called
35123 * by the renderer itself.
35126 * geometry - {<OpenLayers.Geometry>}
35128 * featureId - {String}
35131 * {Boolean} true if the geometry has been drawn completely; null if
35132 * incomplete; false otherwise
35134 drawGeometry: function(geometry, style, featureId) {
35135 var className = geometry.CLASS_NAME;
35136 var rendered = true;
35137 if ((className == "OpenLayers.Geometry.Collection") ||
35138 (className == "OpenLayers.Geometry.MultiPoint") ||
35139 (className == "OpenLayers.Geometry.MultiLineString") ||
35140 (className == "OpenLayers.Geometry.MultiPolygon")) {
35141 for (var i = 0, len=geometry.components.length; i<len; i++) {
35142 rendered = this.drawGeometry(
35143 geometry.components[i], style, featureId) && rendered;
35149 var removeBackground = false;
35150 if (style.display != "none") {
35151 if (style.backgroundGraphic) {
35152 this.redrawBackgroundNode(geometry.id, geometry, style,
35155 removeBackground = true;
35157 rendered = this.redrawNode(geometry.id, geometry, style,
35160 if (rendered == false) {
35161 var node = document.getElementById(geometry.id);
35163 if (node._style.backgroundGraphic) {
35164 removeBackground = true;
35166 node.parentNode.removeChild(node);
35169 if (removeBackground) {
35170 var node = document.getElementById(
35171 geometry.id + this.BACKGROUND_ID_SUFFIX);
35173 node.parentNode.removeChild(node);
35180 * Method: redrawNode
35184 * geometry - {<OpenLayers.Geometry>}
35186 * featureId - {String}
35189 * {Boolean} true if the complete geometry could be drawn, null if parts of
35190 * the geometry could not be drawn, false otherwise
35192 redrawNode: function(id, geometry, style, featureId) {
35193 style = this.applyDefaultSymbolizer(style);
35194 // Get the node if it's already on the map.
35195 var node = this.nodeFactory(id, this.getNodeType(geometry, style));
35197 // Set the data for the node, then draw it.
35198 node._featureId = featureId;
35199 node._boundsBottom = geometry.getBounds().bottom;
35200 node._geometryClass = geometry.CLASS_NAME;
35201 node._style = style;
35203 var drawResult = this.drawGeometryNode(node, geometry, style);
35204 if(drawResult === false) {
35208 node = drawResult.node;
35210 // Insert the node into the indexer so it can show us where to
35211 // place it. Note that this operation is O(log(n)). If there's a
35212 // performance problem (when dragging, for instance) this is
35213 // likely where it would be.
35214 if (this.indexer) {
35215 var insert = this.indexer.insert(node);
35217 this.vectorRoot.insertBefore(node, insert);
35219 this.vectorRoot.appendChild(node);
35222 // if there's no indexer, simply append the node to root,
35223 // but only if the node is a new one
35224 if (node.parentNode !== this.vectorRoot){
35225 this.vectorRoot.appendChild(node);
35229 this.postDraw(node);
35231 return drawResult.complete;
35235 * Method: redrawBackgroundNode
35236 * Redraws the node using special 'background' style properties. Basically
35237 * just calls redrawNode(), but instead of directly using the
35238 * 'externalGraphic', 'graphicXOffset', 'graphicYOffset', and
35239 * 'graphicZIndex' properties directly from the specified 'style'
35240 * parameter, we create a new style object and set those properties
35241 * from the corresponding 'background'-prefixed properties from
35242 * specified 'style' parameter.
35246 * geometry - {<OpenLayers.Geometry>}
35248 * featureId - {String}
35251 * {Boolean} true if the complete geometry could be drawn, null if parts of
35252 * the geometry could not be drawn, false otherwise
35254 redrawBackgroundNode: function(id, geometry, style, featureId) {
35255 var backgroundStyle = OpenLayers.Util.extend({}, style);
35257 // Set regular style attributes to apply to the background styles.
35258 backgroundStyle.externalGraphic = backgroundStyle.backgroundGraphic;
35259 backgroundStyle.graphicXOffset = backgroundStyle.backgroundXOffset;
35260 backgroundStyle.graphicYOffset = backgroundStyle.backgroundYOffset;
35261 backgroundStyle.graphicZIndex = backgroundStyle.backgroundGraphicZIndex;
35262 backgroundStyle.graphicWidth = backgroundStyle.backgroundWidth || backgroundStyle.graphicWidth;
35263 backgroundStyle.graphicHeight = backgroundStyle.backgroundHeight || backgroundStyle.graphicHeight;
35265 // Erase background styles.
35266 backgroundStyle.backgroundGraphic = null;
35267 backgroundStyle.backgroundXOffset = null;
35268 backgroundStyle.backgroundYOffset = null;
35269 backgroundStyle.backgroundGraphicZIndex = null;
35271 return this.redrawNode(
35272 id + this.BACKGROUND_ID_SUFFIX,
35280 * Method: drawGeometryNode
35281 * Given a node, draw a geometry on the specified layer.
35282 * node and geometry are required arguments, style is optional.
35283 * This method is only called by the render itself.
35286 * node - {DOMElement}
35287 * geometry - {<OpenLayers.Geometry>}
35291 * {Object} a hash with properties "node" (the drawn node) and "complete"
35292 * (null if parts of the geometry could not be drawn, false if nothing
35295 drawGeometryNode: function(node, geometry, style) {
35296 style = style || node._style;
35299 'isFilled': style.fill === undefined ?
35302 'isStroked': style.stroke === undefined ?
35303 !!style.strokeWidth :
35307 switch (geometry.CLASS_NAME) {
35308 case "OpenLayers.Geometry.Point":
35309 if(style.graphic === false) {
35310 options.isFilled = false;
35311 options.isStroked = false;
35313 drawn = this.drawPoint(node, geometry);
35315 case "OpenLayers.Geometry.LineString":
35316 options.isFilled = false;
35317 drawn = this.drawLineString(node, geometry);
35319 case "OpenLayers.Geometry.LinearRing":
35320 drawn = this.drawLinearRing(node, geometry);
35322 case "OpenLayers.Geometry.Polygon":
35323 drawn = this.drawPolygon(node, geometry);
35325 case "OpenLayers.Geometry.Rectangle":
35326 drawn = this.drawRectangle(node, geometry);
35332 node._options = options;
35335 //TBD simplify this
35336 if (drawn != false) {
35338 node: this.setStyle(node, style, options, geometry),
35348 * Things that have do be done after the geometry node is appended
35349 * to its parent node. To be overridden by subclasses.
35352 * node - {DOMElement}
35354 postDraw: function(node) {},
35357 * Method: drawPoint
35358 * Virtual function for drawing Point Geometry.
35359 * Should be implemented by subclasses.
35360 * This method is only called by the renderer itself.
35363 * node - {DOMElement}
35364 * geometry - {<OpenLayers.Geometry>}
35367 * {DOMElement} or false if the renderer could not draw the point
35369 drawPoint: function(node, geometry) {},
35372 * Method: drawLineString
35373 * Virtual function for drawing LineString Geometry.
35374 * Should be implemented by subclasses.
35375 * This method is only called by the renderer itself.
35378 * node - {DOMElement}
35379 * geometry - {<OpenLayers.Geometry>}
35382 * {DOMElement} or null if the renderer could not draw all components of
35383 * the linestring, or false if nothing could be drawn
35385 drawLineString: function(node, geometry) {},
35388 * Method: drawLinearRing
35389 * Virtual function for drawing LinearRing Geometry.
35390 * Should be implemented by subclasses.
35391 * This method is only called by the renderer itself.
35394 * node - {DOMElement}
35395 * geometry - {<OpenLayers.Geometry>}
35398 * {DOMElement} or null if the renderer could not draw all components
35399 * of the linear ring, or false if nothing could be drawn
35401 drawLinearRing: function(node, geometry) {},
35404 * Method: drawPolygon
35405 * Virtual function for drawing Polygon Geometry.
35406 * Should be implemented by subclasses.
35407 * This method is only called by the renderer itself.
35410 * node - {DOMElement}
35411 * geometry - {<OpenLayers.Geometry>}
35414 * {DOMElement} or null if the renderer could not draw all components
35415 * of the polygon, or false if nothing could be drawn
35417 drawPolygon: function(node, geometry) {},
35420 * Method: drawRectangle
35421 * Virtual function for drawing Rectangle Geometry.
35422 * Should be implemented by subclasses.
35423 * This method is only called by the renderer itself.
35426 * node - {DOMElement}
35427 * geometry - {<OpenLayers.Geometry>}
35430 * {DOMElement} or false if the renderer could not draw the rectangle
35432 drawRectangle: function(node, geometry) {},
35435 * Method: drawCircle
35436 * Virtual function for drawing Circle Geometry.
35437 * Should be implemented by subclasses.
35438 * This method is only called by the renderer itself.
35441 * node - {DOMElement}
35442 * geometry - {<OpenLayers.Geometry>}
35445 * {DOMElement} or false if the renderer could not draw the circle
35447 drawCircle: function(node, geometry) {},
35450 * Method: removeText
35454 * featureId - {String}
35456 removeText: function(featureId) {
35457 var label = document.getElementById(featureId + this.LABEL_ID_SUFFIX);
35459 this.textRoot.removeChild(label);
35461 var outline = document.getElementById(featureId + this.LABEL_OUTLINE_SUFFIX);
35463 this.textRoot.removeChild(outline);
35468 * Method: getFeatureIdFromEvent
35471 * evt - {Object} An <OpenLayers.Event> object
35474 * {String} A feature id or undefined.
35476 getFeatureIdFromEvent: function(evt) {
35477 var target = evt.target;
35478 var useElement = target && target.correspondingUseElement;
35479 var node = useElement ? useElement : (target || evt.srcElement);
35480 return node._featureId;
35484 * Method: eraseGeometry
35485 * Erase a geometry from the renderer. In the case of a multi-geometry,
35486 * we cycle through and recurse on ourselves. Otherwise, we look for a
35487 * node with the geometry.id, destroy its geometry, and remove it from
35491 * geometry - {<OpenLayers.Geometry>}
35492 * featureId - {String}
35494 eraseGeometry: function(geometry, featureId) {
35495 if ((geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPoint") ||
35496 (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiLineString") ||
35497 (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPolygon") ||
35498 (geometry.CLASS_NAME == "OpenLayers.Geometry.Collection")) {
35499 for (var i=0, len=geometry.components.length; i<len; i++) {
35500 this.eraseGeometry(geometry.components[i], featureId);
35503 var element = OpenLayers.Util.getElement(geometry.id);
35504 if (element && element.parentNode) {
35505 if (element.geometry) {
35506 element.geometry.destroy();
35507 element.geometry = null;
35509 element.parentNode.removeChild(element);
35511 if (this.indexer) {
35512 this.indexer.remove(element);
35515 if (element._style.backgroundGraphic) {
35516 var backgroundId = geometry.id + this.BACKGROUND_ID_SUFFIX;
35517 var bElem = OpenLayers.Util.getElement(backgroundId);
35518 if (bElem && bElem.parentNode) {
35519 // No need to destroy the geometry since the element and the background
35520 // node share the same geometry.
35521 bElem.parentNode.removeChild(bElem);
35529 * Method: nodeFactory
35530 * Create new node of the specified type, with the (optional) specified id.
35532 * If node already exists with same ID and a different type, we remove it
35533 * and then call ourselves again to recreate it.
35537 * type - {String} type Kind of node to draw.
35540 * {DOMElement} A new node of the given type and id.
35542 nodeFactory: function(id, type) {
35543 var node = OpenLayers.Util.getElement(id);
35545 if (!this.nodeTypeCompare(node, type)) {
35546 node.parentNode.removeChild(node);
35547 node = this.nodeFactory(id, type);
35550 node = this.createNode(type, id);
35556 * Method: nodeTypeCompare
35559 * node - {DOMElement}
35560 * type - {String} Kind of node
35563 * {Boolean} Whether or not the specified node is of the specified type
35564 * This function must be overridden by subclasses.
35566 nodeTypeCompare: function(node, type) {},
35569 * Method: createNode
35572 * type - {String} Kind of node to draw.
35573 * id - {String} Id for node.
35576 * {DOMElement} A new node of the given type and id.
35577 * This function must be overridden by subclasses.
35579 createNode: function(type, id) {},
35583 * moves this renderer's root to a different renderer.
35586 * renderer - {<OpenLayers.Renderer>} target renderer for the moved root
35588 moveRoot: function(renderer) {
35589 var root = this.root;
35590 if(renderer.root.parentNode == this.rendererRoot) {
35591 root = renderer.root;
35593 root.parentNode.removeChild(root);
35594 renderer.rendererRoot.appendChild(root);
35598 * Method: getRenderLayerId
35599 * Gets the layer that this renderer's output appears on. If moveRoot was
35600 * used, this will be different from the id of the layer containing the
35601 * features rendered by this renderer.
35604 * {String} the id of the output layer.
35606 getRenderLayerId: function() {
35607 return this.root.parentNode.parentNode.id;
35611 * Method: isComplexSymbol
35612 * Determines if a symbol cannot be rendered using drawCircle
35615 * graphicName - {String}
35618 * {Boolean} true if the symbol is complex, false if not
35620 isComplexSymbol: function(graphicName) {
35621 return (graphicName != "circle") && !!graphicName;
35624 CLASS_NAME: "OpenLayers.Renderer.Elements"
35627 /* ======================================================================
35628 OpenLayers/Renderer/SVG.js
35629 ====================================================================== */
35631 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
35632 * full list of contributors). Published under the 2-clause BSD license.
35633 * See license.txt in the OpenLayers distribution or repository for the
35634 * full text of the license. */
35637 * @requires OpenLayers/Renderer/Elements.js
35641 * Class: OpenLayers.Renderer.SVG
35644 * - <OpenLayers.Renderer.Elements>
35646 OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
35652 xmlns: "http://www.w3.org/2000/svg",
35655 * Property: xlinkns
35658 xlinkns: "http://www.w3.org/1999/xlink",
35661 * Constant: MAX_PIXEL
35662 * {Integer} Firefox has a limitation where values larger or smaller than
35663 * about 15000 in an SVG document lock the browser up. This
35669 * Property: translationParameters
35670 * {Object} Hash with "x" and "y" properties
35672 translationParameters: null,
35675 * Property: symbolMetrics
35676 * {Object} Cache for symbol metrics according to their svg coordinate
35677 * space. This is an object keyed by the symbol's id, and values are
35678 * an array of [width, centerX, centerY].
35680 symbolMetrics: null,
35683 * Constructor: OpenLayers.Renderer.SVG
35686 * containerID - {String}
35688 initialize: function(containerID) {
35689 if (!this.supported()) {
35692 OpenLayers.Renderer.Elements.prototype.initialize.apply(this,
35694 this.translationParameters = {x: 0, y: 0};
35696 this.symbolMetrics = {};
35700 * APIMethod: supported
35703 * {Boolean} Whether or not the browser supports the SVG renderer
35705 supported: function() {
35706 var svgFeature = "http://www.w3.org/TR/SVG11/feature#";
35707 return (document.implementation &&
35708 (document.implementation.hasFeature("org.w3c.svg", "1.0") ||
35709 document.implementation.hasFeature(svgFeature + "SVG", "1.1") ||
35710 document.implementation.hasFeature(svgFeature + "BasicStructure", "1.1") ));
35714 * Method: inValidRange
35715 * See #669 for more information
35720 * xyOnly - {Boolean} whether or not to just check for x and y, which means
35721 * to not take the current translation parameters into account if true.
35724 * {Boolean} Whether or not the 'x' and 'y' coordinates are in the
35727 inValidRange: function(x, y, xyOnly) {
35728 var left = x + (xyOnly ? 0 : this.translationParameters.x);
35729 var top = y + (xyOnly ? 0 : this.translationParameters.y);
35730 return (left >= -this.MAX_PIXEL && left <= this.MAX_PIXEL &&
35731 top >= -this.MAX_PIXEL && top <= this.MAX_PIXEL);
35735 * Method: setExtent
35738 * extent - {<OpenLayers.Bounds>}
35739 * resolutionChanged - {Boolean}
35742 * {Boolean} true to notify the layer that the new extent does not exceed
35743 * the coordinate range, and the features will not need to be redrawn.
35746 setExtent: function(extent, resolutionChanged) {
35747 var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments);
35749 var resolution = this.getResolution(),
35750 left = -extent.left / resolution,
35751 top = extent.top / resolution;
35753 // If the resolution has changed, start over changing the corner, because
35754 // the features will redraw.
35755 if (resolutionChanged) {
35759 var extentString = "0 0 " + this.size.w + " " + this.size.h;
35761 this.rendererRoot.setAttributeNS(null, "viewBox", extentString);
35762 this.translate(this.xOffset, 0);
35765 var inRange = this.translate(left - this.left + this.xOffset, top - this.top);
35767 // recenter the coordinate system
35768 this.setExtent(extent, true);
35770 return coordSysUnchanged && inRange;
35775 * Method: translate
35776 * Transforms the SVG coordinate system
35783 * {Boolean} true if the translation parameters are in the valid coordinates
35784 * range, false otherwise.
35786 translate: function(x, y) {
35787 if (!this.inValidRange(x, y, true)) {
35790 var transformString = "";
35792 transformString = "translate(" + x + "," + y + ")";
35794 this.root.setAttributeNS(null, "transform", transformString);
35795 this.translationParameters = {x: x, y: y};
35802 * Sets the size of the drawing surface.
35805 * size - {<OpenLayers.Size>} The size of the drawing surface
35807 setSize: function(size) {
35808 OpenLayers.Renderer.prototype.setSize.apply(this, arguments);
35810 this.rendererRoot.setAttributeNS(null, "width", this.size.w);
35811 this.rendererRoot.setAttributeNS(null, "height", this.size.h);
35815 * Method: getNodeType
35818 * geometry - {<OpenLayers.Geometry>}
35822 * {String} The corresponding node type for the specified geometry
35824 getNodeType: function(geometry, style) {
35825 var nodeType = null;
35826 switch (geometry.CLASS_NAME) {
35827 case "OpenLayers.Geometry.Point":
35828 if (style.externalGraphic) {
35829 nodeType = "image";
35830 } else if (this.isComplexSymbol(style.graphicName)) {
35833 nodeType = "circle";
35836 case "OpenLayers.Geometry.Rectangle":
35839 case "OpenLayers.Geometry.LineString":
35840 nodeType = "polyline";
35842 case "OpenLayers.Geometry.LinearRing":
35843 nodeType = "polygon";
35845 case "OpenLayers.Geometry.Polygon":
35846 case "OpenLayers.Geometry.Curve":
35857 * Use to set all the style attributes to a SVG node.
35859 * Takes care to adjust stroke width and point radius to be
35860 * resolution-relative
35863 * node - {SVGDomElement} An SVG element to decorate
35865 * options - {Object} Currently supported options include
35866 * 'isFilled' {Boolean} and
35867 * 'isStroked' {Boolean}
35869 setStyle: function(node, style, options) {
35870 style = style || node._style;
35871 options = options || node._options;
35873 var title = style.title || style.graphicTitle;
35875 node.setAttributeNS(null, "title", title);
35876 //Standards-conformant SVG
35877 // Prevent duplicate nodes. See issue https://github.com/openlayers/ol2/issues/92
35878 var titleNode = node.getElementsByTagName("title");
35879 if (titleNode.length > 0) {
35880 titleNode[0].firstChild.textContent = title;
35882 var label = this.nodeFactory(null, "title");
35883 label.textContent = title;
35884 node.appendChild(label);
35888 var r = parseFloat(node.getAttributeNS(null, "r"));
35889 var widthFactor = 1;
35891 if (node._geometryClass == "OpenLayers.Geometry.Point" && r) {
35892 node.style.visibility = "";
35893 if (style.graphic === false) {
35894 node.style.visibility = "hidden";
35895 } else if (style.externalGraphic) {
35896 pos = this.getPosition(node);
35897 if (style.graphicWidth && style.graphicHeight) {
35898 node.setAttributeNS(null, "preserveAspectRatio", "none");
35900 var width = style.graphicWidth || style.graphicHeight;
35901 var height = style.graphicHeight || style.graphicWidth;
35902 width = width ? width : style.pointRadius*2;
35903 height = height ? height : style.pointRadius*2;
35904 var xOffset = (style.graphicXOffset != undefined) ?
35905 style.graphicXOffset : -(0.5 * width);
35906 var yOffset = (style.graphicYOffset != undefined) ?
35907 style.graphicYOffset : -(0.5 * height);
35909 var opacity = style.graphicOpacity || style.fillOpacity;
35911 node.setAttributeNS(null, "x", (pos.x + xOffset).toFixed());
35912 node.setAttributeNS(null, "y", (pos.y + yOffset).toFixed());
35913 node.setAttributeNS(null, "width", width);
35914 node.setAttributeNS(null, "height", height);
35915 node.setAttributeNS(this.xlinkns, "xlink:href", style.externalGraphic);
35916 node.setAttributeNS(null, "style", "opacity: "+opacity);
35917 node.onclick = OpenLayers.Event.preventDefault;
35918 } else if (this.isComplexSymbol(style.graphicName)) {
35919 // the symbol viewBox is three times as large as the symbol
35920 var offset = style.pointRadius * 3;
35921 var size = offset * 2;
35922 var src = this.importSymbol(style.graphicName);
35923 pos = this.getPosition(node);
35924 widthFactor = this.symbolMetrics[src.id][0] * 3 / size;
35926 // remove the node from the dom before we modify it. This
35927 // prevents various rendering issues in Safari and FF
35928 var parent = node.parentNode;
35929 var nextSibling = node.nextSibling;
35931 parent.removeChild(node);
35934 // The more appropriate way to implement this would be use/defs,
35935 // but due to various issues in several browsers, it is safer to
35936 // copy the symbols instead of referencing them.
35937 // See e.g. ticket http://trac.osgeo.org/openlayers/ticket/2985
35938 // and this email thread
35939 // http://osgeo-org.1803224.n2.nabble.com/Select-Control-Ctrl-click-on-Feature-with-a-graphicName-opens-new-browser-window-tc5846039.html
35940 node.firstChild && node.removeChild(node.firstChild);
35941 node.appendChild(src.firstChild.cloneNode(true));
35942 node.setAttributeNS(null, "viewBox", src.getAttributeNS(null, "viewBox"));
35944 node.setAttributeNS(null, "width", size);
35945 node.setAttributeNS(null, "height", size);
35946 node.setAttributeNS(null, "x", pos.x - offset);
35947 node.setAttributeNS(null, "y", pos.y - offset);
35949 // now that the node has all its new properties, insert it
35950 // back into the dom where it was
35952 parent.insertBefore(node, nextSibling);
35953 } else if(parent) {
35954 parent.appendChild(node);
35957 node.setAttributeNS(null, "r", style.pointRadius);
35960 var rotation = style.rotation;
35962 if ((rotation !== undefined || node._rotation !== undefined) && pos) {
35963 node._rotation = rotation;
35965 if (node.nodeName !== "svg") {
35966 node.setAttributeNS(null, "transform",
35967 "rotate(" + rotation + " " + pos.x + " " +
35970 var metrics = this.symbolMetrics[src.id];
35971 node.firstChild.setAttributeNS(null, "transform", "rotate("
35974 + metrics[2] + ")");
35979 if (options.isFilled) {
35980 node.setAttributeNS(null, "fill", style.fillColor);
35981 node.setAttributeNS(null, "fill-opacity", style.fillOpacity);
35983 node.setAttributeNS(null, "fill", "none");
35986 if (options.isStroked) {
35987 node.setAttributeNS(null, "stroke", style.strokeColor);
35988 node.setAttributeNS(null, "stroke-opacity", style.strokeOpacity);
35989 node.setAttributeNS(null, "stroke-width", style.strokeWidth * widthFactor);
35990 node.setAttributeNS(null, "stroke-linecap", style.strokeLinecap || "round");
35991 // Hard-coded linejoin for now, to make it look the same as in VML.
35992 // There is no strokeLinejoin property yet for symbolizers.
35993 node.setAttributeNS(null, "stroke-linejoin", "round");
35994 style.strokeDashstyle && node.setAttributeNS(null,
35995 "stroke-dasharray", this.dashStyle(style, widthFactor));
35997 node.setAttributeNS(null, "stroke", "none");
36000 if (style.pointerEvents) {
36001 node.setAttributeNS(null, "pointer-events", style.pointerEvents);
36004 if (style.cursor != null) {
36005 node.setAttributeNS(null, "cursor", style.cursor);
36012 * Method: dashStyle
36016 * widthFactor - {Number}
36019 * {String} A SVG compliant 'stroke-dasharray' value
36021 dashStyle: function(style, widthFactor) {
36022 var w = style.strokeWidth * widthFactor;
36023 var str = style.strokeDashstyle;
36028 return [1, 4 * w].join();
36030 return [4 * w, 4 * w].join();
36032 return [4 * w, 4 * w, 1, 4 * w].join();
36034 return [8 * w, 4 * w].join();
36035 case 'longdashdot':
36036 return [8 * w, 4 * w, 1, 4 * w].join();
36038 return OpenLayers.String.trim(str).replace(/\s+/g, ",");
36043 * Method: createNode
36046 * type - {String} Kind of node to draw
36047 * id - {String} Id for node
36050 * {DOMElement} A new node of the given type and id
36052 createNode: function(type, id) {
36053 var node = document.createElementNS(this.xmlns, type);
36055 node.setAttributeNS(null, "id", id);
36061 * Method: nodeTypeCompare
36064 * node - {SVGDomElement} An SVG element
36065 * type - {String} Kind of node
36068 * {Boolean} Whether or not the specified node is of the specified type
36070 nodeTypeCompare: function(node, type) {
36071 return (type == node.nodeName);
36075 * Method: createRenderRoot
36078 * {DOMElement} The specific render engine's root element
36080 createRenderRoot: function() {
36081 var svg = this.nodeFactory(this.container.id + "_svgRoot", "svg");
36082 svg.style.display = "block";
36087 * Method: createRoot
36090 * suffix - {String} suffix to append to the id
36095 createRoot: function(suffix) {
36096 return this.nodeFactory(this.container.id + suffix, "g");
36100 * Method: createDefs
36103 * {DOMElement} The element to which we'll add the symbol definitions
36105 createDefs: function() {
36106 var defs = this.nodeFactory(this.container.id + "_defs", "defs");
36107 this.rendererRoot.appendChild(defs);
36111 /**************************************
36113 * GEOMETRY DRAWING FUNCTIONS *
36115 **************************************/
36118 * Method: drawPoint
36119 * This method is only called by the renderer itself.
36122 * node - {DOMElement}
36123 * geometry - {<OpenLayers.Geometry>}
36126 * {DOMElement} or false if the renderer could not draw the point
36128 drawPoint: function(node, geometry) {
36129 return this.drawCircle(node, geometry, 1);
36133 * Method: drawCircle
36134 * This method is only called by the renderer itself.
36137 * node - {DOMElement}
36138 * geometry - {<OpenLayers.Geometry>}
36142 * {DOMElement} or false if the renderer could not draw the circle
36144 drawCircle: function(node, geometry, radius) {
36145 var resolution = this.getResolution();
36146 var x = ((geometry.x - this.featureDx) / resolution + this.left);
36147 var y = (this.top - geometry.y / resolution);
36149 if (this.inValidRange(x, y)) {
36150 node.setAttributeNS(null, "cx", x);
36151 node.setAttributeNS(null, "cy", y);
36152 node.setAttributeNS(null, "r", radius);
36161 * Method: drawLineString
36162 * This method is only called by the renderer itself.
36165 * node - {DOMElement}
36166 * geometry - {<OpenLayers.Geometry>}
36169 * {DOMElement} or null if the renderer could not draw all components of
36170 * the linestring, or false if nothing could be drawn
36172 drawLineString: function(node, geometry) {
36173 var componentsResult = this.getComponentsString(geometry.components);
36174 if (componentsResult.path) {
36175 node.setAttributeNS(null, "points", componentsResult.path);
36176 return (componentsResult.complete ? node : null);
36183 * Method: drawLinearRing
36184 * This method is only called by the renderer itself.
36187 * node - {DOMElement}
36188 * geometry - {<OpenLayers.Geometry>}
36191 * {DOMElement} or null if the renderer could not draw all components
36192 * of the linear ring, or false if nothing could be drawn
36194 drawLinearRing: function(node, geometry) {
36195 var componentsResult = this.getComponentsString(geometry.components);
36196 if (componentsResult.path) {
36197 node.setAttributeNS(null, "points", componentsResult.path);
36198 return (componentsResult.complete ? node : null);
36205 * Method: drawPolygon
36206 * This method is only called by the renderer itself.
36209 * node - {DOMElement}
36210 * geometry - {<OpenLayers.Geometry>}
36213 * {DOMElement} or null if the renderer could not draw all components
36214 * of the polygon, or false if nothing could be drawn
36216 drawPolygon: function(node, geometry) {
36219 var complete = true;
36220 var linearRingResult, path;
36221 for (var j=0, len=geometry.components.length; j<len; j++) {
36223 linearRingResult = this.getComponentsString(
36224 geometry.components[j].components, " ");
36225 path = linearRingResult.path;
36228 complete = linearRingResult.complete && complete;
36235 node.setAttributeNS(null, "d", d);
36236 node.setAttributeNS(null, "fill-rule", "evenodd");
36237 return complete ? node : null;
36244 * Method: drawRectangle
36245 * This method is only called by the renderer itself.
36248 * node - {DOMElement}
36249 * geometry - {<OpenLayers.Geometry>}
36252 * {DOMElement} or false if the renderer could not draw the rectangle
36254 drawRectangle: function(node, geometry) {
36255 var resolution = this.getResolution();
36256 var x = ((geometry.x - this.featureDx) / resolution + this.left);
36257 var y = (this.top - geometry.y / resolution);
36259 if (this.inValidRange(x, y)) {
36260 node.setAttributeNS(null, "x", x);
36261 node.setAttributeNS(null, "y", y);
36262 node.setAttributeNS(null, "width", geometry.width / resolution);
36263 node.setAttributeNS(null, "height", geometry.height / resolution);
36272 * This method is only called by the renderer itself.
36275 * featureId - {String}
36277 * location - {<OpenLayers.Geometry.Point>}
36279 drawText: function(featureId, style, location) {
36280 var drawOutline = (!!style.labelOutlineWidth);
36281 // First draw text in halo color and size and overlay the
36282 // normal text afterwards
36284 var outlineStyle = OpenLayers.Util.extend({}, style);
36285 outlineStyle.fontColor = outlineStyle.labelOutlineColor;
36286 outlineStyle.fontStrokeColor = outlineStyle.labelOutlineColor;
36287 outlineStyle.fontStrokeWidth = style.labelOutlineWidth;
36288 if (style.labelOutlineOpacity) {
36289 outlineStyle.fontOpacity = style.labelOutlineOpacity;
36291 delete outlineStyle.labelOutlineWidth;
36292 this.drawText(featureId, outlineStyle, location);
36295 var resolution = this.getResolution();
36297 var x = ((location.x - this.featureDx) / resolution + this.left);
36298 var y = (location.y / resolution - this.top);
36300 var suffix = (drawOutline)?this.LABEL_OUTLINE_SUFFIX:this.LABEL_ID_SUFFIX;
36301 var label = this.nodeFactory(featureId + suffix, "text");
36303 label.setAttributeNS(null, "x", x);
36304 label.setAttributeNS(null, "y", -y);
36306 if (style.fontColor) {
36307 label.setAttributeNS(null, "fill", style.fontColor);
36309 if (style.fontStrokeColor) {
36310 label.setAttributeNS(null, "stroke", style.fontStrokeColor);
36312 if (style.fontStrokeWidth) {
36313 label.setAttributeNS(null, "stroke-width", style.fontStrokeWidth);
36315 if (style.fontOpacity) {
36316 label.setAttributeNS(null, "opacity", style.fontOpacity);
36318 if (style.fontFamily) {
36319 label.setAttributeNS(null, "font-family", style.fontFamily);
36321 if (style.fontSize) {
36322 label.setAttributeNS(null, "font-size", style.fontSize);
36324 if (style.fontWeight) {
36325 label.setAttributeNS(null, "font-weight", style.fontWeight);
36327 if (style.fontStyle) {
36328 label.setAttributeNS(null, "font-style", style.fontStyle);
36330 if (style.labelSelect === true) {
36331 label.setAttributeNS(null, "pointer-events", "visible");
36332 label._featureId = featureId;
36334 label.setAttributeNS(null, "pointer-events", "none");
36336 var align = style.labelAlign || OpenLayers.Renderer.defaultSymbolizer.labelAlign;
36337 label.setAttributeNS(null, "text-anchor",
36338 OpenLayers.Renderer.SVG.LABEL_ALIGN[align[0]] || "middle");
36340 if (OpenLayers.IS_GECKO === true) {
36341 label.setAttributeNS(null, "dominant-baseline",
36342 OpenLayers.Renderer.SVG.LABEL_ALIGN[align[1]] || "central");
36345 var labelRows = style.label.split('\n');
36346 var numRows = labelRows.length;
36347 while (label.childNodes.length > numRows) {
36348 label.removeChild(label.lastChild);
36350 for (var i = 0; i < numRows; i++) {
36351 var tspan = this.nodeFactory(featureId + suffix + "_tspan_" + i, "tspan");
36352 if (style.labelSelect === true) {
36353 tspan._featureId = featureId;
36354 tspan._geometry = location;
36355 tspan._geometryClass = location.CLASS_NAME;
36357 if (OpenLayers.IS_GECKO === false) {
36358 tspan.setAttributeNS(null, "baseline-shift",
36359 OpenLayers.Renderer.SVG.LABEL_VSHIFT[align[1]] || "-35%");
36361 tspan.setAttribute("x", x);
36363 var vfactor = OpenLayers.Renderer.SVG.LABEL_VFACTOR[align[1]];
36364 if (vfactor == null) {
36367 tspan.setAttribute("dy", (vfactor*(numRows-1)) + "em");
36369 tspan.setAttribute("dy", "1em");
36371 tspan.textContent = (labelRows[i] === '') ? ' ' : labelRows[i];
36372 if (!tspan.parentNode) {
36373 label.appendChild(tspan);
36377 if (!label.parentNode) {
36378 this.textRoot.appendChild(label);
36383 * Method: getComponentString
36386 * components - {Array(<OpenLayers.Geometry.Point>)} Array of points
36387 * separator - {String} character between coordinate pairs. Defaults to ","
36390 * {Object} hash with properties "path" (the string created from the
36391 * components and "complete" (false if the renderer was unable to
36392 * draw all components)
36394 getComponentsString: function(components, separator) {
36395 var renderCmp = [];
36396 var complete = true;
36397 var len = components.length;
36399 var str, component;
36400 for(var i=0; i<len; i++) {
36401 component = components[i];
36402 renderCmp.push(component);
36403 str = this.getShortString(component);
36407 // The current component is outside the valid range. Let's
36408 // see if the previous or next component is inside the range.
36409 // If so, add the coordinate of the intersection with the
36410 // valid range bounds.
36412 if (this.getShortString(components[i - 1])) {
36413 strings.push(this.clipLine(components[i],
36418 if (this.getShortString(components[i + 1])) {
36419 strings.push(this.clipLine(components[i],
36428 path: strings.join(separator || ","),
36435 * Given two points (one inside the valid range, and one outside),
36436 * clips the line betweeen the two points so that the new points are both
36437 * inside the valid range.
36440 * badComponent - {<OpenLayers.Geometry.Point>} original geometry of the
36442 * goodComponent - {<OpenLayers.Geometry.Point>} original geometry of the
36445 * {String} the SVG coordinate pair of the clipped point (like
36446 * getShortString), or an empty string if both passed componets are at
36449 clipLine: function(badComponent, goodComponent) {
36450 if (goodComponent.equals(badComponent)) {
36453 var resolution = this.getResolution();
36454 var maxX = this.MAX_PIXEL - this.translationParameters.x;
36455 var maxY = this.MAX_PIXEL - this.translationParameters.y;
36456 var x1 = (goodComponent.x - this.featureDx) / resolution + this.left;
36457 var y1 = this.top - goodComponent.y / resolution;
36458 var x2 = (badComponent.x - this.featureDx) / resolution + this.left;
36459 var y2 = this.top - badComponent.y / resolution;
36461 if (x2 < -maxX || x2 > maxX) {
36462 k = (y2 - y1) / (x2 - x1);
36463 x2 = x2 < 0 ? -maxX : maxX;
36464 y2 = y1 + (x2 - x1) * k;
36466 if (y2 < -maxY || y2 > maxY) {
36467 k = (x2 - x1) / (y2 - y1);
36468 y2 = y2 < 0 ? -maxY : maxY;
36469 x2 = x1 + (y2 - y1) * k;
36471 return x2 + "," + y2;
36475 * Method: getShortString
36478 * point - {<OpenLayers.Geometry.Point>}
36481 * {String} or false if point is outside the valid range
36483 getShortString: function(point) {
36484 var resolution = this.getResolution();
36485 var x = ((point.x - this.featureDx) / resolution + this.left);
36486 var y = (this.top - point.y / resolution);
36488 if (this.inValidRange(x, y)) {
36489 return x + "," + y;
36496 * Method: getPosition
36497 * Finds the position of an svg node.
36500 * node - {DOMElement}
36503 * {Object} hash with x and y properties, representing the coordinates
36504 * within the svg coordinate system
36506 getPosition: function(node) {
36508 x: parseFloat(node.getAttributeNS(null, "cx")),
36509 y: parseFloat(node.getAttributeNS(null, "cy"))
36514 * Method: importSymbol
36515 * add a new symbol definition from the rendererer's symbol hash
36518 * graphicName - {String} name of the symbol to import
36521 * {DOMElement} - the imported symbol
36523 importSymbol: function (graphicName) {
36525 // create svg defs tag
36526 this.defs = this.createDefs();
36528 var id = this.container.id + "-" + graphicName;
36530 // check if symbol already exists in the defs
36531 var existing = document.getElementById(id);
36532 if (existing != null) {
36536 var symbol = OpenLayers.Renderer.symbol[graphicName];
36538 throw new Error(graphicName + ' is not a valid symbol name');
36541 var symbolNode = this.nodeFactory(id, "symbol");
36542 var node = this.nodeFactory(null, "polygon");
36543 symbolNode.appendChild(node);
36544 var symbolExtent = new OpenLayers.Bounds(
36545 Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
36549 for (var i=0; i<symbol.length; i=i+2) {
36552 symbolExtent.left = Math.min(symbolExtent.left, x);
36553 symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
36554 symbolExtent.right = Math.max(symbolExtent.right, x);
36555 symbolExtent.top = Math.max(symbolExtent.top, y);
36556 points.push(x, ",", y);
36559 node.setAttributeNS(null, "points", points.join(" "));
36561 var width = symbolExtent.getWidth();
36562 var height = symbolExtent.getHeight();
36563 // create a viewBox three times as large as the symbol itself,
36564 // to allow for strokeWidth being displayed correctly at the corners.
36565 var viewBox = [symbolExtent.left - width,
36566 symbolExtent.bottom - height, width * 3, height * 3];
36567 symbolNode.setAttributeNS(null, "viewBox", viewBox.join(" "));
36568 this.symbolMetrics[id] = [
36569 Math.max(width, height),
36570 symbolExtent.getCenterLonLat().lon,
36571 symbolExtent.getCenterLonLat().lat
36574 this.defs.appendChild(symbolNode);
36579 * Method: getFeatureIdFromEvent
36582 * evt - {Object} An <OpenLayers.Event> object
36585 * {String} A feature id or undefined.
36587 getFeatureIdFromEvent: function(evt) {
36588 var featureId = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent.apply(this, arguments);
36590 var target = evt.target;
36591 featureId = target.parentNode && target != this.rendererRoot ?
36592 target.parentNode._featureId : undefined;
36597 CLASS_NAME: "OpenLayers.Renderer.SVG"
36601 * Constant: OpenLayers.Renderer.SVG.LABEL_ALIGN
36604 OpenLayers.Renderer.SVG.LABEL_ALIGN = {
36612 * Constant: OpenLayers.Renderer.SVG.LABEL_VSHIFT
36615 OpenLayers.Renderer.SVG.LABEL_VSHIFT = {
36617 // http://www.w3.org/Graphics/SVG/Test/20061213/htmlObjectHarness/full-text-align-02-b.html
36618 // a baseline-shift of -70% shifts the text exactly from the
36619 // bottom to the top of the baseline, so -35% moves the text to
36620 // the center of the baseline.
36626 * Constant: OpenLayers.Renderer.SVG.LABEL_VFACTOR
36629 OpenLayers.Renderer.SVG.LABEL_VFACTOR = {
36635 * Function: OpenLayers.Renderer.SVG.preventDefault
36636 * *Deprecated*. Use <OpenLayers.Event.preventDefault> method instead.
36637 * Used to prevent default events (especially opening images in a new tab on
36638 * ctrl-click) from being executed for externalGraphic symbols
36640 OpenLayers.Renderer.SVG.preventDefault = function(e) {
36641 OpenLayers.Event.preventDefault(e);
36643 /* ======================================================================
36644 OpenLayers/Renderer/VML.js
36645 ====================================================================== */
36647 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
36648 * full list of contributors). Published under the 2-clause BSD license.
36649 * See license.txt in the OpenLayers distribution or repository for the
36650 * full text of the license. */
36653 * @requires OpenLayers/Renderer/Elements.js
36657 * Class: OpenLayers.Renderer.VML
36658 * Render vector features in browsers with VML capability. Construct a new
36659 * VML renderer with the <OpenLayers.Renderer.VML> constructor.
36661 * Note that for all calculations in this class, we use (num | 0) to truncate a
36662 * float value to an integer. This is done because it seems that VML doesn't
36663 * support float values.
36666 * - <OpenLayers.Renderer.Elements>
36668 OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
36672 * {String} XML Namespace URN
36674 xmlns: "urn:schemas-microsoft-com:vml",
36677 * Property: symbolCache
36678 * {DOMElement} node holding symbols. This hash is keyed by symbol name,
36679 * and each value is a hash with a "path" and an "extent" property.
36685 * {Object} Hash with "x" and "y" properties
36690 * Constructor: OpenLayers.Renderer.VML
36691 * Create a new VML renderer.
36694 * containerID - {String} The id for the element that contains the renderer
36696 initialize: function(containerID) {
36697 if (!this.supported()) {
36700 if (!document.namespaces.olv) {
36701 document.namespaces.add("olv", this.xmlns);
36702 var style = document.createStyleSheet();
36703 var shapes = ['shape','rect', 'oval', 'fill', 'stroke', 'imagedata', 'group','textbox'];
36704 for (var i = 0, len = shapes.length; i < len; i++) {
36706 style.addRule('olv\\:' + shapes[i], "behavior: url(#default#VML); " +
36707 "position: absolute; display: inline-block;");
36711 OpenLayers.Renderer.Elements.prototype.initialize.apply(this,
36716 * APIMethod: supported
36717 * Determine whether a browser supports this renderer.
36720 * {Boolean} The browser supports the VML renderer
36722 supported: function() {
36723 return !!(document.namespaces);
36727 * Method: setExtent
36728 * Set the renderer's extent
36731 * extent - {<OpenLayers.Bounds>}
36732 * resolutionChanged - {Boolean}
36735 * {Boolean} true to notify the layer that the new extent does not exceed
36736 * the coordinate range, and the features will not need to be redrawn.
36738 setExtent: function(extent, resolutionChanged) {
36739 var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments);
36740 var resolution = this.getResolution();
36742 var left = (extent.left/resolution) | 0;
36743 var top = (extent.top/resolution - this.size.h) | 0;
36744 if (resolutionChanged || !this.offset) {
36745 this.offset = {x: left, y: top};
36749 left = left - this.offset.x;
36750 top = top - this.offset.y;
36754 var org = (left - this.xOffset) + " " + top;
36755 this.root.coordorigin = org;
36756 var roots = [this.root, this.vectorRoot, this.textRoot];
36758 for(var i=0, len=roots.length; i<len; ++i) {
36761 var size = this.size.w + " " + this.size.h;
36762 root.coordsize = size;
36765 // flip the VML display Y axis upside down so it
36766 // matches the display Y axis of the map
36767 this.root.style.flip = "y";
36769 return coordSysUnchanged;
36775 * Set the size of the drawing surface
36778 * size - {<OpenLayers.Size>} the size of the drawing surface
36780 setSize: function(size) {
36781 OpenLayers.Renderer.prototype.setSize.apply(this, arguments);
36783 // setting width and height on all roots to avoid flicker which we
36784 // would get with 100% width and height on child roots
36791 var w = this.size.w + "px";
36792 var h = this.size.h + "px";
36794 for(var i=0, len=roots.length; i<len; ++i) {
36796 root.style.width = w;
36797 root.style.height = h;
36802 * Method: getNodeType
36803 * Get the node type for a geometry and style
36806 * geometry - {<OpenLayers.Geometry>}
36810 * {String} The corresponding node type for the specified geometry
36812 getNodeType: function(geometry, style) {
36813 var nodeType = null;
36814 switch (geometry.CLASS_NAME) {
36815 case "OpenLayers.Geometry.Point":
36816 if (style.externalGraphic) {
36817 nodeType = "olv:rect";
36818 } else if (this.isComplexSymbol(style.graphicName)) {
36819 nodeType = "olv:shape";
36821 nodeType = "olv:oval";
36824 case "OpenLayers.Geometry.Rectangle":
36825 nodeType = "olv:rect";
36827 case "OpenLayers.Geometry.LineString":
36828 case "OpenLayers.Geometry.LinearRing":
36829 case "OpenLayers.Geometry.Polygon":
36830 case "OpenLayers.Geometry.Curve":
36831 nodeType = "olv:shape";
36841 * Use to set all the style attributes to a VML node.
36844 * node - {DOMElement} An VML element to decorate
36846 * options - {Object} Currently supported options include
36847 * 'isFilled' {Boolean} and
36848 * 'isStroked' {Boolean}
36849 * geometry - {<OpenLayers.Geometry>}
36851 setStyle: function(node, style, options, geometry) {
36852 style = style || node._style;
36853 options = options || node._options;
36854 var fillColor = style.fillColor;
36856 var title = style.title || style.graphicTitle;
36858 node.title = title;
36861 if (node._geometryClass === "OpenLayers.Geometry.Point") {
36862 if (style.externalGraphic) {
36863 options.isFilled = true;
36864 var width = style.graphicWidth || style.graphicHeight;
36865 var height = style.graphicHeight || style.graphicWidth;
36866 width = width ? width : style.pointRadius*2;
36867 height = height ? height : style.pointRadius*2;
36869 var resolution = this.getResolution();
36870 var xOffset = (style.graphicXOffset != undefined) ?
36871 style.graphicXOffset : -(0.5 * width);
36872 var yOffset = (style.graphicYOffset != undefined) ?
36873 style.graphicYOffset : -(0.5 * height);
36875 node.style.left = ((((geometry.x - this.featureDx)/resolution - this.offset.x)+xOffset) | 0) + "px";
36876 node.style.top = (((geometry.y/resolution - this.offset.y)-(yOffset+height)) | 0) + "px";
36877 node.style.width = width + "px";
36878 node.style.height = height + "px";
36879 node.style.flip = "y";
36881 // modify fillColor and options for stroke styling below
36882 fillColor = "none";
36883 options.isStroked = false;
36884 } else if (this.isComplexSymbol(style.graphicName)) {
36885 var cache = this.importSymbol(style.graphicName);
36886 node.path = cache.path;
36887 node.coordorigin = cache.left + "," + cache.bottom;
36888 var size = cache.size;
36889 node.coordsize = size + "," + size;
36890 this.drawCircle(node, geometry, style.pointRadius);
36891 node.style.flip = "y";
36893 this.drawCircle(node, geometry, style.pointRadius);
36898 if (options.isFilled) {
36899 node.fillcolor = fillColor;
36901 node.filled = "false";
36903 var fills = node.getElementsByTagName("fill");
36904 var fill = (fills.length == 0) ? null : fills[0];
36905 if (!options.isFilled) {
36907 node.removeChild(fill);
36911 fill = this.createNode('olv:fill', node.id + "_fill");
36913 fill.opacity = style.fillOpacity;
36915 if (node._geometryClass === "OpenLayers.Geometry.Point" &&
36916 style.externalGraphic) {
36918 // override fillOpacity
36919 if (style.graphicOpacity) {
36920 fill.opacity = style.graphicOpacity;
36923 fill.src = style.externalGraphic;
36924 fill.type = "frame";
36926 if (!(style.graphicWidth && style.graphicHeight)) {
36927 fill.aspect = "atmost";
36930 if (fill.parentNode != node) {
36931 node.appendChild(fill);
36935 // additional rendering for rotated graphics or symbols
36936 var rotation = style.rotation;
36937 if ((rotation !== undefined || node._rotation !== undefined)) {
36938 node._rotation = rotation;
36939 if (style.externalGraphic) {
36940 this.graphicRotate(node, xOffset, yOffset, style);
36941 // make the fill fully transparent, because we now have
36942 // the graphic as imagedata element. We cannot just remove
36943 // the fill, because this is part of the hack described
36944 // in graphicRotate
36946 } else if(node._geometryClass === "OpenLayers.Geometry.Point") {
36947 node.style.rotation = rotation || 0;
36952 var strokes = node.getElementsByTagName("stroke");
36953 var stroke = (strokes.length == 0) ? null : strokes[0];
36954 if (!options.isStroked) {
36955 node.stroked = false;
36961 stroke = this.createNode('olv:stroke', node.id + "_stroke");
36962 node.appendChild(stroke);
36965 stroke.color = style.strokeColor;
36966 stroke.weight = style.strokeWidth + "px";
36967 stroke.opacity = style.strokeOpacity;
36968 stroke.endcap = style.strokeLinecap == 'butt' ? 'flat' :
36969 (style.strokeLinecap || 'round');
36970 if (style.strokeDashstyle) {
36971 stroke.dashstyle = this.dashStyle(style);
36975 if (style.cursor != "inherit" && style.cursor != null) {
36976 node.style.cursor = style.cursor;
36982 * Method: graphicRotate
36983 * If a point is to be styled with externalGraphic and rotation, VML fills
36984 * cannot be used to display the graphic, because rotation of graphic
36985 * fills is not supported by the VML implementation of Internet Explorer.
36986 * This method creates a olv:imagedata element inside the VML node,
36987 * DXImageTransform.Matrix and BasicImage filters for rotation and
36988 * opacity, and a 3-step hack to remove rendering artefacts from the
36989 * graphic and preserve the ability of graphics to trigger events.
36990 * Finally, OpenLayers methods are used to determine the correct
36991 * insertion point of the rotated image, because DXImageTransform.Matrix
36992 * does the rotation without the ability to specify a rotation center
36996 * node - {DOMElement}
36997 * xOffset - {Number} rotation center relative to image, x coordinate
36998 * yOffset - {Number} rotation center relative to image, y coordinate
37001 graphicRotate: function(node, xOffset, yOffset, style) {
37002 var style = style || node._style;
37003 var rotation = style.rotation || 0;
37005 var aspectRatio, size;
37006 if (!(style.graphicWidth && style.graphicHeight)) {
37007 // load the image to determine its size
37008 var img = new Image();
37009 img.onreadystatechange = OpenLayers.Function.bind(function() {
37010 if(img.readyState == "complete" ||
37011 img.readyState == "interactive") {
37012 aspectRatio = img.width / img.height;
37013 size = Math.max(style.pointRadius * 2,
37014 style.graphicWidth || 0,
37015 style.graphicHeight || 0);
37016 xOffset = xOffset * aspectRatio;
37017 style.graphicWidth = size * aspectRatio;
37018 style.graphicHeight = size;
37019 this.graphicRotate(node, xOffset, yOffset, style);
37022 img.src = style.externalGraphic;
37024 // will be called again by the onreadystate handler
37027 size = Math.max(style.graphicWidth, style.graphicHeight);
37028 aspectRatio = style.graphicWidth / style.graphicHeight;
37031 var width = Math.round(style.graphicWidth || size * aspectRatio);
37032 var height = Math.round(style.graphicHeight || size);
37033 node.style.width = width + "px";
37034 node.style.height = height + "px";
37036 // Three steps are required to remove artefacts for images with
37037 // transparent backgrounds (resulting from using DXImageTransform
37038 // filters on svg objects), while preserving awareness for browser
37039 // events on images:
37040 // - Use the fill as usual (like for unrotated images) to handle
37042 // - specify an imagedata element with the same src as the fill
37043 // - style the imagedata element with an AlphaImageLoader filter
37045 var image = document.getElementById(node.id + "_image");
37047 image = this.createNode("olv:imagedata", node.id + "_image");
37048 node.appendChild(image);
37050 image.style.width = width + "px";
37051 image.style.height = height + "px";
37052 image.src = style.externalGraphic;
37053 image.style.filter =
37054 "progid:DXImageTransform.Microsoft.AlphaImageLoader(" +
37055 "src='', sizingMethod='scale')";
37057 var rot = rotation * Math.PI / 180;
37058 var sintheta = Math.sin(rot);
37059 var costheta = Math.cos(rot);
37061 // do the rotation on the image
37063 "progid:DXImageTransform.Microsoft.Matrix(M11=" + costheta +
37064 ",M12=" + (-sintheta) + ",M21=" + sintheta + ",M22=" + costheta +
37065 ",SizingMethod='auto expand')\n";
37067 // set the opacity (needed for the imagedata)
37068 var opacity = style.graphicOpacity || style.fillOpacity;
37069 if (opacity && opacity != 1) {
37071 "progid:DXImageTransform.Microsoft.BasicImage(opacity=" +
37074 node.style.filter = filter;
37076 // do the rotation again on a box, so we know the insertion point
37077 var centerPoint = new OpenLayers.Geometry.Point(-xOffset, -yOffset);
37078 var imgBox = new OpenLayers.Bounds(0, 0, width, height).toGeometry();
37079 imgBox.rotate(style.rotation, centerPoint);
37080 var imgBounds = imgBox.getBounds();
37082 node.style.left = Math.round(
37083 parseInt(node.style.left) + imgBounds.left) + "px";
37084 node.style.top = Math.round(
37085 parseInt(node.style.top) - imgBounds.bottom) + "px";
37090 * Does some node postprocessing to work around browser issues:
37091 * - Some versions of Internet Explorer seem to be unable to set fillcolor
37092 * and strokecolor to "none" correctly before the fill node is appended
37093 * to a visible vml node. This method takes care of that and sets
37094 * fillcolor and strokecolor again if needed.
37095 * - In some cases, a node won't become visible after being drawn. Setting
37096 * style.visibility to "visible" works around that.
37099 * node - {DOMElement}
37101 postDraw: function(node) {
37102 node.style.visibility = "visible";
37103 var fillColor = node._style.fillColor;
37104 var strokeColor = node._style.strokeColor;
37105 if (fillColor == "none" &&
37106 node.fillcolor != fillColor) {
37107 node.fillcolor = fillColor;
37109 if (strokeColor == "none" &&
37110 node.strokecolor != strokeColor) {
37111 node.strokecolor = strokeColor;
37117 * Method: setNodeDimension
37118 * Get the geometry's bounds, convert it to our vml coordinate system,
37119 * then set the node's position, size, and local coordinate system.
37122 * node - {DOMElement}
37123 * geometry - {<OpenLayers.Geometry>}
37125 setNodeDimension: function(node, geometry) {
37127 var bbox = geometry.getBounds();
37129 var resolution = this.getResolution();
37132 new OpenLayers.Bounds(((bbox.left - this.featureDx)/resolution - this.offset.x) | 0,
37133 (bbox.bottom/resolution - this.offset.y) | 0,
37134 ((bbox.right - this.featureDx)/resolution - this.offset.x) | 0,
37135 (bbox.top/resolution - this.offset.y) | 0);
37137 // Set the internal coordinate system to draw the path
37138 node.style.left = scaledBox.left + "px";
37139 node.style.top = scaledBox.top + "px";
37140 node.style.width = scaledBox.getWidth() + "px";
37141 node.style.height = scaledBox.getHeight() + "px";
37143 node.coordorigin = scaledBox.left + " " + scaledBox.top;
37144 node.coordsize = scaledBox.getWidth()+ " " + scaledBox.getHeight();
37149 * Method: dashStyle
37155 * {String} A VML compliant 'stroke-dasharray' value
37157 dashStyle: function(style) {
37158 var dash = style.strokeDashstyle;
37165 case 'longdashdot':
37168 // very basic guessing of dash style patterns
37169 var parts = dash.split(/[ ,]/);
37170 if (parts.length == 2) {
37171 if (1*parts[0] >= 2*parts[1]) {
37174 return (parts[0] == 1 || parts[1] == 1) ? "dot" : "dash";
37175 } else if (parts.length == 4) {
37176 return (1*parts[0] >= 2*parts[1]) ? "longdashdot" :
37184 * Method: createNode
37185 * Create a new node
37188 * type - {String} Kind of node to draw
37189 * id - {String} Id for node
37192 * {DOMElement} A new node of the given type and id
37194 createNode: function(type, id) {
37195 var node = document.createElement(type);
37200 // IE hack to make elements unselectable, to prevent 'blue flash'
37201 // while dragging vectors; #1410
37202 node.unselectable = 'on';
37203 node.onselectstart = OpenLayers.Function.False;
37209 * Method: nodeTypeCompare
37210 * Determine whether a node is of a given type
37213 * node - {DOMElement} An VML element
37214 * type - {String} Kind of node
37217 * {Boolean} Whether or not the specified node is of the specified type
37219 nodeTypeCompare: function(node, type) {
37222 var subType = type;
37223 var splitIndex = subType.indexOf(":");
37224 if (splitIndex != -1) {
37225 subType = subType.substr(splitIndex+1);
37229 var nodeName = node.nodeName;
37230 splitIndex = nodeName.indexOf(":");
37231 if (splitIndex != -1) {
37232 nodeName = nodeName.substr(splitIndex+1);
37235 return (subType == nodeName);
37239 * Method: createRenderRoot
37240 * Create the renderer root
37243 * {DOMElement} The specific render engine's root element
37245 createRenderRoot: function() {
37246 return this.nodeFactory(this.container.id + "_vmlRoot", "div");
37250 * Method: createRoot
37251 * Create the main root element
37254 * suffix - {String} suffix to append to the id
37259 createRoot: function(suffix) {
37260 return this.nodeFactory(this.container.id + suffix, "olv:group");
37263 /**************************************
37265 * GEOMETRY DRAWING FUNCTIONS *
37267 **************************************/
37270 * Method: drawPoint
37274 * node - {DOMElement}
37275 * geometry - {<OpenLayers.Geometry>}
37278 * {DOMElement} or false if the point could not be drawn
37280 drawPoint: function(node, geometry) {
37281 return this.drawCircle(node, geometry, 1);
37285 * Method: drawCircle
37287 * Size and Center a circle given geometry (x,y center) and radius
37290 * node - {DOMElement}
37291 * geometry - {<OpenLayers.Geometry>}
37295 * {DOMElement} or false if the circle could not ne drawn
37297 drawCircle: function(node, geometry, radius) {
37298 if(!isNaN(geometry.x)&& !isNaN(geometry.y)) {
37299 var resolution = this.getResolution();
37301 node.style.left = ((((geometry.x - this.featureDx) /resolution - this.offset.x) | 0) - radius) + "px";
37302 node.style.top = (((geometry.y /resolution - this.offset.y) | 0) - radius) + "px";
37304 var diameter = radius * 2;
37306 node.style.width = diameter + "px";
37307 node.style.height = diameter + "px";
37315 * Method: drawLineString
37316 * Render a linestring.
37319 * node - {DOMElement}
37320 * geometry - {<OpenLayers.Geometry>}
37325 drawLineString: function(node, geometry) {
37326 return this.drawLine(node, geometry, false);
37330 * Method: drawLinearRing
37331 * Render a linearring
37334 * node - {DOMElement}
37335 * geometry - {<OpenLayers.Geometry>}
37340 drawLinearRing: function(node, geometry) {
37341 return this.drawLine(node, geometry, true);
37349 * node - {DOMElement}
37350 * geometry - {<OpenLayers.Geometry>}
37351 * closeLine - {Boolean} Close the line? (make it a ring?)
37356 drawLine: function(node, geometry, closeLine) {
37358 this.setNodeDimension(node, geometry);
37360 var resolution = this.getResolution();
37361 var numComponents = geometry.components.length;
37362 var parts = new Array(numComponents);
37365 for (var i = 0; i < numComponents; i++) {
37366 comp = geometry.components[i];
37367 x = ((comp.x - this.featureDx)/resolution - this.offset.x) | 0;
37368 y = (comp.y/resolution - this.offset.y) | 0;
37369 parts[i] = " " + x + "," + y + " l ";
37371 var end = (closeLine) ? " x e" : " e";
37372 node.path = "m" + parts.join("") + end;
37377 * Method: drawPolygon
37381 * node - {DOMElement}
37382 * geometry - {<OpenLayers.Geometry>}
37387 drawPolygon: function(node, geometry) {
37388 this.setNodeDimension(node, geometry);
37390 var resolution = this.getResolution();
37393 var j, jj, points, area, first, second, i, ii, comp, pathComp, x, y;
37394 for (j=0, jj=geometry.components.length; j<jj; j++) {
37396 points = geometry.components[j].components;
37397 // we only close paths of interior rings with area
37401 for (i=0, ii=points.length; i<ii; i++) {
37403 x = ((comp.x - this.featureDx) / resolution - this.offset.x) | 0;
37404 y = (comp.y / resolution - this.offset.y) | 0;
37405 pathComp = " " + x + "," + y;
37406 path.push(pathComp);
37411 // IE improperly renders sub-paths that have no area.
37412 // Instead of checking the area of every ring, we confirm
37413 // the ring has at least three distinct points. This does
37414 // not catch all non-zero area cases, but it greatly improves
37415 // interior ring digitizing and is a minor performance hit
37416 // when rendering rings with many points.
37419 } else if (first != pathComp) {
37422 } else if (second != pathComp) {
37429 path.push(area ? " x " : " ");
37432 node.path = path.join("");
37437 * Method: drawRectangle
37438 * Render a rectangle
37441 * node - {DOMElement}
37442 * geometry - {<OpenLayers.Geometry>}
37447 drawRectangle: function(node, geometry) {
37448 var resolution = this.getResolution();
37450 node.style.left = (((geometry.x - this.featureDx)/resolution - this.offset.x) | 0) + "px";
37451 node.style.top = ((geometry.y/resolution - this.offset.y) | 0) + "px";
37452 node.style.width = ((geometry.width/resolution) | 0) + "px";
37453 node.style.height = ((geometry.height/resolution) | 0) + "px";
37460 * This method is only called by the renderer itself.
37463 * featureId - {String}
37465 * location - {<OpenLayers.Geometry.Point>}
37467 drawText: function(featureId, style, location) {
37468 var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "olv:rect");
37469 var textbox = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_textbox", "olv:textbox");
37471 var resolution = this.getResolution();
37472 label.style.left = (((location.x - this.featureDx)/resolution - this.offset.x) | 0) + "px";
37473 label.style.top = ((location.y/resolution - this.offset.y) | 0) + "px";
37474 label.style.flip = "y";
37476 textbox.innerText = style.label;
37478 if (style.cursor != "inherit" && style.cursor != null) {
37479 textbox.style.cursor = style.cursor;
37481 if (style.fontColor) {
37482 textbox.style.color = style.fontColor;
37484 if (style.fontOpacity) {
37485 textbox.style.filter = 'alpha(opacity=' + (style.fontOpacity * 100) + ')';
37487 if (style.fontFamily) {
37488 textbox.style.fontFamily = style.fontFamily;
37490 if (style.fontSize) {
37491 textbox.style.fontSize = style.fontSize;
37493 if (style.fontWeight) {
37494 textbox.style.fontWeight = style.fontWeight;
37496 if (style.fontStyle) {
37497 textbox.style.fontStyle = style.fontStyle;
37499 if(style.labelSelect === true) {
37500 label._featureId = featureId;
37501 textbox._featureId = featureId;
37502 textbox._geometry = location;
37503 textbox._geometryClass = location.CLASS_NAME;
37505 textbox.style.whiteSpace = "nowrap";
37506 // fun with IE: IE7 in standards compliant mode does not display any
37507 // text with a left inset of 0. So we set this to 1px and subtract one
37508 // pixel later when we set label.style.left
37509 textbox.inset = "1px,0px,0px,0px";
37511 if(!label.parentNode) {
37512 label.appendChild(textbox);
37513 this.textRoot.appendChild(label);
37516 var align = style.labelAlign || "cm";
37517 if (align.length == 1) {
37520 var xshift = textbox.clientWidth *
37521 (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(0,1)]);
37522 var yshift = textbox.clientHeight *
37523 (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(1,1)]);
37524 label.style.left = parseInt(label.style.left)-xshift-1+"px";
37525 label.style.top = parseInt(label.style.top)+yshift+"px";
37531 * moves this renderer's root to a different renderer.
37534 * renderer - {<OpenLayers.Renderer>} target renderer for the moved root
37535 * root - {DOMElement} optional root node. To be used when this renderer
37536 * holds roots from multiple layers to tell this method which one to
37540 * {Boolean} true if successful, false otherwise
37542 moveRoot: function(renderer) {
37543 var layer = this.map.getLayer(renderer.container.id);
37544 if(layer instanceof OpenLayers.Layer.Vector.RootContainer) {
37545 layer = this.map.getLayer(this.container.id);
37547 layer && layer.renderer.clear();
37548 OpenLayers.Renderer.Elements.prototype.moveRoot.apply(this, arguments);
37549 layer && layer.redraw();
37553 * Method: importSymbol
37554 * add a new symbol definition from the rendererer's symbol hash
37557 * graphicName - {String} name of the symbol to import
37560 * {Object} - hash of {DOMElement} "symbol" and {Number} "size"
37562 importSymbol: function (graphicName) {
37563 var id = this.container.id + "-" + graphicName;
37565 // check if symbol already exists in the cache
37566 var cache = this.symbolCache[id];
37571 var symbol = OpenLayers.Renderer.symbol[graphicName];
37573 throw new Error(graphicName + ' is not a valid symbol name');
37576 var symbolExtent = new OpenLayers.Bounds(
37577 Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
37579 var pathitems = ["m"];
37580 for (var i=0; i<symbol.length; i=i+2) {
37582 var y = symbol[i+1];
37583 symbolExtent.left = Math.min(symbolExtent.left, x);
37584 symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
37585 symbolExtent.right = Math.max(symbolExtent.right, x);
37586 symbolExtent.top = Math.max(symbolExtent.top, y);
37591 pathitems.push("l");
37594 pathitems.push("x e");
37595 var path = pathitems.join(" ");
37597 var diff = (symbolExtent.getWidth() - symbolExtent.getHeight()) / 2;
37599 symbolExtent.bottom = symbolExtent.bottom - diff;
37600 symbolExtent.top = symbolExtent.top + diff;
37602 symbolExtent.left = symbolExtent.left + diff;
37603 symbolExtent.right = symbolExtent.right - diff;
37608 size: symbolExtent.getWidth(), // equals getHeight() now
37609 left: symbolExtent.left,
37610 bottom: symbolExtent.bottom
37612 this.symbolCache[id] = cache;
37617 CLASS_NAME: "OpenLayers.Renderer.VML"
37621 * Constant: OpenLayers.Renderer.VML.LABEL_SHIFT
37624 OpenLayers.Renderer.VML.LABEL_SHIFT = {
37632 /* ======================================================================
37634 ====================================================================== */
37636 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
37637 * full list of contributors). Published under the 2-clause BSD license.
37638 * See license.txt in the OpenLayers distribution or repository for the
37639 * full text of the license. */
37643 * @requires OpenLayers/BaseTypes/Class.js
37644 * @requires OpenLayers/Util.js
37648 * Class: OpenLayers.Tile
37649 * This is a class designed to designate a single tile, however
37650 * it is explicitly designed to do relatively little. Tiles store
37651 * information about themselves -- such as the URL that they are related
37652 * to, and their size - but do not add themselves to the layer div
37653 * automatically, for example. Create a new tile with the
37654 * <OpenLayers.Tile> constructor, or a subclass.
37656 * TBD 3.0 - remove reference to url in above paragraph
37659 OpenLayers.Tile = OpenLayers.Class({
37662 * APIProperty: events
37663 * {<OpenLayers.Events>} An events object that handles all
37664 * events on the tile.
37666 * Register a listener for a particular event with the following syntax:
37668 * tile.events.register(type, obj, listener);
37671 * Supported event types:
37672 * beforedraw - Triggered before the tile is drawn. Used to defer
37673 * drawing to an animation queue. To defer drawing, listeners need
37674 * to return false, which will abort drawing. The queue handler needs
37675 * to call <draw>(true) to actually draw the tile.
37676 * loadstart - Triggered when tile loading starts.
37677 * loadend - Triggered when tile loading ends.
37678 * loaderror - Triggered before the loadend event (i.e. when the tile is
37679 * still hidden) if the tile could not be loaded.
37680 * reload - Triggered when an already loading tile is reloaded.
37681 * unload - Triggered before a tile is unloaded.
37686 * APIProperty: eventListeners
37687 * {Object} If set as an option at construction, the eventListeners
37688 * object will be registered with <OpenLayers.Events.on>. Object
37689 * structure must be a listeners object as shown in the example for
37690 * the events.on method.
37692 * This options can be set in the ``tileOptions`` option from
37693 * <OpenLayers.Layer.Grid>. For example, to be notified of the
37694 * ``loadend`` event of each tiles:
37696 * new OpenLayers.Layer.OSM('osm', 'http://tile.openstreetmap.org/${z}/${x}/${y}.png', {
37698 * eventListeners: {
37699 * 'loadend': function(evt) {
37700 * // do something on loadend
37707 eventListeners: null,
37717 * {<OpenLayers.Layer>} layer the tile is attached to
37723 * {String} url of the request.
37726 * Deprecated. The base tile class does not need an url. This should be
37727 * handled in subclasses. Does not belong here.
37732 * APIProperty: bounds
37733 * {<OpenLayers.Bounds>} null
37739 * {<OpenLayers.Size>} null
37744 * Property: position
37745 * {<OpenLayers.Pixel>} Top Left pixel of the tile
37750 * Property: isLoading
37751 * {Boolean} Is the tile loading?
37755 /** TBD 3.0 -- remove 'url' from the list of parameters to the constructor.
37756 * there is no need for the base tile class to have a url.
37760 * Constructor: OpenLayers.Tile
37761 * Constructor for a new <OpenLayers.Tile> instance.
37764 * layer - {<OpenLayers.Layer>} layer that the tile will go in.
37765 * position - {<OpenLayers.Pixel>}
37766 * bounds - {<OpenLayers.Bounds>}
37768 * size - {<OpenLayers.Size>}
37769 * options - {Object}
37771 initialize: function(layer, position, bounds, url, size, options) {
37772 this.layer = layer;
37773 this.position = position.clone();
37774 this.setBounds(bounds);
37777 this.size = size.clone();
37780 //give the tile a unique id based on its BBOX.
37781 this.id = OpenLayers.Util.createUniqueID("Tile_");
37783 OpenLayers.Util.extend(this, options);
37785 this.events = new OpenLayers.Events(this);
37786 if (this.eventListeners instanceof Object) {
37787 this.events.on(this.eventListeners);
37793 * Call immediately before destroying if you are listening to tile
37794 * events, so that counters are properly handled if tile is still
37795 * loading at destroy-time. Will only fire an event if the tile is
37798 unload: function() {
37799 if (this.isLoading) {
37800 this.isLoading = false;
37801 this.events.triggerEvent("unload");
37806 * APIMethod: destroy
37807 * Nullify references to prevent circular references and memory leaks.
37809 destroy:function() {
37811 this.bounds = null;
37813 this.position = null;
37815 if (this.eventListeners) {
37816 this.events.un(this.eventListeners);
37818 this.events.destroy();
37819 this.eventListeners = null;
37820 this.events = null;
37825 * Clear whatever is currently in the tile, then return whether or not
37826 * it should actually be re-drawn. This is an example implementation
37827 * that can be overridden by subclasses. The minimum thing to do here
37828 * is to call <clear> and return the result from <shouldDraw>.
37831 * force - {Boolean} If true, the tile will not be cleared and no beforedraw
37832 * event will be fired. This is used for drawing tiles asynchronously
37833 * after drawing has been cancelled by returning false from a beforedraw
37837 * {Boolean} Whether or not the tile should actually be drawn. Returns null
37838 * if a beforedraw listener returned false.
37840 draw: function(force) {
37842 //clear tile's contents and mark as not drawn
37845 var draw = this.shouldDraw();
37846 if (draw && !force && this.events.triggerEvent("beforedraw") === false) {
37853 * Method: shouldDraw
37854 * Return whether or not the tile should actually be (re-)drawn. The only
37855 * case where we *wouldn't* want to draw the tile is if the tile is outside
37856 * its layer's maxExtent
37859 * {Boolean} Whether or not the tile should actually be drawn.
37861 shouldDraw: function() {
37862 var withinMaxExtent = false,
37863 maxExtent = this.layer.maxExtent;
37865 var map = this.layer.map;
37866 var worldBounds = map.baseLayer.wrapDateLine && map.getMaxExtent();
37867 if (this.bounds.intersectsBounds(maxExtent, {inclusive: false, worldBounds: worldBounds})) {
37868 withinMaxExtent = true;
37872 return withinMaxExtent || this.layer.displayOutsideMaxExtent;
37876 * Method: setBounds
37877 * Sets the bounds on this instance
37880 * bounds {<OpenLayers.Bounds>}
37882 setBounds: function(bounds) {
37883 bounds = bounds.clone();
37884 if (this.layer.map.baseLayer.wrapDateLine) {
37885 var worldExtent = this.layer.map.getMaxExtent(),
37886 tolerance = this.layer.map.getResolution();
37887 bounds = bounds.wrapDateLine(worldExtent, {
37888 leftTolerance: tolerance,
37889 rightTolerance: tolerance
37892 this.bounds = bounds;
37897 * Reposition the tile.
37900 * bounds - {<OpenLayers.Bounds>}
37901 * position - {<OpenLayers.Pixel>}
37902 * redraw - {Boolean} Call draw method on tile after moving.
37905 moveTo: function (bounds, position, redraw) {
37906 if (redraw == null) {
37910 this.setBounds(bounds);
37911 this.position = position.clone();
37919 * Clear the tile of any bounds/position-related data so that it can
37920 * be reused in a new location.
37922 clear: function(draw) {
37923 // to be extended by subclasses
37926 CLASS_NAME: "OpenLayers.Tile"
37928 /* ======================================================================
37929 OpenLayers/Tile/Image.js
37930 ====================================================================== */
37932 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
37933 * full list of contributors). Published under the 2-clause BSD license.
37934 * See license.txt in the OpenLayers distribution or repository for the
37935 * full text of the license. */
37939 * @requires OpenLayers/Tile.js
37940 * @requires OpenLayers/Animation.js
37941 * @requires OpenLayers/Util.js
37945 * Class: OpenLayers.Tile.Image
37946 * Instances of OpenLayers.Tile.Image are used to manage the image tiles
37947 * used by various layers. Create a new image tile with the
37948 * <OpenLayers.Tile.Image> constructor.
37951 * - <OpenLayers.Tile>
37953 OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, {
37956 * APIProperty: events
37957 * {<OpenLayers.Events>} An events object that handles all
37958 * events on the tile.
37960 * Register a listener for a particular event with the following syntax:
37962 * tile.events.register(type, obj, listener);
37965 * Supported event types (in addition to the <OpenLayers.Tile> events):
37966 * beforeload - Triggered before an image is prepared for loading, when the
37967 * url for the image is known already. Listeners may call <setImage> on
37968 * the tile instance. If they do so, that image will be used and no new
37969 * one will be created.
37974 * {String} The URL of the image being requested. No default. Filled in by
37975 * layer.getURL() function. May be modified by loadstart listeners.
37981 * {HTMLImageElement} The image for this tile.
37987 * {DOMElement} The image element is appended to the frame. Any gutter on
37988 * the image will be hidden behind the frame. If no gutter is set,
37989 * this will be null.
37994 * Property: imageReloadAttempts
37995 * {Integer} Attempts to load the image.
37997 imageReloadAttempts: null,
38000 * Property: layerAlphaHack
38001 * {Boolean} True if the png alpha hack needs to be applied on the layer's div.
38003 layerAlphaHack: null,
38006 * Property: asyncRequestId
38007 * {Integer} ID of an request to see if request is still valid. This is a
38008 * number which increments by 1 for each asynchronous request.
38010 asyncRequestId: null,
38013 * APIProperty: maxGetUrlLength
38014 * {Number} If set, requests that would result in GET urls with more
38015 * characters than the number provided will be made using form-encoded
38016 * HTTP POST. It is good practice to avoid urls that are longer than 2048
38020 * Older versions of Gecko based browsers (e.g. Firefox < 3.5) and most
38021 * Opera versions do not fully support this option. On all browsers,
38022 * transition effects are not supported if POST requests are used.
38024 maxGetUrlLength: null,
38027 * Property: canvasContext
38028 * {CanvasRenderingContext2D} A canvas context associated with
38031 canvasContext: null,
38034 * APIProperty: crossOriginKeyword
38035 * The value of the crossorigin keyword to use when loading images. This is
38036 * only relevant when using <getCanvasContext> for tiles from remote
38037 * origins and should be set to either 'anonymous' or 'use-credentials'
38038 * for servers that send Access-Control-Allow-Origin headers with their
38041 crossOriginKeyword: null,
38043 /** TBD 3.0 - reorder the parameters to the init function to remove
38044 * URL. the getUrl() function on the layer gets called on
38045 * each draw(), so no need to specify it here.
38049 * Constructor: OpenLayers.Tile.Image
38050 * Constructor for a new <OpenLayers.Tile.Image> instance.
38053 * layer - {<OpenLayers.Layer>} layer that the tile will go in.
38054 * position - {<OpenLayers.Pixel>}
38055 * bounds - {<OpenLayers.Bounds>}
38056 * url - {<String>} Deprecated. Remove me in 3.0.
38057 * size - {<OpenLayers.Size>}
38058 * options - {Object}
38060 initialize: function(layer, position, bounds, url, size, options) {
38061 OpenLayers.Tile.prototype.initialize.apply(this, arguments);
38063 this.url = url; //deprecated remove me
38065 this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack();
38067 if (this.maxGetUrlLength != null || this.layer.gutter || this.layerAlphaHack) {
38068 // only create frame if it's needed
38069 this.frame = document.createElement("div");
38070 this.frame.style.position = "absolute";
38071 this.frame.style.overflow = "hidden";
38073 if (this.maxGetUrlLength != null) {
38074 OpenLayers.Util.extend(this, OpenLayers.Tile.Image.IFrame);
38079 * APIMethod: destroy
38080 * nullify references to prevent circular references and memory leaks
38082 destroy: function() {
38085 this.imgDiv = null;
38088 // don't handle async requests any more
38089 this.asyncRequestId = null;
38090 OpenLayers.Tile.prototype.destroy.apply(this, arguments);
38095 * Check that a tile should be drawn, and draw it.
38098 * {Boolean} Was a tile drawn? Or null if a beforedraw listener returned
38102 var shouldDraw = OpenLayers.Tile.prototype.draw.apply(this, arguments);
38104 // The layer's reproject option is deprecated.
38105 if (this.layer != this.layer.map.baseLayer && this.layer.reproject) {
38106 // getBoundsFromBaseLayer is defined in deprecated.js.
38107 this.bounds = this.getBoundsFromBaseLayer(this.position);
38109 if (this.isLoading) {
38110 //if we're already loading, send 'reload' instead of 'loadstart'.
38111 this._loadEvent = "reload";
38113 this.isLoading = true;
38114 this._loadEvent = "loadstart";
38117 this.positionTile();
38118 } else if (shouldDraw === false) {
38125 * Method: renderTile
38126 * Internal function to actually initialize the image tile,
38127 * position it correctly, and set its url.
38129 renderTile: function() {
38130 if (this.layer.async) {
38131 // Asynchronous image requests call the asynchronous getURL method
38132 // on the layer to fetch an image that covers 'this.bounds'.
38133 var id = this.asyncRequestId = (this.asyncRequestId || 0) + 1;
38134 this.layer.getURLasync(this.bounds, function(url) {
38135 if (id == this.asyncRequestId) {
38141 // synchronous image requests get the url immediately.
38142 this.url = this.layer.getURL(this.bounds);
38148 * Method: positionTile
38149 * Using the properties currenty set on the layer, position the tile correctly.
38150 * This method is used both by the async and non-async versions of the Tile.Image
38153 positionTile: function() {
38154 var style = this.getTile().style,
38155 size = this.frame ? this.size :
38156 this.layer.getImageSize(this.bounds),
38158 if (this.layer instanceof OpenLayers.Layer.Grid) {
38159 ratio = this.layer.getServerResolution() / this.layer.map.getResolution();
38161 style.left = this.position.x + "px";
38162 style.top = this.position.y + "px";
38163 style.width = Math.round(ratio * size.w) + "px";
38164 style.height = Math.round(ratio * size.h) + "px";
38169 * Remove the tile from the DOM, clear it of any image related data so that
38170 * it can be reused in a new location.
38172 clear: function() {
38173 OpenLayers.Tile.prototype.clear.apply(this, arguments);
38174 var img = this.imgDiv;
38176 var tile = this.getTile();
38177 if (tile.parentNode === this.layer.div) {
38178 this.layer.div.removeChild(tile);
38181 if (this.layerAlphaHack === true) {
38182 img.style.filter = "";
38184 OpenLayers.Element.removeClass(img, "olImageLoadError");
38186 this.canvasContext = null;
38191 * Returns or creates and returns the tile image.
38193 getImage: function() {
38194 if (!this.imgDiv) {
38195 this.imgDiv = OpenLayers.Tile.Image.IMAGE.cloneNode(false);
38197 var style = this.imgDiv.style;
38199 var left = 0, top = 0;
38200 if (this.layer.gutter) {
38201 left = this.layer.gutter / this.layer.tileSize.w * 100;
38202 top = this.layer.gutter / this.layer.tileSize.h * 100;
38204 style.left = -left + "%";
38205 style.top = -top + "%";
38206 style.width = (2 * left + 100) + "%";
38207 style.height = (2 * top + 100) + "%";
38209 style.visibility = "hidden";
38211 if (this.layer.opacity < 1) {
38212 style.filter = 'alpha(opacity=' +
38213 (this.layer.opacity * 100) +
38216 style.position = "absolute";
38217 if (this.layerAlphaHack) {
38218 // move the image out of sight
38219 style.paddingTop = style.height;
38220 style.height = "0";
38221 style.width = "100%";
38224 this.frame.appendChild(this.imgDiv);
38228 return this.imgDiv;
38232 * APIMethod: setImage
38233 * Sets the image element for this tile. This method should only be called
38234 * from beforeload listeners.
38237 * img - {HTMLImageElement} The image to use for this tile.
38239 setImage: function(img) {
38244 * Method: initImage
38245 * Creates the content for the frame on the tile.
38247 initImage: function() {
38248 if (!this.url && !this.imgDiv) {
38249 // fast path out - if there is no tile url and no previous image
38250 this.isLoading = false;
38253 this.events.triggerEvent('beforeload');
38254 this.layer.div.appendChild(this.getTile());
38255 this.events.triggerEvent(this._loadEvent);
38256 var img = this.getImage();
38257 var src = img.getAttribute('src') || '';
38258 if (this.url && OpenLayers.Util.isEquivalentUrl(src, this.url)) {
38259 this._loadTimeout = window.setTimeout(
38260 OpenLayers.Function.bind(this.onImageLoad, this), 0
38263 this.stopLoading();
38264 if (this.crossOriginKeyword) {
38265 img.removeAttribute("crossorigin");
38267 OpenLayers.Event.observe(img, "load",
38268 OpenLayers.Function.bind(this.onImageLoad, this)
38270 OpenLayers.Event.observe(img, "error",
38271 OpenLayers.Function.bind(this.onImageError, this)
38273 this.imageReloadAttempts = 0;
38274 this.setImgSrc(this.url);
38279 * Method: setImgSrc
38280 * Sets the source for the tile image
38283 * url - {String} or undefined to hide the image
38285 setImgSrc: function(url) {
38286 var img = this.imgDiv;
38288 img.style.visibility = 'hidden';
38289 img.style.opacity = 0;
38290 // don't set crossOrigin if the url is a data URL
38291 if (this.crossOriginKeyword) {
38292 if (url.substr(0, 5) !== 'data:') {
38293 img.setAttribute("crossorigin", this.crossOriginKeyword);
38295 img.removeAttribute("crossorigin");
38300 // Remove reference to the image, and leave it to the browser's
38301 // caching and garbage collection.
38302 this.stopLoading();
38303 this.imgDiv = null;
38304 if (img.parentNode) {
38305 img.parentNode.removeChild(img);
38312 * Get the tile's markup.
38315 * {DOMElement} The tile's markup
38317 getTile: function() {
38318 return this.frame ? this.frame : this.getImage();
38322 * Method: createBackBuffer
38323 * Create a backbuffer for this tile. A backbuffer isn't exactly a clone
38324 * of the tile's markup, because we want to avoid the reloading of the
38325 * image. So we clone the frame, and steal the image from the tile.
38328 * {DOMElement} The markup, or undefined if the tile has no image
38329 * or if it's currently loading.
38331 createBackBuffer: function() {
38332 if (!this.imgDiv || this.isLoading) {
38337 backBuffer = this.frame.cloneNode(false);
38338 backBuffer.appendChild(this.imgDiv);
38340 backBuffer = this.imgDiv;
38342 this.imgDiv = null;
38347 * Method: onImageLoad
38348 * Handler for the image onload event
38350 onImageLoad: function() {
38351 var img = this.imgDiv;
38352 this.stopLoading();
38353 img.style.visibility = 'inherit';
38354 img.style.opacity = this.layer.opacity;
38355 this.isLoading = false;
38356 this.canvasContext = null;
38357 this.events.triggerEvent("loadend");
38359 if (this.layerAlphaHack === true) {
38361 "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" +
38362 img.src + "', sizingMethod='scale')";
38367 * Method: onImageError
38368 * Handler for the image onerror event
38370 onImageError: function() {
38371 var img = this.imgDiv;
38372 if (img.src != null) {
38373 this.imageReloadAttempts++;
38374 if (this.imageReloadAttempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) {
38375 this.setImgSrc(this.layer.getURL(this.bounds));
38377 OpenLayers.Element.addClass(img, "olImageLoadError");
38378 this.events.triggerEvent("loaderror");
38379 this.onImageLoad();
38385 * Method: stopLoading
38386 * Stops a loading sequence so <onImageLoad> won't be executed.
38388 stopLoading: function() {
38389 OpenLayers.Event.stopObservingElement(this.imgDiv);
38390 window.clearTimeout(this._loadTimeout);
38391 delete this._loadTimeout;
38395 * APIMethod: getCanvasContext
38396 * Returns a canvas context associated with the tile image (with
38397 * the image drawn on it).
38398 * Returns undefined if the browser does not support canvas, if
38399 * the tile has no image or if it's currently loading.
38401 * The function returns a canvas context instance but the
38402 * underlying canvas is still available in the 'canvas' property:
38404 * var context = tile.getCanvasContext();
38406 * var data = context.canvas.toDataURL('image/jpeg');
38413 getCanvasContext: function() {
38414 if (OpenLayers.CANVAS_SUPPORTED && this.imgDiv && !this.isLoading) {
38415 if (!this.canvasContext) {
38416 var canvas = document.createElement("canvas");
38417 canvas.width = this.size.w;
38418 canvas.height = this.size.h;
38419 this.canvasContext = canvas.getContext("2d");
38420 this.canvasContext.drawImage(this.imgDiv, 0, 0);
38422 return this.canvasContext;
38426 CLASS_NAME: "OpenLayers.Tile.Image"
38431 * Constant: OpenLayers.Tile.Image.IMAGE
38432 * {HTMLImageElement} The image for a tile.
38434 OpenLayers.Tile.Image.IMAGE = (function() {
38435 var img = new Image();
38436 img.className = "olTileImage";
38437 // avoid image gallery menu in IE6
38438 img.galleryImg = "no";
38442 /* ======================================================================
38443 OpenLayers/Layer/Image.js
38444 ====================================================================== */
38446 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
38447 * full list of contributors). Published under the 2-clause BSD license.
38448 * See license.txt in the OpenLayers distribution or repository for the
38449 * full text of the license. */
38452 * @requires OpenLayers/Layer.js
38453 * @requires OpenLayers/Tile/Image.js
38457 * Class: OpenLayers.Layer.Image
38458 * Instances of OpenLayers.Layer.Image are used to display data from a web
38459 * accessible image as a map layer. Create a new image layer with the
38460 * <OpenLayers.Layer.Image> constructor.
38463 * - <OpenLayers.Layer>
38465 OpenLayers.Layer.Image = OpenLayers.Class(OpenLayers.Layer, {
38468 * Property: isBaseLayer
38469 * {Boolean} The layer is a base layer. Default is true. Set this property
38470 * in the layer options
38476 * {String} URL of the image to use
38482 * {<OpenLayers.Bounds>} The image bounds in map units. This extent will
38483 * also be used as the default maxExtent for the layer. If you wish
38484 * to have a maxExtent that is different than the image extent, set the
38485 * maxExtent property of the options argument (as with any other layer).
38491 * {<OpenLayers.Size>} The image size in pixels
38497 * {<OpenLayers.Tile.Image>}
38502 * Property: aspectRatio
38503 * {Float} The ratio of height/width represented by a single pixel in the
38509 * Constructor: OpenLayers.Layer.Image
38510 * Create a new image layer
38513 * name - {String} A name for the layer.
38514 * url - {String} Relative or absolute path to the image
38515 * extent - {<OpenLayers.Bounds>} The extent represented by the image
38516 * size - {<OpenLayers.Size>} The size (in pixels) of the image
38517 * options - {Object} Hashtable of extra options to tag onto the layer
38519 initialize: function(name, url, extent, size, options) {
38521 this.extent = extent;
38522 this.maxExtent = extent;
38524 OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);
38526 this.aspectRatio = (this.extent.getHeight() / this.size.h) /
38527 (this.extent.getWidth() / this.size.w);
38532 * Destroy this layer
38534 destroy: function() {
38536 this.removeTileMonitoringHooks(this.tile);
38537 this.tile.destroy();
38540 OpenLayers.Layer.prototype.destroy.apply(this, arguments);
38545 * Create a clone of this layer
38548 * obj - {Object} An optional layer (is this ever used?)
38551 * {<OpenLayers.Layer.Image>} An exact copy of this layer
38553 clone: function(obj) {
38556 obj = new OpenLayers.Layer.Image(this.name,
38560 this.getOptions());
38563 //get all additions from superclasses
38564 obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
38566 // copy/set any non-init, non-simple values here
38572 * APIMethod: setMap
38575 * map - {<OpenLayers.Map>}
38577 setMap: function(map) {
38579 * If nothing to do with resolutions has been set, assume a single
38580 * resolution determined by ratio*extent/size - if an image has a
38581 * pixel aspect ratio different than one (as calculated above), the
38582 * image will be stretched in one dimension only.
38584 if( this.options.maxResolution == null ) {
38585 this.options.maxResolution = this.aspectRatio *
38586 this.extent.getWidth() /
38589 OpenLayers.Layer.prototype.setMap.apply(this, arguments);
38594 * Create the tile for the image or resize it for the new resolution
38597 * bounds - {<OpenLayers.Bounds>}
38598 * zoomChanged - {Boolean}
38599 * dragging - {Boolean}
38601 moveTo:function(bounds, zoomChanged, dragging) {
38602 OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
38604 var firstRendering = (this.tile == null);
38606 if(zoomChanged || firstRendering) {
38608 //determine new tile size
38609 this.setTileSize();
38611 //determine new position (upper left corner of new bounds)
38612 var ulPx = this.map.getLayerPxFromLonLat({
38613 lon: this.extent.left,
38614 lat: this.extent.top
38617 if(firstRendering) {
38618 //create the new tile
38619 this.tile = new OpenLayers.Tile.Image(this, ulPx, this.extent,
38620 null, this.tileSize);
38621 this.addTileMonitoringHooks(this.tile);
38623 //just resize the tile and set it's new position
38624 this.tile.size = this.tileSize.clone();
38625 this.tile.position = ulPx.clone();
38632 * Set the tile size based on the map size.
38634 setTileSize: function() {
38635 var tileWidth = this.extent.getWidth() / this.map.getResolution();
38636 var tileHeight = this.extent.getHeight() / this.map.getResolution();
38637 this.tileSize = new OpenLayers.Size(tileWidth, tileHeight);
38641 * Method: addTileMonitoringHooks
38642 * This function takes a tile as input and adds the appropriate hooks to
38643 * the tile so that the layer can keep track of the loading tiles.
38646 * tile - {<OpenLayers.Tile>}
38648 addTileMonitoringHooks: function(tile) {
38649 tile.onLoadStart = function() {
38650 this.events.triggerEvent("loadstart");
38652 tile.events.register("loadstart", this, tile.onLoadStart);
38654 tile.onLoadEnd = function() {
38655 this.events.triggerEvent("loadend");
38657 tile.events.register("loadend", this, tile.onLoadEnd);
38658 tile.events.register("unload", this, tile.onLoadEnd);
38662 * Method: removeTileMonitoringHooks
38663 * This function takes a tile as input and removes the tile hooks
38664 * that were added in <addTileMonitoringHooks>.
38667 * tile - {<OpenLayers.Tile>}
38669 removeTileMonitoringHooks: function(tile) {
38672 "loadstart": tile.onLoadStart,
38673 "loadend": tile.onLoadEnd,
38674 "unload": tile.onLoadEnd,
38680 * APIMethod: setUrl
38683 * newUrl - {String}
38685 setUrl: function(newUrl) {
38691 * APIMethod: getURL
38692 * The url we return is always the same (the image itself never changes)
38693 * so we can ignore the bounds parameter (it will always be the same,
38697 * bounds - {<OpenLayers.Bounds>}
38699 getURL: function(bounds) {
38703 CLASS_NAME: "OpenLayers.Layer.Image"
38705 /* ======================================================================
38706 OpenLayers/Layer/HTTPRequest.js
38707 ====================================================================== */
38709 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
38710 * full list of contributors). Published under the 2-clause BSD license.
38711 * See license.txt in the OpenLayers distribution or repository for the
38712 * full text of the license. */
38716 * @requires OpenLayers/Layer.js
38720 * Class: OpenLayers.Layer.HTTPRequest
38723 * - <OpenLayers.Layer>
38725 OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, {
38728 * APIProperty: events
38729 * {<OpenLayers.Events>}
38731 * Supported event types (in addition to those from <OpenLayers.Layer.events>):
38732 * refresh - Triggered when a redraw is forced, to re-fetch data from the
38737 * Constant: URL_HASH_FACTOR
38738 * {Float} Used to hash URL param strings for multi-WMS server selection.
38739 * Set to the Golden Ratio per Knuth's recommendation.
38741 URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2,
38745 * {Array(String) or String} This is either an array of url strings or
38746 * a single url string.
38752 * {Object} Hashtable of key/value parameters
38757 * APIProperty: reproject
38758 * *Deprecated*. See http://docs.openlayers.org/library/spherical_mercator.html
38759 * for information on the replacement for this functionality.
38760 * {Boolean} Whether layer should reproject itself based on base layer
38761 * locations. This allows reprojection onto commercial layers.
38762 * Default is false: Most layers can't reproject, but layers
38763 * which can create non-square geographic pixels can, like WMS.
38769 * Constructor: OpenLayers.Layer.HTTPRequest
38773 * url - {Array(String) or String}
38774 * params - {Object}
38775 * options - {Object} Hashtable of extra options to tag onto the layer
38777 initialize: function(name, url, params, options) {
38778 OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);
38780 if (!this.params) {
38781 this.params = OpenLayers.Util.extend({}, params);
38786 * APIMethod: destroy
38788 destroy: function() {
38790 this.params = null;
38791 OpenLayers.Layer.prototype.destroy.apply(this, arguments);
38801 * {<OpenLayers.Layer.HTTPRequest>} An exact clone of this
38802 * <OpenLayers.Layer.HTTPRequest>
38804 clone: function (obj) {
38807 obj = new OpenLayers.Layer.HTTPRequest(this.name,
38810 this.getOptions());
38813 //get all additions from superclasses
38814 obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
38816 // copy/set any non-init, non-simple values here
38822 * APIMethod: setUrl
38825 * newUrl - {String}
38827 setUrl: function(newUrl) {
38832 * APIMethod: mergeNewParams
38835 * newParams - {Object}
38838 * redrawn: {Boolean} whether the layer was actually redrawn.
38840 mergeNewParams:function(newParams) {
38841 this.params = OpenLayers.Util.extend(this.params, newParams);
38842 var ret = this.redraw();
38843 if(this.map != null) {
38844 this.map.events.triggerEvent("changelayer", {
38853 * APIMethod: redraw
38854 * Redraws the layer. Returns true if the layer was redrawn, false if not.
38857 * force - {Boolean} Force redraw by adding random parameter.
38860 * {Boolean} The layer was redrawn.
38862 redraw: function(force) {
38864 this.events.triggerEvent('refresh');
38865 return this.mergeNewParams({"_olSalt": Math.random()});
38867 return OpenLayers.Layer.prototype.redraw.apply(this, []);
38872 * Method: selectUrl
38873 * selectUrl() implements the standard floating-point multiplicative
38874 * hash function described by Knuth, and hashes the contents of the
38875 * given param string into a float between 0 and 1. This float is then
38876 * scaled to the size of the provided urls array, and used to select
38880 * paramString - {String}
38881 * urls - {Array(String)}
38884 * {String} An entry from the urls array, deterministically selected based
38885 * on the paramString.
38887 selectUrl: function(paramString, urls) {
38889 for (var i=0, len=paramString.length; i<len; i++) {
38890 product *= paramString.charCodeAt(i) * this.URL_HASH_FACTOR;
38891 product -= Math.floor(product);
38893 return urls[Math.floor(product * urls.length)];
38897 * Method: getFullRequestString
38898 * Combine url with layer's params and these newParams.
38900 * does checking on the serverPath variable, allowing for cases when it
38901 * is supplied with trailing ? or &, as well as cases where not.
38903 * return in formatted string like this:
38904 * "server?key1=value1&key2=value2&key3=value3"
38906 * WARNING: The altUrl parameter is deprecated and will be removed in 3.0.
38909 * newParams - {Object}
38910 * altUrl - {String} Use this as the url instead of the layer's url
38915 getFullRequestString:function(newParams, altUrl) {
38917 // if not altUrl passed in, use layer's url
38918 var url = altUrl || this.url;
38920 // create a new params hashtable with all the layer params and the
38921 // new params together. then convert to string
38922 var allParams = OpenLayers.Util.extend({}, this.params);
38923 allParams = OpenLayers.Util.extend(allParams, newParams);
38924 var paramsString = OpenLayers.Util.getParameterString(allParams);
38926 // if url is not a string, it should be an array of strings,
38927 // in which case we will deterministically select one of them in
38928 // order to evenly distribute requests to different urls.
38930 if (OpenLayers.Util.isArray(url)) {
38931 url = this.selectUrl(paramsString, url);
38934 // ignore parameters that are already in the url search string
38936 OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));
38937 for(var key in allParams) {
38938 if(key.toUpperCase() in urlParams) {
38939 delete allParams[key];
38942 paramsString = OpenLayers.Util.getParameterString(allParams);
38944 return OpenLayers.Util.urlAppend(url, paramsString);
38947 CLASS_NAME: "OpenLayers.Layer.HTTPRequest"
38949 /* ======================================================================
38950 OpenLayers/Layer/Grid.js
38951 ====================================================================== */
38953 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
38954 * full list of contributors). Published under the 2-clause BSD license.
38955 * See license.txt in the OpenLayers distribution or repository for the
38956 * full text of the license. */
38960 * @requires OpenLayers/Layer/HTTPRequest.js
38961 * @requires OpenLayers/Tile/Image.js
38965 * Class: OpenLayers.Layer.Grid
38966 * Base class for layers that use a lattice of tiles. Create a new grid
38967 * layer with the <OpenLayers.Layer.Grid> constructor.
38970 * - <OpenLayers.Layer.HTTPRequest>
38972 OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
38975 * APIProperty: tileSize
38976 * {<OpenLayers.Size>}
38981 * Property: tileOriginCorner
38982 * {String} If the <tileOrigin> property is not provided, the tile origin
38983 * will be derived from the layer's <maxExtent>. The corner of the
38984 * <maxExtent> used is determined by this property. Acceptable values
38985 * are "tl" (top left), "tr" (top right), "bl" (bottom left), and "br"
38986 * (bottom right). Default is "bl".
38988 tileOriginCorner: "bl",
38991 * APIProperty: tileOrigin
38992 * {<OpenLayers.LonLat>} Optional origin for aligning the grid of tiles.
38993 * If provided, requests for tiles at all resolutions will be aligned
38994 * with this location (no tiles shall overlap this location). If
38995 * not provided, the grid of tiles will be aligned with the layer's
38996 * <maxExtent>. Default is ``null``.
39000 /** APIProperty: tileOptions
39001 * {Object} optional configuration options for <OpenLayers.Tile> instances
39002 * created by this Layer, if supported by the tile class.
39007 * APIProperty: tileClass
39008 * {<OpenLayers.Tile>} The tile class to use for this layer.
39009 * Defaults is OpenLayers.Tile.Image.
39011 tileClass: OpenLayers.Tile.Image,
39015 * {Array(Array(<OpenLayers.Tile>))} This is an array of rows, each row is
39016 * an array of tiles.
39021 * APIProperty: singleTile
39022 * {Boolean} Moves the layer into single-tile mode, meaning that one tile
39023 * will be loaded. The tile's size will be determined by the 'ratio'
39024 * property. When the tile is dragged such that it does not cover the
39025 * entire viewport, it is reloaded.
39029 /** APIProperty: ratio
39030 * {Float} Used only when in single-tile mode, this specifies the
39031 * ratio of the size of the single tile to the size of the map.
39032 * Default value is 1.5.
39037 * APIProperty: buffer
39038 * {Integer} Used only when in gridded mode, this specifies the number of
39039 * extra rows and columns of tiles on each side which will
39040 * surround the minimum grid tiles to cover the map.
39041 * For very slow loading layers, a larger value may increase
39042 * performance somewhat when dragging, but will increase bandwidth
39043 * use significantly.
39048 * APIProperty: transitionEffect
39049 * {String} The transition effect to use when the map is zoomed.
39050 * Two possible values:
39052 * "resize" - Existing tiles are resized on zoom to provide a visual
39053 * effect of the zoom having taken place immediately. As the
39054 * new tiles become available, they are drawn on top of the
39055 * resized tiles (this is the default setting).
39056 * "map-resize" - Existing tiles are resized on zoom and placed below the
39057 * base layer. New tiles for the base layer will cover existing tiles.
39058 * This setting is recommended when having an overlay duplicated during
39059 * the transition is undesirable (e.g. street labels or big transparent
39061 * null - No transition effect.
39063 * Using "resize" on non-opaque layers can cause undesired visual
39064 * effects. Set transitionEffect to null in this case.
39066 transitionEffect: "resize",
39069 * APIProperty: numLoadingTiles
39070 * {Integer} How many tiles are still loading?
39072 numLoadingTiles: 0,
39075 * Property: serverResolutions
39076 * {Array(Number}} This property is documented in subclasses as
39079 serverResolutions: null,
39082 * Property: loading
39083 * {Boolean} Indicates if tiles are being loaded.
39088 * Property: backBuffer
39089 * {DOMElement} The back buffer.
39094 * Property: gridResolution
39095 * {Number} The resolution of the current grid. Used for backbuffer and
39096 * client zoom. This property is updated every time the grid is
39099 gridResolution: null,
39102 * Property: backBufferResolution
39103 * {Number} The resolution of the current back buffer. This property is
39104 * updated each time a back buffer is created.
39106 backBufferResolution: null,
39109 * Property: backBufferLonLat
39110 * {Object} The top-left corner of the current back buffer. Includes lon
39111 * and lat properties. This object is updated each time a back buffer
39114 backBufferLonLat: null,
39117 * Property: backBufferTimerId
39118 * {Number} The id of the back buffer timer. This timer is used to
39119 * delay the removal of the back buffer, thereby preventing
39120 * flash effects caused by tile animation.
39122 backBufferTimerId: null,
39125 * APIProperty: removeBackBufferDelay
39126 * {Number} Delay for removing the backbuffer when all tiles have finished
39127 * loading. Can be set to 0 when no css opacity transitions for the
39128 * olTileImage class are used. Default is 0 for <singleTile> layers,
39129 * 2500 for tiled layers. See <className> for more information on
39132 removeBackBufferDelay: null,
39135 * APIProperty: className
39136 * {String} Name of the class added to the layer div. If not set in the
39137 * options passed to the constructor then className defaults to
39138 * "olLayerGridSingleTile" for single tile layers (see <singleTile>),
39139 * and "olLayerGrid" for non single tile layers.
39143 * The displaying of tiles is not animated by default for single tile
39144 * layers - OpenLayers' default theme (style.css) includes this:
39146 * .olLayerGrid .olTileImage {
39147 * -webkit-transition: opacity 0.2s linear;
39148 * -moz-transition: opacity 0.2s linear;
39149 * -o-transition: opacity 0.2s linear;
39150 * transition: opacity 0.2s linear;
39153 * To animate tile displaying for any grid layer the following
39154 * CSS rule can be used:
39157 * -webkit-transition: opacity 0.2s linear;
39158 * -moz-transition: opacity 0.2s linear;
39159 * -o-transition: opacity 0.2s linear;
39160 * transition: opacity 0.2s linear;
39163 * In that case, to avoid flash effects, <removeBackBufferDelay>
39164 * should not be zero.
39169 * Register a listener for a particular event with the following syntax:
39171 * layer.events.register(type, obj, listener);
39174 * Listeners will be called with a reference to an event object. The
39175 * properties of this event depends on exactly what happened.
39177 * All event objects have at least the following properties:
39178 * object - {Object} A reference to layer.events.object.
39179 * element - {DOMElement} A reference to layer.events.element.
39181 * Supported event types:
39182 * addtile - Triggered when a tile is added to this layer. Listeners receive
39183 * an object as first argument, which has a tile property that
39184 * references the tile that has been added.
39185 * tileloadstart - Triggered when a tile starts loading. Listeners receive
39186 * an object as first argument, which has a tile property that
39187 * references the tile that starts loading.
39188 * tileloaded - Triggered when each new tile is
39189 * loaded, as a means of progress update to listeners.
39190 * listeners can access 'numLoadingTiles' if they wish to keep
39191 * track of the loading progress. Listeners are called with an object
39192 * with a 'tile' property as first argument, making the loaded tile
39193 * available to the listener, and an 'aborted' property, which will be
39194 * true when loading was aborted and no tile data is available.
39195 * tileerror - Triggered before the tileloaded event (i.e. when the tile is
39196 * still hidden) if a tile failed to load. Listeners receive an object
39197 * as first argument, which has a tile property that references the
39198 * tile that could not be loaded.
39199 * retile - Triggered when the layer recreates its tile grid.
39203 * Property: gridLayout
39204 * {Object} Object containing properties tilelon, tilelat, startcol,
39210 * Property: rowSign
39211 * {Number} 1 for grids starting at the top, -1 for grids starting at the
39212 * bottom. This is used for several grid index and offset calculations.
39217 * Property: transitionendEvents
39218 * {Array} Event names for transitionend
39220 transitionendEvents: [
39221 'transitionend', 'webkitTransitionEnd', 'otransitionend',
39226 * Constructor: OpenLayers.Layer.Grid
39227 * Create a new grid layer
39232 * params - {Object}
39233 * options - {Object} Hashtable of extra options to tag onto the layer
39235 initialize: function(name, url, params, options) {
39236 OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this,
39239 this._removeBackBuffer = OpenLayers.Function.bind(this.removeBackBuffer, this);
39241 this.initProperties();
39243 this.rowSign = this.tileOriginCorner.substr(0, 1) === "t" ? 1 : -1;
39247 * Method: initProperties
39248 * Set any properties that depend on the value of singleTile.
39249 * Currently sets removeBackBufferDelay and className
39251 initProperties: function() {
39252 if (this.options.removeBackBufferDelay === undefined) {
39253 this.removeBackBufferDelay = this.singleTile ? 0 : 2500;
39256 if (this.options.className === undefined) {
39257 this.className = this.singleTile ? 'olLayerGridSingleTile' :
39266 * map - {<OpenLayers.Map>} The map.
39268 setMap: function(map) {
39269 OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this, map);
39270 OpenLayers.Element.addClass(this.div, this.className);
39274 * Method: removeMap
39275 * Called when the layer is removed from the map.
39278 * map - {<OpenLayers.Map>} The map.
39280 removeMap: function(map) {
39281 this.removeBackBuffer();
39285 * APIMethod: destroy
39286 * Deconstruct the layer and clear the grid.
39288 destroy: function() {
39289 this.removeBackBuffer();
39293 this.tileSize = null;
39294 OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments);
39298 * APIMethod: mergeNewParams
39299 * Refetches tiles with new params merged, keeping a backbuffer. Each
39300 * loading new tile will have a css class of '.olTileReplacing'. If a
39301 * stylesheet applies a 'display: none' style to that class, any fade-in
39302 * transition will not apply, and backbuffers for each tile will be removed
39303 * as soon as the tile is loaded.
39306 * newParams - {Object}
39309 * redrawn: {Boolean} whether the layer was actually redrawn.
39313 * Method: clearGrid
39314 * Go through and remove all tiles from the grid, calling
39315 * destroy() on each of them to kill circular references
39317 clearGrid:function() {
39319 for(var iRow=0, len=this.grid.length; iRow<len; iRow++) {
39320 var row = this.grid[iRow];
39321 for(var iCol=0, clen=row.length; iCol<clen; iCol++) {
39322 var tile = row[iCol];
39323 this.destroyTile(tile);
39327 this.gridResolution = null;
39328 this.gridLayout = null;
39333 * APIMethod: addOptions
39336 * newOptions - {Object}
39337 * reinitialize - {Boolean} If set to true, and if resolution options of the
39338 * current baseLayer were changed, the map will be recentered to make
39339 * sure that it is displayed with a valid resolution, and a
39340 * changebaselayer event will be triggered.
39342 addOptions: function (newOptions, reinitialize) {
39343 var singleTileChanged = newOptions.singleTile !== undefined &&
39344 newOptions.singleTile !== this.singleTile;
39345 OpenLayers.Layer.HTTPRequest.prototype.addOptions.apply(this, arguments);
39346 if (this.map && singleTileChanged) {
39347 this.initProperties();
39349 this.tileSize = this.options.tileSize;
39350 this.setTileSize();
39351 if (this.visibility) {
39352 this.moveTo(null, true);
39359 * Create a clone of this layer
39362 * obj - {Object} Is this ever used?
39365 * {<OpenLayers.Layer.Grid>} An exact clone of this OpenLayers.Layer.Grid
39367 clone: function (obj) {
39370 obj = new OpenLayers.Layer.Grid(this.name,
39373 this.getOptions());
39376 //get all additions from superclasses
39377 obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]);
39379 // copy/set any non-init, non-simple values here
39380 if (this.tileSize != null) {
39381 obj.tileSize = this.tileSize.clone();
39384 // we do not want to copy reference to grid, so we make a new array
39386 obj.gridResolution = null;
39387 // same for backbuffer
39388 obj.backBuffer = null;
39389 obj.backBufferTimerId = null;
39390 obj.loading = false;
39391 obj.numLoadingTiles = 0;
39398 * This function is called whenever the map is moved. All the moving
39399 * of actual 'tiles' is done by the map, but moveTo's role is to accept
39400 * a bounds and make sure the data that that bounds requires is pre-loaded.
39403 * bounds - {<OpenLayers.Bounds>}
39404 * zoomChanged - {Boolean}
39405 * dragging - {Boolean}
39407 moveTo:function(bounds, zoomChanged, dragging) {
39409 OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments);
39411 bounds = bounds || this.map.getExtent();
39413 if (bounds != null) {
39415 // if grid is empty or zoom has changed, we *must* re-tile
39416 var forceReTile = !this.grid.length || zoomChanged;
39418 // total bounds of the tiles
39419 var tilesBounds = this.getTilesBounds();
39421 // the new map resolution
39422 var resolution = this.map.getResolution();
39424 // the server-supported resolution for the new map resolution
39425 var serverResolution = this.getServerResolution(resolution);
39427 if (this.singleTile) {
39429 // We want to redraw whenever even the slightest part of the
39430 // current bounds is not contained by our tile.
39431 // (thus, we do not specify partial -- its default is false)
39433 if ( forceReTile ||
39434 (!dragging && !tilesBounds.containsBounds(bounds))) {
39436 // In single tile mode with no transition effect, we insert
39437 // a non-scaled backbuffer when the layer is moved. But if
39438 // a zoom occurs right after a move, i.e. before the new
39439 // image is received, we need to remove the backbuffer, or
39440 // an ill-positioned image will be visible during the zoom
39443 if(zoomChanged && this.transitionEffect !== 'resize') {
39444 this.removeBackBuffer();
39447 if(!zoomChanged || this.transitionEffect === 'resize') {
39448 this.applyBackBuffer(resolution);
39451 this.initSingleTile(bounds);
39455 // if the bounds have changed such that they are not even
39456 // *partially* contained by our tiles (e.g. when user has
39457 // programmatically panned to the other side of the earth on
39458 // zoom level 18), then moveGriddedTiles could potentially have
39459 // to run through thousands of cycles, so we want to reTile
39460 // instead (thus, partial true).
39461 forceReTile = forceReTile ||
39462 !tilesBounds.intersectsBounds(bounds, {
39463 worldBounds: this.map.baseLayer.wrapDateLine &&
39464 this.map.getMaxExtent()
39468 if(zoomChanged && (this.transitionEffect === 'resize' ||
39469 this.gridResolution === resolution)) {
39470 this.applyBackBuffer(resolution);
39472 this.initGriddedTiles(bounds);
39474 this.moveGriddedTiles();
39481 * Method: getTileData
39482 * Given a map location, retrieve a tile and the pixel offset within that
39483 * tile corresponding to the location. If there is not an existing
39484 * tile in the grid that covers the given location, null will be
39488 * loc - {<OpenLayers.LonLat>} map location
39491 * {Object} Object with the following properties: tile ({<OpenLayers.Tile>}),
39492 * i ({Number} x-pixel offset from top left), and j ({Integer} y-pixel
39493 * offset from top left).
39495 getTileData: function(loc) {
39499 numRows = this.grid.length;
39501 if (this.map && numRows) {
39502 var res = this.map.getResolution(),
39503 tileWidth = this.tileSize.w,
39504 tileHeight = this.tileSize.h,
39505 bounds = this.grid[0][0].bounds,
39506 left = bounds.left,
39510 // deal with multiple worlds
39511 if (this.map.baseLayer.wrapDateLine) {
39512 var worldWidth = this.map.getMaxExtent().getWidth();
39513 var worldsAway = Math.ceil((left - x) / worldWidth);
39514 x += worldWidth * worldsAway;
39517 // tile distance to location (fractional number of tiles);
39518 var dtx = (x - left) / (res * tileWidth);
39519 var dty = (top - y) / (res * tileHeight);
39520 // index of tile in grid
39521 var col = Math.floor(dtx);
39522 var row = Math.floor(dty);
39523 if (row >= 0 && row < numRows) {
39524 var tile = this.grid[row][col];
39528 // pixel index within tile
39529 i: Math.floor((dtx - col) * tileWidth),
39530 j: Math.floor((dty - row) * tileHeight)
39539 * Method: destroyTile
39542 * tile - {<OpenLayers.Tile>}
39544 destroyTile: function(tile) {
39545 this.removeTileMonitoringHooks(tile);
39550 * Method: getServerResolution
39551 * Return the closest server-supported resolution.
39554 * resolution - {Number} The base resolution. If undefined the
39555 * map resolution is used.
39558 * {Number} The closest server resolution value.
39560 getServerResolution: function(resolution) {
39561 var distance = Number.POSITIVE_INFINITY;
39562 resolution = resolution || this.map.getResolution();
39563 if(this.serverResolutions &&
39564 OpenLayers.Util.indexOf(this.serverResolutions, resolution) === -1) {
39565 var i, newDistance, newResolution, serverResolution;
39566 for(i=this.serverResolutions.length-1; i>= 0; i--) {
39567 newResolution = this.serverResolutions[i];
39568 newDistance = Math.abs(newResolution - resolution);
39569 if (newDistance > distance) {
39572 distance = newDistance;
39573 serverResolution = newResolution;
39575 resolution = serverResolution;
39581 * Method: getServerZoom
39582 * Return the zoom value corresponding to the best matching server
39583 * resolution, taking into account <serverResolutions> and <zoomOffset>.
39586 * {Number} The closest server supported zoom. This is not the map zoom
39587 * level, but an index of the server's resolutions array.
39589 getServerZoom: function() {
39590 var resolution = this.getServerResolution();
39591 return this.serverResolutions ?
39592 OpenLayers.Util.indexOf(this.serverResolutions, resolution) :
39593 this.map.getZoomForResolution(resolution) + (this.zoomOffset || 0);
39597 * Method: applyBackBuffer
39598 * Create, insert, scale and position a back buffer for the layer.
39601 * resolution - {Number} The resolution to transition to.
39603 applyBackBuffer: function(resolution) {
39604 if(this.backBufferTimerId !== null) {
39605 this.removeBackBuffer();
39607 var backBuffer = this.backBuffer;
39609 backBuffer = this.createBackBuffer();
39613 if (resolution === this.gridResolution) {
39614 this.div.insertBefore(backBuffer, this.div.firstChild);
39616 this.map.baseLayer.div.parentNode.insertBefore(backBuffer, this.map.baseLayer.div);
39618 this.backBuffer = backBuffer;
39620 // set some information in the instance for subsequent
39621 // calls to applyBackBuffer where the same back buffer
39623 var topLeftTileBounds = this.grid[0][0].bounds;
39624 this.backBufferLonLat = {
39625 lon: topLeftTileBounds.left,
39626 lat: topLeftTileBounds.top
39628 this.backBufferResolution = this.gridResolution;
39631 var ratio = this.backBufferResolution / resolution;
39633 // scale the tiles inside the back buffer
39634 var tiles = backBuffer.childNodes, tile;
39635 for (var i=tiles.length-1; i>=0; --i) {
39637 tile.style.top = ((ratio * tile._i * backBuffer._th) | 0) + 'px';
39638 tile.style.left = ((ratio * tile._j * backBuffer._tw) | 0) + 'px';
39639 tile.style.width = Math.round(ratio * tile._w) + 'px';
39640 tile.style.height = Math.round(ratio * tile._h) + 'px';
39643 // and position it (based on the grid's top-left corner)
39644 var position = this.getViewPortPxFromLonLat(
39645 this.backBufferLonLat, resolution);
39646 var leftOffset = this.map.layerContainerOriginPx.x;
39647 var topOffset = this.map.layerContainerOriginPx.y;
39648 backBuffer.style.left = Math.round(position.x - leftOffset) + 'px';
39649 backBuffer.style.top = Math.round(position.y - topOffset) + 'px';
39653 * Method: createBackBuffer
39654 * Create a back buffer.
39657 * {DOMElement} The DOM element for the back buffer, undefined if the
39658 * grid isn't initialized yet.
39660 createBackBuffer: function() {
39662 if(this.grid.length > 0) {
39663 backBuffer = document.createElement('div');
39664 backBuffer.id = this.div.id + '_bb';
39665 backBuffer.className = 'olBackBuffer';
39666 backBuffer.style.position = 'absolute';
39667 var map = this.map;
39668 backBuffer.style.zIndex = this.transitionEffect === 'resize' ?
39669 this.getZIndex() - 1 :
39671 map.Z_INDEX_BASE.BaseLayer -
39672 (map.getNumLayers() - map.getLayerIndex(this));
39673 for(var i=0, lenI=this.grid.length; i<lenI; i++) {
39674 for(var j=0, lenJ=this.grid[i].length; j<lenJ; j++) {
39675 var tile = this.grid[i][j],
39676 markup = this.grid[i][j].createBackBuffer();
39680 markup._w = this.singleTile ?
39681 this.getImageSize(tile.bounds).w : tile.size.w;
39682 markup._h = tile.size.h;
39683 markup.id = tile.id + '_bb';
39684 backBuffer.appendChild(markup);
39688 backBuffer._tw = this.tileSize.w;
39689 backBuffer._th = this.tileSize.h;
39695 * Method: removeBackBuffer
39696 * Remove back buffer from DOM.
39698 removeBackBuffer: function() {
39699 if (this._transitionElement) {
39700 for (var i=this.transitionendEvents.length-1; i>=0; --i) {
39701 OpenLayers.Event.stopObserving(this._transitionElement,
39702 this.transitionendEvents[i], this._removeBackBuffer);
39704 delete this._transitionElement;
39706 if(this.backBuffer) {
39707 if (this.backBuffer.parentNode) {
39708 this.backBuffer.parentNode.removeChild(this.backBuffer);
39710 this.backBuffer = null;
39711 this.backBufferResolution = null;
39712 if(this.backBufferTimerId !== null) {
39713 window.clearTimeout(this.backBufferTimerId);
39714 this.backBufferTimerId = null;
39721 * Move the layer based on pixel vector.
39727 moveByPx: function(dx, dy) {
39728 if (!this.singleTile) {
39729 this.moveGriddedTiles();
39734 * APIMethod: setTileSize
39735 * Check if we are in singleTile mode and if so, set the size as a ratio
39736 * of the map size (as specified by the layer's 'ratio' property).
39739 * size - {<OpenLayers.Size>}
39741 setTileSize: function(size) {
39742 if (this.singleTile) {
39743 size = this.map.getSize();
39744 size.h = parseInt(size.h * this.ratio, 10);
39745 size.w = parseInt(size.w * this.ratio, 10);
39747 OpenLayers.Layer.HTTPRequest.prototype.setTileSize.apply(this, [size]);
39751 * APIMethod: getTilesBounds
39752 * Return the bounds of the tile grid.
39755 * {<OpenLayers.Bounds>} A Bounds object representing the bounds of all the
39756 * currently loaded tiles (including those partially or not at all seen
39759 getTilesBounds: function() {
39762 var length = this.grid.length;
39764 var bottomLeftTileBounds = this.grid[length - 1][0].bounds,
39765 width = this.grid[0].length * bottomLeftTileBounds.getWidth(),
39766 height = this.grid.length * bottomLeftTileBounds.getHeight();
39768 bounds = new OpenLayers.Bounds(bottomLeftTileBounds.left,
39769 bottomLeftTileBounds.bottom,
39770 bottomLeftTileBounds.left + width,
39771 bottomLeftTileBounds.bottom + height);
39777 * Method: initSingleTile
39780 * bounds - {<OpenLayers.Bounds>}
39782 initSingleTile: function(bounds) {
39783 this.events.triggerEvent("retile");
39785 //determine new tile bounds
39786 var center = bounds.getCenterLonLat();
39787 var tileWidth = bounds.getWidth() * this.ratio;
39788 var tileHeight = bounds.getHeight() * this.ratio;
39791 new OpenLayers.Bounds(center.lon - (tileWidth/2),
39792 center.lat - (tileHeight/2),
39793 center.lon + (tileWidth/2),
39794 center.lat + (tileHeight/2));
39796 // store the resolution of the grid
39797 this.gridResolution = this.getServerResolution();
39799 // same logic as OpenLayers.Tile#shouldDraw
39800 var maxExtent = this.maxExtent;
39801 if (maxExtent && (!this.displayOutsideMaxExtent ||
39802 (this.map.baseLayer.wrapDateLine &&
39803 this.maxExtent.equals(this.map.getMaxExtent())))) {
39804 tileBounds.left = Math.max(tileBounds.left, maxExtent.left);
39805 tileBounds.right = Math.min(tileBounds.right, maxExtent.right);
39808 var px = this.map.getLayerPxFromLonLat({
39809 lon: tileBounds.left,
39810 lat: tileBounds.top
39813 if (!this.grid.length) {
39817 var tile = this.grid[0][0];
39819 tile = this.addTile(tileBounds, px);
39821 this.addTileMonitoringHooks(tile);
39823 this.grid[0][0] = tile;
39825 tile.moveTo(tileBounds, px);
39828 //remove all but our single tile
39829 this.removeExcessTiles(1,1);
39833 * Method: calculateGridLayout
39834 * Generate parameters for the grid layout.
39837 * bounds - {<OpenLayers.Bound>|Object} OpenLayers.Bounds or an
39838 * object with a 'left' and 'top' properties.
39839 * origin - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an
39840 * object with a 'lon' and 'lat' properties.
39841 * resolution - {Number}
39844 * {Object} Object containing properties tilelon, tilelat, startcol,
39847 calculateGridLayout: function(bounds, origin, resolution) {
39848 var tilelon = resolution * this.tileSize.w;
39849 var tilelat = resolution * this.tileSize.h;
39851 var offsetlon = bounds.left - origin.lon;
39852 var tilecol = Math.floor(offsetlon/tilelon) - this.buffer;
39854 var rowSign = this.rowSign;
39856 var offsetlat = rowSign * (origin.lat - bounds.top + tilelat);
39857 var tilerow = Math[~rowSign ? 'floor' : 'ceil'](offsetlat/tilelat) - this.buffer * rowSign;
39860 tilelon: tilelon, tilelat: tilelat,
39861 startcol: tilecol, startrow: tilerow
39866 getImageSize: function(bounds) {
39867 var tileSize = OpenLayers.Layer.HTTPRequest.prototype.getImageSize.apply(this, arguments);
39868 if (this.singleTile) {
39869 tileSize = new OpenLayers.Size(
39870 Math.round(bounds.getWidth() / this.gridResolution),
39878 * Method: getTileOrigin
39879 * Determine the origin for aligning the grid of tiles. If a <tileOrigin>
39880 * property is supplied, that will be returned. Otherwise, the origin
39881 * will be derived from the layer's <maxExtent> property. In this case,
39882 * the tile origin will be the corner of the <maxExtent> given by the
39883 * <tileOriginCorner> property.
39886 * {<OpenLayers.LonLat>} The tile origin.
39888 getTileOrigin: function() {
39889 var origin = this.tileOrigin;
39891 var extent = this.getMaxExtent();
39893 "tl": ["left", "top"],
39894 "tr": ["right", "top"],
39895 "bl": ["left", "bottom"],
39896 "br": ["right", "bottom"]
39897 })[this.tileOriginCorner];
39898 origin = new OpenLayers.LonLat(extent[edges[0]], extent[edges[1]]);
39904 * Method: getTileBoundsForGridIndex
39907 * row - {Number} The row of the grid
39908 * col - {Number} The column of the grid
39911 * {<OpenLayers.Bounds>} The bounds for the tile at (row, col)
39913 getTileBoundsForGridIndex: function(row, col) {
39914 var origin = this.getTileOrigin();
39915 var tileLayout = this.gridLayout;
39916 var tilelon = tileLayout.tilelon;
39917 var tilelat = tileLayout.tilelat;
39918 var startcol = tileLayout.startcol;
39919 var startrow = tileLayout.startrow;
39920 var rowSign = this.rowSign;
39921 return new OpenLayers.Bounds(
39922 origin.lon + (startcol + col) * tilelon,
39923 origin.lat - (startrow + row * rowSign) * tilelat * rowSign,
39924 origin.lon + (startcol + col + 1) * tilelon,
39925 origin.lat - (startrow + (row - 1) * rowSign) * tilelat * rowSign
39930 * Method: initGriddedTiles
39933 * bounds - {<OpenLayers.Bounds>}
39935 initGriddedTiles:function(bounds) {
39936 this.events.triggerEvent("retile");
39938 // work out mininum number of rows and columns; this is the number of
39939 // tiles required to cover the viewport plus at least one for panning
39941 var viewSize = this.map.getSize();
39943 var origin = this.getTileOrigin();
39944 var resolution = this.map.getResolution(),
39945 serverResolution = this.getServerResolution(),
39946 ratio = resolution / serverResolution,
39948 w: this.tileSize.w / ratio,
39949 h: this.tileSize.h / ratio
39952 var minRows = Math.ceil(viewSize.h/tileSize.h) +
39953 2 * this.buffer + 1;
39954 var minCols = Math.ceil(viewSize.w/tileSize.w) +
39955 2 * this.buffer + 1;
39957 var tileLayout = this.calculateGridLayout(bounds, origin, serverResolution);
39958 this.gridLayout = tileLayout;
39960 var tilelon = tileLayout.tilelon;
39961 var tilelat = tileLayout.tilelat;
39963 var layerContainerDivLeft = this.map.layerContainerOriginPx.x;
39964 var layerContainerDivTop = this.map.layerContainerOriginPx.y;
39966 var tileBounds = this.getTileBoundsForGridIndex(0, 0);
39967 var startPx = this.map.getViewPortPxFromLonLat(
39968 new OpenLayers.LonLat(tileBounds.left, tileBounds.top)
39970 startPx.x = Math.round(startPx.x) - layerContainerDivLeft;
39971 startPx.y = Math.round(startPx.y) - layerContainerDivTop;
39973 var tileData = [], center = this.map.getCenter();
39977 var row = this.grid[rowidx];
39980 this.grid.push(row);
39985 tileBounds = this.getTileBoundsForGridIndex(rowidx, colidx);
39986 var px = startPx.clone();
39987 px.x = px.x + colidx * Math.round(tileSize.w);
39988 px.y = px.y + rowidx * Math.round(tileSize.h);
39989 var tile = row[colidx];
39991 tile = this.addTile(tileBounds, px);
39992 this.addTileMonitoringHooks(tile);
39995 tile.moveTo(tileBounds, px, false);
39997 var tileCenter = tileBounds.getCenterLonLat();
40000 distance: Math.pow(tileCenter.lon - center.lon, 2) +
40001 Math.pow(tileCenter.lat - center.lat, 2)
40005 } while ((tileBounds.right <= bounds.right + tilelon * this.buffer)
40006 || colidx < minCols);
40009 } while((tileBounds.bottom >= bounds.bottom - tilelat * this.buffer)
40010 || rowidx < minRows);
40012 //shave off excess rows and columns
40013 this.removeExcessTiles(rowidx, colidx);
40015 var resolution = this.getServerResolution();
40016 // store the resolution of the grid
40017 this.gridResolution = resolution;
40019 //now actually draw the tiles
40020 tileData.sort(function(a, b) {
40021 return a.distance - b.distance;
40023 for (var i=0, ii=tileData.length; i<ii; ++i) {
40024 tileData[i].tile.draw();
40029 * Method: getMaxExtent
40030 * Get this layer's maximum extent. (Implemented as a getter for
40031 * potential specific implementations in sub-classes.)
40034 * {<OpenLayers.Bounds>}
40036 getMaxExtent: function() {
40037 return this.maxExtent;
40041 * APIMethod: addTile
40042 * Create a tile, initialize it, and add it to the layer div.
40045 * bounds - {<OpenLayers.Bounds>}
40046 * position - {<OpenLayers.Pixel>}
40049 * {<OpenLayers.Tile>} The added OpenLayers.Tile
40051 addTile: function(bounds, position) {
40052 var tile = new this.tileClass(
40053 this, position, bounds, null, this.tileSize, this.tileOptions
40055 this.events.triggerEvent("addtile", {tile: tile});
40060 * Method: addTileMonitoringHooks
40061 * This function takes a tile as input and adds the appropriate hooks to
40062 * the tile so that the layer can keep track of the loading tiles.
40065 * tile - {<OpenLayers.Tile>}
40067 addTileMonitoringHooks: function(tile) {
40069 var replacingCls = 'olTileReplacing';
40071 tile.onLoadStart = function() {
40072 //if that was first tile then trigger a 'loadstart' on the layer
40073 if (this.loading === false) {
40074 this.loading = true;
40075 this.events.triggerEvent("loadstart");
40077 this.events.triggerEvent("tileloadstart", {tile: tile});
40078 this.numLoadingTiles++;
40079 if (!this.singleTile && this.backBuffer && this.gridResolution === this.backBufferResolution) {
40080 OpenLayers.Element.addClass(tile.getTile(), replacingCls);
40084 tile.onLoadEnd = function(evt) {
40085 this.numLoadingTiles--;
40086 var aborted = evt.type === 'unload';
40087 this.events.triggerEvent("tileloaded", {
40091 if (!this.singleTile && !aborted && this.backBuffer && this.gridResolution === this.backBufferResolution) {
40092 var tileDiv = tile.getTile();
40093 if (OpenLayers.Element.getStyle(tileDiv, 'display') === 'none') {
40094 var bufferTile = document.getElementById(tile.id + '_bb');
40096 bufferTile.parentNode.removeChild(bufferTile);
40099 OpenLayers.Element.removeClass(tileDiv, replacingCls);
40101 //if that was the last tile, then trigger a 'loadend' on the layer
40102 if (this.numLoadingTiles === 0) {
40103 if (this.backBuffer) {
40104 if (this.backBuffer.childNodes.length === 0) {
40105 // no tiles transitioning, remove immediately
40106 this.removeBackBuffer();
40108 // wait until transition has ended or delay has passed
40109 this._transitionElement = aborted ?
40110 this.div.lastChild : tile.imgDiv;
40111 var transitionendEvents = this.transitionendEvents;
40112 for (var i=transitionendEvents.length-1; i>=0; --i) {
40113 OpenLayers.Event.observe(this._transitionElement,
40114 transitionendEvents[i],
40115 this._removeBackBuffer);
40117 // the removal of the back buffer is delayed to prevent
40118 // flash effects due to the animation of tile displaying
40119 this.backBufferTimerId = window.setTimeout(
40120 this._removeBackBuffer, this.removeBackBufferDelay
40124 this.loading = false;
40125 this.events.triggerEvent("loadend");
40129 tile.onLoadError = function() {
40130 this.events.triggerEvent("tileerror", {tile: tile});
40134 "loadstart": tile.onLoadStart,
40135 "loadend": tile.onLoadEnd,
40136 "unload": tile.onLoadEnd,
40137 "loaderror": tile.onLoadError,
40143 * Method: removeTileMonitoringHooks
40144 * This function takes a tile as input and removes the tile hooks
40145 * that were added in addTileMonitoringHooks()
40148 * tile - {<OpenLayers.Tile>}
40150 removeTileMonitoringHooks: function(tile) {
40153 "loadstart": tile.onLoadStart,
40154 "loadend": tile.onLoadEnd,
40155 "unload": tile.onLoadEnd,
40156 "loaderror": tile.onLoadError,
40162 * Method: moveGriddedTiles
40164 moveGriddedTiles: function() {
40165 var buffer = this.buffer + 1;
40167 var tlTile = this.grid[0][0];
40169 x: tlTile.position.x +
40170 this.map.layerContainerOriginPx.x,
40171 y: tlTile.position.y +
40172 this.map.layerContainerOriginPx.y
40174 var ratio = this.getServerResolution() / this.map.getResolution();
40176 w: Math.round(this.tileSize.w * ratio),
40177 h: Math.round(this.tileSize.h * ratio)
40179 if (tlViewPort.x > -tileSize.w * (buffer - 1)) {
40180 this.shiftColumn(true, tileSize);
40181 } else if (tlViewPort.x < -tileSize.w * buffer) {
40182 this.shiftColumn(false, tileSize);
40183 } else if (tlViewPort.y > -tileSize.h * (buffer - 1)) {
40184 this.shiftRow(true, tileSize);
40185 } else if (tlViewPort.y < -tileSize.h * buffer) {
40186 this.shiftRow(false, tileSize);
40198 * prepend - {Boolean} if true, prepend to beginning.
40199 * if false, then append to end
40200 * tileSize - {Object} rendered tile size; object with w and h properties
40202 shiftRow: function(prepend, tileSize) {
40203 var grid = this.grid;
40204 var rowIndex = prepend ? 0 : (grid.length - 1);
40205 var sign = prepend ? -1 : 1;
40206 var rowSign = this.rowSign;
40207 var tileLayout = this.gridLayout;
40208 tileLayout.startrow += sign * rowSign;
40210 var modelRow = grid[rowIndex];
40211 var row = grid[prepend ? 'pop' : 'shift']();
40212 for (var i=0, len=row.length; i<len; i++) {
40214 var position = modelRow[i].position.clone();
40215 position.y += tileSize.h * sign;
40216 tile.moveTo(this.getTileBoundsForGridIndex(rowIndex, i), position);
40218 grid[prepend ? 'unshift' : 'push'](row);
40222 * Method: shiftColumn
40223 * Shift grid work in the other dimension
40226 * prepend - {Boolean} if true, prepend to beginning.
40227 * if false, then append to end
40228 * tileSize - {Object} rendered tile size; object with w and h properties
40230 shiftColumn: function(prepend, tileSize) {
40231 var grid = this.grid;
40232 var colIndex = prepend ? 0 : (grid[0].length - 1);
40233 var sign = prepend ? -1 : 1;
40234 var tileLayout = this.gridLayout;
40235 tileLayout.startcol += sign;
40237 for (var i=0, len=grid.length; i<len; i++) {
40239 var position = row[colIndex].position.clone();
40240 var tile = row[prepend ? 'pop' : 'shift']();
40241 position.x += tileSize.w * sign;
40242 tile.moveTo(this.getTileBoundsForGridIndex(i, colIndex), position);
40243 row[prepend ? 'unshift' : 'push'](tile);
40248 * Method: removeExcessTiles
40249 * When the size of the map or the buffer changes, we may need to
40250 * remove some excess rows and columns.
40253 * rows - {Integer} Maximum number of rows we want our grid to have.
40254 * columns - {Integer} Maximum number of columns we want our grid to have.
40256 removeExcessTiles: function(rows, columns) {
40259 // remove extra rows
40260 while (this.grid.length > rows) {
40261 var row = this.grid.pop();
40262 for (i=0, l=row.length; i<l; i++) {
40264 this.destroyTile(tile);
40268 // remove extra columns
40269 for (i=0, l=this.grid.length; i<l; i++) {
40270 while (this.grid[i].length > columns) {
40271 var row = this.grid[i];
40272 var tile = row.pop();
40273 this.destroyTile(tile);
40279 * Method: onMapResize
40280 * For singleTile layers, this will set a new tile size according to the
40281 * dimensions of the map pane.
40283 onMapResize: function() {
40284 if (this.singleTile) {
40286 this.setTileSize();
40291 * APIMethod: getTileBounds
40292 * Returns The tile bounds for a layer given a pixel location.
40295 * viewPortPx - {<OpenLayers.Pixel>} The location in the viewport.
40298 * {<OpenLayers.Bounds>} Bounds of the tile at the given pixel location.
40300 getTileBounds: function(viewPortPx) {
40301 var maxExtent = this.maxExtent;
40302 var resolution = this.getResolution();
40303 var tileMapWidth = resolution * this.tileSize.w;
40304 var tileMapHeight = resolution * this.tileSize.h;
40305 var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);
40306 var tileLeft = maxExtent.left + (tileMapWidth *
40307 Math.floor((mapPoint.lon -
40310 var tileBottom = maxExtent.bottom + (tileMapHeight *
40311 Math.floor((mapPoint.lat -
40312 maxExtent.bottom) /
40314 return new OpenLayers.Bounds(tileLeft, tileBottom,
40315 tileLeft + tileMapWidth,
40316 tileBottom + tileMapHeight);
40319 CLASS_NAME: "OpenLayers.Layer.Grid"
40321 /* ======================================================================
40322 OpenLayers/Layer/XYZ.js
40323 ====================================================================== */
40325 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
40326 * full list of contributors). Published under the 2-clause BSD license.
40327 * See license.txt in the OpenLayers distribution or repository for the
40328 * full text of the license. */
40331 * @requires OpenLayers/Layer/Grid.js
40335 * Class: OpenLayers.Layer.XYZ
40336 * The XYZ class is designed to make it easier for people who have tiles
40337 * arranged by a standard XYZ grid.
40340 * - <OpenLayers.Layer.Grid>
40342 OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, {
40345 * APIProperty: isBaseLayer
40346 * Default is true, as this is designed to be a base tile source.
40351 * APIProperty: sphericalMercator
40352 * Whether the tile extents should be set to the defaults for
40353 * spherical mercator. Useful for things like OpenStreetMap.
40354 * Default is false, except for the OSM subclass.
40356 sphericalMercator: false,
40359 * APIProperty: zoomOffset
40360 * {Number} If your cache has more zoom levels than you want to provide
40361 * access to with this layer, supply a zoomOffset. This zoom offset
40362 * is added to the current map zoom level to determine the level
40363 * for a requested tile. For example, if you supply a zoomOffset
40364 * of 3, when the map is at the zoom 0, tiles will be requested from
40365 * level 3 of your cache. Default is 0 (assumes cache level and map
40366 * zoom are equivalent). Using <zoomOffset> is an alternative to
40367 * setting <serverResolutions> if you only want to expose a subset
40368 * of the server resolutions.
40373 * APIProperty: serverResolutions
40374 * {Array} A list of all resolutions available on the server. Only set this
40375 * property if the map resolutions differ from the server. This
40376 * property serves two purposes. (a) <serverResolutions> can include
40377 * resolutions that the server supports and that you don't want to
40378 * provide with this layer; you can also look at <zoomOffset>, which is
40379 * an alternative to <serverResolutions> for that specific purpose.
40380 * (b) The map can work with resolutions that aren't supported by
40381 * the server, i.e. that aren't in <serverResolutions>. When the
40382 * map is displayed in such a resolution data for the closest
40383 * server-supported resolution is loaded and the layer div is
40384 * stretched as necessary.
40386 serverResolutions: null,
40389 * Constructor: OpenLayers.Layer.XYZ
40394 * options - {Object} Hashtable of extra options to tag onto the layer
40396 initialize: function(name, url, options) {
40397 if (options && options.sphericalMercator || this.sphericalMercator) {
40398 options = OpenLayers.Util.extend({
40399 projection: "EPSG:900913",
40400 numZoomLevels: this.serverResolutions ?
40401 this.serverResolutions.length : 19
40404 OpenLayers.Layer.Grid.prototype.initialize.apply(this, [
40405 name || this.name, url || this.url, {}, options
40411 * Create a clone of this layer
40414 * obj - {Object} Is this ever used?
40417 * {<OpenLayers.Layer.XYZ>} An exact clone of this OpenLayers.Layer.XYZ
40419 clone: function (obj) {
40422 obj = new OpenLayers.Layer.XYZ(this.name,
40424 this.getOptions());
40427 //get all additions from superclasses
40428 obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
40437 * bounds - {<OpenLayers.Bounds>}
40440 * {String} A string with the layer's url and parameters and also the
40441 * passed-in bounds and appropriate tile size specified as
40444 getURL: function (bounds) {
40445 var xyz = this.getXYZ(bounds);
40446 var url = this.url;
40447 if (OpenLayers.Util.isArray(url)) {
40448 var s = '' + xyz.x + xyz.y + xyz.z;
40449 url = this.selectUrl(s, url);
40452 return OpenLayers.String.format(url, xyz);
40457 * Calculates x, y and z for the given bounds.
40460 * bounds - {<OpenLayers.Bounds>}
40463 * {Object} - an object with x, y and z properties.
40465 getXYZ: function(bounds) {
40466 var res = this.getServerResolution();
40467 var x = Math.round((bounds.left - this.tileOrigin.lon) /
40468 (res * this.tileSize.w));
40469 var y = Math.round((this.tileOrigin.lat - bounds.top) /
40470 (res * this.tileSize.h));
40471 var z = this.getServerZoom();
40473 if (this.wrapDateLine) {
40474 var limit = Math.pow(2, z);
40475 x = ((x % limit) + limit) % limit;
40478 return {'x': x, 'y': y, 'z': z};
40481 /* APIMethod: setMap
40482 * When the layer is added to a map, then we can fetch our origin
40483 * (if we don't have one.)
40486 * map - {<OpenLayers.Map>}
40488 setMap: function(map) {
40489 OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);
40490 if (!this.tileOrigin) {
40491 this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left,
40492 this.maxExtent.top);
40496 CLASS_NAME: "OpenLayers.Layer.XYZ"
40498 /* ======================================================================
40499 OpenLayers/Layer/Bing.js
40500 ====================================================================== */
40502 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
40503 * full list of contributors). Published under the 2-clause BSD license.
40504 * See license.txt in the OpenLayers distribution or repository for the
40505 * full text of the license. */
40508 * @requires OpenLayers/Layer/XYZ.js
40512 * Class: OpenLayers.Layer.Bing
40513 * Bing layer using direct tile access as provided by Bing Maps REST Services.
40514 * See http://msdn.microsoft.com/en-us/library/ff701713.aspx for more
40515 * information. Note: Terms of Service compliant use requires the map to be
40516 * configured with an <OpenLayers.Control.Attribution> control and the
40517 * attribution placed on or near the map.
40520 * - <OpenLayers.Layer.XYZ>
40522 OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, {
40526 * {String} API key for Bing maps, get your own key
40527 * at http://bingmapsportal.com/ .
40532 * Property: serverResolutions
40533 * {Array} the resolutions provided by the Bing servers.
40535 serverResolutions: [
40536 156543.03390625, 78271.516953125, 39135.7584765625,
40537 19567.87923828125, 9783.939619140625, 4891.9698095703125,
40538 2445.9849047851562, 1222.9924523925781, 611.4962261962891,
40539 305.74811309814453, 152.87405654907226, 76.43702827453613,
40540 38.218514137268066, 19.109257068634033, 9.554628534317017,
40541 4.777314267158508, 2.388657133579254, 1.194328566789627,
40542 0.5971642833948135, 0.29858214169740677, 0.14929107084870338,
40543 0.07464553542435169, 0.03732276771218, 0.01866138385609
40547 * Property: attributionTemplate
40550 attributionTemplate: '<span class="olBingAttribution ${type}">' +
40551 '<div><a target="_blank" href="http://www.bing.com/maps/">' +
40552 '<img src="${logo}" /></a></div>${copyrights}' +
40553 '<a style="white-space: nowrap" target="_blank" '+
40554 'href="http://www.microsoft.com/maps/product/terms.html">' +
40555 'Terms of Use</a></span>',
40558 * Property: metadata
40559 * {Object} Metadata for this layer, as returned by the callback script
40564 * Property: protocolRegex
40565 * {RegExp} Regular expression to match and replace http: in bing urls
40567 protocolRegex: /^http:/i,
40570 * APIProperty: type
40571 * {String} The layer identifier. Any non-birdseye imageryType
40572 * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be
40573 * used. Default is "Road".
40578 * APIProperty: culture
40579 * {String} The culture identifier. See http://msdn.microsoft.com/en-us/library/ff701709.aspx
40580 * for the definition and the possible values. Default is "en-US".
40585 * APIProperty: metadataParams
40586 * {Object} Optional url parameters for the Get Imagery Metadata request
40587 * as described here: http://msdn.microsoft.com/en-us/library/ff701716.aspx
40589 metadataParams: null,
40591 /** APIProperty: tileOptions
40592 * {Object} optional configuration options for <OpenLayers.Tile> instances
40593 * created by this Layer. Default is
40596 * {crossOriginKeyword: 'anonymous'}
40601 /** APIProperty: protocol
40602 * {String} Protocol to use to fetch Imagery Metadata, tiles and bing logo
40603 * Can be 'http:' 'https:' or ''
40605 * Warning: tiles may not be available under both HTTP and HTTPS protocols.
40606 * Microsoft approved use of both HTTP and HTTPS urls for tiles. However
40607 * this is undocumented and the Imagery Metadata API always returns HTTP
40610 * Default is '', unless when executed from a file:/// uri, in which case
40613 protocol: ~window.location.href.indexOf('http') ? '' : 'http:',
40616 * Constructor: OpenLayers.Layer.Bing
40617 * Create a new Bing layer.
40621 * var road = new OpenLayers.Layer.Bing({
40622 * name: "My Bing Aerial Layer",
40624 * key: "my-api-key-here",
40629 * options - {Object} Configuration properties for the layer.
40631 * Required configuration properties:
40632 * key - {String} Bing Maps API key for your application. Get one at
40633 * http://bingmapsportal.com/.
40634 * type - {String} The layer identifier. Any non-birdseye imageryType
40635 * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be
40638 * Any other documented layer properties can be provided in the config object.
40640 initialize: function(options) {
40641 options = OpenLayers.Util.applyDefaults({
40642 sphericalMercator: true
40644 var name = options.name || "Bing " + (options.type || this.type);
40646 var newArgs = [name, null, options];
40647 OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs);
40648 this.tileOptions = OpenLayers.Util.extend({
40649 crossOriginKeyword: 'anonymous'
40650 }, this.options.tileOptions);
40651 this.loadMetadata();
40655 * Method: loadMetadata
40657 loadMetadata: function() {
40658 this._callbackId = "_callback_" + this.id.replace(/\./g, "_");
40659 // link the processMetadata method to the global scope and bind it
40660 // to this instance
40661 window[this._callbackId] = OpenLayers.Function.bind(
40662 OpenLayers.Layer.Bing.processMetadata, this
40664 var params = OpenLayers.Util.applyDefaults({
40666 jsonp: this._callbackId,
40667 include: "ImageryProviders"
40668 }, this.metadataParams);
40669 var url = this.protocol + "//dev.virtualearth.net/REST/v1/Imagery/Metadata/" +
40670 this.type + "?" + OpenLayers.Util.getParameterString(params);
40671 var script = document.createElement("script");
40672 script.type = "text/javascript";
40674 script.id = this._callbackId;
40675 document.getElementsByTagName("head")[0].appendChild(script);
40679 * Method: initLayer
40681 * Sets layer properties according to the metadata provided by the API
40683 initLayer: function() {
40684 var res = this.metadata.resourceSets[0].resources[0];
40685 var url = res.imageUrl.replace("{quadkey}", "${quadkey}");
40686 url = url.replace("{culture}", this.culture);
40687 url = url.replace(this.protocolRegex, this.protocol);
40689 for (var i=0; i<res.imageUrlSubdomains.length; ++i) {
40690 this.url.push(url.replace("{subdomain}", res.imageUrlSubdomains[i]));
40693 maxResolution: Math.min(
40694 this.serverResolutions[res.zoomMin],
40695 this.maxResolution || Number.POSITIVE_INFINITY
40697 numZoomLevels: Math.min(
40698 res.zoomMax + 1 - res.zoomMin, this.numZoomLevels
40701 if (!this.isBaseLayer) {
40704 this.updateAttribution();
40711 * bounds - {<OpenLayers.Bounds>}
40713 getURL: function(bounds) {
40717 var xyz = this.getXYZ(bounds), x = xyz.x, y = xyz.y, z = xyz.z;
40718 var quadDigits = [];
40719 for (var i = z; i > 0; --i) {
40721 var mask = 1 << (i - 1);
40722 if ((x & mask) != 0) {
40725 if ((y & mask) != 0) {
40729 quadDigits.push(digit);
40731 var quadKey = quadDigits.join("");
40732 var url = this.selectUrl('' + x + y + z, this.url);
40734 return OpenLayers.String.format(url, {'quadkey': quadKey});
40738 * Method: updateAttribution
40739 * Updates the attribution according to the requirements outlined in
40740 * http://gis.638310.n2.nabble.com/Bing-imagery-td5789168.html
40742 updateAttribution: function() {
40743 var metadata = this.metadata;
40744 if (!metadata.resourceSets || !this.map || !this.map.center) {
40747 var res = metadata.resourceSets[0].resources[0];
40748 var extent = this.map.getExtent().transform(
40749 this.map.getProjectionObject(),
40750 new OpenLayers.Projection("EPSG:4326")
40752 var providers = res.imageryProviders || [],
40753 zoom = OpenLayers.Util.indexOf(this.serverResolutions,
40754 this.getServerResolution()),
40755 copyrights = "", provider, i, ii, j, jj, bbox, coverage;
40756 for (i=0,ii=providers.length; i<ii; ++i) {
40757 provider = providers[i];
40758 for (j=0,jj=provider.coverageAreas.length; j<jj; ++j) {
40759 coverage = provider.coverageAreas[j];
40760 // axis order provided is Y,X
40761 bbox = OpenLayers.Bounds.fromArray(coverage.bbox, true);
40762 if (extent.intersectsBounds(bbox) &&
40763 zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) {
40764 copyrights += provider.attribution + " ";
40768 var logo = metadata.brandLogoUri.replace(this.protocolRegex, this.protocol);
40769 this.attribution = OpenLayers.String.format(this.attributionTemplate, {
40770 type: this.type.toLowerCase(),
40772 copyrights: copyrights
40774 this.map && this.map.events.triggerEvent("changelayer", {
40776 property: "attribution"
40783 setMap: function() {
40784 OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments);
40785 this.map.events.register("moveend", this, this.updateAttribution);
40795 * {<OpenLayers.Layer.Bing>} An exact clone of this <OpenLayers.Layer.Bing>
40797 clone: function(obj) {
40799 obj = new OpenLayers.Layer.Bing(this.options);
40801 //get all additions from superclasses
40802 obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);
40803 // copy/set any non-init, non-simple values here
40810 destroy: function() {
40812 this.map.events.unregister("moveend", this, this.updateAttribution);
40813 OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments);
40816 CLASS_NAME: "OpenLayers.Layer.Bing"
40820 * Function: OpenLayers.Layer.Bing.processMetadata
40821 * This function will be bound to an instance, linked to the global scope with
40822 * an id, and called by the JSONP script returned by the API.
40825 * metadata - {Object} metadata as returned by the API
40827 OpenLayers.Layer.Bing.processMetadata = function(metadata) {
40828 this.metadata = metadata;
40830 var script = document.getElementById(this._callbackId);
40831 script.parentNode.removeChild(script);
40832 window[this._callbackId] = undefined; // cannot delete from window in IE
40833 delete this._callbackId;
40835 /* ======================================================================
40836 OpenLayers/Layer/Markers.js
40837 ====================================================================== */
40839 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
40840 * full list of contributors). Published under the 2-clause BSD license.
40841 * See license.txt in the OpenLayers distribution or repository for the
40842 * full text of the license. */
40846 * @requires OpenLayers/Layer.js
40850 * Class: OpenLayers.Layer.Markers
40853 * - <OpenLayers.Layer>
40855 OpenLayers.Layer.Markers = OpenLayers.Class(OpenLayers.Layer, {
40858 * APIProperty: isBaseLayer
40859 * {Boolean} Markers layer is never a base layer.
40861 isBaseLayer: false,
40864 * APIProperty: markers
40865 * {Array(<OpenLayers.Marker>)} internal marker list
40872 * {Boolean} internal state of drawing. This is a workaround for the fact
40873 * that the map does not call moveTo with a zoomChanged when the map is
40874 * first starting up. This lets us catch the case where we have *never*
40875 * drawn the layer, and draw it even if the zoom hasn't changed.
40880 * Constructor: OpenLayers.Layer.Markers
40881 * Create a Markers layer.
40885 * options - {Object} Hashtable of extra options to tag onto the layer
40887 initialize: function(name, options) {
40888 OpenLayers.Layer.prototype.initialize.apply(this, arguments);
40893 * APIMethod: destroy
40895 destroy: function() {
40896 this.clearMarkers();
40897 this.markers = null;
40898 OpenLayers.Layer.prototype.destroy.apply(this, arguments);
40902 * APIMethod: setOpacity
40903 * Sets the opacity for all the markers.
40906 * opacity - {Float}
40908 setOpacity: function(opacity) {
40909 if (opacity != this.opacity) {
40910 this.opacity = opacity;
40911 for (var i=0, len=this.markers.length; i<len; i++) {
40912 this.markers[i].setOpacity(this.opacity);
40921 * bounds - {<OpenLayers.Bounds>}
40922 * zoomChanged - {Boolean}
40923 * dragging - {Boolean}
40925 moveTo:function(bounds, zoomChanged, dragging) {
40926 OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
40928 if (zoomChanged || !this.drawn) {
40929 for(var i=0, len=this.markers.length; i<len; i++) {
40930 this.drawMarker(this.markers[i]);
40937 * APIMethod: addMarker
40940 * marker - {<OpenLayers.Marker>}
40942 addMarker: function(marker) {
40943 this.markers.push(marker);
40945 if (this.opacity < 1) {
40946 marker.setOpacity(this.opacity);
40949 if (this.map && this.map.getExtent()) {
40950 marker.map = this.map;
40951 this.drawMarker(marker);
40956 * APIMethod: removeMarker
40959 * marker - {<OpenLayers.Marker>}
40961 removeMarker: function(marker) {
40962 if (this.markers && this.markers.length) {
40963 OpenLayers.Util.removeItem(this.markers, marker);
40969 * Method: clearMarkers
40970 * This method removes all markers from a layer. The markers are not
40971 * destroyed by this function, but are removed from the list of markers.
40973 clearMarkers: function() {
40974 if (this.markers != null) {
40975 while(this.markers.length > 0) {
40976 this.removeMarker(this.markers[0]);
40982 * Method: drawMarker
40983 * Calculate the pixel location for the marker, create it, and
40984 * add it to the layer's div
40987 * marker - {<OpenLayers.Marker>}
40989 drawMarker: function(marker) {
40990 var px = this.map.getLayerPxFromLonLat(marker.lonlat);
40992 marker.display(false);
40994 if (!marker.isDrawn()) {
40995 var markerImg = marker.draw(px);
40996 this.div.appendChild(markerImg);
40997 } else if(marker.icon) {
40998 marker.icon.moveTo(px);
41004 * APIMethod: getDataExtent
41005 * Calculates the max extent which includes all of the markers.
41008 * {<OpenLayers.Bounds>}
41010 getDataExtent: function () {
41011 var maxExtent = null;
41013 if ( this.markers && (this.markers.length > 0)) {
41014 var maxExtent = new OpenLayers.Bounds();
41015 for(var i=0, len=this.markers.length; i<len; i++) {
41016 var marker = this.markers[i];
41017 maxExtent.extend(marker.lonlat);
41024 CLASS_NAME: "OpenLayers.Layer.Markers"
41026 /* ======================================================================
41027 OpenLayers/Layer/OSM.js
41028 ====================================================================== */
41030 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
41031 * full list of contributors). Published under the 2-clause BSD license.
41032 * See license.txt in the OpenLayers distribution or repository for the
41033 * full text of the license. */
41036 * @requires OpenLayers/Layer/XYZ.js
41040 * Class: OpenLayers.Layer.OSM
41041 * This layer allows accessing OpenStreetMap tiles. By default the OpenStreetMap
41042 * hosted tile.openstreetmap.org Mapnik tileset is used. If you wish to use
41043 * a different layer instead, you need to provide a different
41044 * URL to the constructor. Here's an example for using OpenCycleMap:
41047 * new OpenLayers.Layer.OSM("OpenCycleMap",
41048 * ["http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png",
41049 * "http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png",
41050 * "http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"]);
41054 * - <OpenLayers.Layer.XYZ>
41056 OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {
41059 * APIProperty: name
41060 * {String} The layer name. Defaults to "OpenStreetMap" if the first
41061 * argument to the constructor is null or undefined.
41063 name: "OpenStreetMap",
41067 * {String} The tileset URL scheme. Defaults to (protocol relative url):
41068 * //[a|b|c].tile.openstreetmap.org/${z}/${x}/${y}.png
41069 * (the official OSM tileset) if the second argument to the constructor
41070 * is null or undefined. To use another tileset you can have something
41073 * new OpenLayers.Layer.OSM("OpenCycleMap",
41074 * ["http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png",
41075 * "http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png",
41076 * "http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"]);
41080 '//a.tile.openstreetmap.org/${z}/${x}/${y}.png',
41081 '//b.tile.openstreetmap.org/${z}/${x}/${y}.png',
41082 '//c.tile.openstreetmap.org/${z}/${x}/${y}.png'
41086 * Property: attribution
41087 * {String} The layer attribution.
41089 attribution: "© <a href='//www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors",
41092 * Property: sphericalMercator
41095 sphericalMercator: true,
41098 * Property: wrapDateLine
41101 wrapDateLine: true,
41103 /** APIProperty: tileOptions
41104 * {Object} optional configuration options for <OpenLayers.Tile> instances
41105 * created by this Layer. Default is
41108 * {crossOriginKeyword: 'anonymous'}
41111 * When using OSM tilesets other than the default ones, it may be
41112 * necessary to set this to
41115 * {crossOriginKeyword: null}
41118 * if the server does not send Access-Control-Allow-Origin headers.
41123 * Constructor: OpenLayers.Layer.OSM
41126 * name - {String} The layer name.
41127 * url - {String} The tileset URL scheme.
41128 * options - {Object} Configuration options for the layer. Any inherited
41129 * layer option can be set in this object (e.g.
41130 * <OpenLayers.Layer.Grid.buffer>).
41132 initialize: function(name, url, options) {
41133 OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments);
41134 this.tileOptions = OpenLayers.Util.extend({
41135 crossOriginKeyword: 'anonymous'
41136 }, this.options && this.options.tileOptions);
41142 clone: function(obj) {
41144 obj = new OpenLayers.Layer.OSM(
41145 this.name, this.url, this.getOptions());
41147 obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);
41151 CLASS_NAME: "OpenLayers.Layer.OSM"
41153 /* ======================================================================
41154 OpenLayers/Layer/SphericalMercator.js
41155 ====================================================================== */
41157 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
41158 * full list of contributors). Published under the 2-clause BSD license.
41159 * See license.txt in the OpenLayers distribution or repository for the
41160 * full text of the license. */
41163 * @requires OpenLayers/Layer.js
41164 * @requires OpenLayers/Projection.js
41168 * Class: OpenLayers.Layer.SphericalMercator
41169 * A mixin for layers that wraps up the pieces necessary to have a coordinate
41170 * conversion for working with commercial APIs which use a spherical
41171 * mercator projection. Using this layer as a base layer, additional
41172 * layers can be used as overlays if they are in the same projection.
41174 * A layer is given properties of this object by setting the sphericalMercator
41175 * property to true.
41177 * More projection information:
41178 * - http://spatialreference.org/ref/user/google-projection/
41181 * +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0
41182 * +k=1.0 +units=m +nadgrids=@null +no_defs
41185 * 900913=PROJCS["WGS84 / Simple Mercator", GEOGCS["WGS 84",
41186 * DATUM["WGS_1984", SPHEROID["WGS_1984", 6378137.0, 298.257223563]],
41187 * PRIMEM["Greenwich", 0.0], UNIT["degree", 0.017453292519943295],
41188 * AXIS["Longitude", EAST], AXIS["Latitude", NORTH]],
41189 * PROJECTION["Mercator_1SP_Google"],
41190 * PARAMETER["latitude_of_origin", 0.0], PARAMETER["central_meridian", 0.0],
41191 * PARAMETER["scale_factor", 1.0], PARAMETER["false_easting", 0.0],
41192 * PARAMETER["false_northing", 0.0], UNIT["m", 1.0], AXIS["x", EAST],
41193 * AXIS["y", NORTH], AUTHORITY["EPSG","900913"]]
41195 OpenLayers.Layer.SphericalMercator = {
41198 * Method: getExtent
41199 * Get the map's extent.
41202 * {<OpenLayers.Bounds>} The map extent.
41204 getExtent: function() {
41206 if (this.sphericalMercator) {
41207 extent = this.map.calculateBounds();
41209 extent = OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this);
41215 * Method: getLonLatFromViewPortPx
41216 * Get a map location from a pixel location
41219 * viewPortPx - {<OpenLayers.Pixel>}
41222 * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view
41223 * port OpenLayers.Pixel, translated into lon/lat by map lib
41224 * If the map lib is not loaded or not centered, returns null
41226 getLonLatFromViewPortPx: function (viewPortPx) {
41227 return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this, arguments);
41231 * Method: getViewPortPxFromLonLat
41232 * Get a pixel location from a map location
41235 * lonlat - {<OpenLayers.LonLat>}
41238 * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in
41239 * OpenLayers.LonLat, translated into view port pixels by map lib
41240 * If map lib is not loaded or not centered, returns null
41242 getViewPortPxFromLonLat: function (lonlat) {
41243 return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this, arguments);
41247 * Method: initMercatorParameters
41248 * Set up the mercator parameters on the layer: resolutions,
41249 * projection, units.
41251 initMercatorParameters: function() {
41252 // set up properties for Mercator - assume EPSG:900913
41253 this.RESOLUTIONS = [];
41254 var maxResolution = 156543.03390625;
41255 for(var zoom=0; zoom<=this.MAX_ZOOM_LEVEL; ++zoom) {
41256 this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom);
41259 this.projection = this.projection || "EPSG:900913";
41263 * APIMethod: forwardMercator
41264 * Given a lon,lat in EPSG:4326, return a point in Spherical Mercator.
41271 * {<OpenLayers.LonLat>} The coordinates transformed to Mercator.
41273 forwardMercator: (function() {
41274 var gg = new OpenLayers.Projection("EPSG:4326");
41275 var sm = new OpenLayers.Projection("EPSG:900913");
41276 return function(lon, lat) {
41277 var point = OpenLayers.Projection.transform({x: lon, y: lat}, gg, sm);
41278 return new OpenLayers.LonLat(point.x, point.y);
41283 * APIMethod: inverseMercator
41284 * Given a x,y in Spherical Mercator, return a point in EPSG:4326.
41287 * x - {float} A map x in Spherical Mercator.
41288 * y - {float} A map y in Spherical Mercator.
41291 * {<OpenLayers.LonLat>} The coordinates transformed to EPSG:4326.
41293 inverseMercator: (function() {
41294 var gg = new OpenLayers.Projection("EPSG:4326");
41295 var sm = new OpenLayers.Projection("EPSG:900913");
41296 return function(x, y) {
41297 var point = OpenLayers.Projection.transform({x: x, y: y}, sm, gg);
41298 return new OpenLayers.LonLat(point.x, point.y);
41303 /* ======================================================================
41304 OpenLayers/Layer/EventPane.js
41305 ====================================================================== */
41307 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
41308 * full list of contributors). Published under the 2-clause BSD license.
41309 * See license.txt in the OpenLayers distribution or repository for the
41310 * full text of the license. */
41314 * @requires OpenLayers/Layer.js
41315 * @requires OpenLayers/Util.js
41319 * Class: OpenLayers.Layer.EventPane
41320 * Base class for 3rd party layers, providing a DOM element which isolates
41321 * the 3rd-party layer from mouse events.
41322 * Only used by Google layers.
41324 * Automatically instantiated by the Google constructor, and not usually instantiated directly.
41326 * Create a new event pane layer with the
41327 * <OpenLayers.Layer.EventPane> constructor.
41330 * - <OpenLayers.Layer>
41332 OpenLayers.Layer.EventPane = OpenLayers.Class(OpenLayers.Layer, {
41335 * APIProperty: smoothDragPan
41336 * {Boolean} smoothDragPan determines whether non-public/internal API
41337 * methods are used for better performance while dragging EventPane
41338 * layers. When not in sphericalMercator mode, the smoother dragging
41339 * doesn't actually move north/south directly with the number of
41340 * pixels moved, resulting in a slight offset when you drag your mouse
41341 * north south with this option on. If this visual disparity bothers
41342 * you, you should turn this option off, or use spherical mercator.
41345 smoothDragPan: true,
41348 * Property: isBaseLayer
41349 * {Boolean} EventPaned layers are always base layers, by necessity.
41354 * APIProperty: isFixed
41355 * {Boolean} EventPaned layers are fixed by default.
41361 * {DOMElement} A reference to the element that controls the events.
41367 * Property: mapObject
41368 * {Object} This is the object which will be used to load the 3rd party library
41369 * in the case of the google layer, this will be of type GMap,
41370 * in the case of the ve layer, this will be of type VEMap
41376 * Constructor: OpenLayers.Layer.EventPane
41377 * Create a new event pane layer
41381 * options - {Object} Hashtable of extra options to tag onto the layer
41383 initialize: function(name, options) {
41384 OpenLayers.Layer.prototype.initialize.apply(this, arguments);
41385 if (this.pane == null) {
41386 this.pane = OpenLayers.Util.createDiv(this.div.id + "_EventPane");
41391 * APIMethod: destroy
41392 * Deconstruct this layer.
41394 destroy: function() {
41395 this.mapObject = null;
41397 OpenLayers.Layer.prototype.destroy.apply(this, arguments);
41403 * Set the map property for the layer. This is done through an accessor
41404 * so that subclasses can override this and take special action once
41405 * they have their map variable set.
41408 * map - {<OpenLayers.Map>}
41410 setMap: function(map) {
41411 OpenLayers.Layer.prototype.setMap.apply(this, arguments);
41413 this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;
41414 this.pane.style.display = this.div.style.display;
41415 this.pane.style.width="100%";
41416 this.pane.style.height="100%";
41417 if (OpenLayers.BROWSER_NAME == "msie") {
41418 this.pane.style.background =
41419 "url(" + OpenLayers.Util.getImageLocation("blank.gif") + ")";
41422 if (this.isFixed) {
41423 this.map.viewPortDiv.appendChild(this.pane);
41425 this.map.layerContainerDiv.appendChild(this.pane);
41428 // once our layer has been added to the map, we can load it
41429 this.loadMapObject();
41431 // if map didn't load, display warning
41432 if (this.mapObject == null) {
41433 this.loadWarningMessage();
41436 this.map.events.register('zoomstart', this, this.onZoomStart);
41440 * APIMethod: removeMap
41441 * On being removed from the map, we'll like to remove the invisible 'pane'
41442 * div that we added to it on creation.
41445 * map - {<OpenLayers.Map>}
41447 removeMap: function(map) {
41448 this.map.events.unregister('zoomstart', this, this.onZoomStart);
41450 if (this.pane && this.pane.parentNode) {
41451 this.pane.parentNode.removeChild(this.pane);
41453 OpenLayers.Layer.prototype.removeMap.apply(this, arguments);
41457 * Method: onZoomStart
41460 * evt - zoomstart event object with center and zoom properties.
41462 onZoomStart: function(evt) {
41463 if (this.mapObject != null) {
41464 var center = this.getMapObjectLonLatFromOLLonLat(evt.center);
41465 var zoom = this.getMapObjectZoomFromOLZoom(evt.zoom);
41466 this.setMapObjectCenter(center, zoom, false);
41471 * Method: loadWarningMessage
41472 * If we can't load the map lib, then display an error message to the
41473 * user and tell them where to go for help.
41475 * This function sets up the layout for the warning message. Each 3rd
41476 * party layer must implement its own getWarningHTML() function to
41477 * provide the actual warning message.
41479 loadWarningMessage:function() {
41481 this.div.style.backgroundColor = "darkblue";
41483 var viewSize = this.map.getSize();
41485 var msgW = Math.min(viewSize.w, 300);
41486 var msgH = Math.min(viewSize.h, 200);
41487 var size = new OpenLayers.Size(msgW, msgH);
41489 var centerPx = new OpenLayers.Pixel(viewSize.w/2, viewSize.h/2);
41491 var topLeft = centerPx.add(-size.w/2, -size.h/2);
41493 var div = OpenLayers.Util.createDiv(this.name + "_warning",
41501 div.style.padding = "7px";
41502 div.style.backgroundColor = "yellow";
41504 div.innerHTML = this.getWarningHTML();
41505 this.div.appendChild(div);
41509 * Method: getWarningHTML
41510 * To be implemented by subclasses.
41513 * {String} String with information on why layer is broken, how to get
41516 getWarningHTML:function() {
41517 //should be implemented by subclasses
41523 * Set the display on the pane
41526 * display - {Boolean}
41528 display: function(display) {
41529 OpenLayers.Layer.prototype.display.apply(this, arguments);
41530 this.pane.style.display = this.div.style.display;
41534 * Method: setZIndex
41535 * Set the z-index order for the pane.
41540 setZIndex: function (zIndex) {
41541 OpenLayers.Layer.prototype.setZIndex.apply(this, arguments);
41542 this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;
41547 * Move the layer based on pixel vector. To be implemented by subclasses.
41550 * dx - {Number} The x coord of the displacement vector.
41551 * dy - {Number} The y coord of the displacement vector.
41553 moveByPx: function(dx, dy) {
41554 OpenLayers.Layer.prototype.moveByPx.apply(this, arguments);
41556 if (this.dragPanMapObject) {
41557 this.dragPanMapObject(dx, -dy);
41559 this.moveTo(this.map.getCachedCenter());
41565 * Handle calls to move the layer.
41568 * bounds - {<OpenLayers.Bounds>}
41569 * zoomChanged - {Boolean}
41570 * dragging - {Boolean}
41572 moveTo:function(bounds, zoomChanged, dragging) {
41573 OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
41575 if (this.mapObject != null) {
41577 var newCenter = this.map.getCenter();
41578 var newZoom = this.map.getZoom();
41580 if (newCenter != null) {
41582 var moOldCenter = this.getMapObjectCenter();
41583 var oldCenter = this.getOLLonLatFromMapObjectLonLat(moOldCenter);
41585 var moOldZoom = this.getMapObjectZoom();
41586 var oldZoom= this.getOLZoomFromMapObjectZoom(moOldZoom);
41588 if (!(newCenter.equals(oldCenter)) || newZoom != oldZoom) {
41590 if (!zoomChanged && oldCenter && this.dragPanMapObject &&
41591 this.smoothDragPan) {
41592 var oldPx = this.map.getViewPortPxFromLonLat(oldCenter);
41593 var newPx = this.map.getViewPortPxFromLonLat(newCenter);
41594 this.dragPanMapObject(newPx.x-oldPx.x, oldPx.y-newPx.y);
41596 var center = this.getMapObjectLonLatFromOLLonLat(newCenter);
41597 var zoom = this.getMapObjectZoomFromOLZoom(newZoom);
41598 this.setMapObjectCenter(center, zoom, dragging);
41606 /********************************************************/
41608 /* Baselayer Functions */
41610 /********************************************************/
41613 * Method: getLonLatFromViewPortPx
41614 * Get a map location from a pixel location
41617 * viewPortPx - {<OpenLayers.Pixel>}
41620 * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view
41621 * port OpenLayers.Pixel, translated into lon/lat by map lib
41622 * If the map lib is not loaded or not centered, returns null
41624 getLonLatFromViewPortPx: function (viewPortPx) {
41626 if ( (this.mapObject != null) &&
41627 (this.getMapObjectCenter() != null) ) {
41628 var moPixel = this.getMapObjectPixelFromOLPixel(viewPortPx);
41629 var moLonLat = this.getMapObjectLonLatFromMapObjectPixel(moPixel);
41630 lonlat = this.getOLLonLatFromMapObjectLonLat(moLonLat);
41637 * Method: getViewPortPxFromLonLat
41638 * Get a pixel location from a map location
41641 * lonlat - {<OpenLayers.LonLat>}
41644 * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in
41645 * OpenLayers.LonLat, translated into view port pixels by map lib
41646 * If map lib is not loaded or not centered, returns null
41648 getViewPortPxFromLonLat: function (lonlat) {
41649 var viewPortPx = null;
41650 if ( (this.mapObject != null) &&
41651 (this.getMapObjectCenter() != null) ) {
41653 var moLonLat = this.getMapObjectLonLatFromOLLonLat(lonlat);
41654 var moPixel = this.getMapObjectPixelFromMapObjectLonLat(moLonLat);
41656 viewPortPx = this.getOLPixelFromMapObjectPixel(moPixel);
41661 /********************************************************/
41663 /* Translation Functions */
41665 /* The following functions translate Map Object and */
41666 /* OL formats for Pixel, LonLat */
41668 /********************************************************/
41671 // TRANSLATION: MapObject LatLng <-> OpenLayers.LonLat
41675 * Method: getOLLonLatFromMapObjectLonLat
41676 * Get an OL style map location from a 3rd party style map location
41679 * moLonLat - {Object}
41682 * {<OpenLayers.LonLat>} An OpenLayers.LonLat, translated from the passed in
41684 * Returns null if null value is passed in
41686 getOLLonLatFromMapObjectLonLat: function(moLonLat) {
41687 var olLonLat = null;
41688 if (moLonLat != null) {
41689 var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);
41690 var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);
41691 olLonLat = new OpenLayers.LonLat(lon, lat);
41697 * Method: getMapObjectLonLatFromOLLonLat
41698 * Get a 3rd party map location from an OL map location.
41701 * olLonLat - {<OpenLayers.LonLat>}
41704 * {Object} A MapObject LonLat, translated from the passed in
41705 * OpenLayers.LonLat
41706 * Returns null if null value is passed in
41708 getMapObjectLonLatFromOLLonLat: function(olLonLat) {
41709 var moLatLng = null;
41710 if (olLonLat != null) {
41711 moLatLng = this.getMapObjectLonLatFromLonLat(olLonLat.lon,
41719 // TRANSLATION: MapObject Pixel <-> OpenLayers.Pixel
41723 * Method: getOLPixelFromMapObjectPixel
41724 * Get an OL pixel location from a 3rd party pixel location.
41727 * moPixel - {Object}
41730 * {<OpenLayers.Pixel>} An OpenLayers.Pixel, translated from the passed in
41732 * Returns null if null value is passed in
41734 getOLPixelFromMapObjectPixel: function(moPixel) {
41735 var olPixel = null;
41736 if (moPixel != null) {
41737 var x = this.getXFromMapObjectPixel(moPixel);
41738 var y = this.getYFromMapObjectPixel(moPixel);
41739 olPixel = new OpenLayers.Pixel(x, y);
41745 * Method: getMapObjectPixelFromOLPixel
41746 * Get a 3rd party pixel location from an OL pixel location
41749 * olPixel - {<OpenLayers.Pixel>}
41752 * {Object} A MapObject Pixel, translated from the passed in
41754 * Returns null if null value is passed in
41756 getMapObjectPixelFromOLPixel: function(olPixel) {
41757 var moPixel = null;
41758 if (olPixel != null) {
41759 moPixel = this.getMapObjectPixelFromXY(olPixel.x, olPixel.y);
41764 CLASS_NAME: "OpenLayers.Layer.EventPane"
41766 /* ======================================================================
41767 OpenLayers/Layer/FixedZoomLevels.js
41768 ====================================================================== */
41770 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
41771 * full list of contributors). Published under the 2-clause BSD license.
41772 * See license.txt in the OpenLayers distribution or repository for the
41773 * full text of the license. */
41776 * @requires OpenLayers/Layer.js
41780 * Class: OpenLayers.Layer.FixedZoomLevels
41781 * Some Layers will already have established zoom levels (like google
41782 * or ve). Instead of trying to determine them and populate a resolutions[]
41783 * Array with those values, we will hijack the resolution functionality
41786 * When you subclass FixedZoomLevels:
41788 * The initResolutions() call gets nullified, meaning no resolutions[] array
41789 * is set up. Which would be a big problem getResolution() in Layer, since
41790 * it merely takes map.zoom and indexes into resolutions[]... but....
41792 * The getResolution() call is also overridden. Instead of using the
41793 * resolutions[] array, we simply calculate the current resolution based
41794 * on the current extent and the current map size. But how will we be able
41795 * to calculate the current extent without knowing the resolution...?
41797 * The getExtent() function is also overridden. Instead of calculating extent
41798 * based on the center point and the current resolution, we instead
41799 * calculate the extent by getting the lonlats at the top-left and
41800 * bottom-right by using the getLonLatFromViewPortPx() translation function,
41801 * taken from the pixel locations (0,0) and the size of the map. But how
41802 * will we be able to do lonlat-px translation without resolution....?
41804 * The getZoomForResolution() method is overridden. Instead of indexing into
41805 * the resolutions[] array, we call OpenLayers.Layer.getExent(), passing in
41806 * the desired resolution. With this extent, we then call getZoomForExtent()
41809 * Whenever you implement a layer using OpenLayers.Layer.FixedZoomLevels,
41810 * it is your responsibility to provide the following three functions:
41812 * - getLonLatFromViewPortPx
41813 * - getViewPortPxFromLonLat
41814 * - getZoomForExtent
41816 * ...those three functions should generally be provided by any reasonable
41817 * API that you might be working from.
41820 OpenLayers.Layer.FixedZoomLevels = OpenLayers.Class({
41822 /********************************************************/
41824 /* Baselayer Functions */
41826 /* The following functions must all be implemented */
41827 /* by all base layers */
41829 /********************************************************/
41832 * Constructor: OpenLayers.Layer.FixedZoomLevels
41833 * Create a new fixed zoom levels layer.
41835 initialize: function() {
41836 //this class is only just to add the following functions...
41837 // nothing to actually do here... but it is probably a good
41838 // idea to have layers that use these functions call this
41839 // inititalize() anyways, in case at some point we decide we
41840 // do want to put some functionality or state in here.
41844 * Method: initResolutions
41845 * Populate the resolutions array
41847 initResolutions: function() {
41849 var props = ['minZoomLevel', 'maxZoomLevel', 'numZoomLevels'];
41851 for(var i=0, len=props.length; i<len; i++) {
41852 var property = props[i];
41853 this[property] = (this.options[property] != null)
41854 ? this.options[property]
41855 : this.map[property];
41858 if ( (this.minZoomLevel == null) ||
41859 (this.minZoomLevel < this.MIN_ZOOM_LEVEL) ){
41860 this.minZoomLevel = this.MIN_ZOOM_LEVEL;
41864 // At this point, we know what the minimum desired zoom level is, and
41865 // we must calculate the total number of zoom levels.
41867 // Because we allow for the setting of either the 'numZoomLevels'
41868 // or the 'maxZoomLevel' properties... on either the layer or the
41869 // map, we have to define some rules to see which we take into
41870 // account first in this calculation.
41872 // The following is the precedence list for these properties:
41874 // (1) numZoomLevels set on layer
41875 // (2) maxZoomLevel set on layer
41876 // (3) numZoomLevels set on map
41877 // (4) maxZoomLevel set on map*
41878 // (5) none of the above*
41880 // *Note that options (4) and (5) are only possible if the user
41881 // _explicitly_ sets the 'numZoomLevels' property on the map to
41882 // null, since it is set by default to 16.
41886 // Note to future: In 3.0, I think we should remove the default
41887 // value of 16 for map.numZoomLevels. Rather, I think that value
41888 // should be set as a default on the Layer.WMS class. If someone
41889 // creates a 3rd party layer and does not specify any 'minZoomLevel',
41890 // 'maxZoomLevel', or 'numZoomLevels', and has not explicitly
41891 // specified any of those on the map object either.. then I think
41892 // it is fair to say that s/he wants all the zoom levels available.
41894 // By making map.numZoomLevels *null* by default, that will be the
41895 // case. As it is, I don't feel comfortable changing that right now
41896 // as it would be a glaring API change and actually would probably
41897 // break many peoples' codes.
41900 //the number of zoom levels we'd like to have.
41901 var desiredZoomLevels;
41903 //this is the maximum number of zoom levels the layer will allow,
41904 // given the specified starting minimum zoom level.
41905 var limitZoomLevels = this.MAX_ZOOM_LEVEL - this.minZoomLevel + 1;
41907 if ( ((this.options.numZoomLevels == null) &&
41908 (this.options.maxZoomLevel != null)) // (2)
41910 ((this.numZoomLevels == null) &&
41911 (this.maxZoomLevel != null)) // (4)
41913 //calculate based on specified maxZoomLevel (on layer or map)
41914 desiredZoomLevels = this.maxZoomLevel - this.minZoomLevel + 1;
41916 //calculate based on specified numZoomLevels (on layer or map)
41917 // this covers cases (1) and (3)
41918 desiredZoomLevels = this.numZoomLevels;
41921 if (desiredZoomLevels != null) {
41922 //Now that we know what we would *like* the number of zoom levels
41923 // to be, based on layer or map options, we have to make sure that
41924 // it does not conflict with the actual limit, as specified by
41925 // the constants on the layer itself (and calculated into the
41926 // 'limitZoomLevels' variable).
41927 this.numZoomLevels = Math.min(desiredZoomLevels, limitZoomLevels);
41929 // case (5) -- neither 'numZoomLevels' not 'maxZoomLevel' was
41930 // set on either the layer or the map. So we just use the
41931 // maximum limit as calculated by the layer's constants.
41932 this.numZoomLevels = limitZoomLevels;
41935 //now that the 'numZoomLevels' is appropriately, safely set,
41936 // we go back and re-calculate the 'maxZoomLevel'.
41937 this.maxZoomLevel = this.minZoomLevel + this.numZoomLevels - 1;
41939 if (this.RESOLUTIONS != null) {
41940 var resolutionsIndex = 0;
41941 this.resolutions = [];
41942 for(var i= this.minZoomLevel; i <= this.maxZoomLevel; i++) {
41943 this.resolutions[resolutionsIndex++] = this.RESOLUTIONS[i];
41945 this.maxResolution = this.resolutions[0];
41946 this.minResolution = this.resolutions[this.resolutions.length - 1];
41951 * APIMethod: getResolution
41952 * Get the current map resolution
41955 * {Float} Map units per Pixel
41957 getResolution: function() {
41959 if (this.resolutions != null) {
41960 return OpenLayers.Layer.prototype.getResolution.apply(this, arguments);
41962 var resolution = null;
41964 var viewSize = this.map.getSize();
41965 var extent = this.getExtent();
41967 if ((viewSize != null) && (extent != null)) {
41968 resolution = Math.max( extent.getWidth() / viewSize.w,
41969 extent.getHeight() / viewSize.h );
41976 * APIMethod: getExtent
41977 * Calculates using px-> lonlat translation functions on tl and br
41978 * corners of viewport
41981 * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat
41982 * bounds of the current viewPort.
41984 getExtent: function () {
41985 var size = this.map.getSize();
41986 var tl = this.getLonLatFromViewPortPx({
41989 var br = this.getLonLatFromViewPortPx({
41990 x: size.w, y: size.h
41993 if ((tl != null) && (br != null)) {
41994 return new OpenLayers.Bounds(tl.lon, br.lat, br.lon, tl.lat);
42001 * Method: getZoomForResolution
42002 * Get the zoom level for a given resolution
42005 * resolution - {Float}
42008 * {Integer} A suitable zoom level for the specified resolution.
42009 * If no baselayer is set, returns null.
42011 getZoomForResolution: function(resolution) {
42013 if (this.resolutions != null) {
42014 return OpenLayers.Layer.prototype.getZoomForResolution.apply(this, arguments);
42016 var extent = OpenLayers.Layer.prototype.getExtent.apply(this, []);
42017 return this.getZoomForExtent(extent);
42024 /********************************************************/
42026 /* Translation Functions */
42028 /* The following functions translate GMaps and OL */
42029 /* formats for Pixel, LonLat, Bounds, and Zoom */
42031 /********************************************************/
42035 // TRANSLATION: MapObject Zoom <-> OpenLayers Zoom
42039 * Method: getOLZoomFromMapObjectZoom
42040 * Get the OL zoom index from the map object zoom level
42043 * moZoom - {Integer}
42046 * {Integer} An OpenLayers Zoom level, translated from the passed in zoom
42047 * Returns null if null value is passed in
42049 getOLZoomFromMapObjectZoom: function(moZoom) {
42051 if (moZoom != null) {
42052 zoom = moZoom - this.minZoomLevel;
42053 if (this.map.baseLayer !== this) {
42054 zoom = this.map.baseLayer.getZoomForResolution(
42055 this.getResolutionForZoom(zoom)
42063 * Method: getMapObjectZoomFromOLZoom
42064 * Get the map object zoom level from the OL zoom level
42067 * olZoom - {Integer}
42070 * {Integer} A MapObject level, translated from the passed in olZoom
42071 * Returns null if null value is passed in
42073 getMapObjectZoomFromOLZoom: function(olZoom) {
42075 if (olZoom != null) {
42076 zoom = olZoom + this.minZoomLevel;
42077 if (this.map.baseLayer !== this) {
42078 zoom = this.getZoomForResolution(
42079 this.map.baseLayer.getResolutionForZoom(zoom)
42086 CLASS_NAME: "OpenLayers.Layer.FixedZoomLevels"
42089 /* ======================================================================
42090 OpenLayers/Layer/Google.js
42091 ====================================================================== */
42093 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
42094 * full list of contributors). Published under the 2-clause BSD license.
42095 * See license.txt in the OpenLayers distribution or repository for the
42096 * full text of the license. */
42100 * @requires OpenLayers/Layer/SphericalMercator.js
42101 * @requires OpenLayers/Layer/EventPane.js
42102 * @requires OpenLayers/Layer/FixedZoomLevels.js
42103 * @requires OpenLayers/Lang.js
42107 * Class: OpenLayers.Layer.Google
42109 * Provides a wrapper for Google's Maps API
42110 * Normally the Terms of Use for this API do not allow wrapping, but Google
42111 * have provided written consent to OpenLayers for this - see email in
42112 * http://osgeo-org.1560.n6.nabble.com/Google-Maps-API-Terms-of-Use-changes-tp4910013p4911981.html
42115 * - <OpenLayers.Layer.SphericalMercator>
42116 * - <OpenLayers.Layer.EventPane>
42117 * - <OpenLayers.Layer.FixedZoomLevels>
42119 OpenLayers.Layer.Google = OpenLayers.Class(
42120 OpenLayers.Layer.EventPane,
42121 OpenLayers.Layer.FixedZoomLevels, {
42124 * Constant: MIN_ZOOM_LEVEL
42130 * Constant: MAX_ZOOM_LEVEL
42133 MAX_ZOOM_LEVEL: 21,
42136 * Constant: RESOLUTIONS
42137 * {Array(Float)} Hardcode these resolutions so that they are more closely
42138 * tied with the standard wms projection
42152 0.0006866455078125,
42153 0.00034332275390625,
42154 0.000171661376953125,
42155 0.0000858306884765625,
42156 0.00004291534423828125,
42157 0.00002145767211914062,
42158 0.00001072883605957031,
42159 0.00000536441802978515,
42160 0.00000268220901489257,
42161 0.0000013411045074462891,
42162 0.00000067055225372314453
42166 * APIProperty: type
42172 * APIProperty: wrapDateLine
42173 * {Boolean} Allow user to pan forever east/west. Default is true.
42174 * Setting this to false only restricts panning if
42175 * <sphericalMercator> is true.
42177 wrapDateLine: true,
42180 * APIProperty: sphericalMercator
42181 * {Boolean} Should the map act as a mercator-projected map? This will
42182 * cause all interactions with the map to be in the actual map
42183 * projection, which allows support for vector drawing, overlaying
42186 sphericalMercator: false,
42189 * APIProperty: useTiltImages
42190 * {Boolean} Should Google use 45° (tilt) imagery when available or
42191 * should it stick to the 0° overhead view? While tilt images look
42192 * impressive, the changed viewing angle can cause the misalignment
42193 * of overlay layers.
42195 useTiltImages: false,
42198 * Property: version
42199 * {Number} The version of the Google Maps API
42204 * Constructor: OpenLayers.Layer.Google
42207 * name - {String} A name for the layer.
42208 * options - {Object} An optional object whose properties will be set
42211 initialize: function(name, options) {
42212 options = options || {};
42213 options.version = "3";
42214 var mixin = OpenLayers.Layer.Google["v" +
42215 options.version.replace(/\./g, "_")];
42217 OpenLayers.Util.applyDefaults(options, mixin);
42219 throw "Unsupported Google Maps API version: " + options.version;
42222 OpenLayers.Util.applyDefaults(options, mixin.DEFAULTS);
42223 if (options.maxExtent) {
42224 options.maxExtent = options.maxExtent.clone();
42227 OpenLayers.Layer.EventPane.prototype.initialize.apply(this,
42229 OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,
42232 if (this.sphericalMercator) {
42233 OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
42234 this.initMercatorParameters();
42240 * Create a clone of this layer
42243 * {<OpenLayers.Layer.Google>} An exact clone of this layer
42245 clone: function() {
42247 * This method isn't intended to be called by a subclass and it
42248 * doesn't call the same method on the superclass. We don't call
42249 * the super's clone because we don't want properties that are set
42250 * on this layer after initialize (i.e. this.mapObject etc.).
42252 return new OpenLayers.Layer.Google(
42253 this.name, this.getOptions()
42258 * APIMethod: setVisibility
42259 * Set the visibility flag for the layer and hide/show & redraw
42260 * accordingly. Fire event unless otherwise specified
42262 * Note that visibility is no longer simply whether or not the layer's
42263 * style.display is set to "block". Now we store a 'visibility' state
42264 * property on the layer class, this allows us to remember whether or
42265 * not we *desire* for a layer to be visible. In the case where the
42266 * map's resolution is out of the layer's range, this desire may be
42270 * visible - {Boolean} Display the layer (if in range)
42272 setVisibility: function(visible) {
42273 // sharing a map container, opacity has to be set per layer
42274 var opacity = this.opacity == null ? 1 : this.opacity;
42275 OpenLayers.Layer.EventPane.prototype.setVisibility.apply(this, arguments);
42276 this.setOpacity(opacity);
42280 * APIMethod: display
42281 * Hide or show the Layer
42284 * visible - {Boolean}
42286 display: function(visible) {
42287 if (!this._dragging) {
42288 this.setGMapVisibility(visible);
42290 OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments);
42297 * bounds - {<OpenLayers.Bounds>}
42298 * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to
42299 * do some init work in that case.
42300 * dragging - {Boolean}
42302 moveTo: function(bounds, zoomChanged, dragging) {
42303 this._dragging = dragging;
42304 OpenLayers.Layer.EventPane.prototype.moveTo.apply(this, arguments);
42305 delete this._dragging;
42309 * APIMethod: setOpacity
42310 * Sets the opacity for the entire layer (all images)
42313 * opacity - {Float}
42315 setOpacity: function(opacity) {
42316 if (opacity !== this.opacity) {
42317 if (this.map != null) {
42318 this.map.events.triggerEvent("changelayer", {
42320 property: "opacity"
42323 this.opacity = opacity;
42325 // Though this layer's opacity may not change, we're sharing a container
42326 // and need to update the opacity for the entire container.
42327 if (this.getVisibility()) {
42328 var container = this.getMapContainer();
42329 OpenLayers.Util.modifyDOMElement(
42330 container, null, null, null, null, null, null, opacity
42336 * APIMethod: destroy
42337 * Clean up this layer.
42339 destroy: function() {
42341 * We have to override this method because the event pane destroy
42342 * deletes the mapObject reference before removing this layer from
42346 this.setGMapVisibility(false);
42347 var cache = OpenLayers.Layer.Google.cache[this.map.id];
42348 if (cache && cache.count <= 1) {
42349 this.removeGMapElements();
42352 OpenLayers.Layer.EventPane.prototype.destroy.apply(this, arguments);
42356 * Method: removeGMapElements
42357 * Remove all elements added to the dom. This should only be called if
42358 * this is the last of the Google layers for the given map.
42360 removeGMapElements: function() {
42361 var cache = OpenLayers.Layer.Google.cache[this.map.id];
42363 // remove shared elements from dom
42364 var container = this.mapObject && this.getMapContainer();
42365 if (container && container.parentNode) {
42366 container.parentNode.removeChild(container);
42368 var termsOfUse = cache.termsOfUse;
42369 if (termsOfUse && termsOfUse.parentNode) {
42370 termsOfUse.parentNode.removeChild(termsOfUse);
42372 var poweredBy = cache.poweredBy;
42373 if (poweredBy && poweredBy.parentNode) {
42374 poweredBy.parentNode.removeChild(poweredBy);
42376 if (this.mapObject && window.google && google.maps &&
42377 google.maps.event && google.maps.event.clearListeners) {
42378 google.maps.event.clearListeners(this.mapObject, 'tilesloaded');
42384 * APIMethod: removeMap
42385 * On being removed from the map, also remove termsOfUse and poweredBy divs
42388 * map - {<OpenLayers.Map>}
42390 removeMap: function(map) {
42391 // hide layer before removing
42392 if (this.visibility && this.mapObject) {
42393 this.setGMapVisibility(false);
42395 // check to see if last Google layer in this map
42396 var cache = OpenLayers.Layer.Google.cache[map.id];
42398 if (cache.count <= 1) {
42399 this.removeGMapElements();
42400 delete OpenLayers.Layer.Google.cache[map.id];
42402 // decrement the layer count
42406 // remove references to gmap elements
42407 delete this.termsOfUse;
42408 delete this.poweredBy;
42409 delete this.mapObject;
42410 delete this.dragObject;
42411 OpenLayers.Layer.EventPane.prototype.removeMap.apply(this, arguments);
42415 // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds
42419 * APIMethod: getOLBoundsFromMapObjectBounds
42422 * moBounds - {Object}
42425 * {<OpenLayers.Bounds>} An <OpenLayers.Bounds>, translated from the
42426 * passed-in MapObject Bounds.
42427 * Returns null if null value is passed in.
42429 getOLBoundsFromMapObjectBounds: function(moBounds) {
42430 var olBounds = null;
42431 if (moBounds != null) {
42432 var sw = moBounds.getSouthWest();
42433 var ne = moBounds.getNorthEast();
42434 if (this.sphericalMercator) {
42435 sw = this.forwardMercator(sw.lng(), sw.lat());
42436 ne = this.forwardMercator(ne.lng(), ne.lat());
42438 sw = new OpenLayers.LonLat(sw.lng(), sw.lat());
42439 ne = new OpenLayers.LonLat(ne.lng(), ne.lat());
42441 olBounds = new OpenLayers.Bounds(sw.lon,
42450 * APIMethod: getWarningHTML
42453 * {String} String with information on why layer is broken, how to get
42456 getWarningHTML:function() {
42457 return OpenLayers.i18n("googleWarning");
42461 /************************************
42463 * MapObject Interface Controls *
42465 ************************************/
42468 // Get&Set Center, Zoom
42471 * APIMethod: getMapObjectCenter
42474 * {Object} The mapObject's current center in Map Object format
42476 getMapObjectCenter: function() {
42477 return this.mapObject.getCenter();
42481 * APIMethod: getMapObjectZoom
42484 * {Integer} The mapObject's current zoom, in Map Object format
42486 getMapObjectZoom: function() {
42487 return this.mapObject.getZoom();
42491 /************************************
42493 * MapObject Primitives *
42495 ************************************/
42501 * APIMethod: getLongitudeFromMapObjectLonLat
42504 * moLonLat - {Object} MapObject LonLat format
42507 * {Float} Longitude of the given MapObject LonLat
42509 getLongitudeFromMapObjectLonLat: function(moLonLat) {
42510 return this.sphericalMercator ?
42511 this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lon :
42516 * APIMethod: getLatitudeFromMapObjectLonLat
42519 * moLonLat - {Object} MapObject LonLat format
42522 * {Float} Latitude of the given MapObject LonLat
42524 getLatitudeFromMapObjectLonLat: function(moLonLat) {
42525 var lat = this.sphericalMercator ?
42526 this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lat :
42534 * APIMethod: getXFromMapObjectPixel
42537 * moPixel - {Object} MapObject Pixel format
42540 * {Integer} X value of the MapObject Pixel
42542 getXFromMapObjectPixel: function(moPixel) {
42547 * APIMethod: getYFromMapObjectPixel
42550 * moPixel - {Object} MapObject Pixel format
42553 * {Integer} Y value of the MapObject Pixel
42555 getYFromMapObjectPixel: function(moPixel) {
42559 CLASS_NAME: "OpenLayers.Layer.Google"
42563 * Property: OpenLayers.Layer.Google.cache
42564 * {Object} Cache for elements that should only be created once per map.
42566 OpenLayers.Layer.Google.cache = {};
42567 /* ======================================================================
42568 OpenLayers/Layer/Google/v3.js
42569 ====================================================================== */
42571 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
42572 * full list of contributors). Published under the 2-clause BSD license.
42573 * See license.txt in the OpenLayers distribution or repository for the
42574 * full text of the license. */
42578 * @requires OpenLayers/Layer/Google.js
42582 * Constant: OpenLayers.Layer.Google.v3
42584 * Mixin providing functionality specific to the Google Maps API v3.
42586 * To use this layer, you must include the GMaps v3 API in your html. To match
42587 * Google's zoom animation better with OpenLayers animated zooming, configure
42588 * your map with a zoomDuration of 10:
42591 * new OpenLayers.Map('map', {zoomDuration: 10});
42594 * Note that this layer configures the google.maps.map object with the
42595 * "disableDefaultUI" option set to true. Using UI controls that the Google
42596 * Maps API provides is not supported by the OpenLayers API.
42598 OpenLayers.Layer.Google.v3 = {
42601 * Constant: DEFAULTS
42602 * {Object} It is not recommended to change the properties set here. Note
42603 * that Google.v3 layers only work when sphericalMercator is set to true.
42607 * sphericalMercator: true,
42608 * projection: "EPSG:900913"
42613 sphericalMercator: true,
42614 projection: "EPSG:900913"
42618 * APIProperty: animationEnabled
42619 * {Boolean} If set to true, the transition between zoom levels will be
42620 * animated (if supported by the GMaps API for the device used). Set to
42621 * false to match the zooming experience of other layer types. Default
42622 * is true. Note that the GMaps API does not give us control over zoom
42623 * animation, so if set to false, when zooming, this will make the
42624 * layer temporarily invisible, wait until GMaps reports the map being
42625 * idle, and make it visible again. The result will be a blank layer
42626 * for a few moments while zooming.
42628 animationEnabled: true,
42631 * Method: loadMapObject
42632 * Load the GMap and register appropriate event listeners.
42634 loadMapObject: function() {
42636 this.type = google.maps.MapTypeId.ROADMAP;
42639 var cache = OpenLayers.Layer.Google.cache[this.map.id];
42641 // there are already Google layers added to this map
42642 mapObject = cache.mapObject;
42643 // increment the layer count
42646 // this is the first Google layer for this map
42648 var center = this.map.getCenter();
42649 var container = document.createElement('div');
42650 container.className = "olForeignContainer";
42651 container.style.width = '100%';
42652 container.style.height = '100%';
42653 mapObject = new google.maps.Map(container, {
42655 new google.maps.LatLng(center.lat, center.lon) :
42656 new google.maps.LatLng(0, 0),
42657 zoom: this.map.getZoom() || 0,
42658 mapTypeId: this.type,
42659 disableDefaultUI: true,
42660 keyboardShortcuts: false,
42662 disableDoubleClickZoom: true,
42663 scrollwheel: false,
42664 streetViewControl: false,
42665 tilt: (this.useTiltImages ? 45: 0)
42667 var googleControl = document.createElement('div');
42668 googleControl.style.width = '100%';
42669 googleControl.style.height = '100%';
42670 mapObject.controls[google.maps.ControlPosition.TOP_LEFT].push(googleControl);
42672 // cache elements for use by any other google layers added to
42675 googleControl: googleControl,
42676 mapObject: mapObject,
42679 OpenLayers.Layer.Google.cache[this.map.id] = cache;
42681 this.mapObject = mapObject;
42682 this.setGMapVisibility(this.visibility);
42686 * APIMethod: onMapResize
42688 onMapResize: function() {
42689 if (this.visibility) {
42690 google.maps.event.trigger(this.mapObject, "resize");
42695 * Method: setGMapVisibility
42696 * Display the GMap container and associated elements.
42699 * visible - {Boolean} Display the GMap elements.
42701 setGMapVisibility: function(visible) {
42702 var cache = OpenLayers.Layer.Google.cache[this.map.id];
42703 var map = this.map;
42705 var type = this.type;
42706 var layers = map.layers;
42708 for (var i=layers.length-1; i>=0; --i) {
42710 if (layer instanceof OpenLayers.Layer.Google &&
42711 layer.visibility === true && layer.inRange === true) {
42717 var container = this.mapObject.getDiv();
42718 if (visible === true) {
42719 if (container.parentNode !== map.div) {
42720 if (!cache.rendered) {
42721 container.style.visibility = 'hidden';
42723 google.maps.event.addListenerOnce(this.mapObject, 'tilesloaded', function() {
42724 cache.rendered = true;
42725 container.style.visibility = '';
42726 me.setGMapVisibility(true);
42727 me.moveTo(me.map.getCenter());
42728 cache.googleControl.appendChild(map.viewPortDiv);
42729 me.setGMapVisibility(me.visible);
42732 cache.googleControl.appendChild(map.viewPortDiv);
42734 map.div.appendChild(container);
42735 google.maps.event.trigger(this.mapObject, 'resize');
42737 this.mapObject.setMapTypeId(type);
42738 } else if (cache.googleControl.hasChildNodes()) {
42739 map.div.appendChild(map.viewPortDiv);
42740 if (map.div.contains(container)) {
42741 map.div.removeChild(container);
42748 * Method: getMapContainer
42751 * {DOMElement} the GMap container's div
42753 getMapContainer: function() {
42754 return this.mapObject.getDiv();
42758 // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds
42762 * APIMethod: getMapObjectBoundsFromOLBounds
42765 * olBounds - {<OpenLayers.Bounds>}
42768 * {Object} A MapObject Bounds, translated from olBounds
42769 * Returns null if null value is passed in
42771 getMapObjectBoundsFromOLBounds: function(olBounds) {
42772 var moBounds = null;
42773 if (olBounds != null) {
42774 var sw = this.sphericalMercator ?
42775 this.inverseMercator(olBounds.bottom, olBounds.left) :
42776 new OpenLayers.LonLat(olBounds.bottom, olBounds.left);
42777 var ne = this.sphericalMercator ?
42778 this.inverseMercator(olBounds.top, olBounds.right) :
42779 new OpenLayers.LonLat(olBounds.top, olBounds.right);
42780 moBounds = new google.maps.LatLngBounds(
42781 new google.maps.LatLng(sw.lat, sw.lon),
42782 new google.maps.LatLng(ne.lat, ne.lon)
42789 /************************************
42791 * MapObject Interface Controls *
42793 ************************************/
42796 // LonLat - Pixel Translation
42799 * APIMethod: getMapObjectLonLatFromMapObjectPixel
42802 * moPixel - {Object} MapObject Pixel format
42805 * {Object} MapObject LonLat translated from MapObject Pixel
42807 getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
42808 var size = this.map.getSize();
42809 var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center);
42810 var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center);
42811 var res = this.map.getResolution();
42813 var delta_x = moPixel.x - (size.w / 2);
42814 var delta_y = moPixel.y - (size.h / 2);
42816 var lonlat = new OpenLayers.LonLat(
42817 lon + delta_x * res,
42818 lat - delta_y * res
42821 if (this.wrapDateLine) {
42822 lonlat = lonlat.wrapDateLine(this.maxExtent);
42824 return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat);
42828 * APIMethod: getMapObjectPixelFromMapObjectLonLat
42831 * moLonLat - {Object} MapObject LonLat format
42834 * {Object} MapObject Pixel transtlated from MapObject LonLat
42836 getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
42837 var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);
42838 var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);
42839 var res = this.map.getResolution();
42840 var extent = this.map.getExtent();
42841 return this.getMapObjectPixelFromXY((1/res * (lon - extent.left)),
42842 (1/res * (extent.top - lat)));
42847 * APIMethod: setMapObjectCenter
42848 * Set the mapObject to the specified center and zoom
42851 * center - {Object} MapObject LonLat format
42852 * zoom - {int} MapObject zoom format
42854 setMapObjectCenter: function(center, zoom) {
42855 if (this.animationEnabled === false && zoom != this.mapObject.zoom) {
42856 var mapContainer = this.getMapContainer();
42857 google.maps.event.addListenerOnce(
42861 mapContainer.style.visibility = "";
42864 mapContainer.style.visibility = "hidden";
42866 this.mapObject.setOptions({
42876 * APIMethod: getMapObjectZoomFromMapObjectBounds
42879 * moBounds - {Object} MapObject Bounds format
42882 * {Object} MapObject Zoom for specified MapObject Bounds
42884 getMapObjectZoomFromMapObjectBounds: function(moBounds) {
42885 return this.mapObject.getBoundsZoomLevel(moBounds);
42888 /************************************
42890 * MapObject Primitives *
42892 ************************************/
42898 * APIMethod: getMapObjectLonLatFromLonLat
42905 * {Object} MapObject LonLat built from lon and lat params
42907 getMapObjectLonLatFromLonLat: function(lon, lat) {
42909 if(this.sphericalMercator) {
42910 var lonlat = this.inverseMercator(lon, lat);
42911 gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon);
42913 gLatLng = new google.maps.LatLng(lat, lon);
42921 * APIMethod: getMapObjectPixelFromXY
42928 * {Object} MapObject Pixel from x and y parameters
42930 getMapObjectPixelFromXY: function(x, y) {
42931 return new google.maps.Point(x, y);
42935 /* ======================================================================
42936 OpenLayers/Strategy.js
42937 ====================================================================== */
42939 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
42940 * full list of contributors). Published under the 2-clause BSD license.
42941 * See license.txt in the OpenLayers distribution or repository for the
42942 * full text of the license. */
42945 * @requires OpenLayers/BaseTypes/Class.js
42949 * Class: OpenLayers.Strategy
42950 * Abstract vector layer strategy class. Not to be instantiated directly. Use
42951 * one of the strategy subclasses instead.
42953 OpenLayers.Strategy = OpenLayers.Class({
42957 * {<OpenLayers.Layer.Vector>} The layer this strategy belongs to.
42962 * Property: options
42963 * {Object} Any options sent to the constructor.
42969 * {Boolean} The control is active.
42974 * Property: autoActivate
42975 * {Boolean} The creator of the strategy can set autoActivate to false
42976 * to fully control when the protocol is activated and deactivated.
42977 * Defaults to true.
42979 autoActivate: true,
42982 * Property: autoDestroy
42983 * {Boolean} The creator of the strategy can set autoDestroy to false
42984 * to fully control when the strategy is destroyed. Defaults to
42990 * Constructor: OpenLayers.Strategy
42991 * Abstract class for vector strategies. Create instances of a subclass.
42994 * options - {Object} Optional object whose properties will be set on the
42997 initialize: function(options) {
42998 OpenLayers.Util.extend(this, options);
42999 this.options = options;
43000 // set the active property here, so that user cannot override it
43001 this.active = false;
43005 * APIMethod: destroy
43006 * Clean up the strategy.
43008 destroy: function() {
43011 this.options = null;
43016 * Called to set the <layer> property.
43019 * layer - {<OpenLayers.Layer.Vector>}
43021 setLayer: function(layer) {
43022 this.layer = layer;
43027 * Activate the strategy. Register any listeners, do appropriate setup.
43030 * {Boolean} True if the strategy was successfully activated or false if
43031 * the strategy was already active.
43033 activate: function() {
43034 if (!this.active) {
43035 this.active = true;
43042 * Method: deactivate
43043 * Deactivate the strategy. Unregister any listeners, do appropriate
43047 * {Boolean} True if the strategy was successfully deactivated or false if
43048 * the strategy was already inactive.
43050 deactivate: function() {
43052 this.active = false;
43058 CLASS_NAME: "OpenLayers.Strategy"
43060 /* ======================================================================
43061 OpenLayers/Strategy/Filter.js
43062 ====================================================================== */
43064 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
43065 * full list of contributors). Published under the 2-clause BSD license.
43066 * See license.txt in the OpenLayers distribution or repository for the
43067 * full text of the license. */
43070 * @requires OpenLayers/Strategy.js
43071 * @requires OpenLayers/Filter.js
43075 * Class: OpenLayers.Strategy.Filter
43076 * Strategy for limiting features that get added to a layer by
43077 * evaluating a filter. The strategy maintains a cache of
43078 * all features until removeFeatures is called on the layer.
43081 * - <OpenLayers.Strategy>
43083 OpenLayers.Strategy.Filter = OpenLayers.Class(OpenLayers.Strategy, {
43086 * APIProperty: filter
43087 * {<OpenLayers.Filter>} Filter for limiting features sent to the layer.
43088 * Use the <setFilter> method to update this filter after construction.
43094 * {Array(<OpenLayers.Feature.Vector>)} List of currently cached
43100 * Property: caching
43101 * {Boolean} The filter is currently caching features.
43106 * Constructor: OpenLayers.Strategy.Filter
43107 * Create a new filter strategy.
43110 * options - {Object} Optional object whose properties will be set on the
43115 * APIMethod: activate
43116 * Activate the strategy. Register any listeners, do appropriate setup.
43117 * By default, this strategy automatically activates itself when a layer
43118 * is added to a map.
43121 * {Boolean} True if the strategy was successfully activated or false if
43122 * the strategy was already active.
43124 activate: function() {
43125 var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments);
43128 this.layer.events.on({
43129 "beforefeaturesadded": this.handleAdd,
43130 "beforefeaturesremoved": this.handleRemove,
43138 * APIMethod: deactivate
43139 * Deactivate the strategy. Clear the feature cache.
43142 * {Boolean} True if the strategy was successfully deactivated or false if
43143 * the strategy was already inactive.
43145 deactivate: function() {
43147 if (this.layer && this.layer.events) {
43148 this.layer.events.un({
43149 "beforefeaturesadded": this.handleAdd,
43150 "beforefeaturesremoved": this.handleRemove,
43154 return OpenLayers.Strategy.prototype.deactivate.apply(this, arguments);
43158 * Method: handleAdd
43160 handleAdd: function(event) {
43161 if (!this.caching && this.filter) {
43162 var features = event.features;
43163 event.features = [];
43165 for (var i=0, ii=features.length; i<ii; ++i) {
43166 feature = features[i];
43167 if (this.filter.evaluate(feature)) {
43168 event.features.push(feature);
43170 this.cache.push(feature);
43177 * Method: handleRemove
43179 handleRemove: function(event) {
43180 if (!this.caching) {
43186 * APIMethod: setFilter
43187 * Update the filter for this strategy. This will re-evaluate
43188 * any features on the layer and in the cache. Only features
43189 * for which filter.evalute(feature) returns true will be
43190 * added to the layer. Others will be cached by the strategy.
43193 * filter - {<OpenLayers.Filter>} A filter for evaluating features.
43195 setFilter: function(filter) {
43196 this.filter = filter;
43197 var previousCache = this.cache;
43199 // look through layer for features to remove from layer
43200 this.handleAdd({features: this.layer.features});
43201 // cache now contains features to remove from layer
43202 if (this.cache.length > 0) {
43203 this.caching = true;
43204 this.layer.removeFeatures(this.cache.slice());
43205 this.caching = false;
43207 // now look through previous cache for features to add to layer
43208 if (previousCache.length > 0) {
43209 var event = {features: previousCache};
43210 this.handleAdd(event);
43211 if (event.features.length > 0) {
43212 // event has features to add to layer
43213 this.caching = true;
43214 this.layer.addFeatures(event.features);
43215 this.caching = false;
43220 CLASS_NAME: "OpenLayers.Strategy.Filter"
43223 /* ======================================================================
43224 OpenLayers/Strategy/BBOX.js
43225 ====================================================================== */
43227 /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
43228 * full list of contributors). Published under the 2-clause BSD license.
43229 * See license.txt in the OpenLayers distribution or repository for the
43230 * full text of the license. */
43233 * @requires OpenLayers/Strategy.js
43234 * @requires OpenLayers/Filter/Spatial.js
43238 * Class: OpenLayers.Strategy.BBOX
43239 * A simple strategy that reads new features when the viewport invalidates
43243 * - <OpenLayers.Strategy>
43245 OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, {
43249 * {<OpenLayers.Bounds>} The current data bounds (in the same projection
43250 * as the layer - not always the same projection as the map).
43255 * Property: resolution
43256 * {Float} The current data resolution.
43261 * APIProperty: ratio
43262 * {Float} The ratio of the data bounds to the viewport bounds (in each
43263 * dimension). Default is 2.
43268 * Property: resFactor
43269 * {Float} Optional factor used to determine when previously requested
43270 * features are invalid. If set, the resFactor will be compared to the
43271 * resolution of the previous request to the current map resolution.
43272 * If resFactor > (old / new) and 1/resFactor < (old / new). If you
43273 * set a resFactor of 1, data will be requested every time the
43274 * resolution changes. If you set a resFactor of 3, data will be
43275 * requested if the old resolution is 3 times the new, or if the new is
43276 * 3 times the old. If the old bounds do not contain the new bounds
43277 * new data will always be requested (with or without considering
43283 * Property: response
43284 * {<OpenLayers.Protocol.Response>} The protocol response object returned
43285 * by the layer protocol.
43290 * Constructor: OpenLayers.Strategy.BBOX
43291 * Create a new BBOX strategy.
43294 * options - {Object} Optional object whose properties will be set on the
43300 * Set up strategy with regard to reading new batches of remote data.
43303 * {Boolean} The strategy was successfully activated.
43305 activate: function() {
43306 var activated = OpenLayers.Strategy.prototype.activate.call(this);
43308 this.layer.events.on({
43309 "moveend": this.update,
43310 "refresh": this.update,
43311 "visibilitychanged": this.update,
43320 * Method: deactivate
43321 * Tear down strategy with regard to reading new batches of remote data.
43324 * {Boolean} The strategy was successfully deactivated.
43326 deactivate: function() {
43327 var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
43329 this.layer.events.un({
43330 "moveend": this.update,
43331 "refresh": this.update,
43332 "visibilitychanged": this.update,
43336 return deactivated;
43341 * Callback function called on "moveend" or "refresh" layer events.
43344 * options - {Object} Optional object whose properties will determine
43345 * the behaviour of this Strategy
43347 * Valid options include:
43348 * force - {Boolean} if true, new data must be unconditionally read.
43349 * noAbort - {Boolean} if true, do not abort previous requests.
43351 update: function(options) {
43352 var mapBounds = this.getMapBounds();
43353 if (mapBounds !== null && ((options && options.force) ||
43354 (this.layer.visibility && this.layer.calculateInRange() && this.invalidBounds(mapBounds)))) {
43355 this.calculateBounds(mapBounds);
43356 this.resolution = this.layer.map.getResolution();
43357 this.triggerRead(options);
43362 * Method: getMapBounds
43363 * Get the map bounds expressed in the same projection as this layer.
43366 * {<OpenLayers.Bounds>} Map bounds in the projection of the layer.
43368 getMapBounds: function() {
43369 if (this.layer.map === null) {
43372 var bounds = this.layer.map.getExtent();
43373 if (bounds && this.layer.projection && !this.layer.projection.equals(
43374 this.layer.map.getProjectionObject())) {
43375 bounds = bounds.clone().transform(
43376 this.layer.map.getProjectionObject(), this.layer.projection
43383 * Method: invalidBounds
43384 * Determine whether the previously requested set of features is invalid.
43385 * This occurs when the new map bounds do not contain the previously
43386 * requested bounds. In addition, if <resFactor> is set, it will be
43390 * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be
43391 * retrieved from the map object if not provided
43396 invalidBounds: function(mapBounds) {
43398 mapBounds = this.getMapBounds();
43400 var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds);
43401 if(!invalid && this.resFactor) {
43402 var ratio = this.resolution / this.layer.map.getResolution();
43403 invalid = (ratio >= this.resFactor || ratio <= (1 / this.resFactor));
43409 * Method: calculateBounds
43412 * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be
43413 * retrieved from the map object if not provided
43415 calculateBounds: function(mapBounds) {
43417 mapBounds = this.getMapBounds();
43419 var center = mapBounds.getCenterLonLat();
43420 var dataWidth = mapBounds.getWidth() * this.ratio;
43421 var dataHeight = mapBounds.getHeight() * this.ratio;
43422 this.bounds = new OpenLayers.Bounds(
43423 center.lon - (dataWidth / 2),
43424 center.lat - (dataHeight / 2),
43425 center.lon + (dataWidth / 2),
43426 center.lat + (dataHeight / 2)
43431 * Method: triggerRead
43434 * options - {Object} Additional options for the protocol's read method
43438 * {<OpenLayers.Protocol.Response>} The protocol response object
43439 * returned by the layer protocol.
43441 triggerRead: function(options) {
43442 if (this.response && !(options && options.noAbort === true)) {
43443 this.layer.protocol.abort(this.response);
43444 this.layer.events.triggerEvent("loadend");
43446 var evt = {filter: this.createFilter()};
43447 this.layer.events.triggerEvent("loadstart", evt);
43448 this.response = this.layer.protocol.read(
43449 OpenLayers.Util.applyDefaults({
43450 filter: evt.filter,
43451 callback: this.merge,
43457 * Method: createFilter
43458 * Creates a spatial BBOX filter. If the layer that this strategy belongs
43459 * to has a filter property, this filter will be combined with the BBOX
43463 * {<OpenLayers.Filter>} The filter object.
43465 createFilter: function() {
43466 var filter = new OpenLayers.Filter.Spatial({
43467 type: OpenLayers.Filter.Spatial.BBOX,
43468 value: this.bounds,
43469 projection: this.layer.projection
43471 if (this.layer.filter) {
43472 filter = new OpenLayers.Filter.Logical({
43473 type: OpenLayers.Filter.Logical.AND,
43474 filters: [this.layer.filter, filter]
43482 * Given a list of features, determine which ones to add to the layer.
43483 * If the layer projection differs from the map projection, features
43484 * will be transformed from the layer projection to the map projection.
43487 * resp - {<OpenLayers.Protocol.Response>} The response object passed
43490 merge: function(resp) {
43491 this.layer.destroyFeatures();
43492 if (resp.success()) {
43493 var features = resp.features;
43494 if(features && features.length > 0) {
43495 var remote = this.layer.projection;
43496 var local = this.layer.map.getProjectionObject();
43497 if(remote && local && !local.equals(remote)) {
43499 for(var i=0, len=features.length; i<len; ++i) {
43500 geom = features[i].geometry;
43502 geom.transform(remote, local);
43506 this.layer.addFeatures(features);
43509 this.bounds = null;
43511 this.response = null;
43512 this.layer.events.triggerEvent("loadend", {response: resp});
43515 CLASS_NAME: "OpenLayers.Strategy.BBOX"