/*
|
|
OpenLayers.js -- OpenLayers Map Viewer Library
|
|
Copyright (c) 2006-2013 by OpenLayers Contributors
|
Published under the 2-clause BSD license.
|
See http://openlayers.org/dev/license.txt for the full text of the license, and http://openlayers.org/dev/authors.txt for full list of contributors.
|
|
Includes compressed code under the following licenses:
|
|
(For uncompressed versions of the code used, please see the
|
OpenLayers Github repository: <https://github.com/openlayers/openlayers>)
|
|
*/
|
|
/**
|
* Contains XMLHttpRequest.js <http://code.google.com/p/xmlhttprequest/>
|
* Copyright 2007 Sergey Ilinsky (http://www.ilinsky.com)
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*/
|
|
/**
|
* OpenLayers.Util.pagePosition is based on Yahoo's getXY method, which is
|
* Copyright (c) 2006, Yahoo! Inc.
|
* All rights reserved.
|
*
|
* Redistribution and use of this software in source and binary forms, with or
|
* without modification, are permitted provided that the following conditions
|
* are met:
|
*
|
* * Redistributions of source code must retain the above copyright notice,
|
* this list of conditions and the following disclaimer.
|
*
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
* this list of conditions and the following disclaimer in the documentation
|
* and/or other materials provided with the distribution.
|
*
|
* * Neither the name of Yahoo! Inc. nor the names of its contributors may be
|
* used to endorse or promote products derived from this software without
|
* specific prior written permission of Yahoo! Inc.
|
*
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
* POSSIBILITY OF SUCH DAMAGE.
|
*/
|
/* ======================================================================
|
OpenLayers/SingleFile.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
var OpenLayers = {
|
/**
|
* Constant: VERSION_NUMBER
|
*/
|
VERSION_NUMBER: "Release 2.13.1",
|
|
/**
|
* Constant: singleFile
|
* TODO: remove this in 3.0 when we stop supporting build profiles that
|
* include OpenLayers.js
|
*/
|
singleFile: true,
|
|
/**
|
* Method: _getScriptLocation
|
* Return the path to this script. This is also implemented in
|
* OpenLayers.js
|
*
|
* Returns:
|
* {String} Path to this script
|
*/
|
_getScriptLocation: (function() {
|
var r = new RegExp("(^|(.*?\\/))(OpenLayers[^\\/]*?\\.js)(\\?|$)"),
|
s = document.getElementsByTagName('script'),
|
src, m, l = "";
|
for(var i=0, len=s.length; i<len; i++) {
|
src = s[i].getAttribute('src');
|
if(src) {
|
m = src.match(r);
|
if(m) {
|
l = m[1];
|
break;
|
}
|
}
|
}
|
return (function() { return l; });
|
})(),
|
|
/**
|
* Property: ImgPath
|
* {String} Set this to the path where control images are stored, a path
|
* given here must end with a slash. If set to '' (which is the default)
|
* OpenLayers will use its script location + "img/".
|
*
|
* You will need to set this property when you have a singlefile build of
|
* OpenLayers that either is not named "OpenLayers.js" or if you move
|
* the file in a way such that the image directory cannot be derived from
|
* the script location.
|
*
|
* If your custom OpenLayers build is named "my-custom-ol.js" and the images
|
* of OpenLayers are in a folder "/resources/external/images/ol" a correct
|
* way of including OpenLayers in your HTML would be:
|
*
|
* (code)
|
* <script src="/path/to/my-custom-ol.js" type="text/javascript"></script>
|
* <script type="text/javascript">
|
* // tell OpenLayers where the control images are
|
* // remember the trailing slash
|
* OpenLayers.ImgPath = "/resources/external/images/ol/";
|
* </script>
|
* (end code)
|
*
|
* Please remember that when your OpenLayers script is not named
|
* "OpenLayers.js" you will have to make sure that the default theme is
|
* loaded into the page by including an appropriate <link>-tag,
|
* e.g.:
|
*
|
* (code)
|
* <link rel="stylesheet" href="/path/to/default/style.css" type="text/css">
|
* (end code)
|
*/
|
ImgPath : ''
|
};
|
/* ======================================================================
|
OpenLayers/BaseTypes/Class.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/SingleFile.js
|
*/
|
|
/**
|
* Constructor: OpenLayers.Class
|
* Base class used to construct all other classes. Includes support for
|
* multiple inheritance.
|
*
|
* This constructor is new in OpenLayers 2.5. At OpenLayers 3.0, the old
|
* syntax for creating classes and dealing with inheritance
|
* will be removed.
|
*
|
* To create a new OpenLayers-style class, use the following syntax:
|
* (code)
|
* var MyClass = OpenLayers.Class(prototype);
|
* (end)
|
*
|
* To create a new OpenLayers-style class with multiple inheritance, use the
|
* following syntax:
|
* (code)
|
* var MyClass = OpenLayers.Class(Class1, Class2, prototype);
|
* (end)
|
*
|
* Note that instanceof reflection will only reveal Class1 as superclass.
|
*
|
*/
|
OpenLayers.Class = function() {
|
var len = arguments.length;
|
var P = arguments[0];
|
var F = arguments[len-1];
|
|
var C = typeof F.initialize == "function" ?
|
F.initialize :
|
function(){ P.prototype.initialize.apply(this, arguments); };
|
|
if (len > 1) {
|
var newArgs = [C, P].concat(
|
Array.prototype.slice.call(arguments).slice(1, len-1), F);
|
OpenLayers.inherit.apply(null, newArgs);
|
} else {
|
C.prototype = F;
|
}
|
return C;
|
};
|
|
/**
|
* Function: OpenLayers.inherit
|
*
|
* Parameters:
|
* C - {Object} the class that inherits
|
* P - {Object} the superclass to inherit from
|
*
|
* In addition to the mandatory C and P parameters, an arbitrary number of
|
* objects can be passed, which will extend C.
|
*/
|
OpenLayers.inherit = function(C, P) {
|
var F = function() {};
|
F.prototype = P.prototype;
|
C.prototype = new F;
|
var i, l, o;
|
for(i=2, l=arguments.length; i<l; i++) {
|
o = arguments[i];
|
if(typeof o === "function") {
|
o = o.prototype;
|
}
|
OpenLayers.Util.extend(C.prototype, o);
|
}
|
};
|
|
/**
|
* APIFunction: extend
|
* Copy all properties of a source object to a destination object. Modifies
|
* the passed in destination object. Any properties on the source object
|
* that are set to undefined will not be (re)set on the destination object.
|
*
|
* Parameters:
|
* destination - {Object} The object that will be modified
|
* source - {Object} The object with properties to be set on the destination
|
*
|
* Returns:
|
* {Object} The destination object.
|
*/
|
OpenLayers.Util = OpenLayers.Util || {};
|
OpenLayers.Util.extend = function(destination, source) {
|
destination = destination || {};
|
if (source) {
|
for (var property in source) {
|
var value = source[property];
|
if (value !== undefined) {
|
destination[property] = value;
|
}
|
}
|
|
/**
|
* IE doesn't include the toString property when iterating over an object's
|
* properties with the for(property in object) syntax. Explicitly check if
|
* the source has its own toString property.
|
*/
|
|
/*
|
* FF/Windows < 2.0.0.13 reports "Illegal operation on WrappedNative
|
* prototype object" when calling hawOwnProperty if the source object
|
* is an instance of window.Event.
|
*/
|
|
var sourceIsEvt = typeof window.Event == "function"
|
&& source instanceof window.Event;
|
|
if (!sourceIsEvt
|
&& source.hasOwnProperty && source.hasOwnProperty("toString")) {
|
destination.toString = source.toString;
|
}
|
}
|
return destination;
|
};
|
/* ======================================================================
|
OpenLayers/BaseTypes.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/SingleFile.js
|
*/
|
|
/**
|
* Header: OpenLayers Base Types
|
* OpenLayers custom string, number and function functions are described here.
|
*/
|
|
/**
|
* Namespace: OpenLayers.String
|
* Contains convenience functions for string manipulation.
|
*/
|
OpenLayers.String = {
|
|
/**
|
* APIFunction: startsWith
|
* Test whether a string starts with another string.
|
*
|
* Parameters:
|
* str - {String} The string to test.
|
* sub - {String} The substring to look for.
|
*
|
* Returns:
|
* {Boolean} The first string starts with the second.
|
*/
|
startsWith: function(str, sub) {
|
return (str.indexOf(sub) == 0);
|
},
|
|
/**
|
* APIFunction: contains
|
* Test whether a string contains another string.
|
*
|
* Parameters:
|
* str - {String} The string to test.
|
* sub - {String} The substring to look for.
|
*
|
* Returns:
|
* {Boolean} The first string contains the second.
|
*/
|
contains: function(str, sub) {
|
return (str.indexOf(sub) != -1);
|
},
|
|
/**
|
* APIFunction: trim
|
* Removes leading and trailing whitespace characters from a string.
|
*
|
* Parameters:
|
* str - {String} The (potentially) space padded string. This string is not
|
* modified.
|
*
|
* Returns:
|
* {String} A trimmed version of the string with all leading and
|
* trailing spaces removed.
|
*/
|
trim: function(str) {
|
return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
|
},
|
|
/**
|
* APIFunction: camelize
|
* Camel-case a hyphenated string.
|
* Ex. "chicken-head" becomes "chickenHead", and
|
* "-chicken-head" becomes "ChickenHead".
|
*
|
* Parameters:
|
* str - {String} The string to be camelized. The original is not modified.
|
*
|
* Returns:
|
* {String} The string, camelized
|
*/
|
camelize: function(str) {
|
var oStringList = str.split('-');
|
var camelizedString = oStringList[0];
|
for (var i=1, len=oStringList.length; i<len; i++) {
|
var s = oStringList[i];
|
camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
|
}
|
return camelizedString;
|
},
|
|
/**
|
* APIFunction: format
|
* Given a string with tokens in the form ${token}, return a string
|
* with tokens replaced with properties from the given context
|
* object. Represent a literal "${" by doubling it, e.g. "${${".
|
*
|
* Parameters:
|
* template - {String} A string with tokens to be replaced. A template
|
* has the form "literal ${token}" where the token will be replaced
|
* by the value of context["token"].
|
* context - {Object} An optional object with properties corresponding
|
* to the tokens in the format string. If no context is sent, the
|
* window object will be used.
|
* args - {Array} Optional arguments to pass to any functions found in
|
* the context. If a context property is a function, the token
|
* will be replaced by the return from the function called with
|
* these arguments.
|
*
|
* Returns:
|
* {String} A string with tokens replaced from the context object.
|
*/
|
format: function(template, context, args) {
|
if(!context) {
|
context = window;
|
}
|
|
// Example matching:
|
// str = ${foo.bar}
|
// match = foo.bar
|
var replacer = function(str, match) {
|
var replacement;
|
|
// Loop through all subs. Example: ${a.b.c}
|
// 0 -> replacement = context[a];
|
// 1 -> replacement = context[a][b];
|
// 2 -> replacement = context[a][b][c];
|
var subs = match.split(/\.+/);
|
for (var i=0; i< subs.length; i++) {
|
if (i == 0) {
|
replacement = context;
|
}
|
if (replacement === undefined) {
|
break;
|
}
|
replacement = replacement[subs[i]];
|
}
|
|
if(typeof replacement == "function") {
|
replacement = args ?
|
replacement.apply(null, args) :
|
replacement();
|
}
|
|
// If replacement is undefined, return the string 'undefined'.
|
// This is a workaround for a bugs in browsers not properly
|
// dealing with non-participating groups in regular expressions:
|
// http://blog.stevenlevithan.com/archives/npcg-javascript
|
if (typeof replacement == 'undefined') {
|
return 'undefined';
|
} else {
|
return replacement;
|
}
|
};
|
|
return template.replace(OpenLayers.String.tokenRegEx, replacer);
|
},
|
|
/**
|
* Property: tokenRegEx
|
* Used to find tokens in a string.
|
* Examples: ${a}, ${a.b.c}, ${a-b}, ${5}
|
*/
|
tokenRegEx: /\$\{([\w.]+?)\}/g,
|
|
/**
|
* Property: numberRegEx
|
* Used to test strings as numbers.
|
*/
|
numberRegEx: /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/,
|
|
/**
|
* APIFunction: isNumeric
|
* Determine whether a string contains only a numeric value.
|
*
|
* Examples:
|
* (code)
|
* OpenLayers.String.isNumeric("6.02e23") // true
|
* OpenLayers.String.isNumeric("12 dozen") // false
|
* OpenLayers.String.isNumeric("4") // true
|
* OpenLayers.String.isNumeric(" 4 ") // false
|
* (end)
|
*
|
* Returns:
|
* {Boolean} String contains only a number.
|
*/
|
isNumeric: function(value) {
|
return OpenLayers.String.numberRegEx.test(value);
|
},
|
|
/**
|
* APIFunction: numericIf
|
* Converts a string that appears to be a numeric value into a number.
|
*
|
* Parameters:
|
* value - {String}
|
* trimWhitespace - {Boolean}
|
*
|
* Returns:
|
* {Number|String} a Number if the passed value is a number, a String
|
* otherwise.
|
*/
|
numericIf: function(value, trimWhitespace) {
|
var originalValue = value;
|
if (trimWhitespace === true && value != null && value.replace) {
|
value = value.replace(/^\s*|\s*$/g, "");
|
}
|
return OpenLayers.String.isNumeric(value) ? parseFloat(value) : originalValue;
|
}
|
|
};
|
|
/**
|
* Namespace: OpenLayers.Number
|
* Contains convenience functions for manipulating numbers.
|
*/
|
OpenLayers.Number = {
|
|
/**
|
* Property: decimalSeparator
|
* Decimal separator to use when formatting numbers.
|
*/
|
decimalSeparator: ".",
|
|
/**
|
* Property: thousandsSeparator
|
* Thousands separator to use when formatting numbers.
|
*/
|
thousandsSeparator: ",",
|
|
/**
|
* APIFunction: limitSigDigs
|
* Limit the number of significant digits on a float.
|
*
|
* Parameters:
|
* num - {Float}
|
* sig - {Integer}
|
*
|
* Returns:
|
* {Float} The number, rounded to the specified number of significant
|
* digits.
|
*/
|
limitSigDigs: function(num, sig) {
|
var fig = 0;
|
if (sig > 0) {
|
fig = parseFloat(num.toPrecision(sig));
|
}
|
return fig;
|
},
|
|
/**
|
* APIFunction: format
|
* Formats a number for output.
|
*
|
* Parameters:
|
* num - {Float}
|
* dec - {Integer} Number of decimal places to round to.
|
* Defaults to 0. Set to null to leave decimal places unchanged.
|
* tsep - {String} Thousands separator.
|
* Default is ",".
|
* dsep - {String} Decimal separator.
|
* Default is ".".
|
*
|
* Returns:
|
* {String} A string representing the formatted number.
|
*/
|
format: function(num, dec, tsep, dsep) {
|
dec = (typeof dec != "undefined") ? dec : 0;
|
tsep = (typeof tsep != "undefined") ? tsep :
|
OpenLayers.Number.thousandsSeparator;
|
dsep = (typeof dsep != "undefined") ? dsep :
|
OpenLayers.Number.decimalSeparator;
|
|
if (dec != null) {
|
num = parseFloat(num.toFixed(dec));
|
}
|
|
var parts = num.toString().split(".");
|
if (parts.length == 1 && dec == null) {
|
// integer where we do not want to touch the decimals
|
dec = 0;
|
}
|
|
var integer = parts[0];
|
if (tsep) {
|
var thousands = /(-?[0-9]+)([0-9]{3})/;
|
while(thousands.test(integer)) {
|
integer = integer.replace(thousands, "$1" + tsep + "$2");
|
}
|
}
|
|
var str;
|
if (dec == 0) {
|
str = integer;
|
} else {
|
var rem = parts.length > 1 ? parts[1] : "0";
|
if (dec != null) {
|
rem = rem + new Array(dec - rem.length + 1).join("0");
|
}
|
str = integer + dsep + rem;
|
}
|
return str;
|
},
|
|
/**
|
* Method: zeroPad
|
* Create a zero padded string optionally with a radix for casting numbers.
|
*
|
* Parameters:
|
* num - {Number} The number to be zero padded.
|
* len - {Number} The length of the string to be returned.
|
* radix - {Number} An integer between 2 and 36 specifying the base to use
|
* for representing numeric values.
|
*/
|
zeroPad: function(num, len, radix) {
|
var str = num.toString(radix || 10);
|
while (str.length < len) {
|
str = "0" + str;
|
}
|
return str;
|
}
|
};
|
|
/**
|
* Namespace: OpenLayers.Function
|
* Contains convenience functions for function manipulation.
|
*/
|
OpenLayers.Function = {
|
/**
|
* APIFunction: bind
|
* Bind a function to an object. Method to easily create closures with
|
* 'this' altered.
|
*
|
* Parameters:
|
* func - {Function} Input function.
|
* object - {Object} The object to bind to the input function (as this).
|
*
|
* Returns:
|
* {Function} A closure with 'this' set to the passed in object.
|
*/
|
bind: function(func, object) {
|
// create a reference to all arguments past the second one
|
var args = Array.prototype.slice.apply(arguments, [2]);
|
return function() {
|
// Push on any additional arguments from the actual function call.
|
// These will come after those sent to the bind call.
|
var newArgs = args.concat(
|
Array.prototype.slice.apply(arguments, [0])
|
);
|
return func.apply(object, newArgs);
|
};
|
},
|
|
/**
|
* APIFunction: bindAsEventListener
|
* Bind a function to an object, and configure it to receive the event
|
* object as first parameter when called.
|
*
|
* Parameters:
|
* func - {Function} Input function to serve as an event listener.
|
* object - {Object} A reference to this.
|
*
|
* Returns:
|
* {Function}
|
*/
|
bindAsEventListener: function(func, object) {
|
return function(event) {
|
return func.call(object, event || window.event);
|
};
|
},
|
|
/**
|
* APIFunction: False
|
* A simple function to that just does "return false". We use this to
|
* avoid attaching anonymous functions to DOM event handlers, which
|
* causes "issues" on IE<8.
|
*
|
* Usage:
|
* document.onclick = OpenLayers.Function.False;
|
*
|
* Returns:
|
* {Boolean}
|
*/
|
False : function() {
|
return false;
|
},
|
|
/**
|
* APIFunction: True
|
* A simple function to that just does "return true". We use this to
|
* avoid attaching anonymous functions to DOM event handlers, which
|
* causes "issues" on IE<8.
|
*
|
* Usage:
|
* document.onclick = OpenLayers.Function.True;
|
*
|
* Returns:
|
* {Boolean}
|
*/
|
True : function() {
|
return true;
|
},
|
|
/**
|
* APIFunction: Void
|
* A reusable function that returns ``undefined``.
|
*
|
* Returns:
|
* {undefined}
|
*/
|
Void: function() {}
|
|
};
|
|
/**
|
* Namespace: OpenLayers.Array
|
* Contains convenience functions for array manipulation.
|
*/
|
OpenLayers.Array = {
|
|
/**
|
* APIMethod: filter
|
* Filter an array. Provides the functionality of the
|
* Array.prototype.filter extension to the ECMA-262 standard. Where
|
* available, Array.prototype.filter will be used.
|
*
|
* Based on well known example from http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/filter
|
*
|
* Parameters:
|
* array - {Array} The array to be filtered. This array is not mutated.
|
* Elements added to this array by the callback will not be visited.
|
* callback - {Function} A function that is called for each element in
|
* the array. If this function returns true, the element will be
|
* included in the return. The function will be called with three
|
* arguments: the element in the array, the index of that element, and
|
* the array itself. If the optional caller parameter is specified
|
* the callback will be called with this set to caller.
|
* caller - {Object} Optional object to be set as this when the callback
|
* is called.
|
*
|
* Returns:
|
* {Array} An array of elements from the passed in array for which the
|
* callback returns true.
|
*/
|
filter: function(array, callback, caller) {
|
var selected = [];
|
if (Array.prototype.filter) {
|
selected = array.filter(callback, caller);
|
} else {
|
var len = array.length;
|
if (typeof callback != "function") {
|
throw new TypeError();
|
}
|
for(var i=0; i<len; i++) {
|
if (i in array) {
|
var val = array[i];
|
if (callback.call(caller, val, i, array)) {
|
selected.push(val);
|
}
|
}
|
}
|
}
|
return selected;
|
}
|
|
};
|
/* ======================================================================
|
OpenLayers/BaseTypes/Bounds.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
*/
|
|
/**
|
* Class: OpenLayers.Bounds
|
* Instances of this class represent bounding boxes. Data stored as left,
|
* bottom, right, top floats. All values are initialized to null, however,
|
* you should make sure you set them before using the bounds for anything.
|
*
|
* Possible use case:
|
* (code)
|
* bounds = new OpenLayers.Bounds();
|
* bounds.extend(new OpenLayers.LonLat(4,5));
|
* bounds.extend(new OpenLayers.LonLat(5,6));
|
* bounds.toBBOX(); // returns 4,5,5,6
|
* (end)
|
*/
|
OpenLayers.Bounds = OpenLayers.Class({
|
|
/**
|
* Property: left
|
* {Number} Minimum horizontal coordinate.
|
*/
|
left: null,
|
|
/**
|
* Property: bottom
|
* {Number} Minimum vertical coordinate.
|
*/
|
bottom: null,
|
|
/**
|
* Property: right
|
* {Number} Maximum horizontal coordinate.
|
*/
|
right: null,
|
|
/**
|
* Property: top
|
* {Number} Maximum vertical coordinate.
|
*/
|
top: null,
|
|
/**
|
* Property: centerLonLat
|
* {<OpenLayers.LonLat>} A cached center location. This should not be
|
* accessed directly. Use <getCenterLonLat> instead.
|
*/
|
centerLonLat: null,
|
|
/**
|
* Constructor: OpenLayers.Bounds
|
* Construct a new bounds object. Coordinates can either be passed as four
|
* arguments, or as a single argument.
|
*
|
* Parameters (four arguments):
|
* left - {Number} The left bounds of the box. Note that for width
|
* calculations, this is assumed to be less than the right value.
|
* bottom - {Number} The bottom bounds of the box. Note that for height
|
* calculations, this is assumed to be less than the top value.
|
* right - {Number} The right bounds.
|
* top - {Number} The top bounds.
|
*
|
* Parameters (single argument):
|
* bounds - {Array(Number)} [left, bottom, right, top]
|
*/
|
initialize: function(left, bottom, right, top) {
|
if (OpenLayers.Util.isArray(left)) {
|
top = left[3];
|
right = left[2];
|
bottom = left[1];
|
left = left[0];
|
}
|
if (left != null) {
|
this.left = OpenLayers.Util.toFloat(left);
|
}
|
if (bottom != null) {
|
this.bottom = OpenLayers.Util.toFloat(bottom);
|
}
|
if (right != null) {
|
this.right = OpenLayers.Util.toFloat(right);
|
}
|
if (top != null) {
|
this.top = OpenLayers.Util.toFloat(top);
|
}
|
},
|
|
/**
|
* Method: clone
|
* Create a cloned instance of this bounds.
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} A fresh copy of the bounds
|
*/
|
clone:function() {
|
return new OpenLayers.Bounds(this.left, this.bottom,
|
this.right, this.top);
|
},
|
|
/**
|
* Method: equals
|
* Test a two bounds for equivalence.
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
*
|
* Returns:
|
* {Boolean} The passed-in bounds object has the same left,
|
* right, top, bottom components as this. Note that if bounds
|
* passed in is null, returns false.
|
*/
|
equals:function(bounds) {
|
var equals = false;
|
if (bounds != null) {
|
equals = ((this.left == bounds.left) &&
|
(this.right == bounds.right) &&
|
(this.top == bounds.top) &&
|
(this.bottom == bounds.bottom));
|
}
|
return equals;
|
},
|
|
/**
|
* APIMethod: toString
|
* Returns a string representation of the bounds object.
|
*
|
* Returns:
|
* {String} String representation of bounds object.
|
*/
|
toString:function() {
|
return [this.left, this.bottom, this.right, this.top].join(",");
|
},
|
|
/**
|
* APIMethod: toArray
|
* Returns an array representation of the bounds object.
|
*
|
* Returns an array of left, bottom, right, top properties, or -- when the
|
* optional parameter is true -- an array of the bottom, left, top,
|
* right properties.
|
*
|
* Parameters:
|
* reverseAxisOrder - {Boolean} Should we reverse the axis order?
|
*
|
* Returns:
|
* {Array} array of left, bottom, right, top
|
*/
|
toArray: function(reverseAxisOrder) {
|
if (reverseAxisOrder === true) {
|
return [this.bottom, this.left, this.top, this.right];
|
} else {
|
return [this.left, this.bottom, this.right, this.top];
|
}
|
},
|
|
/**
|
* APIMethod: toBBOX
|
* Returns a boundingbox-string representation of the bounds object.
|
*
|
* Parameters:
|
* decimal - {Integer} How many significant digits in the bbox coords?
|
* Default is 6
|
* reverseAxisOrder - {Boolean} Should we reverse the axis order?
|
*
|
* Returns:
|
* {String} Simple String representation of bounds object.
|
* (e.g. "5,42,10,45")
|
*/
|
toBBOX:function(decimal, reverseAxisOrder) {
|
if (decimal== null) {
|
decimal = 6;
|
}
|
var mult = Math.pow(10, decimal);
|
var xmin = Math.round(this.left * mult) / mult;
|
var ymin = Math.round(this.bottom * mult) / mult;
|
var xmax = Math.round(this.right * mult) / mult;
|
var ymax = Math.round(this.top * mult) / mult;
|
if (reverseAxisOrder === true) {
|
return ymin + "," + xmin + "," + ymax + "," + xmax;
|
} else {
|
return xmin + "," + ymin + "," + xmax + "," + ymax;
|
}
|
},
|
|
/**
|
* APIMethod: toGeometry
|
* Create a new polygon geometry based on this bounds.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry.Polygon>} A new polygon with the coordinates
|
* of this bounds.
|
*/
|
toGeometry: function() {
|
return new OpenLayers.Geometry.Polygon([
|
new OpenLayers.Geometry.LinearRing([
|
new OpenLayers.Geometry.Point(this.left, this.bottom),
|
new OpenLayers.Geometry.Point(this.right, this.bottom),
|
new OpenLayers.Geometry.Point(this.right, this.top),
|
new OpenLayers.Geometry.Point(this.left, this.top)
|
])
|
]);
|
},
|
|
/**
|
* APIMethod: getWidth
|
* Returns the width of the bounds.
|
*
|
* Returns:
|
* {Float} The width of the bounds (right minus left).
|
*/
|
getWidth:function() {
|
return (this.right - this.left);
|
},
|
|
/**
|
* APIMethod: getHeight
|
* Returns the height of the bounds.
|
*
|
* Returns:
|
* {Float} The height of the bounds (top minus bottom).
|
*/
|
getHeight:function() {
|
return (this.top - this.bottom);
|
},
|
|
/**
|
* APIMethod: getSize
|
* Returns an <OpenLayers.Size> object of the bounds.
|
*
|
* Returns:
|
* {<OpenLayers.Size>} The size of the bounds.
|
*/
|
getSize:function() {
|
return new OpenLayers.Size(this.getWidth(), this.getHeight());
|
},
|
|
/**
|
* APIMethod: getCenterPixel
|
* Returns the <OpenLayers.Pixel> object which represents the center of the
|
* bounds.
|
*
|
* Returns:
|
* {<OpenLayers.Pixel>} The center of the bounds in pixel space.
|
*/
|
getCenterPixel:function() {
|
return new OpenLayers.Pixel( (this.left + this.right) / 2,
|
(this.bottom + this.top) / 2);
|
},
|
|
/**
|
* APIMethod: getCenterLonLat
|
* Returns the <OpenLayers.LonLat> object which represents the center of the
|
* bounds.
|
*
|
* Returns:
|
* {<OpenLayers.LonLat>} The center of the bounds in map space.
|
*/
|
getCenterLonLat:function() {
|
if(!this.centerLonLat) {
|
this.centerLonLat = new OpenLayers.LonLat(
|
(this.left + this.right) / 2, (this.bottom + this.top) / 2
|
);
|
}
|
return this.centerLonLat;
|
},
|
|
/**
|
* APIMethod: scale
|
* Scales the bounds around a pixel or lonlat. Note that the new
|
* bounds may return non-integer properties, even if a pixel
|
* is passed.
|
*
|
* Parameters:
|
* ratio - {Float}
|
* origin - {<OpenLayers.Pixel> or <OpenLayers.LonLat>}
|
* Default is center.
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} A new bounds that is scaled by ratio
|
* from origin.
|
*/
|
scale: function(ratio, origin){
|
if(origin == null){
|
origin = this.getCenterLonLat();
|
}
|
|
var origx,origy;
|
|
// get origin coordinates
|
if(origin.CLASS_NAME == "OpenLayers.LonLat"){
|
origx = origin.lon;
|
origy = origin.lat;
|
} else {
|
origx = origin.x;
|
origy = origin.y;
|
}
|
|
var left = (this.left - origx) * ratio + origx;
|
var bottom = (this.bottom - origy) * ratio + origy;
|
var right = (this.right - origx) * ratio + origx;
|
var top = (this.top - origy) * ratio + origy;
|
|
return new OpenLayers.Bounds(left, bottom, right, top);
|
},
|
|
/**
|
* APIMethod: add
|
* Shifts the coordinates of the bound by the given horizontal and vertical
|
* deltas.
|
*
|
* (start code)
|
* var bounds = new OpenLayers.Bounds(0, 0, 10, 10);
|
* bounds.toString();
|
* // => "0,0,10,10"
|
*
|
* bounds.add(-1.5, 4).toString();
|
* // => "-1.5,4,8.5,14"
|
* (end)
|
*
|
* This method will throw a TypeError if it is passed null as an argument.
|
*
|
* Parameters:
|
* x - {Float} horizontal delta
|
* y - {Float} vertical delta
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} A new bounds whose coordinates are the same as
|
* this, but shifted by the passed-in x and y values.
|
*/
|
add:function(x, y) {
|
if ( (x == null) || (y == null) ) {
|
throw new TypeError('Bounds.add cannot receive null values');
|
}
|
return new OpenLayers.Bounds(this.left + x, this.bottom + y,
|
this.right + x, this.top + y);
|
},
|
|
/**
|
* APIMethod: extend
|
* Extend the bounds to include the <OpenLayers.LonLat>,
|
* <OpenLayers.Geometry.Point> or <OpenLayers.Bounds> specified.
|
*
|
* Please note that this function assumes that left < right and
|
* bottom < top.
|
*
|
* Parameters:
|
* object - {<OpenLayers.LonLat>, <OpenLayers.Geometry.Point> or
|
* <OpenLayers.Bounds>} The object to be included in the new bounds
|
* object.
|
*/
|
extend:function(object) {
|
if (object) {
|
switch(object.CLASS_NAME) {
|
case "OpenLayers.LonLat":
|
this.extendXY(object.lon, object.lat);
|
break;
|
case "OpenLayers.Geometry.Point":
|
this.extendXY(object.x, object.y);
|
break;
|
|
case "OpenLayers.Bounds":
|
// clear cached center location
|
this.centerLonLat = null;
|
|
if ( (this.left == null) || (object.left < this.left)) {
|
this.left = object.left;
|
}
|
if ( (this.bottom == null) || (object.bottom < this.bottom) ) {
|
this.bottom = object.bottom;
|
}
|
if ( (this.right == null) || (object.right > this.right) ) {
|
this.right = object.right;
|
}
|
if ( (this.top == null) || (object.top > this.top) ) {
|
this.top = object.top;
|
}
|
break;
|
}
|
}
|
},
|
|
/**
|
* APIMethod: extendXY
|
* Extend the bounds to include the XY coordinate specified.
|
*
|
* Parameters:
|
* x - {number} The X part of the the coordinate.
|
* y - {number} The Y part of the the coordinate.
|
*/
|
extendXY:function(x, y) {
|
// clear cached center location
|
this.centerLonLat = null;
|
|
if ((this.left == null) || (x < this.left)) {
|
this.left = x;
|
}
|
if ((this.bottom == null) || (y < this.bottom)) {
|
this.bottom = y;
|
}
|
if ((this.right == null) || (x > this.right)) {
|
this.right = x;
|
}
|
if ((this.top == null) || (y > this.top)) {
|
this.top = y;
|
}
|
},
|
|
/**
|
* APIMethod: containsLonLat
|
* Returns whether the bounds object contains the given <OpenLayers.LonLat>.
|
*
|
* Parameters:
|
* ll - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an
|
* object with a 'lon' and 'lat' properties.
|
* options - {Object} Optional parameters
|
*
|
* Acceptable options:
|
* inclusive - {Boolean} Whether or not to include the border.
|
* Default is true.
|
* worldBounds - {<OpenLayers.Bounds>} If a worldBounds is provided, the
|
* ll will be considered as contained if it exceeds the world bounds,
|
* but can be wrapped around the dateline so it is contained by this
|
* bounds.
|
*
|
* Returns:
|
* {Boolean} The passed-in lonlat is within this bounds.
|
*/
|
containsLonLat: function(ll, options) {
|
if (typeof options === "boolean") {
|
options = {inclusive: options};
|
}
|
options = options || {};
|
var contains = this.contains(ll.lon, ll.lat, options.inclusive),
|
worldBounds = options.worldBounds;
|
if (worldBounds && !contains) {
|
var worldWidth = worldBounds.getWidth();
|
var worldCenterX = (worldBounds.left + worldBounds.right) / 2;
|
var worldsAway = Math.round((ll.lon - worldCenterX) / worldWidth);
|
contains = this.containsLonLat({
|
lon: ll.lon - worldsAway * worldWidth,
|
lat: ll.lat
|
}, {inclusive: options.inclusive});
|
}
|
return contains;
|
},
|
|
/**
|
* APIMethod: containsPixel
|
* Returns whether the bounds object contains the given <OpenLayers.Pixel>.
|
*
|
* Parameters:
|
* px - {<OpenLayers.Pixel>}
|
* inclusive - {Boolean} Whether or not to include the border. Default is
|
* true.
|
*
|
* Returns:
|
* {Boolean} The passed-in pixel is within this bounds.
|
*/
|
containsPixel:function(px, inclusive) {
|
return this.contains(px.x, px.y, inclusive);
|
},
|
|
/**
|
* APIMethod: contains
|
* Returns whether the bounds object contains the given x and y.
|
*
|
* Parameters:
|
* x - {Float}
|
* y - {Float}
|
* inclusive - {Boolean} Whether or not to include the border. Default is
|
* true.
|
*
|
* Returns:
|
* {Boolean} Whether or not the passed-in coordinates are within this
|
* bounds.
|
*/
|
contains:function(x, y, inclusive) {
|
//set default
|
if (inclusive == null) {
|
inclusive = true;
|
}
|
|
if (x == null || y == null) {
|
return false;
|
}
|
|
x = OpenLayers.Util.toFloat(x);
|
y = OpenLayers.Util.toFloat(y);
|
|
var contains = false;
|
if (inclusive) {
|
contains = ((x >= this.left) && (x <= this.right) &&
|
(y >= this.bottom) && (y <= this.top));
|
} else {
|
contains = ((x > this.left) && (x < this.right) &&
|
(y > this.bottom) && (y < this.top));
|
}
|
return contains;
|
},
|
|
/**
|
* APIMethod: intersectsBounds
|
* Determine whether the target bounds intersects this bounds. Bounds are
|
* considered intersecting if any of their edges intersect or if one
|
* bounds contains the other.
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>} The target bounds.
|
* options - {Object} Optional parameters.
|
*
|
* Acceptable options:
|
* inclusive - {Boolean} Treat coincident borders as intersecting. Default
|
* is true. If false, bounds that do not overlap but only touch at the
|
* border will not be considered as intersecting.
|
* worldBounds - {<OpenLayers.Bounds>} If a worldBounds is provided, two
|
* bounds will be considered as intersecting if they intersect when
|
* shifted to within the world bounds. This applies only to bounds that
|
* cross or are completely outside the world bounds.
|
*
|
* Returns:
|
* {Boolean} The passed-in bounds object intersects this bounds.
|
*/
|
intersectsBounds:function(bounds, options) {
|
if (typeof options === "boolean") {
|
options = {inclusive: options};
|
}
|
options = options || {};
|
if (options.worldBounds) {
|
var self = this.wrapDateLine(options.worldBounds);
|
bounds = bounds.wrapDateLine(options.worldBounds);
|
} else {
|
self = this;
|
}
|
if (options.inclusive == null) {
|
options.inclusive = true;
|
}
|
var intersects = false;
|
var mightTouch = (
|
self.left == bounds.right ||
|
self.right == bounds.left ||
|
self.top == bounds.bottom ||
|
self.bottom == bounds.top
|
);
|
|
// if the two bounds only touch at an edge, and inclusive is false,
|
// then the bounds don't *really* intersect.
|
if (options.inclusive || !mightTouch) {
|
// otherwise, if one of the boundaries even partially contains another,
|
// inclusive of the edges, then they do intersect.
|
var inBottom = (
|
((bounds.bottom >= self.bottom) && (bounds.bottom <= self.top)) ||
|
((self.bottom >= bounds.bottom) && (self.bottom <= bounds.top))
|
);
|
var inTop = (
|
((bounds.top >= self.bottom) && (bounds.top <= self.top)) ||
|
((self.top > bounds.bottom) && (self.top < bounds.top))
|
);
|
var inLeft = (
|
((bounds.left >= self.left) && (bounds.left <= self.right)) ||
|
((self.left >= bounds.left) && (self.left <= bounds.right))
|
);
|
var inRight = (
|
((bounds.right >= self.left) && (bounds.right <= self.right)) ||
|
((self.right >= bounds.left) && (self.right <= bounds.right))
|
);
|
intersects = ((inBottom || inTop) && (inLeft || inRight));
|
}
|
// document me
|
if (options.worldBounds && !intersects) {
|
var world = options.worldBounds;
|
var width = world.getWidth();
|
var selfCrosses = !world.containsBounds(self);
|
var boundsCrosses = !world.containsBounds(bounds);
|
if (selfCrosses && !boundsCrosses) {
|
bounds = bounds.add(-width, 0);
|
intersects = self.intersectsBounds(bounds, {inclusive: options.inclusive});
|
} else if (boundsCrosses && !selfCrosses) {
|
self = self.add(-width, 0);
|
intersects = bounds.intersectsBounds(self, {inclusive: options.inclusive});
|
}
|
}
|
return intersects;
|
},
|
|
/**
|
* APIMethod: containsBounds
|
* Returns whether the bounds object contains the given <OpenLayers.Bounds>.
|
*
|
* bounds - {<OpenLayers.Bounds>} The target bounds.
|
* partial - {Boolean} If any of the target corners is within this bounds
|
* consider the bounds contained. Default is false. If false, the
|
* entire target bounds must be contained within this bounds.
|
* inclusive - {Boolean} Treat shared edges as contained. Default is
|
* true.
|
*
|
* Returns:
|
* {Boolean} The passed-in bounds object is contained within this bounds.
|
*/
|
containsBounds:function(bounds, partial, inclusive) {
|
if (partial == null) {
|
partial = false;
|
}
|
if (inclusive == null) {
|
inclusive = true;
|
}
|
var bottomLeft = this.contains(bounds.left, bounds.bottom, inclusive);
|
var bottomRight = this.contains(bounds.right, bounds.bottom, inclusive);
|
var topLeft = this.contains(bounds.left, bounds.top, inclusive);
|
var topRight = this.contains(bounds.right, bounds.top, inclusive);
|
|
return (partial) ? (bottomLeft || bottomRight || topLeft || topRight)
|
: (bottomLeft && bottomRight && topLeft && topRight);
|
},
|
|
/**
|
* APIMethod: determineQuadrant
|
* Returns the the quadrant ("br", "tr", "tl", "bl") in which the given
|
* <OpenLayers.LonLat> lies.
|
*
|
* Parameters:
|
* lonlat - {<OpenLayers.LonLat>}
|
*
|
* Returns:
|
* {String} The quadrant ("br" "tr" "tl" "bl") of the bounds in which the
|
* coordinate lies.
|
*/
|
determineQuadrant: function(lonlat) {
|
|
var quadrant = "";
|
var center = this.getCenterLonLat();
|
|
quadrant += (lonlat.lat < center.lat) ? "b" : "t";
|
quadrant += (lonlat.lon < center.lon) ? "l" : "r";
|
|
return quadrant;
|
},
|
|
/**
|
* APIMethod: transform
|
* Transform the Bounds object from source to dest.
|
*
|
* Parameters:
|
* source - {<OpenLayers.Projection>} Source projection.
|
* dest - {<OpenLayers.Projection>} Destination projection.
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} Itself, for use in chaining operations.
|
*/
|
transform: function(source, dest) {
|
// clear cached center location
|
this.centerLonLat = null;
|
var ll = OpenLayers.Projection.transform(
|
{'x': this.left, 'y': this.bottom}, source, dest);
|
var lr = OpenLayers.Projection.transform(
|
{'x': this.right, 'y': this.bottom}, source, dest);
|
var ul = OpenLayers.Projection.transform(
|
{'x': this.left, 'y': this.top}, source, dest);
|
var ur = OpenLayers.Projection.transform(
|
{'x': this.right, 'y': this.top}, source, dest);
|
this.left = Math.min(ll.x, ul.x);
|
this.bottom = Math.min(ll.y, lr.y);
|
this.right = Math.max(lr.x, ur.x);
|
this.top = Math.max(ul.y, ur.y);
|
return this;
|
},
|
|
/**
|
* APIMethod: wrapDateLine
|
* Wraps the bounds object around the dateline.
|
*
|
* Parameters:
|
* maxExtent - {<OpenLayers.Bounds>}
|
* options - {Object} Some possible options are:
|
*
|
* Allowed Options:
|
* leftTolerance - {float} Allow for a margin of error
|
* with the 'left' value of this
|
* bound.
|
* Default is 0.
|
* rightTolerance - {float} Allow for a margin of error
|
* with the 'right' value of
|
* this bound.
|
* Default is 0.
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} A copy of this bounds, but wrapped around the
|
* "dateline" (as specified by the borders of
|
* maxExtent). Note that this function only returns
|
* a different bounds value if this bounds is
|
* *entirely* outside of the maxExtent. If this
|
* bounds straddles the dateline (is part in/part
|
* out of maxExtent), the returned bounds will always
|
* cross the left edge of the given maxExtent.
|
*.
|
*/
|
wrapDateLine: function(maxExtent, options) {
|
options = options || {};
|
|
var leftTolerance = options.leftTolerance || 0;
|
var rightTolerance = options.rightTolerance || 0;
|
|
var newBounds = this.clone();
|
|
if (maxExtent) {
|
var width = maxExtent.getWidth();
|
|
//shift right?
|
while (newBounds.left < maxExtent.left &&
|
newBounds.right - rightTolerance <= maxExtent.left ) {
|
newBounds = newBounds.add(width, 0);
|
}
|
|
//shift left?
|
while (newBounds.left + leftTolerance >= maxExtent.right &&
|
newBounds.right > maxExtent.right ) {
|
newBounds = newBounds.add(-width, 0);
|
}
|
|
// crosses right only? force left
|
var newLeft = newBounds.left + leftTolerance;
|
if (newLeft < maxExtent.right && newLeft > maxExtent.left &&
|
newBounds.right - rightTolerance > maxExtent.right) {
|
newBounds = newBounds.add(-width, 0);
|
}
|
}
|
|
return newBounds;
|
},
|
|
CLASS_NAME: "OpenLayers.Bounds"
|
});
|
|
/**
|
* APIFunction: fromString
|
* Alternative constructor that builds a new OpenLayers.Bounds from a
|
* parameter string.
|
*
|
* (begin code)
|
* OpenLayers.Bounds.fromString("5,42,10,45");
|
* // => equivalent to ...
|
* new OpenLayers.Bounds(5, 42, 10, 45);
|
* (end)
|
*
|
* Parameters:
|
* str - {String} Comma-separated bounds string. (e.g. "5,42,10,45")
|
* reverseAxisOrder - {Boolean} Does the string use reverse axis order?
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} New bounds object built from the
|
* passed-in String.
|
*/
|
OpenLayers.Bounds.fromString = function(str, reverseAxisOrder) {
|
var bounds = str.split(",");
|
return OpenLayers.Bounds.fromArray(bounds, reverseAxisOrder);
|
};
|
|
/**
|
* APIFunction: fromArray
|
* Alternative constructor that builds a new OpenLayers.Bounds from an array.
|
*
|
* (begin code)
|
* OpenLayers.Bounds.fromArray( [5, 42, 10, 45] );
|
* // => equivalent to ...
|
* new OpenLayers.Bounds(5, 42, 10, 45);
|
* (end)
|
*
|
* Parameters:
|
* bbox - {Array(Float)} Array of bounds values (e.g. [5,42,10,45])
|
* reverseAxisOrder - {Boolean} Does the array use reverse axis order?
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} New bounds object built from the passed-in Array.
|
*/
|
OpenLayers.Bounds.fromArray = function(bbox, reverseAxisOrder) {
|
return reverseAxisOrder === true ?
|
new OpenLayers.Bounds(bbox[1], bbox[0], bbox[3], bbox[2]) :
|
new OpenLayers.Bounds(bbox[0], bbox[1], bbox[2], bbox[3]);
|
};
|
|
/**
|
* APIFunction: fromSize
|
* Alternative constructor that builds a new OpenLayers.Bounds from a size.
|
*
|
* (begin code)
|
* OpenLayers.Bounds.fromSize( new OpenLayers.Size(10, 20) );
|
* // => equivalent to ...
|
* new OpenLayers.Bounds(0, 20, 10, 0);
|
* (end)
|
*
|
* Parameters:
|
* size - {<OpenLayers.Size> or Object} <OpenLayers.Size> or an object with
|
* both 'w' and 'h' properties.
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} New bounds object built from the passed-in size.
|
*/
|
OpenLayers.Bounds.fromSize = function(size) {
|
return new OpenLayers.Bounds(0,
|
size.h,
|
size.w,
|
0);
|
};
|
|
/**
|
* Function: oppositeQuadrant
|
* Get the opposite quadrant for a given quadrant string.
|
*
|
* (begin code)
|
* OpenLayers.Bounds.oppositeQuadrant( "tl" );
|
* // => "br"
|
*
|
* OpenLayers.Bounds.oppositeQuadrant( "tr" );
|
* // => "bl"
|
* (end)
|
*
|
* Parameters:
|
* quadrant - {String} two character quadrant shortstring
|
*
|
* Returns:
|
* {String} The opposing quadrant ("br" "tr" "tl" "bl"). For Example, if
|
* you pass in "bl" it returns "tr", if you pass in "br" it
|
* returns "tl", etc.
|
*/
|
OpenLayers.Bounds.oppositeQuadrant = function(quadrant) {
|
var opp = "";
|
|
opp += (quadrant.charAt(0) == 't') ? 'b' : 't';
|
opp += (quadrant.charAt(1) == 'l') ? 'r' : 'l';
|
|
return opp;
|
};
|
/* ======================================================================
|
OpenLayers/BaseTypes/Element.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Util.js
|
* @requires OpenLayers/BaseTypes.js
|
*/
|
|
/**
|
* Namespace: OpenLayers.Element
|
*/
|
OpenLayers.Element = {
|
|
/**
|
* APIFunction: visible
|
*
|
* Parameters:
|
* element - {DOMElement}
|
*
|
* Returns:
|
* {Boolean} Is the element visible?
|
*/
|
visible: function(element) {
|
return OpenLayers.Util.getElement(element).style.display != 'none';
|
},
|
|
/**
|
* APIFunction: toggle
|
* Toggle the visibility of element(s) passed in
|
*
|
* Parameters:
|
* element - {DOMElement} Actually user can pass any number of elements
|
*/
|
toggle: function() {
|
for (var i=0, len=arguments.length; i<len; i++) {
|
var element = OpenLayers.Util.getElement(arguments[i]);
|
var display = OpenLayers.Element.visible(element) ? 'none'
|
: '';
|
element.style.display = display;
|
}
|
},
|
|
/**
|
* APIFunction: remove
|
* Remove the specified element from the DOM.
|
*
|
* Parameters:
|
* element - {DOMElement}
|
*/
|
remove: function(element) {
|
element = OpenLayers.Util.getElement(element);
|
element.parentNode.removeChild(element);
|
},
|
|
/**
|
* APIFunction: getHeight
|
*
|
* Parameters:
|
* element - {DOMElement}
|
*
|
* Returns:
|
* {Integer} The offset height of the element passed in
|
*/
|
getHeight: function(element) {
|
element = OpenLayers.Util.getElement(element);
|
return element.offsetHeight;
|
},
|
|
/**
|
* Function: hasClass
|
* Tests if an element has the given CSS class name.
|
*
|
* Parameters:
|
* element - {DOMElement} A DOM element node.
|
* name - {String} The CSS class name to search for.
|
*
|
* Returns:
|
* {Boolean} The element has the given class name.
|
*/
|
hasClass: function(element, name) {
|
var names = element.className;
|
return (!!names && new RegExp("(^|\\s)" + name + "(\\s|$)").test(names));
|
},
|
|
/**
|
* Function: addClass
|
* Add a CSS class name to an element. Safe where element already has
|
* the class name.
|
*
|
* Parameters:
|
* element - {DOMElement} A DOM element node.
|
* name - {String} The CSS class name to add.
|
*
|
* Returns:
|
* {DOMElement} The element.
|
*/
|
addClass: function(element, name) {
|
if(!OpenLayers.Element.hasClass(element, name)) {
|
element.className += (element.className ? " " : "") + name;
|
}
|
return element;
|
},
|
|
/**
|
* Function: removeClass
|
* Remove a CSS class name from an element. Safe where element does not
|
* have the class name.
|
*
|
* Parameters:
|
* element - {DOMElement} A DOM element node.
|
* name - {String} The CSS class name to remove.
|
*
|
* Returns:
|
* {DOMElement} The element.
|
*/
|
removeClass: function(element, name) {
|
var names = element.className;
|
if(names) {
|
element.className = OpenLayers.String.trim(
|
names.replace(
|
new RegExp("(^|\\s+)" + name + "(\\s+|$)"), " "
|
)
|
);
|
}
|
return element;
|
},
|
|
/**
|
* Function: toggleClass
|
* Remove a CSS class name from an element if it exists. Add the class name
|
* if it doesn't exist.
|
*
|
* Parameters:
|
* element - {DOMElement} A DOM element node.
|
* name - {String} The CSS class name to toggle.
|
*
|
* Returns:
|
* {DOMElement} The element.
|
*/
|
toggleClass: function(element, name) {
|
if(OpenLayers.Element.hasClass(element, name)) {
|
OpenLayers.Element.removeClass(element, name);
|
} else {
|
OpenLayers.Element.addClass(element, name);
|
}
|
return element;
|
},
|
|
/**
|
* APIFunction: getStyle
|
*
|
* Parameters:
|
* element - {DOMElement}
|
* style - {?}
|
*
|
* Returns:
|
* {?}
|
*/
|
getStyle: function(element, style) {
|
element = OpenLayers.Util.getElement(element);
|
|
var value = null;
|
if (element && element.style) {
|
value = element.style[OpenLayers.String.camelize(style)];
|
if (!value) {
|
if (document.defaultView &&
|
document.defaultView.getComputedStyle) {
|
|
var css = document.defaultView.getComputedStyle(element, null);
|
value = css ? css.getPropertyValue(style) : null;
|
} else if (element.currentStyle) {
|
value = element.currentStyle[OpenLayers.String.camelize(style)];
|
}
|
}
|
|
var positions = ['left', 'top', 'right', 'bottom'];
|
if (window.opera &&
|
(OpenLayers.Util.indexOf(positions,style) != -1) &&
|
(OpenLayers.Element.getStyle(element, 'position') == 'static')) {
|
value = 'auto';
|
}
|
}
|
|
return value == 'auto' ? null : value;
|
}
|
|
};
|
/* ======================================================================
|
OpenLayers/BaseTypes/LonLat.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
*/
|
|
/**
|
* Class: OpenLayers.LonLat
|
* This class represents a longitude and latitude pair
|
*/
|
OpenLayers.LonLat = OpenLayers.Class({
|
|
/**
|
* APIProperty: lon
|
* {Float} The x-axis coodinate in map units
|
*/
|
lon: 0.0,
|
|
/**
|
* APIProperty: lat
|
* {Float} The y-axis coordinate in map units
|
*/
|
lat: 0.0,
|
|
/**
|
* Constructor: OpenLayers.LonLat
|
* Create a new map location. Coordinates can be passed either as two
|
* arguments, or as a single argument.
|
*
|
* Parameters (two arguments):
|
* lon - {Number} The x-axis coordinate in map units. If your map is in
|
* a geographic projection, this will be the Longitude. Otherwise,
|
* it will be the x coordinate of the map location in your map units.
|
* lat - {Number} The y-axis coordinate in map units. If your map is in
|
* a geographic projection, this will be the Latitude. Otherwise,
|
* it will be the y coordinate of the map location in your map units.
|
*
|
* Parameters (single argument):
|
* location - {Array(Float)} [lon, lat]
|
*/
|
initialize: function(lon, lat) {
|
if (OpenLayers.Util.isArray(lon)) {
|
lat = lon[1];
|
lon = lon[0];
|
}
|
this.lon = OpenLayers.Util.toFloat(lon);
|
this.lat = OpenLayers.Util.toFloat(lat);
|
},
|
|
/**
|
* Method: toString
|
* Return a readable string version of the lonlat
|
*
|
* Returns:
|
* {String} String representation of OpenLayers.LonLat object.
|
* (e.g. <i>"lon=5,lat=42"</i>)
|
*/
|
toString:function() {
|
return ("lon=" + this.lon + ",lat=" + this.lat);
|
},
|
|
/**
|
* APIMethod: toShortString
|
*
|
* Returns:
|
* {String} Shortened String representation of OpenLayers.LonLat object.
|
* (e.g. <i>"5, 42"</i>)
|
*/
|
toShortString:function() {
|
return (this.lon + ", " + this.lat);
|
},
|
|
/**
|
* APIMethod: clone
|
*
|
* Returns:
|
* {<OpenLayers.LonLat>} New OpenLayers.LonLat object with the same lon
|
* and lat values
|
*/
|
clone:function() {
|
return new OpenLayers.LonLat(this.lon, this.lat);
|
},
|
|
/**
|
* APIMethod: add
|
*
|
* Parameters:
|
* lon - {Float}
|
* lat - {Float}
|
*
|
* Returns:
|
* {<OpenLayers.LonLat>} A new OpenLayers.LonLat object with the lon and
|
* lat passed-in added to this's.
|
*/
|
add:function(lon, lat) {
|
if ( (lon == null) || (lat == null) ) {
|
throw new TypeError('LonLat.add cannot receive null values');
|
}
|
return new OpenLayers.LonLat(this.lon + OpenLayers.Util.toFloat(lon),
|
this.lat + OpenLayers.Util.toFloat(lat));
|
},
|
|
/**
|
* APIMethod: equals
|
*
|
* Parameters:
|
* ll - {<OpenLayers.LonLat>}
|
*
|
* Returns:
|
* {Boolean} Boolean value indicating whether the passed-in
|
* <OpenLayers.LonLat> object has the same lon and lat
|
* components as this.
|
* Note: if ll passed in is null, returns false
|
*/
|
equals:function(ll) {
|
var equals = false;
|
if (ll != null) {
|
equals = ((this.lon == ll.lon && this.lat == ll.lat) ||
|
(isNaN(this.lon) && isNaN(this.lat) && isNaN(ll.lon) && isNaN(ll.lat)));
|
}
|
return equals;
|
},
|
|
/**
|
* APIMethod: transform
|
* Transform the LonLat object from source to dest. This transformation is
|
* *in place*: if you want a *new* lonlat, use .clone() first.
|
*
|
* Parameters:
|
* source - {<OpenLayers.Projection>} Source projection.
|
* dest - {<OpenLayers.Projection>} Destination projection.
|
*
|
* Returns:
|
* {<OpenLayers.LonLat>} Itself, for use in chaining operations.
|
*/
|
transform: function(source, dest) {
|
var point = OpenLayers.Projection.transform(
|
{'x': this.lon, 'y': this.lat}, source, dest);
|
this.lon = point.x;
|
this.lat = point.y;
|
return this;
|
},
|
|
/**
|
* APIMethod: wrapDateLine
|
*
|
* Parameters:
|
* maxExtent - {<OpenLayers.Bounds>}
|
*
|
* Returns:
|
* {<OpenLayers.LonLat>} A copy of this lonlat, but wrapped around the
|
* "dateline" (as specified by the borders of
|
* maxExtent)
|
*/
|
wrapDateLine: function(maxExtent) {
|
|
var newLonLat = this.clone();
|
|
if (maxExtent) {
|
//shift right?
|
while (newLonLat.lon < maxExtent.left) {
|
newLonLat.lon += maxExtent.getWidth();
|
}
|
|
//shift left?
|
while (newLonLat.lon > maxExtent.right) {
|
newLonLat.lon -= maxExtent.getWidth();
|
}
|
}
|
|
return newLonLat;
|
},
|
|
CLASS_NAME: "OpenLayers.LonLat"
|
});
|
|
/**
|
* Function: fromString
|
* Alternative constructor that builds a new <OpenLayers.LonLat> from a
|
* parameter string
|
*
|
* Parameters:
|
* str - {String} Comma-separated Lon,Lat coordinate string.
|
* (e.g. <i>"5,40"</i>)
|
*
|
* Returns:
|
* {<OpenLayers.LonLat>} New <OpenLayers.LonLat> object built from the
|
* passed-in String.
|
*/
|
OpenLayers.LonLat.fromString = function(str) {
|
var pair = str.split(",");
|
return new OpenLayers.LonLat(pair[0], pair[1]);
|
};
|
|
/**
|
* Function: fromArray
|
* Alternative constructor that builds a new <OpenLayers.LonLat> from an
|
* array of two numbers that represent lon- and lat-values.
|
*
|
* Parameters:
|
* arr - {Array(Float)} Array of lon/lat values (e.g. [5,-42])
|
*
|
* Returns:
|
* {<OpenLayers.LonLat>} New <OpenLayers.LonLat> object built from the
|
* passed-in array.
|
*/
|
OpenLayers.LonLat.fromArray = function(arr) {
|
var gotArr = OpenLayers.Util.isArray(arr),
|
lon = gotArr && arr[0],
|
lat = gotArr && arr[1];
|
return new OpenLayers.LonLat(lon, lat);
|
};
|
/* ======================================================================
|
OpenLayers/BaseTypes/Pixel.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
*/
|
|
/**
|
* Class: OpenLayers.Pixel
|
* This class represents a screen coordinate, in x and y coordinates
|
*/
|
OpenLayers.Pixel = OpenLayers.Class({
|
|
/**
|
* APIProperty: x
|
* {Number} The x coordinate
|
*/
|
x: 0.0,
|
|
/**
|
* APIProperty: y
|
* {Number} The y coordinate
|
*/
|
y: 0.0,
|
|
/**
|
* Constructor: OpenLayers.Pixel
|
* Create a new OpenLayers.Pixel instance
|
*
|
* Parameters:
|
* x - {Number} The x coordinate
|
* y - {Number} The y coordinate
|
*
|
* Returns:
|
* An instance of OpenLayers.Pixel
|
*/
|
initialize: function(x, y) {
|
this.x = parseFloat(x);
|
this.y = parseFloat(y);
|
},
|
|
/**
|
* Method: toString
|
* Cast this object into a string
|
*
|
* Returns:
|
* {String} The string representation of Pixel. ex: "x=200.4,y=242.2"
|
*/
|
toString:function() {
|
return ("x=" + this.x + ",y=" + this.y);
|
},
|
|
/**
|
* APIMethod: clone
|
* Return a clone of this pixel object
|
*
|
* Returns:
|
* {<OpenLayers.Pixel>} A clone pixel
|
*/
|
clone:function() {
|
return new OpenLayers.Pixel(this.x, this.y);
|
},
|
|
/**
|
* APIMethod: equals
|
* Determine whether one pixel is equivalent to another
|
*
|
* Parameters:
|
* px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object with
|
* a 'x' and 'y' properties.
|
*
|
* Returns:
|
* {Boolean} The point passed in as parameter is equal to this. Note that
|
* if px passed in is null, returns false.
|
*/
|
equals:function(px) {
|
var equals = false;
|
if (px != null) {
|
equals = ((this.x == px.x && this.y == px.y) ||
|
(isNaN(this.x) && isNaN(this.y) && isNaN(px.x) && isNaN(px.y)));
|
}
|
return equals;
|
},
|
|
/**
|
* APIMethod: distanceTo
|
* Returns the distance to the pixel point passed in as a parameter.
|
*
|
* Parameters:
|
* px - {<OpenLayers.Pixel>}
|
*
|
* Returns:
|
* {Float} The pixel point passed in as parameter to calculate the
|
* distance to.
|
*/
|
distanceTo:function(px) {
|
return Math.sqrt(
|
Math.pow(this.x - px.x, 2) +
|
Math.pow(this.y - px.y, 2)
|
);
|
},
|
|
/**
|
* APIMethod: add
|
*
|
* Parameters:
|
* x - {Integer}
|
* y - {Integer}
|
*
|
* Returns:
|
* {<OpenLayers.Pixel>} A new Pixel with this pixel's x&y augmented by the
|
* values passed in.
|
*/
|
add:function(x, y) {
|
if ( (x == null) || (y == null) ) {
|
throw new TypeError('Pixel.add cannot receive null values');
|
}
|
return new OpenLayers.Pixel(this.x + x, this.y + y);
|
},
|
|
/**
|
* APIMethod: offset
|
*
|
* Parameters
|
* px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object with
|
* a 'x' and 'y' properties.
|
*
|
* Returns:
|
* {<OpenLayers.Pixel>} A new Pixel with this pixel's x&y augmented by the
|
* x&y values of the pixel passed in.
|
*/
|
offset:function(px) {
|
var newPx = this.clone();
|
if (px) {
|
newPx = this.add(px.x, px.y);
|
}
|
return newPx;
|
},
|
|
CLASS_NAME: "OpenLayers.Pixel"
|
});
|
/* ======================================================================
|
OpenLayers/BaseTypes/Size.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
*/
|
|
/**
|
* Class: OpenLayers.Size
|
* Instances of this class represent a width/height pair
|
*/
|
OpenLayers.Size = OpenLayers.Class({
|
|
/**
|
* APIProperty: w
|
* {Number} width
|
*/
|
w: 0.0,
|
|
/**
|
* APIProperty: h
|
* {Number} height
|
*/
|
h: 0.0,
|
|
|
/**
|
* Constructor: OpenLayers.Size
|
* Create an instance of OpenLayers.Size
|
*
|
* Parameters:
|
* w - {Number} width
|
* h - {Number} height
|
*/
|
initialize: function(w, h) {
|
this.w = parseFloat(w);
|
this.h = parseFloat(h);
|
},
|
|
/**
|
* Method: toString
|
* Return the string representation of a size object
|
*
|
* Returns:
|
* {String} The string representation of OpenLayers.Size object.
|
* (e.g. <i>"w=55,h=66"</i>)
|
*/
|
toString:function() {
|
return ("w=" + this.w + ",h=" + this.h);
|
},
|
|
/**
|
* APIMethod: clone
|
* Create a clone of this size object
|
*
|
* Returns:
|
* {<OpenLayers.Size>} A new OpenLayers.Size object with the same w and h
|
* values
|
*/
|
clone:function() {
|
return new OpenLayers.Size(this.w, this.h);
|
},
|
|
/**
|
*
|
* APIMethod: equals
|
* Determine where this size is equal to another
|
*
|
* Parameters:
|
* sz - {<OpenLayers.Size>|Object} An OpenLayers.Size or an object with
|
* a 'w' and 'h' properties.
|
*
|
* Returns:
|
* {Boolean} The passed in size has the same h and w properties as this one.
|
* Note that if sz passed in is null, returns false.
|
*/
|
equals:function(sz) {
|
var equals = false;
|
if (sz != null) {
|
equals = ((this.w == sz.w && this.h == sz.h) ||
|
(isNaN(this.w) && isNaN(this.h) && isNaN(sz.w) && isNaN(sz.h)));
|
}
|
return equals;
|
},
|
|
CLASS_NAME: "OpenLayers.Size"
|
});
|
/* ======================================================================
|
OpenLayers/Console.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
*/
|
|
/**
|
* Namespace: OpenLayers.Console
|
* The OpenLayers.Console namespace is used for debugging and error logging.
|
* If the Firebug Lite (../Firebug/firebug.js) is included before this script,
|
* calls to OpenLayers.Console methods will get redirected to window.console.
|
* This makes use of the Firebug extension where available and allows for
|
* cross-browser debugging Firebug style.
|
*
|
* Note:
|
* Note that behavior will differ with the Firebug extention and Firebug Lite.
|
* Most notably, the Firebug Lite console does not currently allow for
|
* hyperlinks to code or for clicking on object to explore their properties.
|
*
|
*/
|
OpenLayers.Console = {
|
/**
|
* Create empty functions for all console methods. The real value of these
|
* properties will be set if Firebug Lite (../Firebug/firebug.js script) is
|
* included. We explicitly require the Firebug Lite script to trigger
|
* functionality of the OpenLayers.Console methods.
|
*/
|
|
/**
|
* APIFunction: log
|
* Log an object in the console. The Firebug Lite console logs string
|
* representation of objects. Given multiple arguments, they will
|
* be cast to strings and logged with a space delimiter. If the first
|
* argument is a string with printf-like formatting, subsequent arguments
|
* will be used in string substitution. Any additional arguments (beyond
|
* the number substituted in a format string) will be appended in a space-
|
* delimited line.
|
*
|
* Parameters:
|
* object - {Object}
|
*/
|
log: function() {},
|
|
/**
|
* APIFunction: debug
|
* Writes a message to the console, including a hyperlink to the line
|
* where it was called.
|
*
|
* May be called with multiple arguments as with OpenLayers.Console.log().
|
*
|
* Parameters:
|
* object - {Object}
|
*/
|
debug: function() {},
|
|
/**
|
* APIFunction: info
|
* Writes a message to the console with the visual "info" icon and color
|
* coding and a hyperlink to the line where it was called.
|
*
|
* May be called with multiple arguments as with OpenLayers.Console.log().
|
*
|
* Parameters:
|
* object - {Object}
|
*/
|
info: function() {},
|
|
/**
|
* APIFunction: warn
|
* Writes a message to the console with the visual "warning" icon and
|
* color coding and a hyperlink to the line where it was called.
|
*
|
* May be called with multiple arguments as with OpenLayers.Console.log().
|
*
|
* Parameters:
|
* object - {Object}
|
*/
|
warn: function() {},
|
|
/**
|
* APIFunction: error
|
* Writes a message to the console with the visual "error" icon and color
|
* coding and a hyperlink to the line where it was called.
|
*
|
* May be called with multiple arguments as with OpenLayers.Console.log().
|
*
|
* Parameters:
|
* object - {Object}
|
*/
|
error: function() {},
|
|
/**
|
* APIFunction: userError
|
* A single interface for showing error messages to the user. The default
|
* behavior is a Javascript alert, though this can be overridden by
|
* reassigning OpenLayers.Console.userError to a different function.
|
*
|
* Expects a single error message
|
*
|
* Parameters:
|
* error - {Object}
|
*/
|
userError: function(error) {
|
alert(error);
|
},
|
|
/**
|
* APIFunction: assert
|
* Tests that an expression is true. If not, it will write a message to
|
* the console and throw an exception.
|
*
|
* May be called with multiple arguments as with OpenLayers.Console.log().
|
*
|
* Parameters:
|
* object - {Object}
|
*/
|
assert: function() {},
|
|
/**
|
* APIFunction: dir
|
* Prints an interactive listing of all properties of the object. This
|
* looks identical to the view that you would see in the DOM tab.
|
*
|
* Parameters:
|
* object - {Object}
|
*/
|
dir: function() {},
|
|
/**
|
* APIFunction: dirxml
|
* Prints the XML source tree of an HTML or XML element. This looks
|
* identical to the view that you would see in the HTML tab. You can click
|
* on any node to inspect it in the HTML tab.
|
*
|
* Parameters:
|
* object - {Object}
|
*/
|
dirxml: function() {},
|
|
/**
|
* APIFunction: trace
|
* Prints an interactive stack trace of JavaScript execution at the point
|
* where it is called. The stack trace details the functions on the stack,
|
* as well as the values that were passed as arguments to each function.
|
* You can click each function to take you to its source in the Script tab,
|
* and click each argument value to inspect it in the DOM or HTML tabs.
|
*
|
*/
|
trace: function() {},
|
|
/**
|
* APIFunction: group
|
* Writes a message to the console and opens a nested block to indent all
|
* future messages sent to the console. Call OpenLayers.Console.groupEnd()
|
* to close the block.
|
*
|
* May be called with multiple arguments as with OpenLayers.Console.log().
|
*
|
* Parameters:
|
* object - {Object}
|
*/
|
group: function() {},
|
|
/**
|
* APIFunction: groupEnd
|
* Closes the most recently opened block created by a call to
|
* OpenLayers.Console.group
|
*/
|
groupEnd: function() {},
|
|
/**
|
* APIFunction: time
|
* Creates a new timer under the given name. Call
|
* OpenLayers.Console.timeEnd(name)
|
* with the same name to stop the timer and print the time elapsed.
|
*
|
* Parameters:
|
* name - {String}
|
*/
|
time: function() {},
|
|
/**
|
* APIFunction: timeEnd
|
* Stops a timer created by a call to OpenLayers.Console.time(name) and
|
* writes the time elapsed.
|
*
|
* Parameters:
|
* name - {String}
|
*/
|
timeEnd: function() {},
|
|
/**
|
* APIFunction: profile
|
* Turns on the JavaScript profiler. The optional argument title would
|
* contain the text to be printed in the header of the profile report.
|
*
|
* This function is not currently implemented in Firebug Lite.
|
*
|
* Parameters:
|
* title - {String} Optional title for the profiler
|
*/
|
profile: function() {},
|
|
/**
|
* APIFunction: profileEnd
|
* Turns off the JavaScript profiler and prints its report.
|
*
|
* This function is not currently implemented in Firebug Lite.
|
*/
|
profileEnd: function() {},
|
|
/**
|
* APIFunction: count
|
* Writes the number of times that the line of code where count was called
|
* was executed. The optional argument title will print a message in
|
* addition to the number of the count.
|
*
|
* This function is not currently implemented in Firebug Lite.
|
*
|
* Parameters:
|
* title - {String} Optional title to be printed with count
|
*/
|
count: function() {},
|
|
CLASS_NAME: "OpenLayers.Console"
|
};
|
|
/**
|
* Execute an anonymous function to extend the OpenLayers.Console namespace
|
* if the firebug.js script is included. This closure is used so that the
|
* "scripts" and "i" variables don't pollute the global namespace.
|
*/
|
(function() {
|
/**
|
* If Firebug Lite is included (before this script), re-route all
|
* OpenLayers.Console calls to the console object.
|
*/
|
var scripts = document.getElementsByTagName("script");
|
for(var i=0, len=scripts.length; i<len; ++i) {
|
if(scripts[i].src.indexOf("firebug.js") != -1) {
|
if(console) {
|
OpenLayers.Util.extend(OpenLayers.Console, console);
|
break;
|
}
|
}
|
}
|
})();
|
/* ======================================================================
|
OpenLayers/Lang.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/BaseTypes.js
|
* @requires OpenLayers/Console.js
|
*/
|
|
/**
|
* Namespace: OpenLayers.Lang
|
* Internationalization namespace. Contains dictionaries in various languages
|
* and methods to set and get the current language.
|
*/
|
OpenLayers.Lang = {
|
|
/**
|
* Property: code
|
* {String} Current language code to use in OpenLayers. Use the
|
* <setCode> method to set this value and the <getCode> method to
|
* retrieve it.
|
*/
|
code: null,
|
|
/**
|
* APIProperty: defaultCode
|
* {String} Default language to use when a specific language can't be
|
* found. Default is "en".
|
*/
|
defaultCode: "en",
|
|
/**
|
* APIFunction: getCode
|
* Get the current language code.
|
*
|
* Returns:
|
* {String} The current language code.
|
*/
|
getCode: function() {
|
if(!OpenLayers.Lang.code) {
|
OpenLayers.Lang.setCode();
|
}
|
return OpenLayers.Lang.code;
|
},
|
|
/**
|
* APIFunction: setCode
|
* Set the language code for string translation. This code is used by
|
* the <OpenLayers.Lang.translate> method.
|
*
|
* Parameters:
|
* code - {String} These codes follow the IETF recommendations at
|
* http://www.ietf.org/rfc/rfc3066.txt. If no value is set, the
|
* browser's language setting will be tested. If no <OpenLayers.Lang>
|
* dictionary exists for the code, the <OpenLayers.String.defaultLang>
|
* will be used.
|
*/
|
setCode: function(code) {
|
var lang;
|
if(!code) {
|
code = (OpenLayers.BROWSER_NAME == "msie") ?
|
navigator.userLanguage : navigator.language;
|
}
|
var parts = code.split('-');
|
parts[0] = parts[0].toLowerCase();
|
if(typeof OpenLayers.Lang[parts[0]] == "object") {
|
lang = parts[0];
|
}
|
|
// check for regional extensions
|
if(parts[1]) {
|
var testLang = parts[0] + '-' + parts[1].toUpperCase();
|
if(typeof OpenLayers.Lang[testLang] == "object") {
|
lang = testLang;
|
}
|
}
|
if(!lang) {
|
OpenLayers.Console.warn(
|
'Failed to find OpenLayers.Lang.' + parts.join("-") +
|
' dictionary, falling back to default language'
|
);
|
lang = OpenLayers.Lang.defaultCode;
|
}
|
|
OpenLayers.Lang.code = lang;
|
},
|
|
/**
|
* APIMethod: translate
|
* Looks up a key from a dictionary based on the current language string.
|
* The value of <getCode> will be used to determine the appropriate
|
* dictionary. Dictionaries are stored in <OpenLayers.Lang>.
|
*
|
* Parameters:
|
* key - {String} The key for an i18n string value in the dictionary.
|
* context - {Object} Optional context to be used with
|
* <OpenLayers.String.format>.
|
*
|
* Returns:
|
* {String} A internationalized string.
|
*/
|
translate: function(key, context) {
|
var dictionary = OpenLayers.Lang[OpenLayers.Lang.getCode()];
|
var message = dictionary && dictionary[key];
|
if(!message) {
|
// Message not found, fall back to message key
|
message = key;
|
}
|
if(context) {
|
message = OpenLayers.String.format(message, context);
|
}
|
return message;
|
}
|
|
};
|
|
|
/**
|
* APIMethod: OpenLayers.i18n
|
* Alias for <OpenLayers.Lang.translate>. Looks up a key from a dictionary
|
* based on the current language string. The value of
|
* <OpenLayers.Lang.getCode> will be used to determine the appropriate
|
* dictionary. Dictionaries are stored in <OpenLayers.Lang>.
|
*
|
* Parameters:
|
* key - {String} The key for an i18n string value in the dictionary.
|
* context - {Object} Optional context to be used with
|
* <OpenLayers.String.format>.
|
*
|
* Returns:
|
* {String} A internationalized string.
|
*/
|
OpenLayers.i18n = OpenLayers.Lang.translate;
|
/* ======================================================================
|
OpenLayers/Util.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/BaseTypes.js
|
* @requires OpenLayers/BaseTypes/Bounds.js
|
* @requires OpenLayers/BaseTypes/Element.js
|
* @requires OpenLayers/BaseTypes/LonLat.js
|
* @requires OpenLayers/BaseTypes/Pixel.js
|
* @requires OpenLayers/BaseTypes/Size.js
|
* @requires OpenLayers/Lang.js
|
*/
|
|
/**
|
* Namespace: Util
|
*/
|
OpenLayers.Util = OpenLayers.Util || {};
|
|
/**
|
* Function: getElement
|
* This is the old $() from prototype
|
*
|
* Parameters:
|
* e - {String or DOMElement or Window}
|
*
|
* Returns:
|
* {Array(DOMElement) or DOMElement}
|
*/
|
OpenLayers.Util.getElement = function() {
|
var elements = [];
|
|
for (var i=0, len=arguments.length; i<len; i++) {
|
var element = arguments[i];
|
if (typeof element == 'string') {
|
element = document.getElementById(element);
|
}
|
if (arguments.length == 1) {
|
return element;
|
}
|
elements.push(element);
|
}
|
return elements;
|
};
|
|
/**
|
* Function: isElement
|
* A cross-browser implementation of "e instanceof Element".
|
*
|
* Parameters:
|
* o - {Object} The object to test.
|
*
|
* Returns:
|
* {Boolean}
|
*/
|
OpenLayers.Util.isElement = function(o) {
|
return !!(o && o.nodeType === 1);
|
};
|
|
/**
|
* Function: isArray
|
* Tests that the provided object is an array.
|
* This test handles the cross-IFRAME case not caught
|
* by "a instanceof Array" and should be used instead.
|
*
|
* Parameters:
|
* a - {Object} the object test.
|
*
|
* Returns:
|
* {Boolean} true if the object is an array.
|
*/
|
OpenLayers.Util.isArray = function(a) {
|
return (Object.prototype.toString.call(a) === '[object Array]');
|
};
|
|
/**
|
* Function: removeItem
|
* Remove an object from an array. Iterates through the array
|
* to find the item, then removes it.
|
*
|
* Parameters:
|
* array - {Array}
|
* item - {Object}
|
*
|
* Returns:
|
* {Array} A reference to the array
|
*/
|
OpenLayers.Util.removeItem = function(array, item) {
|
for(var i = array.length - 1; i >= 0; i--) {
|
if(array[i] == item) {
|
array.splice(i,1);
|
//break;more than once??
|
}
|
}
|
return array;
|
};
|
|
/**
|
* Function: indexOf
|
* Seems to exist already in FF, but not in MOZ.
|
*
|
* Parameters:
|
* array - {Array}
|
* obj - {*}
|
*
|
* Returns:
|
* {Integer} The index at which the first object was found in the array.
|
* If not found, returns -1.
|
*/
|
OpenLayers.Util.indexOf = function(array, obj) {
|
// use the build-in function if available.
|
if (typeof array.indexOf == "function") {
|
return array.indexOf(obj);
|
} else {
|
for (var i = 0, len = array.length; i < len; i++) {
|
if (array[i] == obj) {
|
return i;
|
}
|
}
|
return -1;
|
}
|
};
|
|
|
/**
|
* Property: dotless
|
* {RegExp}
|
* Compiled regular expression to match dots ("."). This is used for replacing
|
* dots in identifiers. Because object identifiers are frequently used for
|
* DOM element identifiers by the library, we avoid using dots to make for
|
* more sensible CSS selectors.
|
*
|
* TODO: Use a module pattern to avoid bloating the API with stuff like this.
|
*/
|
OpenLayers.Util.dotless = /\./g;
|
|
/**
|
* Function: modifyDOMElement
|
*
|
* Modifies many properties of a DOM element all at once. Passing in
|
* null to an individual parameter will avoid setting the attribute.
|
*
|
* Parameters:
|
* element - {DOMElement} DOM element to modify.
|
* id - {String} The element id attribute to set. Note that dots (".") will be
|
* replaced with underscore ("_") in setting the element id.
|
* px - {<OpenLayers.Pixel>|Object} The element left and top position,
|
* OpenLayers.Pixel or an object with
|
* a 'x' and 'y' properties.
|
* sz - {<OpenLayers.Size>|Object} The element width and height,
|
* OpenLayers.Size or an object with a
|
* 'w' and 'h' properties.
|
* position - {String} The position attribute. eg: absolute,
|
* relative, etc.
|
* border - {String} The style.border attribute. eg:
|
* solid black 2px
|
* overflow - {String} The style.overview attribute.
|
* opacity - {Float} Fractional value (0.0 - 1.0)
|
*/
|
OpenLayers.Util.modifyDOMElement = function(element, id, px, sz, position,
|
border, overflow, opacity) {
|
|
if (id) {
|
element.id = id.replace(OpenLayers.Util.dotless, "_");
|
}
|
if (px) {
|
element.style.left = px.x + "px";
|
element.style.top = px.y + "px";
|
}
|
if (sz) {
|
element.style.width = sz.w + "px";
|
element.style.height = sz.h + "px";
|
}
|
if (position) {
|
element.style.position = position;
|
}
|
if (border) {
|
element.style.border = border;
|
}
|
if (overflow) {
|
element.style.overflow = overflow;
|
}
|
if (parseFloat(opacity) >= 0.0 && parseFloat(opacity) < 1.0) {
|
element.style.filter = 'alpha(opacity=' + (opacity * 100) + ')';
|
element.style.opacity = opacity;
|
} else if (parseFloat(opacity) == 1.0) {
|
element.style.filter = '';
|
element.style.opacity = '';
|
}
|
};
|
|
/**
|
* Function: createDiv
|
* Creates a new div and optionally set some standard attributes.
|
* Null may be passed to each parameter if you do not wish to
|
* set a particular attribute.
|
* Note - zIndex is NOT set on the resulting div.
|
*
|
* Parameters:
|
* id - {String} An identifier for this element. If no id is
|
* passed an identifier will be created
|
* automatically. Note that dots (".") will be replaced with
|
* underscore ("_") when generating ids.
|
* px - {<OpenLayers.Pixel>|Object} The element left and top position,
|
* OpenLayers.Pixel or an object with
|
* a 'x' and 'y' properties.
|
* sz - {<OpenLayers.Size>|Object} The element width and height,
|
* OpenLayers.Size or an object with a
|
* 'w' and 'h' properties.
|
* imgURL - {String} A url pointing to an image to use as a
|
* background image.
|
* position - {String} The style.position value. eg: absolute,
|
* relative etc.
|
* border - {String} The the style.border value.
|
* eg: 2px solid black
|
* overflow - {String} The style.overflow value. Eg. hidden
|
* opacity - {Float} Fractional value (0.0 - 1.0)
|
*
|
* Returns:
|
* {DOMElement} A DOM Div created with the specified attributes.
|
*/
|
OpenLayers.Util.createDiv = function(id, px, sz, imgURL, position,
|
border, overflow, opacity) {
|
|
var dom = document.createElement('div');
|
|
if (imgURL) {
|
dom.style.backgroundImage = 'url(' + imgURL + ')';
|
}
|
|
//set generic properties
|
if (!id) {
|
id = OpenLayers.Util.createUniqueID("OpenLayersDiv");
|
}
|
if (!position) {
|
position = "absolute";
|
}
|
OpenLayers.Util.modifyDOMElement(dom, id, px, sz, position,
|
border, overflow, opacity);
|
|
return dom;
|
};
|
|
/**
|
* Function: createImage
|
* Creates an img element with specific attribute values.
|
*
|
* Parameters:
|
* id - {String} The id field for the img. If none assigned one will be
|
* automatically generated.
|
* px - {<OpenLayers.Pixel>|Object} The element left and top position,
|
* OpenLayers.Pixel or an object with
|
* a 'x' and 'y' properties.
|
* sz - {<OpenLayers.Size>|Object} The element width and height,
|
* OpenLayers.Size or an object with a
|
* 'w' and 'h' properties.
|
* imgURL - {String} The url to use as the image source.
|
* position - {String} The style.position value.
|
* border - {String} The border to place around the image.
|
* opacity - {Float} Fractional value (0.0 - 1.0)
|
* delayDisplay - {Boolean} If true waits until the image has been
|
* loaded.
|
*
|
* Returns:
|
* {DOMElement} A DOM Image created with the specified attributes.
|
*/
|
OpenLayers.Util.createImage = function(id, px, sz, imgURL, position, border,
|
opacity, delayDisplay) {
|
|
var image = document.createElement("img");
|
|
//set generic properties
|
if (!id) {
|
id = OpenLayers.Util.createUniqueID("OpenLayersDiv");
|
}
|
if (!position) {
|
position = "relative";
|
}
|
OpenLayers.Util.modifyDOMElement(image, id, px, sz, position,
|
border, null, opacity);
|
|
if (delayDisplay) {
|
image.style.display = "none";
|
function display() {
|
image.style.display = "";
|
OpenLayers.Event.stopObservingElement(image);
|
}
|
OpenLayers.Event.observe(image, "load", display);
|
OpenLayers.Event.observe(image, "error", display);
|
}
|
|
//set special properties
|
image.style.alt = id;
|
image.galleryImg = "no";
|
if (imgURL) {
|
image.src = imgURL;
|
}
|
|
return image;
|
};
|
|
/**
|
* Property: IMAGE_RELOAD_ATTEMPTS
|
* {Integer} How many times should we try to reload an image before giving up?
|
* Default is 0
|
*/
|
OpenLayers.IMAGE_RELOAD_ATTEMPTS = 0;
|
|
/**
|
* Property: alphaHackNeeded
|
* {Boolean} true if the png alpha hack is necessary and possible, false otherwise.
|
*/
|
OpenLayers.Util.alphaHackNeeded = null;
|
|
/**
|
* Function: alphaHack
|
* Checks whether it's necessary (and possible) to use the png alpha
|
* hack which allows alpha transparency for png images under Internet
|
* Explorer.
|
*
|
* Returns:
|
* {Boolean} true if the png alpha hack is necessary and possible, false otherwise.
|
*/
|
OpenLayers.Util.alphaHack = function() {
|
if (OpenLayers.Util.alphaHackNeeded == null) {
|
var arVersion = navigator.appVersion.split("MSIE");
|
var version = parseFloat(arVersion[1]);
|
var filter = false;
|
|
// IEs4Lin dies when trying to access document.body.filters, because
|
// the property is there, but requires a DLL that can't be provided. This
|
// means that we need to wrap this in a try/catch so that this can
|
// continue.
|
|
try {
|
filter = !!(document.body.filters);
|
} catch (e) {}
|
|
OpenLayers.Util.alphaHackNeeded = (filter &&
|
(version >= 5.5) && (version < 7));
|
}
|
return OpenLayers.Util.alphaHackNeeded;
|
};
|
|
/**
|
* Function: modifyAlphaImageDiv
|
*
|
* Parameters:
|
* div - {DOMElement} Div containing Alpha-adjusted Image
|
* id - {String}
|
* px - {<OpenLayers.Pixel>|Object} OpenLayers.Pixel or an object with
|
* a 'x' and 'y' properties.
|
* sz - {<OpenLayers.Size>|Object} OpenLayers.Size or an object with
|
* a 'w' and 'h' properties.
|
* imgURL - {String}
|
* position - {String}
|
* border - {String}
|
* sizing - {String} 'crop', 'scale', or 'image'. Default is "scale"
|
* opacity - {Float} Fractional value (0.0 - 1.0)
|
*/
|
OpenLayers.Util.modifyAlphaImageDiv = function(div, id, px, sz, imgURL,
|
position, border, sizing,
|
opacity) {
|
|
OpenLayers.Util.modifyDOMElement(div, id, px, sz, position,
|
null, null, opacity);
|
|
var img = div.childNodes[0];
|
|
if (imgURL) {
|
img.src = imgURL;
|
}
|
OpenLayers.Util.modifyDOMElement(img, div.id + "_innerImage", null, sz,
|
"relative", border);
|
|
if (OpenLayers.Util.alphaHack()) {
|
if(div.style.display != "none") {
|
div.style.display = "inline-block";
|
}
|
if (sizing == null) {
|
sizing = "scale";
|
}
|
|
div.style.filter = "progid:DXImageTransform.Microsoft" +
|
".AlphaImageLoader(src='" + img.src + "', " +
|
"sizingMethod='" + sizing + "')";
|
if (parseFloat(div.style.opacity) >= 0.0 &&
|
parseFloat(div.style.opacity) < 1.0) {
|
div.style.filter += " alpha(opacity=" + div.style.opacity * 100 + ")";
|
}
|
|
img.style.filter = "alpha(opacity=0)";
|
}
|
};
|
|
/**
|
* Function: createAlphaImageDiv
|
*
|
* Parameters:
|
* id - {String}
|
* px - {<OpenLayers.Pixel>|Object} OpenLayers.Pixel or an object with
|
* a 'x' and 'y' properties.
|
* sz - {<OpenLayers.Size>|Object} OpenLayers.Size or an object with
|
* a 'w' and 'h' properties.
|
* imgURL - {String}
|
* position - {String}
|
* border - {String}
|
* sizing - {String} 'crop', 'scale', or 'image'. Default is "scale"
|
* opacity - {Float} Fractional value (0.0 - 1.0)
|
* delayDisplay - {Boolean} If true waits until the image has been
|
* loaded.
|
*
|
* Returns:
|
* {DOMElement} A DOM Div created with a DOM Image inside it. If the hack is
|
* needed for transparency in IE, it is added.
|
*/
|
OpenLayers.Util.createAlphaImageDiv = function(id, px, sz, imgURL,
|
position, border, sizing,
|
opacity, delayDisplay) {
|
|
var div = OpenLayers.Util.createDiv();
|
var img = OpenLayers.Util.createImage(null, null, null, null, null, null,
|
null, delayDisplay);
|
img.className = "olAlphaImg";
|
div.appendChild(img);
|
|
OpenLayers.Util.modifyAlphaImageDiv(div, id, px, sz, imgURL, position,
|
border, sizing, opacity);
|
|
return div;
|
};
|
|
|
/**
|
* Function: upperCaseObject
|
* Creates a new hashtable and copies over all the keys from the
|
* passed-in object, but storing them under an uppercased
|
* version of the key at which they were stored.
|
*
|
* Parameters:
|
* object - {Object}
|
*
|
* Returns:
|
* {Object} A new Object with all the same keys but uppercased
|
*/
|
OpenLayers.Util.upperCaseObject = function (object) {
|
var uObject = {};
|
for (var key in object) {
|
uObject[key.toUpperCase()] = object[key];
|
}
|
return uObject;
|
};
|
|
/**
|
* Function: applyDefaults
|
* Takes an object and copies any properties that don't exist from
|
* another properties, by analogy with OpenLayers.Util.extend() from
|
* Prototype.js.
|
*
|
* Parameters:
|
* to - {Object} The destination object.
|
* from - {Object} The source object. Any properties of this object that
|
* are undefined in the to object will be set on the to object.
|
*
|
* Returns:
|
* {Object} A reference to the to object. Note that the to argument is modified
|
* in place and returned by this function.
|
*/
|
OpenLayers.Util.applyDefaults = function (to, from) {
|
to = to || {};
|
/*
|
* FF/Windows < 2.0.0.13 reports "Illegal operation on WrappedNative
|
* prototype object" when calling hawOwnProperty if the source object is an
|
* instance of window.Event.
|
*/
|
var fromIsEvt = typeof window.Event == "function"
|
&& from instanceof window.Event;
|
|
for (var key in from) {
|
if (to[key] === undefined ||
|
(!fromIsEvt && from.hasOwnProperty
|
&& from.hasOwnProperty(key) && !to.hasOwnProperty(key))) {
|
to[key] = from[key];
|
}
|
}
|
/**
|
* IE doesn't include the toString property when iterating over an object's
|
* properties with the for(property in object) syntax. Explicitly check if
|
* the source has its own toString property.
|
*/
|
if(!fromIsEvt && from && from.hasOwnProperty
|
&& from.hasOwnProperty('toString') && !to.hasOwnProperty('toString')) {
|
to.toString = from.toString;
|
}
|
|
return to;
|
};
|
|
/**
|
* Function: getParameterString
|
*
|
* Parameters:
|
* params - {Object}
|
*
|
* Returns:
|
* {String} A concatenation of the properties of an object in
|
* http parameter notation.
|
* (ex. <i>"key1=value1&key2=value2&key3=value3"</i>)
|
* If a parameter is actually a list, that parameter will then
|
* be set to a comma-seperated list of values (foo,bar) instead
|
* of being URL escaped (foo%3Abar).
|
*/
|
OpenLayers.Util.getParameterString = function(params) {
|
var paramsArray = [];
|
|
for (var key in params) {
|
var value = params[key];
|
if ((value != null) && (typeof value != 'function')) {
|
var encodedValue;
|
if (typeof value == 'object' && value.constructor == Array) {
|
/* value is an array; encode items and separate with "," */
|
var encodedItemArray = [];
|
var item;
|
for (var itemIndex=0, len=value.length; itemIndex<len; itemIndex++) {
|
item = value[itemIndex];
|
encodedItemArray.push(encodeURIComponent(
|
(item === null || item === undefined) ? "" : item)
|
);
|
}
|
encodedValue = encodedItemArray.join(",");
|
}
|
else {
|
/* value is a string; simply encode */
|
encodedValue = encodeURIComponent(value);
|
}
|
paramsArray.push(encodeURIComponent(key) + "=" + encodedValue);
|
}
|
}
|
|
return paramsArray.join("&");
|
};
|
|
/**
|
* Function: urlAppend
|
* Appends a parameter string to a url. This function includes the logic for
|
* using the appropriate character (none, & or ?) to append to the url before
|
* appending the param string.
|
*
|
* Parameters:
|
* url - {String} The url to append to
|
* paramStr - {String} The param string to append
|
*
|
* Returns:
|
* {String} The new url
|
*/
|
OpenLayers.Util.urlAppend = function(url, paramStr) {
|
var newUrl = url;
|
if(paramStr) {
|
var parts = (url + " ").split(/[?&]/);
|
newUrl += (parts.pop() === " " ?
|
paramStr :
|
parts.length ? "&" + paramStr : "?" + paramStr);
|
}
|
return newUrl;
|
};
|
|
/**
|
* Function: getImagesLocation
|
*
|
* Returns:
|
* {String} The fully formatted image location string
|
*/
|
OpenLayers.Util.getImagesLocation = function() {
|
return OpenLayers.ImgPath || (OpenLayers._getScriptLocation() + "img/");
|
};
|
|
/**
|
* Function: getImageLocation
|
*
|
* Returns:
|
* {String} The fully formatted location string for a specified image
|
*/
|
OpenLayers.Util.getImageLocation = function(image) {
|
return OpenLayers.Util.getImagesLocation() + image;
|
};
|
|
|
/**
|
* Function: Try
|
* Execute functions until one of them doesn't throw an error.
|
* Capitalized because "try" is a reserved word in JavaScript.
|
* Taken directly from OpenLayers.Util.Try()
|
*
|
* Parameters:
|
* [*] - {Function} Any number of parameters may be passed to Try()
|
* It will attempt to execute each of them until one of them
|
* successfully executes.
|
* If none executes successfully, returns null.
|
*
|
* Returns:
|
* {*} The value returned by the first successfully executed function.
|
*/
|
OpenLayers.Util.Try = function() {
|
var returnValue = null;
|
|
for (var i=0, len=arguments.length; i<len; i++) {
|
var lambda = arguments[i];
|
try {
|
returnValue = lambda();
|
break;
|
} catch (e) {}
|
}
|
|
return returnValue;
|
};
|
|
/**
|
* Function: getXmlNodeValue
|
*
|
* Parameters:
|
* node - {XMLNode}
|
*
|
* Returns:
|
* {String} The text value of the given node, without breaking in firefox or IE
|
*/
|
OpenLayers.Util.getXmlNodeValue = function(node) {
|
var val = null;
|
OpenLayers.Util.Try(
|
function() {
|
val = node.text;
|
if (!val) {
|
val = node.textContent;
|
}
|
if (!val) {
|
val = node.firstChild.nodeValue;
|
}
|
},
|
function() {
|
val = node.textContent;
|
});
|
return val;
|
};
|
|
/**
|
* Function: mouseLeft
|
*
|
* Parameters:
|
* evt - {Event}
|
* div - {HTMLDivElement}
|
*
|
* Returns:
|
* {Boolean}
|
*/
|
OpenLayers.Util.mouseLeft = function (evt, div) {
|
// start with the element to which the mouse has moved
|
var target = (evt.relatedTarget) ? evt.relatedTarget : evt.toElement;
|
// walk up the DOM tree.
|
while (target != div && target != null) {
|
target = target.parentNode;
|
}
|
// if the target we stop at isn't the div, then we've left the div.
|
return (target != div);
|
};
|
|
/**
|
* Property: precision
|
* {Number} The number of significant digits to retain to avoid
|
* floating point precision errors.
|
*
|
* We use 14 as a "safe" default because, although IEEE 754 double floats
|
* (standard on most modern operating systems) support up to about 16
|
* significant digits, 14 significant digits are sufficient to represent
|
* sub-millimeter accuracy in any coordinate system that anyone is likely to
|
* use with OpenLayers.
|
*
|
* If DEFAULT_PRECISION is set to 0, the original non-truncating behavior
|
* of OpenLayers <2.8 is preserved. Be aware that this will cause problems
|
* with certain projections, e.g. spherical Mercator.
|
*
|
*/
|
OpenLayers.Util.DEFAULT_PRECISION = 14;
|
|
/**
|
* Function: toFloat
|
* Convenience method to cast an object to a Number, rounded to the
|
* desired floating point precision.
|
*
|
* Parameters:
|
* number - {Number} The number to cast and round.
|
* precision - {Number} An integer suitable for use with
|
* Number.toPrecision(). Defaults to OpenLayers.Util.DEFAULT_PRECISION.
|
* If set to 0, no rounding is performed.
|
*
|
* Returns:
|
* {Number} The cast, rounded number.
|
*/
|
OpenLayers.Util.toFloat = function (number, precision) {
|
if (precision == null) {
|
precision = OpenLayers.Util.DEFAULT_PRECISION;
|
}
|
if (typeof number !== "number") {
|
number = parseFloat(number);
|
}
|
return precision === 0 ? number :
|
parseFloat(number.toPrecision(precision));
|
};
|
|
/**
|
* Function: rad
|
*
|
* Parameters:
|
* x - {Float}
|
*
|
* Returns:
|
* {Float}
|
*/
|
OpenLayers.Util.rad = function(x) {return x*Math.PI/180;};
|
|
/**
|
* Function: deg
|
*
|
* Parameters:
|
* x - {Float}
|
*
|
* Returns:
|
* {Float}
|
*/
|
OpenLayers.Util.deg = function(x) {return x*180/Math.PI;};
|
|
/**
|
* Property: VincentyConstants
|
* {Object} Constants for Vincenty functions.
|
*/
|
OpenLayers.Util.VincentyConstants = {
|
a: 6378137,
|
b: 6356752.3142,
|
f: 1/298.257223563
|
};
|
|
/**
|
* APIFunction: distVincenty
|
* Given two objects representing points with geographic coordinates, this
|
* calculates the distance between those points on the surface of an
|
* ellipsoid.
|
*
|
* Parameters:
|
* p1 - {<OpenLayers.LonLat>} (or any object with both .lat, .lon properties)
|
* p2 - {<OpenLayers.LonLat>} (or any object with both .lat, .lon properties)
|
*
|
* Returns:
|
* {Float} The distance (in km) between the two input points as measured on an
|
* ellipsoid. Note that the input point objects must be in geographic
|
* coordinates (decimal degrees) and the return distance is in kilometers.
|
*/
|
OpenLayers.Util.distVincenty = function(p1, p2) {
|
var ct = OpenLayers.Util.VincentyConstants;
|
var a = ct.a, b = ct.b, f = ct.f;
|
|
var L = OpenLayers.Util.rad(p2.lon - p1.lon);
|
var U1 = Math.atan((1-f) * Math.tan(OpenLayers.Util.rad(p1.lat)));
|
var U2 = Math.atan((1-f) * Math.tan(OpenLayers.Util.rad(p2.lat)));
|
var sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
|
var sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
|
var lambda = L, lambdaP = 2*Math.PI;
|
var iterLimit = 20;
|
while (Math.abs(lambda-lambdaP) > 1e-12 && --iterLimit>0) {
|
var sinLambda = Math.sin(lambda), cosLambda = Math.cos(lambda);
|
var sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) +
|
(cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda));
|
if (sinSigma==0) {
|
return 0; // co-incident points
|
}
|
var cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda;
|
var sigma = Math.atan2(sinSigma, cosSigma);
|
var alpha = Math.asin(cosU1 * cosU2 * sinLambda / sinSigma);
|
var cosSqAlpha = Math.cos(alpha) * Math.cos(alpha);
|
var cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha;
|
var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
|
lambdaP = lambda;
|
lambda = L + (1-C) * f * Math.sin(alpha) *
|
(sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
|
}
|
if (iterLimit==0) {
|
return NaN; // formula failed to converge
|
}
|
var uSq = cosSqAlpha * (a*a - b*b) / (b*b);
|
var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
|
var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
|
var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
|
B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
|
var s = b*A*(sigma-deltaSigma);
|
var d = s.toFixed(3)/1000; // round to 1mm precision
|
return d;
|
};
|
|
/**
|
* APIFunction: destinationVincenty
|
* Calculate destination point given start point lat/long (numeric degrees),
|
* bearing (numeric degrees) & distance (in m).
|
* Adapted from Chris Veness work, see
|
* http://www.movable-type.co.uk/scripts/latlong-vincenty-direct.html
|
*
|
* Parameters:
|
* lonlat - {<OpenLayers.LonLat>} (or any object with both .lat, .lon
|
* properties) The start point.
|
* brng - {Float} The bearing (degrees).
|
* dist - {Float} The ground distance (meters).
|
*
|
* Returns:
|
* {<OpenLayers.LonLat>} The destination point.
|
*/
|
OpenLayers.Util.destinationVincenty = function(lonlat, brng, dist) {
|
var u = OpenLayers.Util;
|
var ct = u.VincentyConstants;
|
var a = ct.a, b = ct.b, f = ct.f;
|
|
var lon1 = lonlat.lon;
|
var lat1 = lonlat.lat;
|
|
var s = dist;
|
var alpha1 = u.rad(brng);
|
var sinAlpha1 = Math.sin(alpha1);
|
var cosAlpha1 = Math.cos(alpha1);
|
|
var tanU1 = (1-f) * Math.tan(u.rad(lat1));
|
var cosU1 = 1 / Math.sqrt((1 + tanU1*tanU1)), sinU1 = tanU1*cosU1;
|
var sigma1 = Math.atan2(tanU1, cosAlpha1);
|
var sinAlpha = cosU1 * sinAlpha1;
|
var cosSqAlpha = 1 - sinAlpha*sinAlpha;
|
var uSq = cosSqAlpha * (a*a - b*b) / (b*b);
|
var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
|
var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
|
|
var sigma = s / (b*A), sigmaP = 2*Math.PI;
|
while (Math.abs(sigma-sigmaP) > 1e-12) {
|
var cos2SigmaM = Math.cos(2*sigma1 + sigma);
|
var sinSigma = Math.sin(sigma);
|
var cosSigma = Math.cos(sigma);
|
var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
|
B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
|
sigmaP = sigma;
|
sigma = s / (b*A) + deltaSigma;
|
}
|
|
var tmp = sinU1*sinSigma - cosU1*cosSigma*cosAlpha1;
|
var lat2 = Math.atan2(sinU1*cosSigma + cosU1*sinSigma*cosAlpha1,
|
(1-f)*Math.sqrt(sinAlpha*sinAlpha + tmp*tmp));
|
var lambda = Math.atan2(sinSigma*sinAlpha1, cosU1*cosSigma - sinU1*sinSigma*cosAlpha1);
|
var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
|
var L = lambda - (1-C) * f * sinAlpha *
|
(sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
|
|
var revAz = Math.atan2(sinAlpha, -tmp); // final bearing
|
|
return new OpenLayers.LonLat(lon1+u.deg(L), u.deg(lat2));
|
};
|
|
/**
|
* Function: getParameters
|
* Parse the parameters from a URL or from the current page itself into a
|
* JavaScript Object. Note that parameter values with commas are separated
|
* out into an Array.
|
*
|
* Parameters:
|
* url - {String} Optional url used to extract the query string.
|
* If url is null or is not supplied, query string is taken
|
* from the page location.
|
* options - {Object} Additional options. Optional.
|
*
|
* Valid options:
|
* splitArgs - {Boolean} Split comma delimited params into arrays? Default is
|
* true.
|
*
|
* Returns:
|
* {Object} An object of key/value pairs from the query string.
|
*/
|
OpenLayers.Util.getParameters = function(url, options) {
|
options = options || {};
|
// if no url specified, take it from the location bar
|
url = (url === null || url === undefined) ? window.location.href : url;
|
|
//parse out parameters portion of url string
|
var paramsString = "";
|
if (OpenLayers.String.contains(url, '?')) {
|
var start = url.indexOf('?') + 1;
|
var end = OpenLayers.String.contains(url, "#") ?
|
url.indexOf('#') : url.length;
|
paramsString = url.substring(start, end);
|
}
|
|
var parameters = {};
|
var pairs = paramsString.split(/[&;]/);
|
for(var i=0, len=pairs.length; i<len; ++i) {
|
var keyValue = pairs[i].split('=');
|
if (keyValue[0]) {
|
|
var key = keyValue[0];
|
try {
|
key = decodeURIComponent(key);
|
} catch (err) {
|
key = unescape(key);
|
}
|
|
// being liberal by replacing "+" with " "
|
var value = (keyValue[1] || '').replace(/\+/g, " ");
|
|
try {
|
value = decodeURIComponent(value);
|
} catch (err) {
|
value = unescape(value);
|
}
|
|
// follow OGC convention of comma delimited values
|
if (options.splitArgs !== false) {
|
value = value.split(",");
|
}
|
|
//if there's only one value, do not return as array
|
if (value.length == 1) {
|
value = value[0];
|
}
|
|
parameters[key] = value;
|
}
|
}
|
return parameters;
|
};
|
|
/**
|
* Property: lastSeqID
|
* {Integer} The ever-incrementing count variable.
|
* Used for generating unique ids.
|
*/
|
OpenLayers.Util.lastSeqID = 0;
|
|
/**
|
* Function: createUniqueID
|
* Create a unique identifier for this session. Each time this function
|
* is called, a counter is incremented. The return will be the optional
|
* prefix (defaults to "id_") appended with the counter value.
|
*
|
* Parameters:
|
* prefix - {String} Optional string to prefix unique id. Default is "id_".
|
* Note that dots (".") in the prefix will be replaced with underscore ("_").
|
*
|
* Returns:
|
* {String} A unique id string, built on the passed in prefix.
|
*/
|
OpenLayers.Util.createUniqueID = function(prefix) {
|
if (prefix == null) {
|
prefix = "id_";
|
} else {
|
prefix = prefix.replace(OpenLayers.Util.dotless, "_");
|
}
|
OpenLayers.Util.lastSeqID += 1;
|
return prefix + OpenLayers.Util.lastSeqID;
|
};
|
|
/**
|
* Constant: INCHES_PER_UNIT
|
* {Object} Constant inches per unit -- borrowed from MapServer mapscale.c
|
* derivation of nautical miles from http://en.wikipedia.org/wiki/Nautical_mile
|
* Includes the full set of units supported by CS-MAP (http://trac.osgeo.org/csmap/)
|
* and PROJ.4 (http://trac.osgeo.org/proj/)
|
* The hardcoded table is maintain in a CS-MAP source code module named CSdataU.c
|
* The hardcoded table of PROJ.4 units are in pj_units.c.
|
*/
|
OpenLayers.INCHES_PER_UNIT = {
|
'inches': 1.0,
|
'ft': 12.0,
|
'mi': 63360.0,
|
'm': 39.37,
|
'km': 39370,
|
'dd': 4374754,
|
'yd': 36
|
};
|
OpenLayers.INCHES_PER_UNIT["in"]= OpenLayers.INCHES_PER_UNIT.inches;
|
OpenLayers.INCHES_PER_UNIT["degrees"] = OpenLayers.INCHES_PER_UNIT.dd;
|
OpenLayers.INCHES_PER_UNIT["nmi"] = 1852 * OpenLayers.INCHES_PER_UNIT.m;
|
|
// Units from CS-Map
|
OpenLayers.METERS_PER_INCH = 0.02540005080010160020;
|
OpenLayers.Util.extend(OpenLayers.INCHES_PER_UNIT, {
|
"Inch": OpenLayers.INCHES_PER_UNIT.inches,
|
"Meter": 1.0 / OpenLayers.METERS_PER_INCH, //EPSG:9001
|
"Foot": 0.30480060960121920243 / OpenLayers.METERS_PER_INCH, //EPSG:9003
|
"IFoot": 0.30480000000000000000 / OpenLayers.METERS_PER_INCH, //EPSG:9002
|
"ClarkeFoot": 0.3047972651151 / OpenLayers.METERS_PER_INCH, //EPSG:9005
|
"SearsFoot": 0.30479947153867624624 / OpenLayers.METERS_PER_INCH, //EPSG:9041
|
"GoldCoastFoot": 0.30479971018150881758 / OpenLayers.METERS_PER_INCH, //EPSG:9094
|
"IInch": 0.02540000000000000000 / OpenLayers.METERS_PER_INCH,
|
"MicroInch": 0.00002540000000000000 / OpenLayers.METERS_PER_INCH,
|
"Mil": 0.00000002540000000000 / OpenLayers.METERS_PER_INCH,
|
"Centimeter": 0.01000000000000000000 / OpenLayers.METERS_PER_INCH,
|
"Kilometer": 1000.00000000000000000000 / OpenLayers.METERS_PER_INCH, //EPSG:9036
|
"Yard": 0.91440182880365760731 / OpenLayers.METERS_PER_INCH,
|
"SearsYard": 0.914398414616029 / OpenLayers.METERS_PER_INCH, //EPSG:9040
|
"IndianYard": 0.91439853074444079983 / OpenLayers.METERS_PER_INCH, //EPSG:9084
|
"IndianYd37": 0.91439523 / OpenLayers.METERS_PER_INCH, //EPSG:9085
|
"IndianYd62": 0.9143988 / OpenLayers.METERS_PER_INCH, //EPSG:9086
|
"IndianYd75": 0.9143985 / OpenLayers.METERS_PER_INCH, //EPSG:9087
|
"IndianFoot": 0.30479951 / OpenLayers.METERS_PER_INCH, //EPSG:9080
|
"IndianFt37": 0.30479841 / OpenLayers.METERS_PER_INCH, //EPSG:9081
|
"IndianFt62": 0.3047996 / OpenLayers.METERS_PER_INCH, //EPSG:9082
|
"IndianFt75": 0.3047995 / OpenLayers.METERS_PER_INCH, //EPSG:9083
|
"Mile": 1609.34721869443738887477 / OpenLayers.METERS_PER_INCH,
|
"IYard": 0.91440000000000000000 / OpenLayers.METERS_PER_INCH, //EPSG:9096
|
"IMile": 1609.34400000000000000000 / OpenLayers.METERS_PER_INCH, //EPSG:9093
|
"NautM": 1852.00000000000000000000 / OpenLayers.METERS_PER_INCH, //EPSG:9030
|
"Lat-66": 110943.316488932731 / OpenLayers.METERS_PER_INCH,
|
"Lat-83": 110946.25736872234125 / OpenLayers.METERS_PER_INCH,
|
"Decimeter": 0.10000000000000000000 / OpenLayers.METERS_PER_INCH,
|
"Millimeter": 0.00100000000000000000 / OpenLayers.METERS_PER_INCH,
|
"Dekameter": 10.00000000000000000000 / OpenLayers.METERS_PER_INCH,
|
"Decameter": 10.00000000000000000000 / OpenLayers.METERS_PER_INCH,
|
"Hectometer": 100.00000000000000000000 / OpenLayers.METERS_PER_INCH,
|
"GermanMeter": 1.0000135965 / OpenLayers.METERS_PER_INCH, //EPSG:9031
|
"CaGrid": 0.999738 / OpenLayers.METERS_PER_INCH,
|
"ClarkeChain": 20.1166194976 / OpenLayers.METERS_PER_INCH, //EPSG:9038
|
"GunterChain": 20.11684023368047 / OpenLayers.METERS_PER_INCH, //EPSG:9033
|
"BenoitChain": 20.116782494375872 / OpenLayers.METERS_PER_INCH, //EPSG:9062
|
"SearsChain": 20.11676512155 / OpenLayers.METERS_PER_INCH, //EPSG:9042
|
"ClarkeLink": 0.201166194976 / OpenLayers.METERS_PER_INCH, //EPSG:9039
|
"GunterLink": 0.2011684023368047 / OpenLayers.METERS_PER_INCH, //EPSG:9034
|
"BenoitLink": 0.20116782494375872 / OpenLayers.METERS_PER_INCH, //EPSG:9063
|
"SearsLink": 0.2011676512155 / OpenLayers.METERS_PER_INCH, //EPSG:9043
|
"Rod": 5.02921005842012 / OpenLayers.METERS_PER_INCH,
|
"IntnlChain": 20.1168 / OpenLayers.METERS_PER_INCH, //EPSG:9097
|
"IntnlLink": 0.201168 / OpenLayers.METERS_PER_INCH, //EPSG:9098
|
"Perch": 5.02921005842012 / OpenLayers.METERS_PER_INCH,
|
"Pole": 5.02921005842012 / OpenLayers.METERS_PER_INCH,
|
"Furlong": 201.1684023368046 / OpenLayers.METERS_PER_INCH,
|
"Rood": 3.778266898 / OpenLayers.METERS_PER_INCH,
|
"CapeFoot": 0.3047972615 / OpenLayers.METERS_PER_INCH,
|
"Brealey": 375.00000000000000000000 / OpenLayers.METERS_PER_INCH,
|
"ModAmFt": 0.304812252984505969011938 / OpenLayers.METERS_PER_INCH,
|
"Fathom": 1.8288 / OpenLayers.METERS_PER_INCH,
|
"NautM-UK": 1853.184 / OpenLayers.METERS_PER_INCH,
|
"50kilometers": 50000.0 / OpenLayers.METERS_PER_INCH,
|
"150kilometers": 150000.0 / OpenLayers.METERS_PER_INCH
|
});
|
|
//unit abbreviations supported by PROJ.4
|
OpenLayers.Util.extend(OpenLayers.INCHES_PER_UNIT, {
|
"mm": OpenLayers.INCHES_PER_UNIT["Meter"] / 1000.0,
|
"cm": OpenLayers.INCHES_PER_UNIT["Meter"] / 100.0,
|
"dm": OpenLayers.INCHES_PER_UNIT["Meter"] * 100.0,
|
"km": OpenLayers.INCHES_PER_UNIT["Meter"] * 1000.0,
|
"kmi": OpenLayers.INCHES_PER_UNIT["nmi"], //International Nautical Mile
|
"fath": OpenLayers.INCHES_PER_UNIT["Fathom"], //International Fathom
|
"ch": OpenLayers.INCHES_PER_UNIT["IntnlChain"], //International Chain
|
"link": OpenLayers.INCHES_PER_UNIT["IntnlLink"], //International Link
|
"us-in": OpenLayers.INCHES_PER_UNIT["inches"], //U.S. Surveyor's Inch
|
"us-ft": OpenLayers.INCHES_PER_UNIT["Foot"], //U.S. Surveyor's Foot
|
"us-yd": OpenLayers.INCHES_PER_UNIT["Yard"], //U.S. Surveyor's Yard
|
"us-ch": OpenLayers.INCHES_PER_UNIT["GunterChain"], //U.S. Surveyor's Chain
|
"us-mi": OpenLayers.INCHES_PER_UNIT["Mile"], //U.S. Surveyor's Statute Mile
|
"ind-yd": OpenLayers.INCHES_PER_UNIT["IndianYd37"], //Indian Yard
|
"ind-ft": OpenLayers.INCHES_PER_UNIT["IndianFt37"], //Indian Foot
|
"ind-ch": 20.11669506 / OpenLayers.METERS_PER_INCH //Indian Chain
|
});
|
|
/**
|
* Constant: DOTS_PER_INCH
|
* {Integer} 72 (A sensible default)
|
*/
|
OpenLayers.DOTS_PER_INCH = 72;
|
|
/**
|
* Function: normalizeScale
|
*
|
* Parameters:
|
* scale - {float}
|
*
|
* Returns:
|
* {Float} A normalized scale value, in 1 / X format.
|
* This means that if a value less than one ( already 1/x) is passed
|
* in, it just returns scale directly. Otherwise, it returns
|
* 1 / scale
|
*/
|
OpenLayers.Util.normalizeScale = function (scale) {
|
var normScale = (scale > 1.0) ? (1.0 / scale)
|
: scale;
|
return normScale;
|
};
|
|
/**
|
* Function: getResolutionFromScale
|
*
|
* Parameters:
|
* scale - {Float}
|
* units - {String} Index into OpenLayers.INCHES_PER_UNIT hashtable.
|
* Default is degrees
|
*
|
* Returns:
|
* {Float} The corresponding resolution given passed-in scale and unit
|
* parameters. If the given scale is falsey, the returned resolution will
|
* be undefined.
|
*/
|
OpenLayers.Util.getResolutionFromScale = function (scale, units) {
|
var resolution;
|
if (scale) {
|
if (units == null) {
|
units = "degrees";
|
}
|
var normScale = OpenLayers.Util.normalizeScale(scale);
|
resolution = 1 / (normScale * OpenLayers.INCHES_PER_UNIT[units]
|
* OpenLayers.DOTS_PER_INCH);
|
}
|
return resolution;
|
};
|
|
/**
|
* Function: getScaleFromResolution
|
*
|
* Parameters:
|
* resolution - {Float}
|
* units - {String} Index into OpenLayers.INCHES_PER_UNIT hashtable.
|
* Default is degrees
|
*
|
* Returns:
|
* {Float} The corresponding scale given passed-in resolution and unit
|
* parameters.
|
*/
|
OpenLayers.Util.getScaleFromResolution = function (resolution, units) {
|
|
if (units == null) {
|
units = "degrees";
|
}
|
|
var scale = resolution * OpenLayers.INCHES_PER_UNIT[units] *
|
OpenLayers.DOTS_PER_INCH;
|
return scale;
|
};
|
|
/**
|
* Function: pagePosition
|
* Calculates the position of an element on the page (see
|
* http://code.google.com/p/doctype/wiki/ArticlePageOffset)
|
*
|
* OpenLayers.Util.pagePosition is based on Yahoo's getXY method, which is
|
* Copyright (c) 2006, Yahoo! Inc.
|
* All rights reserved.
|
*
|
* Redistribution and use of this software in source and binary forms, with or
|
* without modification, are permitted provided that the following conditions
|
* are met:
|
*
|
* * Redistributions of source code must retain the above copyright notice,
|
* this list of conditions and the following disclaimer.
|
*
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
* this list of conditions and the following disclaimer in the documentation
|
* and/or other materials provided with the distribution.
|
*
|
* * Neither the name of Yahoo! Inc. nor the names of its contributors may be
|
* used to endorse or promote products derived from this software without
|
* specific prior written permission of Yahoo! Inc.
|
*
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
* POSSIBILITY OF SUCH DAMAGE.
|
*
|
* Parameters:
|
* forElement - {DOMElement}
|
*
|
* Returns:
|
* {Array} two item array, Left value then Top value.
|
*/
|
OpenLayers.Util.pagePosition = function(forElement) {
|
// NOTE: If element is hidden (display none or disconnected or any the
|
// ancestors are hidden) we get (0,0) by default but we still do the
|
// accumulation of scroll position.
|
|
var pos = [0, 0];
|
var viewportElement = OpenLayers.Util.getViewportElement();
|
if (!forElement || forElement == window || forElement == viewportElement) {
|
// viewport is always at 0,0 as that defined the coordinate system for
|
// this function - this avoids special case checks in the code below
|
return pos;
|
}
|
|
// Gecko browsers normally use getBoxObjectFor to calculate the position.
|
// When invoked for an element with an implicit absolute position though it
|
// can be off by one. Therefore the recursive implementation is used in
|
// those (relatively rare) cases.
|
var BUGGY_GECKO_BOX_OBJECT =
|
OpenLayers.IS_GECKO && document.getBoxObjectFor &&
|
OpenLayers.Element.getStyle(forElement, 'position') == 'absolute' &&
|
(forElement.style.top == '' || forElement.style.left == '');
|
|
var parent = null;
|
var box;
|
|
if (forElement.getBoundingClientRect) { // IE
|
box = forElement.getBoundingClientRect();
|
var scrollTop = window.pageYOffset || viewportElement.scrollTop;
|
var scrollLeft = window.pageXOffset || viewportElement.scrollLeft;
|
|
pos[0] = box.left + scrollLeft;
|
pos[1] = box.top + scrollTop;
|
|
} else if (document.getBoxObjectFor && !BUGGY_GECKO_BOX_OBJECT) { // gecko
|
// Gecko ignores the scroll values for ancestors, up to 1.9. See:
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=328881 and
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=330619
|
|
box = document.getBoxObjectFor(forElement);
|
var vpBox = document.getBoxObjectFor(viewportElement);
|
pos[0] = box.screenX - vpBox.screenX;
|
pos[1] = box.screenY - vpBox.screenY;
|
|
} else { // safari/opera
|
pos[0] = forElement.offsetLeft;
|
pos[1] = forElement.offsetTop;
|
parent = forElement.offsetParent;
|
if (parent != forElement) {
|
while (parent) {
|
pos[0] += parent.offsetLeft;
|
pos[1] += parent.offsetTop;
|
parent = parent.offsetParent;
|
}
|
}
|
|
var browser = OpenLayers.BROWSER_NAME;
|
|
// opera & (safari absolute) incorrectly account for body offsetTop
|
if (browser == "opera" || (browser == "safari" &&
|
OpenLayers.Element.getStyle(forElement, 'position') == 'absolute')) {
|
pos[1] -= document.body.offsetTop;
|
}
|
|
// accumulate the scroll positions for everything but the body element
|
parent = forElement.offsetParent;
|
while (parent && parent != document.body) {
|
pos[0] -= parent.scrollLeft;
|
// see https://bugs.opera.com/show_bug.cgi?id=249965
|
if (browser != "opera" || parent.tagName != 'TR') {
|
pos[1] -= parent.scrollTop;
|
}
|
parent = parent.offsetParent;
|
}
|
}
|
|
return pos;
|
};
|
|
/**
|
* Function: getViewportElement
|
* Returns die viewport element of the document. The viewport element is
|
* usually document.documentElement, except in IE,where it is either
|
* document.body or document.documentElement, depending on the document's
|
* compatibility mode (see
|
* http://code.google.com/p/doctype/wiki/ArticleClientViewportElement)
|
*
|
* Returns:
|
* {DOMElement}
|
*/
|
OpenLayers.Util.getViewportElement = function() {
|
var viewportElement = arguments.callee.viewportElement;
|
if (viewportElement == undefined) {
|
viewportElement = (OpenLayers.BROWSER_NAME == "msie" &&
|
document.compatMode != 'CSS1Compat') ? document.body :
|
document.documentElement;
|
arguments.callee.viewportElement = viewportElement;
|
}
|
return viewportElement;
|
};
|
|
/**
|
* Function: isEquivalentUrl
|
* Test two URLs for equivalence.
|
*
|
* Setting 'ignoreCase' allows for case-independent comparison.
|
*
|
* Comparison is based on:
|
* - Protocol
|
* - Host (evaluated without the port)
|
* - Port (set 'ignorePort80' to ignore "80" values)
|
* - Hash ( set 'ignoreHash' to disable)
|
* - Pathname (for relative <-> absolute comparison)
|
* - Arguments (so they can be out of order)
|
*
|
* Parameters:
|
* url1 - {String}
|
* url2 - {String}
|
* options - {Object} Allows for customization of comparison:
|
* 'ignoreCase' - Default is True
|
* 'ignorePort80' - Default is True
|
* 'ignoreHash' - Default is True
|
*
|
* Returns:
|
* {Boolean} Whether or not the two URLs are equivalent
|
*/
|
OpenLayers.Util.isEquivalentUrl = function(url1, url2, options) {
|
options = options || {};
|
|
OpenLayers.Util.applyDefaults(options, {
|
ignoreCase: true,
|
ignorePort80: true,
|
ignoreHash: true,
|
splitArgs: false
|
});
|
|
var urlObj1 = OpenLayers.Util.createUrlObject(url1, options);
|
var urlObj2 = OpenLayers.Util.createUrlObject(url2, options);
|
|
//compare all keys except for "args" (treated below)
|
for(var key in urlObj1) {
|
if(key !== "args") {
|
if(urlObj1[key] != urlObj2[key]) {
|
return false;
|
}
|
}
|
}
|
|
// compare search args - irrespective of order
|
for(var key in urlObj1.args) {
|
if(urlObj1.args[key] != urlObj2.args[key]) {
|
return false;
|
}
|
delete urlObj2.args[key];
|
}
|
// urlObj2 shouldn't have any args left
|
for(var key in urlObj2.args) {
|
return false;
|
}
|
|
return true;
|
};
|
|
/**
|
* Function: createUrlObject
|
*
|
* Parameters:
|
* url - {String}
|
* options - {Object} A hash of options.
|
*
|
* Valid options:
|
* ignoreCase - {Boolean} lowercase url,
|
* ignorePort80 - {Boolean} don't include explicit port if port is 80,
|
* ignoreHash - {Boolean} Don't include part of url after the hash (#).
|
* splitArgs - {Boolean} Split comma delimited params into arrays? Default is
|
* true.
|
*
|
* Returns:
|
* {Object} An object with separate url, a, port, host, and args parsed out
|
* and ready for comparison
|
*/
|
OpenLayers.Util.createUrlObject = function(url, options) {
|
options = options || {};
|
|
// deal with relative urls first
|
if(!(/^\w+:\/\//).test(url)) {
|
var loc = window.location;
|
var port = loc.port ? ":" + loc.port : "";
|
var fullUrl = loc.protocol + "//" + loc.host.split(":").shift() + port;
|
if(url.indexOf("/") === 0) {
|
// full pathname
|
url = fullUrl + url;
|
} else {
|
// relative to current path
|
var parts = loc.pathname.split("/");
|
parts.pop();
|
url = fullUrl + parts.join("/") + "/" + url;
|
}
|
}
|
|
if (options.ignoreCase) {
|
url = url.toLowerCase();
|
}
|
|
var a = document.createElement('a');
|
a.href = url;
|
|
var urlObject = {};
|
|
//host (without port)
|
urlObject.host = a.host.split(":").shift();
|
|
//protocol
|
urlObject.protocol = a.protocol;
|
|
//port (get uniform browser behavior with port 80 here)
|
if(options.ignorePort80) {
|
urlObject.port = (a.port == "80" || a.port == "0") ? "" : a.port;
|
} else {
|
urlObject.port = (a.port == "" || a.port == "0") ? "80" : a.port;
|
}
|
|
//hash
|
urlObject.hash = (options.ignoreHash || a.hash === "#") ? "" : a.hash;
|
|
//args
|
var queryString = a.search;
|
if (!queryString) {
|
var qMark = url.indexOf("?");
|
queryString = (qMark != -1) ? url.substr(qMark) : "";
|
}
|
urlObject.args = OpenLayers.Util.getParameters(queryString,
|
{splitArgs: options.splitArgs});
|
|
// pathname
|
//
|
// This is a workaround for Internet Explorer where
|
// window.location.pathname has a leading "/", but
|
// a.pathname has no leading "/".
|
urlObject.pathname = (a.pathname.charAt(0) == "/") ? a.pathname : "/" + a.pathname;
|
|
return urlObject;
|
};
|
|
/**
|
* Function: removeTail
|
* Takes a url and removes everything after the ? and #
|
*
|
* Parameters:
|
* url - {String} The url to process
|
*
|
* Returns:
|
* {String} The string with all queryString and Hash removed
|
*/
|
OpenLayers.Util.removeTail = function(url) {
|
var head = null;
|
|
var qMark = url.indexOf("?");
|
var hashMark = url.indexOf("#");
|
|
if (qMark == -1) {
|
head = (hashMark != -1) ? url.substr(0,hashMark) : url;
|
} else {
|
head = (hashMark != -1) ? url.substr(0,Math.min(qMark, hashMark))
|
: url.substr(0, qMark);
|
}
|
return head;
|
};
|
|
/**
|
* Constant: IS_GECKO
|
* {Boolean} True if the userAgent reports the browser to use the Gecko engine
|
*/
|
OpenLayers.IS_GECKO = (function() {
|
var ua = navigator.userAgent.toLowerCase();
|
return ua.indexOf("webkit") == -1 && ua.indexOf("gecko") != -1;
|
})();
|
|
/**
|
* Constant: CANVAS_SUPPORTED
|
* {Boolean} True if canvas 2d is supported.
|
*/
|
OpenLayers.CANVAS_SUPPORTED = (function() {
|
var elem = document.createElement('canvas');
|
return !!(elem.getContext && elem.getContext('2d'));
|
})();
|
|
/**
|
* Constant: BROWSER_NAME
|
* {String}
|
* A substring of the navigator.userAgent property. Depending on the userAgent
|
* property, this will be the empty string or one of the following:
|
* * "opera" -- Opera
|
* * "msie" -- Internet Explorer
|
* * "safari" -- Safari
|
* * "firefox" -- Firefox
|
* * "mozilla" -- Mozilla
|
*/
|
OpenLayers.BROWSER_NAME = (function() {
|
var name = "";
|
var ua = navigator.userAgent.toLowerCase();
|
if (ua.indexOf("opera") != -1) {
|
name = "opera";
|
} else if (ua.indexOf("msie") != -1) {
|
name = "msie";
|
} else if (ua.indexOf("safari") != -1) {
|
name = "safari";
|
} else if (ua.indexOf("mozilla") != -1) {
|
if (ua.indexOf("firefox") != -1) {
|
name = "firefox";
|
} else {
|
name = "mozilla";
|
}
|
}
|
return name;
|
})();
|
|
/**
|
* Function: getBrowserName
|
*
|
* Returns:
|
* {String} A string which specifies which is the current
|
* browser in which we are running.
|
*
|
* Currently-supported browser detection and codes:
|
* * 'opera' -- Opera
|
* * 'msie' -- Internet Explorer
|
* * 'safari' -- Safari
|
* * 'firefox' -- Firefox
|
* * 'mozilla' -- Mozilla
|
*
|
* If we are unable to property identify the browser, we
|
* return an empty string.
|
*/
|
OpenLayers.Util.getBrowserName = function() {
|
return OpenLayers.BROWSER_NAME;
|
};
|
|
/**
|
* Method: getRenderedDimensions
|
* Renders the contentHTML offscreen to determine actual dimensions for
|
* popup sizing. As we need layout to determine dimensions the content
|
* is rendered -9999px to the left and absolute to ensure the
|
* scrollbars do not flicker
|
*
|
* Parameters:
|
* contentHTML
|
* size - {<OpenLayers.Size>} If either the 'w' or 'h' properties is
|
* specified, we fix that dimension of the div to be measured. This is
|
* useful in the case where we have a limit in one dimension and must
|
* therefore meaure the flow in the other dimension.
|
* options - {Object}
|
*
|
* Allowed Options:
|
* displayClass - {String} Optional parameter. A CSS class name(s) string
|
* to provide the CSS context of the rendered content.
|
* containerElement - {DOMElement} Optional parameter. Insert the HTML to
|
* this node instead of the body root when calculating dimensions.
|
*
|
* Returns:
|
* {<OpenLayers.Size>}
|
*/
|
OpenLayers.Util.getRenderedDimensions = function(contentHTML, size, options) {
|
|
var w, h;
|
|
// create temp container div with restricted size
|
var container = document.createElement("div");
|
container.style.visibility = "hidden";
|
|
var containerElement = (options && options.containerElement)
|
? options.containerElement : document.body;
|
|
// Opera and IE7 can't handle a node with position:aboslute if it inherits
|
// position:absolute from a parent.
|
var parentHasPositionAbsolute = false;
|
var superContainer = null;
|
var parent = containerElement;
|
while (parent && parent.tagName.toLowerCase()!="body") {
|
var parentPosition = OpenLayers.Element.getStyle(parent, "position");
|
if(parentPosition == "absolute") {
|
parentHasPositionAbsolute = true;
|
break;
|
} else if (parentPosition && parentPosition != "static") {
|
break;
|
}
|
parent = parent.parentNode;
|
}
|
if(parentHasPositionAbsolute && (containerElement.clientHeight === 0 ||
|
containerElement.clientWidth === 0) ){
|
superContainer = document.createElement("div");
|
superContainer.style.visibility = "hidden";
|
superContainer.style.position = "absolute";
|
superContainer.style.overflow = "visible";
|
superContainer.style.width = document.body.clientWidth + "px";
|
superContainer.style.height = document.body.clientHeight + "px";
|
superContainer.appendChild(container);
|
}
|
container.style.position = "absolute";
|
|
//fix a dimension, if specified.
|
if (size) {
|
if (size.w) {
|
w = size.w;
|
container.style.width = w + "px";
|
} else if (size.h) {
|
h = size.h;
|
container.style.height = h + "px";
|
}
|
}
|
|
//add css classes, if specified
|
if (options && options.displayClass) {
|
container.className = options.displayClass;
|
}
|
|
// create temp content div and assign content
|
var content = document.createElement("div");
|
content.innerHTML = contentHTML;
|
|
// we need overflow visible when calculating the size
|
content.style.overflow = "visible";
|
if (content.childNodes) {
|
for (var i=0, l=content.childNodes.length; i<l; i++) {
|
if (!content.childNodes[i].style) continue;
|
content.childNodes[i].style.overflow = "visible";
|
}
|
}
|
|
// add content to restricted container
|
container.appendChild(content);
|
|
// append container to body for rendering
|
if (superContainer) {
|
containerElement.appendChild(superContainer);
|
} else {
|
containerElement.appendChild(container);
|
}
|
|
// calculate scroll width of content and add corners and shadow width
|
if (!w) {
|
w = parseInt(content.scrollWidth);
|
|
// update container width to allow height to adjust
|
container.style.width = w + "px";
|
}
|
// capture height and add shadow and corner image widths
|
if (!h) {
|
h = parseInt(content.scrollHeight);
|
}
|
|
// remove elements
|
container.removeChild(content);
|
if (superContainer) {
|
superContainer.removeChild(container);
|
containerElement.removeChild(superContainer);
|
} else {
|
containerElement.removeChild(container);
|
}
|
|
return new OpenLayers.Size(w, h);
|
};
|
|
/**
|
* APIFunction: getScrollbarWidth
|
* This function has been modified by the OpenLayers from the original version,
|
* written by Matthew Eernisse and released under the Apache 2
|
* license here:
|
*
|
* http://www.fleegix.org/articles/2006/05/30/getting-the-scrollbar-width-in-pixels
|
*
|
* It has been modified simply to cache its value, since it is physically
|
* impossible that this code could ever run in more than one browser at
|
* once.
|
*
|
* Returns:
|
* {Integer}
|
*/
|
OpenLayers.Util.getScrollbarWidth = function() {
|
|
var scrollbarWidth = OpenLayers.Util._scrollbarWidth;
|
|
if (scrollbarWidth == null) {
|
var scr = null;
|
var inn = null;
|
var wNoScroll = 0;
|
var wScroll = 0;
|
|
// Outer scrolling div
|
scr = document.createElement('div');
|
scr.style.position = 'absolute';
|
scr.style.top = '-1000px';
|
scr.style.left = '-1000px';
|
scr.style.width = '100px';
|
scr.style.height = '50px';
|
// Start with no scrollbar
|
scr.style.overflow = 'hidden';
|
|
// Inner content div
|
inn = document.createElement('div');
|
inn.style.width = '100%';
|
inn.style.height = '200px';
|
|
// Put the inner div in the scrolling div
|
scr.appendChild(inn);
|
// Append the scrolling div to the doc
|
document.body.appendChild(scr);
|
|
// Width of the inner div sans scrollbar
|
wNoScroll = inn.offsetWidth;
|
|
// Add the scrollbar
|
scr.style.overflow = 'scroll';
|
// Width of the inner div width scrollbar
|
wScroll = inn.offsetWidth;
|
|
// Remove the scrolling div from the doc
|
document.body.removeChild(document.body.lastChild);
|
|
// Pixel width of the scroller
|
OpenLayers.Util._scrollbarWidth = (wNoScroll - wScroll);
|
scrollbarWidth = OpenLayers.Util._scrollbarWidth;
|
}
|
|
return scrollbarWidth;
|
};
|
|
/**
|
* APIFunction: getFormattedLonLat
|
* This function will return latitude or longitude value formatted as
|
*
|
* Parameters:
|
* coordinate - {Float} the coordinate value to be formatted
|
* axis - {String} value of either 'lat' or 'lon' to indicate which axis is to
|
* to be formatted (default = lat)
|
* dmsOption - {String} specify the precision of the output can be one of:
|
* 'dms' show degrees minutes and seconds
|
* 'dm' show only degrees and minutes
|
* 'd' show only degrees
|
*
|
* Returns:
|
* {String} the coordinate value formatted as a string
|
*/
|
OpenLayers.Util.getFormattedLonLat = function(coordinate, axis, dmsOption) {
|
if (!dmsOption) {
|
dmsOption = 'dms'; //default to show degree, minutes, seconds
|
}
|
|
coordinate = (coordinate+540)%360 - 180; // normalize for sphere being round
|
|
var abscoordinate = Math.abs(coordinate);
|
var coordinatedegrees = Math.floor(abscoordinate);
|
|
var coordinateminutes = (abscoordinate - coordinatedegrees)/(1/60);
|
var tempcoordinateminutes = coordinateminutes;
|
coordinateminutes = Math.floor(coordinateminutes);
|
var coordinateseconds = (tempcoordinateminutes - coordinateminutes)/(1/60);
|
coordinateseconds = Math.round(coordinateseconds*10);
|
coordinateseconds /= 10;
|
|
if( coordinateseconds >= 60) {
|
coordinateseconds -= 60;
|
coordinateminutes += 1;
|
if( coordinateminutes >= 60) {
|
coordinateminutes -= 60;
|
coordinatedegrees += 1;
|
}
|
}
|
|
if( coordinatedegrees < 10 ) {
|
coordinatedegrees = "0" + coordinatedegrees;
|
}
|
var str = coordinatedegrees + "\u00B0";
|
|
if (dmsOption.indexOf('dm') >= 0) {
|
if( coordinateminutes < 10 ) {
|
coordinateminutes = "0" + coordinateminutes;
|
}
|
str += coordinateminutes + "'";
|
|
if (dmsOption.indexOf('dms') >= 0) {
|
if( coordinateseconds < 10 ) {
|
coordinateseconds = "0" + coordinateseconds;
|
}
|
str += coordinateseconds + '"';
|
}
|
}
|
|
if (axis == "lon") {
|
str += coordinate < 0 ? OpenLayers.i18n("W") : OpenLayers.i18n("E");
|
} else {
|
str += coordinate < 0 ? OpenLayers.i18n("S") : OpenLayers.i18n("N");
|
}
|
return str;
|
};
|
|
/* ======================================================================
|
OpenLayers/Format.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
* @requires OpenLayers/Util.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format
|
* Base class for format reading/writing a variety of formats. Subclasses
|
* of OpenLayers.Format are expected to have read and write methods.
|
*/
|
OpenLayers.Format = OpenLayers.Class({
|
|
/**
|
* Property: options
|
* {Object} A reference to options passed to the constructor.
|
*/
|
options: null,
|
|
/**
|
* APIProperty: externalProjection
|
* {<OpenLayers.Projection>} When passed a externalProjection and
|
* internalProjection, the format will reproject the geometries it
|
* reads or writes. The externalProjection is the projection used by
|
* the content which is passed into read or which comes out of write.
|
* In order to reproject, a projection transformation function for the
|
* specified projections must be available. This support may be
|
* provided via proj4js or via a custom transformation function. See
|
* {<OpenLayers.Projection.addTransform>} for more information on
|
* custom transformations.
|
*/
|
externalProjection: null,
|
|
/**
|
* APIProperty: internalProjection
|
* {<OpenLayers.Projection>} When passed a externalProjection and
|
* internalProjection, the format will reproject the geometries it
|
* reads or writes. The internalProjection is the projection used by
|
* the geometries which are returned by read or which are passed into
|
* write. In order to reproject, a projection transformation function
|
* for the specified projections must be available. This support may be
|
* provided via proj4js or via a custom transformation function. See
|
* {<OpenLayers.Projection.addTransform>} for more information on
|
* custom transformations.
|
*/
|
internalProjection: null,
|
|
/**
|
* APIProperty: data
|
* {Object} When <keepData> is true, this is the parsed string sent to
|
* <read>.
|
*/
|
data: null,
|
|
/**
|
* APIProperty: keepData
|
* {Object} Maintain a reference (<data>) to the most recently read data.
|
* Default is false.
|
*/
|
keepData: false,
|
|
/**
|
* Constructor: OpenLayers.Format
|
* Instances of this class are not useful. See one of the subclasses.
|
*
|
* Parameters:
|
* options - {Object} An optional object with properties to set on the
|
* format
|
*
|
* Valid options:
|
* keepData - {Boolean} If true, upon <read>, the data property will be
|
* set to the parsed object (e.g. the json or xml object).
|
*
|
* Returns:
|
* An instance of OpenLayers.Format
|
*/
|
initialize: function(options) {
|
OpenLayers.Util.extend(this, options);
|
this.options = options;
|
},
|
|
/**
|
* APIMethod: destroy
|
* Clean up.
|
*/
|
destroy: function() {
|
},
|
|
/**
|
* Method: read
|
* Read data from a string, and return an object whose type depends on the
|
* subclass.
|
*
|
* Parameters:
|
* data - {string} Data to read/parse.
|
*
|
* Returns:
|
* Depends on the subclass
|
*/
|
read: function(data) {
|
throw new Error('Read not implemented.');
|
},
|
|
/**
|
* Method: write
|
* Accept an object, and return a string.
|
*
|
* Parameters:
|
* object - {Object} Object to be serialized
|
*
|
* Returns:
|
* {String} A string representation of the object.
|
*/
|
write: function(object) {
|
throw new Error('Write not implemented.');
|
},
|
|
CLASS_NAME: "OpenLayers.Format"
|
});
|
/* ======================================================================
|
OpenLayers/Format/CSWGetRecords.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.CSWGetRecords
|
* Default version is 2.0.2.
|
*
|
* Returns:
|
* {<OpenLayers.Format>} A CSWGetRecords format of the given version.
|
*/
|
OpenLayers.Format.CSWGetRecords = function(options) {
|
options = OpenLayers.Util.applyDefaults(
|
options, OpenLayers.Format.CSWGetRecords.DEFAULTS
|
);
|
var cls = OpenLayers.Format.CSWGetRecords["v"+options.version.replace(/\./g, "_")];
|
if(!cls) {
|
throw "Unsupported CSWGetRecords version: " + options.version;
|
}
|
return new cls(options);
|
};
|
|
/**
|
* Constant: DEFAULTS
|
* {Object} Default properties for the CSWGetRecords format.
|
*/
|
OpenLayers.Format.CSWGetRecords.DEFAULTS = {
|
"version": "2.0.2"
|
};
|
/* ======================================================================
|
OpenLayers/Control.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control
|
* Controls affect the display or behavior of the map. They allow everything
|
* from panning and zooming to displaying a scale indicator. Controls by
|
* default are added to the map they are contained within however it is
|
* possible to add a control to an external div by passing the div in the
|
* options parameter.
|
*
|
* Example:
|
* The following example shows how to add many of the common controls
|
* to a map.
|
*
|
* > var map = new OpenLayers.Map('map', { controls: [] });
|
* >
|
* > map.addControl(new OpenLayers.Control.PanZoomBar());
|
* > map.addControl(new OpenLayers.Control.LayerSwitcher({'ascending':false}));
|
* > map.addControl(new OpenLayers.Control.Permalink());
|
* > map.addControl(new OpenLayers.Control.Permalink('permalink'));
|
* > map.addControl(new OpenLayers.Control.MousePosition());
|
* > map.addControl(new OpenLayers.Control.OverviewMap());
|
* > map.addControl(new OpenLayers.Control.KeyboardDefaults());
|
*
|
* The next code fragment is a quick example of how to intercept
|
* shift-mouse click to display the extent of the bounding box
|
* dragged out by the user. Usually controls are not created
|
* in exactly this manner. See the source for a more complete
|
* example:
|
*
|
* > var control = new OpenLayers.Control();
|
* > OpenLayers.Util.extend(control, {
|
* > draw: function () {
|
* > // this Handler.Box will intercept the shift-mousedown
|
* > // before Control.MouseDefault gets to see it
|
* > this.box = new OpenLayers.Handler.Box( control,
|
* > {"done": this.notice},
|
* > {keyMask: OpenLayers.Handler.MOD_SHIFT});
|
* > this.box.activate();
|
* > },
|
* >
|
* > notice: function (bounds) {
|
* > OpenLayers.Console.userError(bounds);
|
* > }
|
* > });
|
* > map.addControl(control);
|
*
|
*/
|
OpenLayers.Control = OpenLayers.Class({
|
|
/**
|
* Property: id
|
* {String}
|
*/
|
id: null,
|
|
/**
|
* Property: map
|
* {<OpenLayers.Map>} this gets set in the addControl() function in
|
* OpenLayers.Map
|
*/
|
map: null,
|
|
/**
|
* APIProperty: div
|
* {DOMElement} The element that contains the control, if not present the
|
* control is placed inside the map.
|
*/
|
div: null,
|
|
/**
|
* APIProperty: type
|
* {Number} Controls can have a 'type'. The type determines the type of
|
* interactions which are possible with them when they are placed in an
|
* <OpenLayers.Control.Panel>.
|
*/
|
type: null,
|
|
/**
|
* Property: allowSelection
|
* {Boolean} By default, controls do not allow selection, because
|
* it may interfere with map dragging. If this is true, OpenLayers
|
* will not prevent selection of the control.
|
* Default is false.
|
*/
|
allowSelection: false,
|
|
/**
|
* Property: displayClass
|
* {string} This property is used for CSS related to the drawing of the
|
* Control.
|
*/
|
displayClass: "",
|
|
/**
|
* APIProperty: title
|
* {string} This property is used for showing a tooltip over the
|
* Control.
|
*/
|
title: "",
|
|
/**
|
* APIProperty: autoActivate
|
* {Boolean} Activate the control when it is added to a map. Default is
|
* false.
|
*/
|
autoActivate: false,
|
|
/**
|
* APIProperty: active
|
* {Boolean} The control is active (read-only). Use <activate> and
|
* <deactivate> to change control state.
|
*/
|
active: null,
|
|
/**
|
* Property: handlerOptions
|
* {Object} Used to set non-default properties on the control's handler
|
*/
|
handlerOptions: null,
|
|
/**
|
* Property: handler
|
* {<OpenLayers.Handler>} null
|
*/
|
handler: null,
|
|
/**
|
* APIProperty: eventListeners
|
* {Object} If set as an option at construction, the eventListeners
|
* object will be registered with <OpenLayers.Events.on>. Object
|
* structure must be a listeners object as shown in the example for
|
* the events.on method.
|
*/
|
eventListeners: null,
|
|
/**
|
* APIProperty: events
|
* {<OpenLayers.Events>} Events instance for listeners and triggering
|
* control specific events.
|
*
|
* Register a listener for a particular event with the following syntax:
|
* (code)
|
* control.events.register(type, obj, listener);
|
* (end)
|
*
|
* Listeners will be called with a reference to an event object. The
|
* properties of this event depends on exactly what happened.
|
*
|
* All event objects have at least the following properties:
|
* object - {Object} A reference to control.events.object (a reference
|
* to the control).
|
* element - {DOMElement} A reference to control.events.element (which
|
* will be null unless documented otherwise).
|
*
|
* Supported map event types:
|
* activate - Triggered when activated.
|
* deactivate - Triggered when deactivated.
|
*/
|
events: null,
|
|
/**
|
* Constructor: OpenLayers.Control
|
* Create an OpenLayers Control. The options passed as a parameter
|
* directly extend the control. For example passing the following:
|
*
|
* > var control = new OpenLayers.Control({div: myDiv});
|
*
|
* Overrides the default div attribute value of null.
|
*
|
* Parameters:
|
* options - {Object}
|
*/
|
initialize: function (options) {
|
// We do this before the extend so that instances can override
|
// className in options.
|
this.displayClass =
|
this.CLASS_NAME.replace("OpenLayers.", "ol").replace(/\./g, "");
|
|
OpenLayers.Util.extend(this, options);
|
|
this.events = new OpenLayers.Events(this);
|
if(this.eventListeners instanceof Object) {
|
this.events.on(this.eventListeners);
|
}
|
if (this.id == null) {
|
this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
|
}
|
},
|
|
/**
|
* Method: destroy
|
* The destroy method is used to perform any clean up before the control
|
* is dereferenced. Typically this is where event listeners are removed
|
* to prevent memory leaks.
|
*/
|
destroy: function () {
|
if(this.events) {
|
if(this.eventListeners) {
|
this.events.un(this.eventListeners);
|
}
|
this.events.destroy();
|
this.events = null;
|
}
|
this.eventListeners = null;
|
|
// eliminate circular references
|
if (this.handler) {
|
this.handler.destroy();
|
this.handler = null;
|
}
|
if(this.handlers) {
|
for(var key in this.handlers) {
|
if(this.handlers.hasOwnProperty(key) &&
|
typeof this.handlers[key].destroy == "function") {
|
this.handlers[key].destroy();
|
}
|
}
|
this.handlers = null;
|
}
|
if (this.map) {
|
this.map.removeControl(this);
|
this.map = null;
|
}
|
this.div = null;
|
},
|
|
/**
|
* Method: setMap
|
* Set the map property for the control. This is done through an accessor
|
* so that subclasses can override this and take special action once
|
* they have their map variable set.
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
setMap: function(map) {
|
this.map = map;
|
if (this.handler) {
|
this.handler.setMap(map);
|
}
|
},
|
|
/**
|
* Method: draw
|
* The draw method is called when the control is ready to be displayed
|
* on the page. If a div has not been created one is created. Controls
|
* with a visual component will almost always want to override this method
|
* to customize the look of control.
|
*
|
* Parameters:
|
* px - {<OpenLayers.Pixel>} The top-left pixel position of the control
|
* or null.
|
*
|
* Returns:
|
* {DOMElement} A reference to the DIV DOMElement containing the control
|
*/
|
draw: function (px) {
|
if (this.div == null) {
|
this.div = OpenLayers.Util.createDiv(this.id);
|
this.div.className = this.displayClass;
|
if (!this.allowSelection) {
|
this.div.className += " olControlNoSelect";
|
this.div.setAttribute("unselectable", "on", 0);
|
this.div.onselectstart = OpenLayers.Function.False;
|
}
|
if (this.title != "") {
|
this.div.title = this.title;
|
}
|
}
|
if (px != null) {
|
this.position = px.clone();
|
}
|
this.moveTo(this.position);
|
return this.div;
|
},
|
|
/**
|
* Method: moveTo
|
* Sets the left and top style attributes to the passed in pixel
|
* coordinates.
|
*
|
* Parameters:
|
* px - {<OpenLayers.Pixel>}
|
*/
|
moveTo: function (px) {
|
if ((px != null) && (this.div != null)) {
|
this.div.style.left = px.x + "px";
|
this.div.style.top = px.y + "px";
|
}
|
},
|
|
/**
|
* APIMethod: activate
|
* Explicitly activates a control and it's associated
|
* handler if one has been set. Controls can be
|
* deactivated by calling the deactivate() method.
|
*
|
* Returns:
|
* {Boolean} True if the control was successfully activated or
|
* false if the control was already active.
|
*/
|
activate: function () {
|
if (this.active) {
|
return false;
|
}
|
if (this.handler) {
|
this.handler.activate();
|
}
|
this.active = true;
|
if(this.map) {
|
OpenLayers.Element.addClass(
|
this.map.viewPortDiv,
|
this.displayClass.replace(/ /g, "") + "Active"
|
);
|
}
|
this.events.triggerEvent("activate");
|
return true;
|
},
|
|
/**
|
* APIMethod: deactivate
|
* Deactivates a control and it's associated handler if any. The exact
|
* effect of this depends on the control itself.
|
*
|
* Returns:
|
* {Boolean} True if the control was effectively deactivated or false
|
* if the control was already inactive.
|
*/
|
deactivate: function () {
|
if (this.active) {
|
if (this.handler) {
|
this.handler.deactivate();
|
}
|
this.active = false;
|
if(this.map) {
|
OpenLayers.Element.removeClass(
|
this.map.viewPortDiv,
|
this.displayClass.replace(/ /g, "") + "Active"
|
);
|
}
|
this.events.triggerEvent("deactivate");
|
return true;
|
}
|
return false;
|
},
|
|
CLASS_NAME: "OpenLayers.Control"
|
});
|
|
/**
|
* Constant: OpenLayers.Control.TYPE_BUTTON
|
*/
|
OpenLayers.Control.TYPE_BUTTON = 1;
|
|
/**
|
* Constant: OpenLayers.Control.TYPE_TOGGLE
|
*/
|
OpenLayers.Control.TYPE_TOGGLE = 2;
|
|
/**
|
* Constant: OpenLayers.Control.TYPE_TOOL
|
*/
|
OpenLayers.Control.TYPE_TOOL = 3;
|
/* ======================================================================
|
OpenLayers/Events.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Util.js
|
*/
|
|
/**
|
* Namespace: OpenLayers.Event
|
* Utility functions for event handling.
|
*/
|
OpenLayers.Event = {
|
|
/**
|
* Property: observers
|
* {Object} A hashtable cache of the event observers. Keyed by
|
* element._eventCacheID
|
*/
|
observers: false,
|
|
/**
|
* Constant: KEY_SPACE
|
* {int}
|
*/
|
KEY_SPACE: 32,
|
|
/**
|
* Constant: KEY_BACKSPACE
|
* {int}
|
*/
|
KEY_BACKSPACE: 8,
|
|
/**
|
* Constant: KEY_TAB
|
* {int}
|
*/
|
KEY_TAB: 9,
|
|
/**
|
* Constant: KEY_RETURN
|
* {int}
|
*/
|
KEY_RETURN: 13,
|
|
/**
|
* Constant: KEY_ESC
|
* {int}
|
*/
|
KEY_ESC: 27,
|
|
/**
|
* Constant: KEY_LEFT
|
* {int}
|
*/
|
KEY_LEFT: 37,
|
|
/**
|
* Constant: KEY_UP
|
* {int}
|
*/
|
KEY_UP: 38,
|
|
/**
|
* Constant: KEY_RIGHT
|
* {int}
|
*/
|
KEY_RIGHT: 39,
|
|
/**
|
* Constant: KEY_DOWN
|
* {int}
|
*/
|
KEY_DOWN: 40,
|
|
/**
|
* Constant: KEY_DELETE
|
* {int}
|
*/
|
KEY_DELETE: 46,
|
|
|
/**
|
* Method: element
|
* Cross browser event element detection.
|
*
|
* Parameters:
|
* event - {Event}
|
*
|
* Returns:
|
* {DOMElement} The element that caused the event
|
*/
|
element: function(event) {
|
return event.target || event.srcElement;
|
},
|
|
/**
|
* Method: isSingleTouch
|
* Determine whether event was caused by a single touch
|
*
|
* Parameters:
|
* event - {Event}
|
*
|
* Returns:
|
* {Boolean}
|
*/
|
isSingleTouch: function(event) {
|
return event.touches && event.touches.length == 1;
|
},
|
|
/**
|
* Method: isMultiTouch
|
* Determine whether event was caused by a multi touch
|
*
|
* Parameters:
|
* event - {Event}
|
*
|
* Returns:
|
* {Boolean}
|
*/
|
isMultiTouch: function(event) {
|
return event.touches && event.touches.length > 1;
|
},
|
|
/**
|
* Method: isLeftClick
|
* Determine whether event was caused by a left click.
|
*
|
* Parameters:
|
* event - {Event}
|
*
|
* Returns:
|
* {Boolean}
|
*/
|
isLeftClick: function(event) {
|
return (((event.which) && (event.which == 1)) ||
|
((event.button) && (event.button == 1)));
|
},
|
|
/**
|
* Method: isRightClick
|
* Determine whether event was caused by a right mouse click.
|
*
|
* Parameters:
|
* event - {Event}
|
*
|
* Returns:
|
* {Boolean}
|
*/
|
isRightClick: function(event) {
|
return (((event.which) && (event.which == 3)) ||
|
((event.button) && (event.button == 2)));
|
},
|
|
/**
|
* Method: stop
|
* Stops an event from propagating.
|
*
|
* Parameters:
|
* event - {Event}
|
* allowDefault - {Boolean} If true, we stop the event chain but
|
* still allow the default browser behaviour (text selection,
|
* radio-button clicking, etc). Default is false.
|
*/
|
stop: function(event, allowDefault) {
|
|
if (!allowDefault) {
|
OpenLayers.Event.preventDefault(event);
|
}
|
|
if (event.stopPropagation) {
|
event.stopPropagation();
|
} else {
|
event.cancelBubble = true;
|
}
|
},
|
|
/**
|
* Method: preventDefault
|
* Cancels the event if it is cancelable, without stopping further
|
* propagation of the event.
|
*
|
* Parameters:
|
* event - {Event}
|
*/
|
preventDefault: function(event) {
|
if (event.preventDefault) {
|
event.preventDefault();
|
} else {
|
event.returnValue = false;
|
}
|
},
|
|
/**
|
* Method: findElement
|
*
|
* Parameters:
|
* event - {Event}
|
* tagName - {String}
|
*
|
* Returns:
|
* {DOMElement} The first node with the given tagName, starting from the
|
* node the event was triggered on and traversing the DOM upwards
|
*/
|
findElement: function(event, tagName) {
|
var element = OpenLayers.Event.element(event);
|
while (element.parentNode && (!element.tagName ||
|
(element.tagName.toUpperCase() != tagName.toUpperCase()))){
|
element = element.parentNode;
|
}
|
return element;
|
},
|
|
/**
|
* Method: observe
|
*
|
* Parameters:
|
* elementParam - {DOMElement || String}
|
* name - {String}
|
* observer - {function}
|
* useCapture - {Boolean}
|
*/
|
observe: function(elementParam, name, observer, useCapture) {
|
var element = OpenLayers.Util.getElement(elementParam);
|
useCapture = useCapture || false;
|
|
if (name == 'keypress' &&
|
(navigator.appVersion.match(/Konqueror|Safari|KHTML/)
|
|| element.attachEvent)) {
|
name = 'keydown';
|
}
|
|
//if observers cache has not yet been created, create it
|
if (!this.observers) {
|
this.observers = {};
|
}
|
|
//if not already assigned, make a new unique cache ID
|
if (!element._eventCacheID) {
|
var idPrefix = "eventCacheID_";
|
if (element.id) {
|
idPrefix = element.id + "_" + idPrefix;
|
}
|
element._eventCacheID = OpenLayers.Util.createUniqueID(idPrefix);
|
}
|
|
var cacheID = element._eventCacheID;
|
|
//if there is not yet a hash entry for this element, add one
|
if (!this.observers[cacheID]) {
|
this.observers[cacheID] = [];
|
}
|
|
//add a new observer to this element's list
|
this.observers[cacheID].push({
|
'element': element,
|
'name': name,
|
'observer': observer,
|
'useCapture': useCapture
|
});
|
|
//add the actual browser event listener
|
if (element.addEventListener) {
|
element.addEventListener(name, observer, useCapture);
|
} else if (element.attachEvent) {
|
element.attachEvent('on' + name, observer);
|
}
|
},
|
|
/**
|
* Method: stopObservingElement
|
* Given the id of an element to stop observing, cycle through the
|
* element's cached observers, calling stopObserving on each one,
|
* skipping those entries which can no longer be removed.
|
*
|
* parameters:
|
* elementParam - {DOMElement || String}
|
*/
|
stopObservingElement: function(elementParam) {
|
var element = OpenLayers.Util.getElement(elementParam);
|
var cacheID = element._eventCacheID;
|
|
this._removeElementObservers(OpenLayers.Event.observers[cacheID]);
|
},
|
|
/**
|
* Method: _removeElementObservers
|
*
|
* Parameters:
|
* elementObservers - {Array(Object)} Array of (element, name,
|
* observer, usecapture) objects,
|
* taken directly from hashtable
|
*/
|
_removeElementObservers: function(elementObservers) {
|
if (elementObservers) {
|
for(var i = elementObservers.length-1; i >= 0; i--) {
|
var entry = elementObservers[i];
|
OpenLayers.Event.stopObserving.apply(this, [
|
entry.element, entry.name, entry.observer, entry.useCapture
|
]);
|
}
|
}
|
},
|
|
/**
|
* Method: stopObserving
|
*
|
* Parameters:
|
* elementParam - {DOMElement || String}
|
* name - {String}
|
* observer - {function}
|
* useCapture - {Boolean}
|
*
|
* Returns:
|
* {Boolean} Whether or not the event observer was removed
|
*/
|
stopObserving: function(elementParam, name, observer, useCapture) {
|
useCapture = useCapture || false;
|
|
var element = OpenLayers.Util.getElement(elementParam);
|
var cacheID = element._eventCacheID;
|
|
if (name == 'keypress') {
|
if ( navigator.appVersion.match(/Konqueror|Safari|KHTML/) ||
|
element.detachEvent) {
|
name = 'keydown';
|
}
|
}
|
|
// find element's entry in this.observers cache and remove it
|
var foundEntry = false;
|
var elementObservers = OpenLayers.Event.observers[cacheID];
|
if (elementObservers) {
|
|
// find the specific event type in the element's list
|
var i=0;
|
while(!foundEntry && i < elementObservers.length) {
|
var cacheEntry = elementObservers[i];
|
|
if ((cacheEntry.name == name) &&
|
(cacheEntry.observer == observer) &&
|
(cacheEntry.useCapture == useCapture)) {
|
|
elementObservers.splice(i, 1);
|
if (elementObservers.length == 0) {
|
delete OpenLayers.Event.observers[cacheID];
|
}
|
foundEntry = true;
|
break;
|
}
|
i++;
|
}
|
}
|
|
//actually remove the event listener from browser
|
if (foundEntry) {
|
if (element.removeEventListener) {
|
element.removeEventListener(name, observer, useCapture);
|
} else if (element && element.detachEvent) {
|
element.detachEvent('on' + name, observer);
|
}
|
}
|
return foundEntry;
|
},
|
|
/**
|
* Method: unloadCache
|
* Cycle through all the element entries in the events cache and call
|
* stopObservingElement on each.
|
*/
|
unloadCache: function() {
|
// check for OpenLayers.Event before checking for observers, because
|
// OpenLayers.Event may be undefined in IE if no map instance was
|
// created
|
if (OpenLayers.Event && OpenLayers.Event.observers) {
|
for (var cacheID in OpenLayers.Event.observers) {
|
var elementObservers = OpenLayers.Event.observers[cacheID];
|
OpenLayers.Event._removeElementObservers.apply(this,
|
[elementObservers]);
|
}
|
OpenLayers.Event.observers = false;
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Event"
|
};
|
|
/* prevent memory leaks in IE */
|
OpenLayers.Event.observe(window, 'unload', OpenLayers.Event.unloadCache, false);
|
|
/**
|
* Class: OpenLayers.Events
|
*/
|
OpenLayers.Events = OpenLayers.Class({
|
|
/**
|
* Constant: BROWSER_EVENTS
|
* {Array(String)} supported events
|
*/
|
BROWSER_EVENTS: [
|
"mouseover", "mouseout",
|
"mousedown", "mouseup", "mousemove",
|
"click", "dblclick", "rightclick", "dblrightclick",
|
"resize", "focus", "blur",
|
"touchstart", "touchmove", "touchend",
|
"keydown"
|
],
|
|
/**
|
* Property: listeners
|
* {Object} Hashtable of Array(Function): events listener functions
|
*/
|
listeners: null,
|
|
/**
|
* Property: object
|
* {Object} the code object issuing application events
|
*/
|
object: null,
|
|
/**
|
* Property: element
|
* {DOMElement} the DOM element receiving browser events
|
*/
|
element: null,
|
|
/**
|
* Property: eventHandler
|
* {Function} bound event handler attached to elements
|
*/
|
eventHandler: null,
|
|
/**
|
* APIProperty: fallThrough
|
* {Boolean}
|
*/
|
fallThrough: null,
|
|
/**
|
* APIProperty: includeXY
|
* {Boolean} Should the .xy property automatically be created for browser
|
* mouse events? In general, this should be false. If it is true, then
|
* mouse events will automatically generate a '.xy' property on the
|
* event object that is passed. (Prior to OpenLayers 2.7, this was true
|
* by default.) Otherwise, you can call the getMousePosition on the
|
* relevant events handler on the object available via the 'evt.object'
|
* property of the evt object. So, for most events, you can call:
|
* function named(evt) {
|
* this.xy = this.object.events.getMousePosition(evt)
|
* }
|
*
|
* This option typically defaults to false for performance reasons:
|
* when creating an events object whose primary purpose is to manage
|
* relatively positioned mouse events within a div, it may make
|
* sense to set it to true.
|
*
|
* This option is also used to control whether the events object caches
|
* offsets. If this is false, it will not: the reason for this is that
|
* it is only expected to be called many times if the includeXY property
|
* is set to true. If you set this to true, you are expected to clear
|
* the offset cache manually (using this.clearMouseCache()) if:
|
* the border of the element changes
|
* the location of the element in the page changes
|
*/
|
includeXY: false,
|
|
/**
|
* APIProperty: extensions
|
* {Object} Event extensions registered with this instance. Keys are
|
* event types, values are {OpenLayers.Events.*} extension instances or
|
* {Boolean} for events that an instantiated extension provides in
|
* addition to the one it was created for.
|
*
|
* Extensions create an event in addition to browser events, which usually
|
* fires when a sequence of browser events is completed. Extensions are
|
* automatically instantiated when a listener is registered for an event
|
* provided by an extension.
|
*
|
* Extensions are created in the <OpenLayers.Events> namespace using
|
* <OpenLayers.Class>, and named after the event they provide.
|
* The constructor receives the target <OpenLayers.Events> instance as
|
* argument. Extensions that need to capture browser events before they
|
* propagate can register their listeners events using <register>, with
|
* {extension: true} as 4th argument.
|
*
|
* If an extension creates more than one event, an alias for each event
|
* type should be created and reference the same class. The constructor
|
* should set a reference in the target's extensions registry to itself.
|
*
|
* Below is a minimal extension that provides the "foostart" and "fooend"
|
* event types, which replace the native "click" event type if clicked on
|
* an element with the css class "foo":
|
*
|
* (code)
|
* OpenLayers.Events.foostart = OpenLayers.Class({
|
* initialize: function(target) {
|
* this.target = target;
|
* this.target.register("click", this, this.doStuff, {extension: true});
|
* // only required if extension provides more than one event type
|
* this.target.extensions["foostart"] = true;
|
* this.target.extensions["fooend"] = true;
|
* },
|
* destroy: function() {
|
* var target = this.target;
|
* target.unregister("click", this, this.doStuff);
|
* delete this.target;
|
* // only required if extension provides more than one event type
|
* delete target.extensions["foostart"];
|
* delete target.extensions["fooend"];
|
* },
|
* doStuff: function(evt) {
|
* var propagate = true;
|
* if (OpenLayers.Event.element(evt).className === "foo") {
|
* propagate = false;
|
* var target = this.target;
|
* target.triggerEvent("foostart");
|
* window.setTimeout(function() {
|
* target.triggerEvent("fooend");
|
* }, 1000);
|
* }
|
* return propagate;
|
* }
|
* });
|
* // only required if extension provides more than one event type
|
* OpenLayers.Events.fooend = OpenLayers.Events.foostart;
|
* (end)
|
*
|
*/
|
extensions: null,
|
|
/**
|
* Property: extensionCount
|
* {Object} Keys are event types (like in <listeners>), values are the
|
* number of extension listeners for each event type.
|
*/
|
extensionCount: null,
|
|
/**
|
* Method: clearMouseListener
|
* A version of <clearMouseCache> that is bound to this instance so that
|
* it can be used with <OpenLayers.Event.observe> and
|
* <OpenLayers.Event.stopObserving>.
|
*/
|
clearMouseListener: null,
|
|
/**
|
* Constructor: OpenLayers.Events
|
* Construct an OpenLayers.Events object.
|
*
|
* Parameters:
|
* object - {Object} The js object to which this Events object is being added
|
* element - {DOMElement} A dom element to respond to browser events
|
* eventTypes - {Array(String)} Deprecated. Array of custom application
|
* events. A listener may be registered for any named event, regardless
|
* of the values provided here.
|
* fallThrough - {Boolean} Allow events to fall through after these have
|
* been handled?
|
* options - {Object} Options for the events object.
|
*/
|
initialize: function (object, element, eventTypes, fallThrough, options) {
|
OpenLayers.Util.extend(this, options);
|
this.object = object;
|
this.fallThrough = fallThrough;
|
this.listeners = {};
|
this.extensions = {};
|
this.extensionCount = {};
|
this._msTouches = [];
|
|
// if a dom element is specified, add a listeners list
|
// for browser events on the element and register them
|
if (element != null) {
|
this.attachToElement(element);
|
}
|
},
|
|
/**
|
* APIMethod: destroy
|
*/
|
destroy: function () {
|
for (var e in this.extensions) {
|
if (typeof this.extensions[e] !== "boolean") {
|
this.extensions[e].destroy();
|
}
|
}
|
this.extensions = null;
|
if (this.element) {
|
OpenLayers.Event.stopObservingElement(this.element);
|
if(this.element.hasScrollEvent) {
|
OpenLayers.Event.stopObserving(
|
window, "scroll", this.clearMouseListener
|
);
|
}
|
}
|
this.element = null;
|
|
this.listeners = null;
|
this.object = null;
|
this.fallThrough = null;
|
this.eventHandler = null;
|
},
|
|
/**
|
* APIMethod: addEventType
|
* Deprecated. Any event can be triggered without adding it first.
|
*
|
* Parameters:
|
* eventName - {String}
|
*/
|
addEventType: function(eventName) {
|
},
|
|
/**
|
* Method: attachToElement
|
*
|
* Parameters:
|
* element - {HTMLDOMElement} a DOM element to attach browser events to
|
*/
|
attachToElement: function (element) {
|
if (this.element) {
|
OpenLayers.Event.stopObservingElement(this.element);
|
} else {
|
// keep a bound copy of handleBrowserEvent() so that we can
|
// pass the same function to both Event.observe() and .stopObserving()
|
this.eventHandler = OpenLayers.Function.bindAsEventListener(
|
this.handleBrowserEvent, this
|
);
|
|
// to be used with observe and stopObserving
|
this.clearMouseListener = OpenLayers.Function.bind(
|
this.clearMouseCache, this
|
);
|
}
|
this.element = element;
|
var msTouch = !!window.navigator.msMaxTouchPoints;
|
var type;
|
for (var i = 0, len = this.BROWSER_EVENTS.length; i < len; i++) {
|
type = this.BROWSER_EVENTS[i];
|
// register the event cross-browser
|
OpenLayers.Event.observe(element, type, this.eventHandler
|
);
|
if (msTouch && type.indexOf('touch') === 0) {
|
this.addMsTouchListener(element, type, this.eventHandler);
|
}
|
}
|
// disable dragstart in IE so that mousedown/move/up works normally
|
OpenLayers.Event.observe(element, "dragstart", OpenLayers.Event.stop);
|
},
|
|
/**
|
* APIMethod: on
|
* Convenience method for registering listeners with a common scope.
|
* Internally, this method calls <register> as shown in the examples
|
* below.
|
*
|
* Example use:
|
* (code)
|
* // register a single listener for the "loadstart" event
|
* events.on({"loadstart": loadStartListener});
|
*
|
* // this is equivalent to the following
|
* events.register("loadstart", undefined, loadStartListener);
|
*
|
* // register multiple listeners to be called with the same `this` object
|
* events.on({
|
* "loadstart": loadStartListener,
|
* "loadend": loadEndListener,
|
* scope: object
|
* });
|
*
|
* // this is equivalent to the following
|
* events.register("loadstart", object, loadStartListener);
|
* events.register("loadend", object, loadEndListener);
|
* (end)
|
*
|
* Parameters:
|
* object - {Object}
|
*/
|
on: function(object) {
|
for(var type in object) {
|
if(type != "scope" && object.hasOwnProperty(type)) {
|
this.register(type, object.scope, object[type]);
|
}
|
}
|
},
|
|
/**
|
* APIMethod: register
|
* Register an event on the events object.
|
*
|
* When the event is triggered, the 'func' function will be called, in the
|
* context of 'obj'. Imagine we were to register an event, specifying an
|
* OpenLayers.Bounds Object as 'obj'. When the event is triggered, the
|
* context in the callback function will be our Bounds object. This means
|
* that within our callback function, we can access the properties and
|
* methods of the Bounds object through the "this" variable. So our
|
* callback could execute something like:
|
* : leftStr = "Left: " + this.left;
|
*
|
* or
|
*
|
* : centerStr = "Center: " + this.getCenterLonLat();
|
*
|
* Parameters:
|
* type - {String} Name of the event to register
|
* obj - {Object} The object to bind the context to for the callback#.
|
* If no object is specified, default is the Events's 'object' property.
|
* func - {Function} The callback function. If no callback is
|
* specified, this function does nothing.
|
* priority - {Boolean|Object} If true, adds the new listener to the
|
* *front* of the events queue instead of to the end.
|
*
|
* Valid options for priority:
|
* extension - {Boolean} If true, then the event will be registered as
|
* extension event. Extension events are handled before all other
|
* events.
|
*/
|
register: function (type, obj, func, priority) {
|
if (type in OpenLayers.Events && !this.extensions[type]) {
|
this.extensions[type] = new OpenLayers.Events[type](this);
|
}
|
if (func != null) {
|
if (obj == null) {
|
obj = this.object;
|
}
|
var listeners = this.listeners[type];
|
if (!listeners) {
|
listeners = [];
|
this.listeners[type] = listeners;
|
this.extensionCount[type] = 0;
|
}
|
var listener = {obj: obj, func: func};
|
if (priority) {
|
listeners.splice(this.extensionCount[type], 0, listener);
|
if (typeof priority === "object" && priority.extension) {
|
this.extensionCount[type]++;
|
}
|
} else {
|
listeners.push(listener);
|
}
|
}
|
},
|
|
/**
|
* APIMethod: registerPriority
|
* Same as register() but adds the new listener to the *front* of the
|
* events queue instead of to the end.
|
*
|
* TODO: get rid of this in 3.0 - Decide whether listeners should be
|
* called in the order they were registered or in reverse order.
|
*
|
*
|
* Parameters:
|
* type - {String} Name of the event to register
|
* obj - {Object} The object to bind the context to for the callback#.
|
* If no object is specified, default is the Events's
|
* 'object' property.
|
* func - {Function} The callback function. If no callback is
|
* specified, this function does nothing.
|
*/
|
registerPriority: function (type, obj, func) {
|
this.register(type, obj, func, true);
|
},
|
|
/**
|
* APIMethod: un
|
* Convenience method for unregistering listeners with a common scope.
|
* Internally, this method calls <unregister> as shown in the examples
|
* below.
|
*
|
* Example use:
|
* (code)
|
* // unregister a single listener for the "loadstart" event
|
* events.un({"loadstart": loadStartListener});
|
*
|
* // this is equivalent to the following
|
* events.unregister("loadstart", undefined, loadStartListener);
|
*
|
* // unregister multiple listeners with the same `this` object
|
* events.un({
|
* "loadstart": loadStartListener,
|
* "loadend": loadEndListener,
|
* scope: object
|
* });
|
*
|
* // this is equivalent to the following
|
* events.unregister("loadstart", object, loadStartListener);
|
* events.unregister("loadend", object, loadEndListener);
|
* (end)
|
*/
|
un: function(object) {
|
for(var type in object) {
|
if(type != "scope" && object.hasOwnProperty(type)) {
|
this.unregister(type, object.scope, object[type]);
|
}
|
}
|
},
|
|
/**
|
* APIMethod: unregister
|
*
|
* Parameters:
|
* type - {String}
|
* obj - {Object} If none specified, defaults to this.object
|
* func - {Function}
|
*/
|
unregister: function (type, obj, func) {
|
if (obj == null) {
|
obj = this.object;
|
}
|
var listeners = this.listeners[type];
|
if (listeners != null) {
|
for (var i=0, len=listeners.length; i<len; i++) {
|
if (listeners[i].obj == obj && listeners[i].func == func) {
|
listeners.splice(i, 1);
|
break;
|
}
|
}
|
}
|
},
|
|
/**
|
* Method: remove
|
* Remove all listeners for a given event type. If type is not registered,
|
* does nothing.
|
*
|
* Parameters:
|
* type - {String}
|
*/
|
remove: function(type) {
|
if (this.listeners[type] != null) {
|
this.listeners[type] = [];
|
}
|
},
|
|
/**
|
* APIMethod: triggerEvent
|
* Trigger a specified registered event.
|
*
|
* Parameters:
|
* type - {String}
|
* evt - {Event || Object} will be passed to the listeners.
|
*
|
* Returns:
|
* {Boolean} The last listener return. If a listener returns false, the
|
* chain of listeners will stop getting called.
|
*/
|
triggerEvent: function (type, evt) {
|
var listeners = this.listeners[type];
|
|
// fast path
|
if(!listeners || listeners.length == 0) {
|
return undefined;
|
}
|
|
// prep evt object with object & div references
|
if (evt == null) {
|
evt = {};
|
}
|
evt.object = this.object;
|
evt.element = this.element;
|
if(!evt.type) {
|
evt.type = type;
|
}
|
|
// execute all callbacks registered for specified type
|
// get a clone of the listeners array to
|
// allow for splicing during callbacks
|
listeners = listeners.slice();
|
var continueChain;
|
for (var i=0, len=listeners.length; i<len; i++) {
|
var callback = listeners[i];
|
// bind the context to callback.obj
|
continueChain = callback.func.apply(callback.obj, [evt]);
|
|
if ((continueChain != undefined) && (continueChain == false)) {
|
// if callback returns false, execute no more callbacks.
|
break;
|
}
|
}
|
// don't fall through to other DOM elements
|
if (!this.fallThrough) {
|
OpenLayers.Event.stop(evt, true);
|
}
|
return continueChain;
|
},
|
|
/**
|
* Method: handleBrowserEvent
|
* Basically just a wrapper to the triggerEvent() function, but takes
|
* care to set a property 'xy' on the event with the current mouse
|
* position.
|
*
|
* Parameters:
|
* evt - {Event}
|
*/
|
handleBrowserEvent: function (evt) {
|
var type = evt.type, listeners = this.listeners[type];
|
if(!listeners || listeners.length == 0) {
|
// noone's listening, bail out
|
return;
|
}
|
// add clientX & clientY to all events - corresponds to average x, y
|
var touches = evt.touches;
|
if (touches && touches[0]) {
|
var x = 0;
|
var y = 0;
|
var num = touches.length;
|
var touch;
|
for (var i=0; i<num; ++i) {
|
touch = this.getTouchClientXY(touches[i]);
|
x += touch.clientX;
|
y += touch.clientY;
|
}
|
evt.clientX = x / num;
|
evt.clientY = y / num;
|
}
|
if (this.includeXY) {
|
evt.xy = this.getMousePosition(evt);
|
}
|
this.triggerEvent(type, evt);
|
},
|
|
/**
|
* Method: getTouchClientXY
|
* WebKit has a few bugs for clientX/clientY. This method detects them
|
* and calculate the correct values.
|
*
|
* Parameters:
|
* evt - {Touch} a Touch object from a TouchEvent
|
*
|
* Returns:
|
* {Object} An object with only clientX and clientY properties with the
|
* calculated values.
|
*/
|
getTouchClientXY: function (evt) {
|
// olMochWin is to override window, used for testing
|
var win = window.olMockWin || window,
|
winPageX = win.pageXOffset,
|
winPageY = win.pageYOffset,
|
x = evt.clientX,
|
y = evt.clientY;
|
|
if (evt.pageY === 0 && Math.floor(y) > Math.floor(evt.pageY) ||
|
evt.pageX === 0 && Math.floor(x) > Math.floor(evt.pageX)) {
|
// iOS4 include scroll offset in clientX/Y
|
x = x - winPageX;
|
y = y - winPageY;
|
} else if (y < (evt.pageY - winPageY) || x < (evt.pageX - winPageX) ) {
|
// Some Android browsers have totally bogus values for clientX/Y
|
// when scrolling/zooming a page
|
x = evt.pageX - winPageX;
|
y = evt.pageY - winPageY;
|
}
|
|
evt.olClientX = x;
|
evt.olClientY = y;
|
|
return {
|
clientX: x,
|
clientY: y
|
};
|
},
|
|
/**
|
* APIMethod: clearMouseCache
|
* Clear cached data about the mouse position. This should be called any
|
* time the element that events are registered on changes position
|
* within the page.
|
*/
|
clearMouseCache: function() {
|
this.element.scrolls = null;
|
this.element.lefttop = null;
|
this.element.offsets = null;
|
},
|
|
/**
|
* Method: getMousePosition
|
*
|
* Parameters:
|
* evt - {Event}
|
*
|
* Returns:
|
* {<OpenLayers.Pixel>} The current xy coordinate of the mouse, adjusted
|
* for offsets
|
*/
|
getMousePosition: function (evt) {
|
if (!this.includeXY) {
|
this.clearMouseCache();
|
} else if (!this.element.hasScrollEvent) {
|
OpenLayers.Event.observe(window, "scroll", this.clearMouseListener);
|
this.element.hasScrollEvent = true;
|
}
|
|
if (!this.element.scrolls) {
|
var viewportElement = OpenLayers.Util.getViewportElement();
|
this.element.scrolls = [
|
window.pageXOffset || viewportElement.scrollLeft,
|
window.pageYOffset || viewportElement.scrollTop
|
];
|
}
|
|
if (!this.element.lefttop) {
|
this.element.lefttop = [
|
(document.documentElement.clientLeft || 0),
|
(document.documentElement.clientTop || 0)
|
];
|
}
|
|
if (!this.element.offsets) {
|
this.element.offsets = OpenLayers.Util.pagePosition(this.element);
|
}
|
|
return new OpenLayers.Pixel(
|
(evt.clientX + this.element.scrolls[0]) - this.element.offsets[0]
|
- this.element.lefttop[0],
|
(evt.clientY + this.element.scrolls[1]) - this.element.offsets[1]
|
- this.element.lefttop[1]
|
);
|
},
|
|
/**
|
* Method: addMsTouchListener
|
*
|
* Parameters:
|
* element - {DOMElement} The DOM element to register the listener on
|
* type - {String} The event type
|
* handler - {Function} the handler
|
*/
|
addMsTouchListener: function (element, type, handler) {
|
var eventHandler = this.eventHandler;
|
var touches = this._msTouches;
|
|
function msHandler(evt) {
|
handler(OpenLayers.Util.applyDefaults({
|
stopPropagation: function() {
|
for (var i=touches.length-1; i>=0; --i) {
|
touches[i].stopPropagation();
|
}
|
},
|
preventDefault: function() {
|
for (var i=touches.length-1; i>=0; --i) {
|
touches[i].preventDefault();
|
}
|
},
|
type: type
|
}, evt));
|
}
|
|
switch (type) {
|
case 'touchstart':
|
return this.addMsTouchListenerStart(element, type, msHandler);
|
case 'touchend':
|
return this.addMsTouchListenerEnd(element, type, msHandler);
|
case 'touchmove':
|
return this.addMsTouchListenerMove(element, type, msHandler);
|
default:
|
throw 'Unknown touch event type';
|
}
|
},
|
|
/**
|
* Method: addMsTouchListenerStart
|
*
|
* Parameters:
|
* element - {DOMElement} The DOM element to register the listener on
|
* type - {String} The event type
|
* handler - {Function} the handler
|
*/
|
addMsTouchListenerStart: function(element, type, handler) {
|
var touches = this._msTouches;
|
|
var cb = function(e) {
|
|
var alreadyInArray = false;
|
for (var i=0, ii=touches.length; i<ii; ++i) {
|
if (touches[i].pointerId == e.pointerId) {
|
alreadyInArray = true;
|
break;
|
}
|
}
|
if (!alreadyInArray) {
|
touches.push(e);
|
}
|
|
e.touches = touches.slice();
|
handler(e);
|
};
|
|
OpenLayers.Event.observe(element, 'MSPointerDown', cb);
|
|
// Need to also listen for end events to keep the _msTouches list
|
// accurate
|
var internalCb = function(e) {
|
for (var i=0, ii=touches.length; i<ii; ++i) {
|
if (touches[i].pointerId == e.pointerId) {
|
touches.splice(i, 1);
|
break;
|
}
|
}
|
};
|
OpenLayers.Event.observe(element, 'MSPointerUp', internalCb);
|
},
|
|
/**
|
* Method: addMsTouchListenerMove
|
*
|
* Parameters:
|
* element - {DOMElement} The DOM element to register the listener on
|
* type - {String} The event type
|
* handler - {Function} the handler
|
*/
|
addMsTouchListenerMove: function (element, type, handler) {
|
var touches = this._msTouches;
|
var cb = function(e) {
|
|
//Don't fire touch moves when mouse isn't down
|
if (e.pointerType == e.MSPOINTER_TYPE_MOUSE && e.buttons == 0) {
|
return;
|
}
|
|
if (touches.length == 1 && touches[0].pageX == e.pageX &&
|
touches[0].pageY == e.pageY) {
|
// don't trigger event when pointer has not moved
|
return;
|
}
|
for (var i=0, ii=touches.length; i<ii; ++i) {
|
if (touches[i].pointerId == e.pointerId) {
|
touches[i] = e;
|
break;
|
}
|
}
|
|
e.touches = touches.slice();
|
handler(e);
|
};
|
|
OpenLayers.Event.observe(element, 'MSPointerMove', cb);
|
},
|
|
/**
|
* Method: addMsTouchListenerEnd
|
*
|
* Parameters:
|
* element - {DOMElement} The DOM element to register the listener on
|
* type - {String} The event type
|
* handler - {Function} the handler
|
*/
|
addMsTouchListenerEnd: function (element, type, handler) {
|
var touches = this._msTouches;
|
|
var cb = function(e) {
|
|
for (var i=0, ii=touches.length; i<ii; ++i) {
|
if (touches[i].pointerId == e.pointerId) {
|
touches.splice(i, 1);
|
break;
|
}
|
}
|
|
e.touches = touches.slice();
|
handler(e);
|
};
|
|
OpenLayers.Event.observe(element, 'MSPointerUp', cb);
|
},
|
|
CLASS_NAME: "OpenLayers.Events"
|
});
|
/* ======================================================================
|
OpenLayers/Events/buttonclick.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Events.js
|
*/
|
|
/**
|
* Class: OpenLayers.Events.buttonclick
|
* Extension event type for handling buttons on top of a dom element. This
|
* event type fires "buttonclick" on its <target> when a button was
|
* clicked. Buttons are detected by the "olButton" class.
|
*
|
* This event type makes sure that button clicks do not interfere with other
|
* events that are registered on the same <element>.
|
*
|
* Event types provided by this extension:
|
* - *buttonclick* Triggered when a button is clicked. Listeners receive an
|
* object with a *buttonElement* property referencing the dom element of
|
* the clicked button, and an *buttonXY* property with the click position
|
* relative to the button.
|
*/
|
OpenLayers.Events.buttonclick = OpenLayers.Class({
|
|
/**
|
* Property: target
|
* {<OpenLayers.Events>} The events instance that the buttonclick event will
|
* be triggered on.
|
*/
|
target: null,
|
|
/**
|
* Property: events
|
* {Array} Events to observe and conditionally stop from propagating when
|
* an element with the olButton class (or its olAlphaImg child) is
|
* clicked.
|
*/
|
events: [
|
'mousedown', 'mouseup', 'click', 'dblclick',
|
'touchstart', 'touchmove', 'touchend', 'keydown'
|
],
|
|
/**
|
* Property: startRegEx
|
* {RegExp} Regular expression to test Event.type for events that start
|
* a buttonclick sequence.
|
*/
|
startRegEx: /^mousedown|touchstart$/,
|
|
/**
|
* Property: cancelRegEx
|
* {RegExp} Regular expression to test Event.type for events that cancel
|
* a buttonclick sequence.
|
*/
|
cancelRegEx: /^touchmove$/,
|
|
/**
|
* Property: completeRegEx
|
* {RegExp} Regular expression to test Event.type for events that complete
|
* a buttonclick sequence.
|
*/
|
completeRegEx: /^mouseup|touchend$/,
|
|
/**
|
* Property: startEvt
|
* {Event} The event that started the click sequence
|
*/
|
|
/**
|
* Constructor: OpenLayers.Events.buttonclick
|
* Construct a buttonclick event type. Applications are not supposed to
|
* create instances of this class - they are created on demand by
|
* <OpenLayers.Events> instances.
|
*
|
* Parameters:
|
* target - {<OpenLayers.Events>} The events instance that the buttonclick
|
* event will be triggered on.
|
*/
|
initialize: function(target) {
|
this.target = target;
|
for (var i=this.events.length-1; i>=0; --i) {
|
this.target.register(this.events[i], this, this.buttonClick, {
|
extension: true
|
});
|
}
|
},
|
|
/**
|
* Method: destroy
|
*/
|
destroy: function() {
|
for (var i=this.events.length-1; i>=0; --i) {
|
this.target.unregister(this.events[i], this, this.buttonClick);
|
}
|
delete this.target;
|
},
|
|
/**
|
* Method: getPressedButton
|
* Get the pressed button, if any. Returns undefined if no button
|
* was pressed.
|
*
|
* Arguments:
|
* element - {DOMElement} The event target.
|
*
|
* Returns:
|
* {DOMElement} The button element, or undefined.
|
*/
|
getPressedButton: function(element) {
|
var depth = 3, // limit the search depth
|
button;
|
do {
|
if(OpenLayers.Element.hasClass(element, "olButton")) {
|
// hit!
|
button = element;
|
break;
|
}
|
element = element.parentNode;
|
} while(--depth > 0 && element);
|
return button;
|
},
|
|
/**
|
* Method: ignore
|
* Check for event target elements that should be ignored by OpenLayers.
|
*
|
* Parameters:
|
* element - {DOMElement} The event target.
|
*/
|
ignore: function(element) {
|
var depth = 3,
|
ignore = false;
|
do {
|
if (element.nodeName.toLowerCase() === 'a') {
|
ignore = true;
|
break;
|
}
|
element = element.parentNode;
|
} while (--depth > 0 && element);
|
return ignore;
|
},
|
|
/**
|
* Method: buttonClick
|
* Check if a button was clicked, and fire the buttonclick event
|
*
|
* Parameters:
|
* evt - {Event}
|
*/
|
buttonClick: function(evt) {
|
var propagate = true,
|
element = OpenLayers.Event.element(evt);
|
if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf("mouse"))) {
|
// was a button pressed?
|
var button = this.getPressedButton(element);
|
if (button) {
|
if (evt.type === "keydown") {
|
switch (evt.keyCode) {
|
case OpenLayers.Event.KEY_RETURN:
|
case OpenLayers.Event.KEY_SPACE:
|
this.target.triggerEvent("buttonclick", {
|
buttonElement: button
|
});
|
OpenLayers.Event.stop(evt);
|
propagate = false;
|
break;
|
}
|
} else if (this.startEvt) {
|
if (this.completeRegEx.test(evt.type)) {
|
var pos = OpenLayers.Util.pagePosition(button);
|
var viewportElement = OpenLayers.Util.getViewportElement();
|
var scrollTop = window.pageYOffset || viewportElement.scrollTop;
|
var scrollLeft = window.pageXOffset || viewportElement.scrollLeft;
|
pos[0] = pos[0] - scrollLeft;
|
pos[1] = pos[1] - scrollTop;
|
|
this.target.triggerEvent("buttonclick", {
|
buttonElement: button,
|
buttonXY: {
|
x: this.startEvt.clientX - pos[0],
|
y: this.startEvt.clientY - pos[1]
|
}
|
});
|
}
|
if (this.cancelRegEx.test(evt.type)) {
|
delete this.startEvt;
|
}
|
OpenLayers.Event.stop(evt);
|
propagate = false;
|
}
|
if (this.startRegEx.test(evt.type)) {
|
this.startEvt = evt;
|
OpenLayers.Event.stop(evt);
|
propagate = false;
|
}
|
} else {
|
propagate = !this.ignore(OpenLayers.Event.element(evt));
|
delete this.startEvt;
|
}
|
}
|
return propagate;
|
}
|
|
});
|
/* ======================================================================
|
OpenLayers/Util/vendorPrefix.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/SingleFile.js
|
*/
|
|
OpenLayers.Util = OpenLayers.Util || {};
|
/**
|
* Namespace: OpenLayers.Util.vendorPrefix
|
* A collection of utility functions to detect vendor prefixed features
|
*/
|
OpenLayers.Util.vendorPrefix = (function() {
|
"use strict";
|
|
var VENDOR_PREFIXES = ["", "O", "ms", "Moz", "Webkit"],
|
divStyle = document.createElement("div").style,
|
cssCache = {},
|
jsCache = {};
|
|
|
/**
|
* Function: domToCss
|
* Converts a upper camel case DOM style property name to a CSS property
|
* i.e. transformOrigin -> transform-origin
|
* or WebkitTransformOrigin -> -webkit-transform-origin
|
*
|
* Parameters:
|
* prefixedDom - {String} The property to convert
|
*
|
* Returns:
|
* {String} The CSS property
|
*/
|
function domToCss(prefixedDom) {
|
if (!prefixedDom) { return null; }
|
return prefixedDom.
|
replace(/([A-Z])/g, function(c) { return "-" + c.toLowerCase(); }).
|
replace(/^ms-/, "-ms-");
|
}
|
|
/**
|
* APIMethod: css
|
* Detect which property is used for a CSS property
|
*
|
* Parameters:
|
* property - {String} The standard (unprefixed) CSS property name
|
*
|
* Returns:
|
* {String} The standard CSS property, prefixed property or null if not
|
* supported
|
*/
|
function css(property) {
|
if (cssCache[property] === undefined) {
|
var domProperty = property.
|
replace(/(-[\s\S])/g, function(c) { return c.charAt(1).toUpperCase(); });
|
var prefixedDom = style(domProperty);
|
cssCache[property] = domToCss(prefixedDom);
|
}
|
return cssCache[property];
|
}
|
|
/**
|
* APIMethod: js
|
* Detect which property is used for a JS property/method
|
*
|
* Parameters:
|
* obj - {Object} The object to test on
|
* property - {String} The standard (unprefixed) JS property name
|
*
|
* Returns:
|
* {String} The standard JS property, prefixed property or null if not
|
* supported
|
*/
|
function js(obj, property) {
|
if (jsCache[property] === undefined) {
|
var tmpProp,
|
i = 0,
|
l = VENDOR_PREFIXES.length,
|
prefix,
|
isStyleObj = (typeof obj.cssText !== "undefined");
|
|
jsCache[property] = null;
|
for(; i<l; i++) {
|
prefix = VENDOR_PREFIXES[i];
|
if(prefix) {
|
if (!isStyleObj) {
|
// js prefix should be lower-case, while style
|
// properties have upper case on first character
|
prefix = prefix.toLowerCase();
|
}
|
tmpProp = prefix + property.charAt(0).toUpperCase() + property.slice(1);
|
} else {
|
tmpProp = property;
|
}
|
|
if(obj[tmpProp] !== undefined) {
|
jsCache[property] = tmpProp;
|
break;
|
}
|
}
|
}
|
return jsCache[property];
|
}
|
|
/**
|
* APIMethod: style
|
* Detect which property is used for a DOM style property
|
*
|
* Parameters:
|
* property - {String} The standard (unprefixed) style property name
|
*
|
* Returns:
|
* {String} The standard style property, prefixed property or null if not
|
* supported
|
*/
|
function style(property) {
|
return js(divStyle, property);
|
}
|
|
return {
|
css: css,
|
js: js,
|
style: style,
|
|
// used for testing
|
cssCache: cssCache,
|
jsCache: jsCache
|
};
|
}());
|
/* ======================================================================
|
OpenLayers/Animation.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/SingleFile.js
|
* @requires OpenLayers/Util/vendorPrefix.js
|
*/
|
|
/**
|
* Namespace: OpenLayers.Animation
|
* A collection of utility functions for executing methods that repaint a
|
* portion of the browser window. These methods take advantage of the
|
* browser's scheduled repaints where requestAnimationFrame is available.
|
*/
|
OpenLayers.Animation = (function(window) {
|
|
/**
|
* Property: isNative
|
* {Boolean} true if a native requestAnimationFrame function is available
|
*/
|
var requestAnimationFrame = OpenLayers.Util.vendorPrefix.js(window, "requestAnimationFrame");
|
var isNative = !!(requestAnimationFrame);
|
|
/**
|
* Function: requestFrame
|
* Schedule a function to be called at the next available animation frame.
|
* Uses the native method where available. Where requestAnimationFrame is
|
* not available, setTimeout will be called with a 16ms delay.
|
*
|
* Parameters:
|
* callback - {Function} The function to be called at the next animation frame.
|
* element - {DOMElement} Optional element that visually bounds the animation.
|
*/
|
var requestFrame = (function() {
|
var request = window[requestAnimationFrame] ||
|
function(callback, element) {
|
window.setTimeout(callback, 16);
|
};
|
// bind to window to avoid illegal invocation of native function
|
return function(callback, element) {
|
request.apply(window, [callback, element]);
|
};
|
})();
|
|
// private variables for animation loops
|
var counter = 0;
|
var loops = {};
|
|
/**
|
* Function: start
|
* Executes a method with <requestFrame> in series for some
|
* duration.
|
*
|
* Parameters:
|
* callback - {Function} The function to be called at the next animation frame.
|
* duration - {Number} Optional duration for the loop. If not provided, the
|
* animation loop will execute indefinitely.
|
* element - {DOMElement} Optional element that visually bounds the animation.
|
*
|
* Returns:
|
* {Number} Identifier for the animation loop. Used to stop animations with
|
* <stop>.
|
*/
|
function start(callback, duration, element) {
|
duration = duration > 0 ? duration : Number.POSITIVE_INFINITY;
|
var id = ++counter;
|
var start = +new Date;
|
loops[id] = function() {
|
if (loops[id] && +new Date - start <= duration) {
|
callback();
|
if (loops[id]) {
|
requestFrame(loops[id], element);
|
}
|
} else {
|
delete loops[id];
|
}
|
};
|
requestFrame(loops[id], element);
|
return id;
|
}
|
|
/**
|
* Function: stop
|
* Terminates an animation loop started with <start>.
|
*
|
* Parameters:
|
* id - {Number} Identifier returned from <start>.
|
*/
|
function stop(id) {
|
delete loops[id];
|
}
|
|
return {
|
isNative: isNative,
|
requestFrame: requestFrame,
|
start: start,
|
stop: stop
|
};
|
|
})(window);
|
/* ======================================================================
|
OpenLayers/Tween.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
* @requires OpenLayers/Animation.js
|
*/
|
|
/**
|
* Namespace: OpenLayers.Tween
|
*/
|
OpenLayers.Tween = OpenLayers.Class({
|
|
/**
|
* APIProperty: easing
|
* {<OpenLayers.Easing>(Function)} Easing equation used for the animation
|
* Defaultly set to OpenLayers.Easing.Expo.easeOut
|
*/
|
easing: null,
|
|
/**
|
* APIProperty: begin
|
* {Object} Values to start the animation with
|
*/
|
begin: null,
|
|
/**
|
* APIProperty: finish
|
* {Object} Values to finish the animation with
|
*/
|
finish: null,
|
|
/**
|
* APIProperty: duration
|
* {int} duration of the tween (number of steps)
|
*/
|
duration: null,
|
|
/**
|
* APIProperty: callbacks
|
* {Object} An object with start, eachStep and done properties whose values
|
* are functions to be call during the animation. They are passed the
|
* current computed value as argument.
|
*/
|
callbacks: null,
|
|
/**
|
* Property: time
|
* {int} Step counter
|
*/
|
time: null,
|
|
/**
|
* APIProperty: minFrameRate
|
* {Number} The minimum framerate for animations in frames per second. After
|
* each step, the time spent in the animation is compared to the calculated
|
* time at this frame rate. If the animation runs longer than the calculated
|
* time, the next step is skipped. Default is 30.
|
*/
|
minFrameRate: null,
|
|
/**
|
* Property: startTime
|
* {Number} The timestamp of the first execution step. Used for skipping
|
* frames
|
*/
|
startTime: null,
|
|
/**
|
* Property: animationId
|
* {int} Loop id returned by OpenLayers.Animation.start
|
*/
|
animationId: null,
|
|
/**
|
* Property: playing
|
* {Boolean} Tells if the easing is currently playing
|
*/
|
playing: false,
|
|
/**
|
* Constructor: OpenLayers.Tween
|
* Creates a Tween.
|
*
|
* Parameters:
|
* easing - {<OpenLayers.Easing>(Function)} easing function method to use
|
*/
|
initialize: function(easing) {
|
this.easing = (easing) ? easing : OpenLayers.Easing.Expo.easeOut;
|
},
|
|
/**
|
* APIMethod: start
|
* Plays the Tween, and calls the callback method on each step
|
*
|
* Parameters:
|
* begin - {Object} values to start the animation with
|
* finish - {Object} values to finish the animation with
|
* duration - {int} duration of the tween (number of steps)
|
* options - {Object} hash of options (callbacks (start, eachStep, done),
|
* minFrameRate)
|
*/
|
start: function(begin, finish, duration, options) {
|
this.playing = true;
|
this.begin = begin;
|
this.finish = finish;
|
this.duration = duration;
|
this.callbacks = options.callbacks;
|
this.minFrameRate = options.minFrameRate || 30;
|
this.time = 0;
|
this.startTime = new Date().getTime();
|
OpenLayers.Animation.stop(this.animationId);
|
this.animationId = null;
|
if (this.callbacks && this.callbacks.start) {
|
this.callbacks.start.call(this, this.begin);
|
}
|
this.animationId = OpenLayers.Animation.start(
|
OpenLayers.Function.bind(this.play, this)
|
);
|
},
|
|
/**
|
* APIMethod: stop
|
* Stops the Tween, and calls the done callback
|
* Doesn't do anything if animation is already finished
|
*/
|
stop: function() {
|
if (!this.playing) {
|
return;
|
}
|
|
if (this.callbacks && this.callbacks.done) {
|
this.callbacks.done.call(this, this.finish);
|
}
|
OpenLayers.Animation.stop(this.animationId);
|
this.animationId = null;
|
this.playing = false;
|
},
|
|
/**
|
* Method: play
|
* Calls the appropriate easing method
|
*/
|
play: function() {
|
var value = {};
|
for (var i in this.begin) {
|
var b = this.begin[i];
|
var f = this.finish[i];
|
if (b == null || f == null || isNaN(b) || isNaN(f)) {
|
throw new TypeError('invalid value for Tween');
|
}
|
|
var c = f - b;
|
value[i] = this.easing.apply(this, [this.time, b, c, this.duration]);
|
}
|
this.time++;
|
|
if (this.callbacks && this.callbacks.eachStep) {
|
// skip frames if frame rate drops below threshold
|
if ((new Date().getTime() - this.startTime) / this.time <= 1000 / this.minFrameRate) {
|
this.callbacks.eachStep.call(this, value);
|
}
|
}
|
|
if (this.time > this.duration) {
|
this.stop();
|
}
|
},
|
|
/**
|
* Create empty functions for all easing methods.
|
*/
|
CLASS_NAME: "OpenLayers.Tween"
|
});
|
|
/**
|
* Namespace: OpenLayers.Easing
|
*
|
* Credits:
|
* Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>
|
*/
|
OpenLayers.Easing = {
|
/**
|
* Create empty functions for all easing methods.
|
*/
|
CLASS_NAME: "OpenLayers.Easing"
|
};
|
|
/**
|
* Namespace: OpenLayers.Easing.Linear
|
*/
|
OpenLayers.Easing.Linear = {
|
|
/**
|
* Function: easeIn
|
*
|
* Parameters:
|
* t - {Float} time
|
* b - {Float} beginning position
|
* c - {Float} total change
|
* d - {Float} duration of the transition
|
*
|
* Returns:
|
* {Float}
|
*/
|
easeIn: function(t, b, c, d) {
|
return c*t/d + b;
|
},
|
|
/**
|
* Function: easeOut
|
*
|
* Parameters:
|
* t - {Float} time
|
* b - {Float} beginning position
|
* c - {Float} total change
|
* d - {Float} duration of the transition
|
*
|
* Returns:
|
* {Float}
|
*/
|
easeOut: function(t, b, c, d) {
|
return c*t/d + b;
|
},
|
|
/**
|
* Function: easeInOut
|
*
|
* Parameters:
|
* t - {Float} time
|
* b - {Float} beginning position
|
* c - {Float} total change
|
* d - {Float} duration of the transition
|
*
|
* Returns:
|
* {Float}
|
*/
|
easeInOut: function(t, b, c, d) {
|
return c*t/d + b;
|
},
|
|
CLASS_NAME: "OpenLayers.Easing.Linear"
|
};
|
|
/**
|
* Namespace: OpenLayers.Easing.Expo
|
*/
|
OpenLayers.Easing.Expo = {
|
|
/**
|
* Function: easeIn
|
*
|
* Parameters:
|
* t - {Float} time
|
* b - {Float} beginning position
|
* c - {Float} total change
|
* d - {Float} duration of the transition
|
*
|
* Returns:
|
* {Float}
|
*/
|
easeIn: function(t, b, c, d) {
|
return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
|
},
|
|
/**
|
* Function: easeOut
|
*
|
* Parameters:
|
* t - {Float} time
|
* b - {Float} beginning position
|
* c - {Float} total change
|
* d - {Float} duration of the transition
|
*
|
* Returns:
|
* {Float}
|
*/
|
easeOut: function(t, b, c, d) {
|
return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
|
},
|
|
/**
|
* Function: easeInOut
|
*
|
* Parameters:
|
* t - {Float} time
|
* b - {Float} beginning position
|
* c - {Float} total change
|
* d - {Float} duration of the transition
|
*
|
* Returns:
|
* {Float}
|
*/
|
easeInOut: function(t, b, c, d) {
|
if (t==0) return b;
|
if (t==d) return b+c;
|
if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
|
return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
|
},
|
|
CLASS_NAME: "OpenLayers.Easing.Expo"
|
};
|
|
/**
|
* Namespace: OpenLayers.Easing.Quad
|
*/
|
OpenLayers.Easing.Quad = {
|
|
/**
|
* Function: easeIn
|
*
|
* Parameters:
|
* t - {Float} time
|
* b - {Float} beginning position
|
* c - {Float} total change
|
* d - {Float} duration of the transition
|
*
|
* Returns:
|
* {Float}
|
*/
|
easeIn: function(t, b, c, d) {
|
return c*(t/=d)*t + b;
|
},
|
|
/**
|
* Function: easeOut
|
*
|
* Parameters:
|
* t - {Float} time
|
* b - {Float} beginning position
|
* c - {Float} total change
|
* d - {Float} duration of the transition
|
*
|
* Returns:
|
* {Float}
|
*/
|
easeOut: function(t, b, c, d) {
|
return -c *(t/=d)*(t-2) + b;
|
},
|
|
/**
|
* Function: easeInOut
|
*
|
* Parameters:
|
* t - {Float} time
|
* b - {Float} beginning position
|
* c - {Float} total change
|
* d - {Float} duration of the transition
|
*
|
* Returns:
|
* {Float}
|
*/
|
easeInOut: function(t, b, c, d) {
|
if ((t/=d/2) < 1) return c/2*t*t + b;
|
return -c/2 * ((--t)*(t-2) - 1) + b;
|
},
|
|
CLASS_NAME: "OpenLayers.Easing.Quad"
|
};
|
/* ======================================================================
|
OpenLayers/Projection.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
* @requires OpenLayers/Util.js
|
*/
|
|
/**
|
* Namespace: OpenLayers.Projection
|
* Methods for coordinate transforms between coordinate systems. By default,
|
* OpenLayers ships with the ability to transform coordinates between
|
* geographic (EPSG:4326) and web or spherical mercator (EPSG:900913 et al.)
|
* coordinate reference systems. See the <transform> method for details
|
* on usage.
|
*
|
* Additional transforms may be added by using the <proj4js at http://proj4js.org/>
|
* library. If the proj4js library is included, the <transform> method
|
* will work between any two coordinate reference systems with proj4js
|
* definitions.
|
*
|
* If the proj4js library is not included, or if you wish to allow transforms
|
* between arbitrary coordinate reference systems, use the <addTransform>
|
* method to register a custom transform method.
|
*/
|
OpenLayers.Projection = OpenLayers.Class({
|
|
/**
|
* Property: proj
|
* {Object} Proj4js.Proj instance.
|
*/
|
proj: null,
|
|
/**
|
* Property: projCode
|
* {String}
|
*/
|
projCode: null,
|
|
/**
|
* Property: titleRegEx
|
* {RegExp} regular expression to strip the title from a proj4js definition
|
*/
|
titleRegEx: /\+title=[^\+]*/,
|
|
/**
|
* Constructor: OpenLayers.Projection
|
* This class offers several methods for interacting with a wrapped
|
* pro4js projection object.
|
*
|
* Parameters:
|
* projCode - {String} A string identifying the Well Known Identifier for
|
* the projection.
|
* options - {Object} An optional object to set additional properties
|
* on the projection.
|
*
|
* Returns:
|
* {<OpenLayers.Projection>} A projection object.
|
*/
|
initialize: function(projCode, options) {
|
OpenLayers.Util.extend(this, options);
|
this.projCode = projCode;
|
if (typeof Proj4js == "object") {
|
this.proj = new Proj4js.Proj(projCode);
|
}
|
},
|
|
/**
|
* APIMethod: getCode
|
* Get the string SRS code.
|
*
|
* Returns:
|
* {String} The SRS code.
|
*/
|
getCode: function() {
|
return this.proj ? this.proj.srsCode : this.projCode;
|
},
|
|
/**
|
* APIMethod: getUnits
|
* Get the units string for the projection -- returns null if
|
* proj4js is not available.
|
*
|
* Returns:
|
* {String} The units abbreviation.
|
*/
|
getUnits: function() {
|
return this.proj ? this.proj.units : null;
|
},
|
|
/**
|
* Method: toString
|
* Convert projection to string (getCode wrapper).
|
*
|
* Returns:
|
* {String} The projection code.
|
*/
|
toString: function() {
|
return this.getCode();
|
},
|
|
/**
|
* Method: equals
|
* Test equality of two projection instances. Determines equality based
|
* soley on the projection code.
|
*
|
* Returns:
|
* {Boolean} The two projections are equivalent.
|
*/
|
equals: function(projection) {
|
var p = projection, equals = false;
|
if (p) {
|
if (!(p instanceof OpenLayers.Projection)) {
|
p = new OpenLayers.Projection(p);
|
}
|
if ((typeof Proj4js == "object") && this.proj.defData && p.proj.defData) {
|
equals = this.proj.defData.replace(this.titleRegEx, "") ==
|
p.proj.defData.replace(this.titleRegEx, "");
|
} else if (p.getCode) {
|
var source = this.getCode(), target = p.getCode();
|
equals = source == target ||
|
!!OpenLayers.Projection.transforms[source] &&
|
OpenLayers.Projection.transforms[source][target] ===
|
OpenLayers.Projection.nullTransform;
|
}
|
}
|
return equals;
|
},
|
|
/* Method: destroy
|
* Destroy projection object.
|
*/
|
destroy: function() {
|
delete this.proj;
|
delete this.projCode;
|
},
|
|
CLASS_NAME: "OpenLayers.Projection"
|
});
|
|
/**
|
* Property: transforms
|
* {Object} Transforms is an object, with from properties, each of which may
|
* have a to property. This allows you to define projections without
|
* requiring support for proj4js to be included.
|
*
|
* This object has keys which correspond to a 'source' projection object. The
|
* keys should be strings, corresponding to the projection.getCode() value.
|
* Each source projection object should have a set of destination projection
|
* keys included in the object.
|
*
|
* Each value in the destination object should be a transformation function,
|
* where the function is expected to be passed an object with a .x and a .y
|
* property. The function should return the object, with the .x and .y
|
* transformed according to the transformation function.
|
*
|
* Note - Properties on this object should not be set directly. To add a
|
* transform method to this object, use the <addTransform> method. For an
|
* example of usage, see the OpenLayers.Layer.SphericalMercator file.
|
*/
|
OpenLayers.Projection.transforms = {};
|
|
/**
|
* APIProperty: defaults
|
* {Object} Defaults for the SRS codes known to OpenLayers (currently
|
* EPSG:4326, CRS:84, urn:ogc:def:crs:EPSG:6.6:4326, EPSG:900913, EPSG:3857,
|
* EPSG:102113 and EPSG:102100). Keys are the SRS code, values are units,
|
* maxExtent (the validity extent for the SRS) and yx (true if this SRS is
|
* known to have a reverse axis order).
|
*/
|
OpenLayers.Projection.defaults = {
|
"EPSG:4326": {
|
units: "degrees",
|
maxExtent: [-180, -90, 180, 90],
|
yx: true
|
},
|
"CRS:84": {
|
units: "degrees",
|
maxExtent: [-180, -90, 180, 90]
|
},
|
"EPSG:900913": {
|
units: "m",
|
maxExtent: [-20037508.34, -20037508.34, 20037508.34, 20037508.34]
|
}
|
};
|
|
/**
|
* APIMethod: addTransform
|
* Set a custom transform method between two projections. Use this method in
|
* cases where the proj4js lib is not available or where custom projections
|
* need to be handled.
|
*
|
* Parameters:
|
* from - {String} The code for the source projection
|
* to - {String} the code for the destination projection
|
* method - {Function} A function that takes a point as an argument and
|
* transforms that point from the source to the destination projection
|
* in place. The original point should be modified.
|
*/
|
OpenLayers.Projection.addTransform = function(from, to, method) {
|
if (method === OpenLayers.Projection.nullTransform) {
|
var defaults = OpenLayers.Projection.defaults[from];
|
if (defaults && !OpenLayers.Projection.defaults[to]) {
|
OpenLayers.Projection.defaults[to] = defaults;
|
}
|
}
|
if(!OpenLayers.Projection.transforms[from]) {
|
OpenLayers.Projection.transforms[from] = {};
|
}
|
OpenLayers.Projection.transforms[from][to] = method;
|
};
|
|
/**
|
* APIMethod: transform
|
* Transform a point coordinate from one projection to another. Note that
|
* the input point is transformed in place.
|
*
|
* Parameters:
|
* point - {<OpenLayers.Geometry.Point> | Object} An object with x and y
|
* properties representing coordinates in those dimensions.
|
* source - {OpenLayers.Projection} Source map coordinate system
|
* dest - {OpenLayers.Projection} Destination map coordinate system
|
*
|
* Returns:
|
* point - {object} A transformed coordinate. The original point is modified.
|
*/
|
OpenLayers.Projection.transform = function(point, source, dest) {
|
if (source && dest) {
|
if (!(source instanceof OpenLayers.Projection)) {
|
source = new OpenLayers.Projection(source);
|
}
|
if (!(dest instanceof OpenLayers.Projection)) {
|
dest = new OpenLayers.Projection(dest);
|
}
|
if (source.proj && dest.proj) {
|
point = Proj4js.transform(source.proj, dest.proj, point);
|
} else {
|
var sourceCode = source.getCode();
|
var destCode = dest.getCode();
|
var transforms = OpenLayers.Projection.transforms;
|
if (transforms[sourceCode] && transforms[sourceCode][destCode]) {
|
transforms[sourceCode][destCode](point);
|
}
|
}
|
}
|
return point;
|
};
|
|
/**
|
* APIFunction: nullTransform
|
* A null transformation - useful for defining projection aliases when
|
* proj4js is not available:
|
*
|
* (code)
|
* OpenLayers.Projection.addTransform("EPSG:3857", "EPSG:900913",
|
* OpenLayers.Projection.nullTransform);
|
* OpenLayers.Projection.addTransform("EPSG:900913", "EPSG:3857",
|
* OpenLayers.Projection.nullTransform);
|
* (end)
|
*/
|
OpenLayers.Projection.nullTransform = function(point) {
|
return point;
|
};
|
|
/**
|
* Note: Transforms for web mercator <-> geographic
|
* OpenLayers recognizes EPSG:3857, EPSG:900913, EPSG:102113 and EPSG:102100.
|
* OpenLayers originally started referring to EPSG:900913 as web mercator.
|
* The EPSG has declared EPSG:3857 to be web mercator.
|
* ArcGIS 10 recognizes the EPSG:3857, EPSG:102113, and EPSG:102100 as
|
* equivalent. See http://blogs.esri.com/Dev/blogs/arcgisserver/archive/2009/11/20/ArcGIS-Online-moving-to-Google-_2F00_-Bing-tiling-scheme_3A00_-What-does-this-mean-for-you_3F00_.aspx#12084.
|
* For geographic, OpenLayers recognizes EPSG:4326, CRS:84 and
|
* urn:ogc:def:crs:EPSG:6.6:4326. OpenLayers also knows about the reverse axis
|
* order for EPSG:4326.
|
*/
|
(function() {
|
|
var pole = 20037508.34;
|
|
function inverseMercator(xy) {
|
xy.x = 180 * xy.x / pole;
|
xy.y = 180 / Math.PI * (2 * Math.atan(Math.exp((xy.y / pole) * Math.PI)) - Math.PI / 2);
|
return xy;
|
}
|
|
function forwardMercator(xy) {
|
xy.x = xy.x * pole / 180;
|
var y = Math.log(Math.tan((90 + xy.y) * Math.PI / 360)) / Math.PI * pole;
|
xy.y = Math.max(-20037508.34, Math.min(y, 20037508.34));
|
return xy;
|
}
|
|
function map(base, codes) {
|
var add = OpenLayers.Projection.addTransform;
|
var same = OpenLayers.Projection.nullTransform;
|
var i, len, code, other, j;
|
for (i=0, len=codes.length; i<len; ++i) {
|
code = codes[i];
|
add(base, code, forwardMercator);
|
add(code, base, inverseMercator);
|
for (j=i+1; j<len; ++j) {
|
other = codes[j];
|
add(code, other, same);
|
add(other, code, same);
|
}
|
}
|
}
|
|
// list of equivalent codes for web mercator
|
var mercator = ["EPSG:900913", "EPSG:3857", "EPSG:102113", "EPSG:102100"],
|
geographic = ["CRS:84", "urn:ogc:def:crs:EPSG:6.6:4326", "EPSG:4326"],
|
i;
|
for (i=mercator.length-1; i>=0; --i) {
|
map(mercator[i], geographic);
|
}
|
for (i=geographic.length-1; i>=0; --i) {
|
map(geographic[i], mercator);
|
}
|
|
})();
|
/* ======================================================================
|
OpenLayers/Map.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
* @requires OpenLayers/Util.js
|
* @requires OpenLayers/Util/vendorPrefix.js
|
* @requires OpenLayers/Events.js
|
* @requires OpenLayers/Tween.js
|
* @requires OpenLayers/Projection.js
|
*/
|
|
/**
|
* Class: OpenLayers.Map
|
* Instances of OpenLayers.Map are interactive maps embedded in a web page.
|
* Create a new map with the <OpenLayers.Map> constructor.
|
*
|
* On their own maps do not provide much functionality. To extend a map
|
* it's necessary to add controls (<OpenLayers.Control>) and
|
* layers (<OpenLayers.Layer>) to the map.
|
*/
|
OpenLayers.Map = OpenLayers.Class({
|
|
/**
|
* Constant: Z_INDEX_BASE
|
* {Object} Base z-indexes for different classes of thing
|
*/
|
Z_INDEX_BASE: {
|
BaseLayer: 100,
|
Overlay: 325,
|
Feature: 725,
|
Popup: 750,
|
Control: 1000
|
},
|
|
/**
|
* APIProperty: events
|
* {<OpenLayers.Events>}
|
*
|
* Register a listener for a particular event with the following syntax:
|
* (code)
|
* map.events.register(type, obj, listener);
|
* (end)
|
*
|
* Listeners will be called with a reference to an event object. The
|
* properties of this event depends on exactly what happened.
|
*
|
* All event objects have at least the following properties:
|
* object - {Object} A reference to map.events.object.
|
* element - {DOMElement} A reference to map.events.element.
|
*
|
* Browser events have the following additional properties:
|
* xy - {<OpenLayers.Pixel>} The pixel location of the event (relative
|
* to the the map viewport).
|
*
|
* Supported map event types:
|
* preaddlayer - triggered before a layer has been added. The event
|
* object will include a *layer* property that references the layer
|
* to be added. When a listener returns "false" the adding will be
|
* aborted.
|
* addlayer - triggered after a layer has been added. The event object
|
* will include a *layer* property that references the added layer.
|
* preremovelayer - triggered before a layer has been removed. The event
|
* object will include a *layer* property that references the layer
|
* to be removed. When a listener returns "false" the removal will be
|
* aborted.
|
* removelayer - triggered after a layer has been removed. The event
|
* object will include a *layer* property that references the removed
|
* layer.
|
* changelayer - triggered after a layer name change, order change,
|
* opacity change, params change, visibility change (actual visibility,
|
* not the layer's visibility property) or attribution change (due to
|
* extent change). Listeners will receive an event object with *layer*
|
* and *property* properties. The *layer* property will be a reference
|
* to the changed layer. The *property* property will be a key to the
|
* changed property (name, order, opacity, params, visibility or
|
* attribution).
|
* movestart - triggered after the start of a drag, pan, or zoom. The event
|
* object may include a *zoomChanged* property that tells whether the
|
* zoom has changed.
|
* move - triggered after each drag, pan, or zoom
|
* moveend - triggered after a drag, pan, or zoom completes
|
* zoomend - triggered after a zoom completes
|
* mouseover - triggered after mouseover the map
|
* mouseout - triggered after mouseout the map
|
* mousemove - triggered after mousemove the map
|
* changebaselayer - triggered after the base layer changes
|
* updatesize - triggered after the <updateSize> method was executed
|
*/
|
|
/**
|
* Property: id
|
* {String} Unique identifier for the map
|
*/
|
id: null,
|
|
/**
|
* Property: fractionalZoom
|
* {Boolean} For a base layer that supports it, allow the map resolution
|
* to be set to a value between one of the values in the resolutions
|
* array. Default is false.
|
*
|
* When fractionalZoom is set to true, it is possible to zoom to
|
* an arbitrary extent. This requires a base layer from a source
|
* that supports requests for arbitrary extents (i.e. not cached
|
* tiles on a regular lattice). This means that fractionalZoom
|
* will not work with commercial layers (Google, Yahoo, VE), layers
|
* using TileCache, or any other pre-cached data sources.
|
*
|
* If you are using fractionalZoom, then you should also use
|
* <getResolutionForZoom> instead of layer.resolutions[zoom] as the
|
* former works for non-integer zoom levels.
|
*/
|
fractionalZoom: false,
|
|
/**
|
* APIProperty: events
|
* {<OpenLayers.Events>} An events object that handles all
|
* events on the map
|
*/
|
events: null,
|
|
/**
|
* APIProperty: allOverlays
|
* {Boolean} Allow the map to function with "overlays" only. Defaults to
|
* false. If true, the lowest layer in the draw order will act as
|
* the base layer. In addition, if set to true, all layers will
|
* have isBaseLayer set to false when they are added to the map.
|
*
|
* Note:
|
* If you set map.allOverlays to true, then you *cannot* use
|
* map.setBaseLayer or layer.setIsBaseLayer. With allOverlays true,
|
* the lowest layer in the draw layer is the base layer. So, to change
|
* the base layer, use <setLayerIndex> or <raiseLayer> to set the layer
|
* index to 0.
|
*/
|
allOverlays: false,
|
|
/**
|
* APIProperty: div
|
* {DOMElement|String} The element that contains the map (or an id for
|
* that element). If the <OpenLayers.Map> constructor is called
|
* with two arguments, this should be provided as the first argument.
|
* Alternatively, the map constructor can be called with the options
|
* object as the only argument. In this case (one argument), a
|
* div property may or may not be provided. If the div property
|
* is not provided, the map can be rendered to a container later
|
* using the <render> method.
|
*
|
* Note:
|
* If you are calling <render> after map construction, do not use
|
* <maxResolution> auto. Instead, divide your <maxExtent> by your
|
* maximum expected dimension.
|
*/
|
div: null,
|
|
/**
|
* Property: dragging
|
* {Boolean} The map is currently being dragged.
|
*/
|
dragging: false,
|
|
/**
|
* Property: size
|
* {<OpenLayers.Size>} Size of the main div (this.div)
|
*/
|
size: null,
|
|
/**
|
* Property: viewPortDiv
|
* {HTMLDivElement} The element that represents the map viewport
|
*/
|
viewPortDiv: null,
|
|
/**
|
* Property: layerContainerOrigin
|
* {<OpenLayers.LonLat>} The lonlat at which the later container was
|
* re-initialized (on-zoom)
|
*/
|
layerContainerOrigin: null,
|
|
/**
|
* Property: layerContainerDiv
|
* {HTMLDivElement} The element that contains the layers.
|
*/
|
layerContainerDiv: null,
|
|
/**
|
* APIProperty: layers
|
* {Array(<OpenLayers.Layer>)} Ordered list of layers in the map
|
*/
|
layers: null,
|
|
/**
|
* APIProperty: controls
|
* {Array(<OpenLayers.Control>)} List of controls associated with the map.
|
*
|
* If not provided in the map options at construction, the map will
|
* by default be given the following controls if present in the build:
|
* - <OpenLayers.Control.Navigation> or <OpenLayers.Control.TouchNavigation>
|
* - <OpenLayers.Control.Zoom> or <OpenLayers.Control.PanZoom>
|
* - <OpenLayers.Control.ArgParser>
|
* - <OpenLayers.Control.Attribution>
|
*/
|
controls: null,
|
|
/**
|
* Property: popups
|
* {Array(<OpenLayers.Popup>)} List of popups associated with the map
|
*/
|
popups: null,
|
|
/**
|
* APIProperty: baseLayer
|
* {<OpenLayers.Layer>} The currently selected base layer. This determines
|
* min/max zoom level, projection, etc.
|
*/
|
baseLayer: null,
|
|
/**
|
* Property: center
|
* {<OpenLayers.LonLat>} The current center of the map
|
*/
|
center: null,
|
|
/**
|
* Property: resolution
|
* {Float} The resolution of the map.
|
*/
|
resolution: null,
|
|
/**
|
* Property: zoom
|
* {Integer} The current zoom level of the map
|
*/
|
zoom: 0,
|
|
/**
|
* Property: panRatio
|
* {Float} The ratio of the current extent within
|
* which panning will tween.
|
*/
|
panRatio: 1.5,
|
|
/**
|
* APIProperty: options
|
* {Object} The options object passed to the class constructor. Read-only.
|
*/
|
options: null,
|
|
// Options
|
|
/**
|
* APIProperty: tileSize
|
* {<OpenLayers.Size>} Set in the map options to override the default tile
|
* size for this map.
|
*/
|
tileSize: null,
|
|
/**
|
* APIProperty: projection
|
* {String} Set in the map options to specify the default projection
|
* for layers added to this map. When using a projection other than EPSG:4326
|
* (CRS:84, Geographic) or EPSG:3857 (EPSG:900913, Web Mercator),
|
* also set maxExtent, maxResolution or resolutions. Default is "EPSG:4326".
|
* Note that the projection of the map is usually determined
|
* by that of the current baseLayer (see <baseLayer> and <getProjectionObject>).
|
*/
|
projection: "EPSG:4326",
|
|
/**
|
* APIProperty: units
|
* {String} The map units. Possible values are 'degrees' (or 'dd'), 'm',
|
* 'ft', 'km', 'mi', 'inches'. Normally taken from the projection.
|
* Only required if both map and layers do not define a projection,
|
* or if they define a projection which does not define units
|
*/
|
units: null,
|
|
/**
|
* APIProperty: resolutions
|
* {Array(Float)} A list of map resolutions (map units per pixel) in
|
* descending order. If this is not set in the layer constructor, it
|
* will be set based on other resolution related properties
|
* (maxExtent, maxResolution, maxScale, etc.).
|
*/
|
resolutions: null,
|
|
/**
|
* APIProperty: maxResolution
|
* {Float} Required if you are not displaying the whole world on a tile
|
* with the size specified in <tileSize>.
|
*/
|
maxResolution: null,
|
|
/**
|
* APIProperty: minResolution
|
* {Float}
|
*/
|
minResolution: null,
|
|
/**
|
* APIProperty: maxScale
|
* {Float}
|
*/
|
maxScale: null,
|
|
/**
|
* APIProperty: minScale
|
* {Float}
|
*/
|
minScale: null,
|
|
/**
|
* APIProperty: maxExtent
|
* {<OpenLayers.Bounds>|Array} If provided as an array, the array
|
* should consist of four values (left, bottom, right, top).
|
* The maximum extent for the map.
|
* Default depends on projection; if this is one of those defined in OpenLayers.Projection.defaults
|
* (EPSG:4326 or web mercator), maxExtent will be set to the value defined there;
|
* else, defaults to null.
|
* To restrict user panning and zooming of the map, use <restrictedExtent> instead.
|
* The value for <maxExtent> will change calculations for tile URLs.
|
*/
|
maxExtent: null,
|
|
/**
|
* APIProperty: minExtent
|
* {<OpenLayers.Bounds>|Array} If provided as an array, the array
|
* should consist of four values (left, bottom, right, top).
|
* The minimum extent for the map. Defaults to null.
|
*/
|
minExtent: null,
|
|
/**
|
* APIProperty: restrictedExtent
|
* {<OpenLayers.Bounds>|Array} If provided as an array, the array
|
* should consist of four values (left, bottom, right, top).
|
* Limit map navigation to this extent where possible.
|
* If a non-null restrictedExtent is set, panning will be restricted
|
* to the given bounds. In addition, zooming to a resolution that
|
* displays more than the restricted extent will center the map
|
* on the restricted extent. If you wish to limit the zoom level
|
* or resolution, use maxResolution.
|
*/
|
restrictedExtent: null,
|
|
/**
|
* APIProperty: numZoomLevels
|
* {Integer} Number of zoom levels for the map. Defaults to 16. Set a
|
* different value in the map options if needed.
|
*/
|
numZoomLevels: 16,
|
|
/**
|
* APIProperty: theme
|
* {String} Relative path to a CSS file from which to load theme styles.
|
* Specify null in the map options (e.g. {theme: null}) if you
|
* want to get cascading style declarations - by putting links to
|
* stylesheets or style declarations directly in your page.
|
*/
|
theme: null,
|
|
/**
|
* APIProperty: displayProjection
|
* {<OpenLayers.Projection>} Requires proj4js support for projections other
|
* than EPSG:4326 or EPSG:900913/EPSG:3857. Projection used by
|
* several controls to display data to user. If this property is set,
|
* it will be set on any control which has a null displayProjection
|
* property at the time the control is added to the map.
|
*/
|
displayProjection: null,
|
|
/**
|
* APIProperty: tileManager
|
* {<OpenLayers.TileManager>|Object} By default, and if the build contains
|
* TileManager.js, the map will use the TileManager to queue image requests
|
* and to cache tile image elements. To create a map without a TileManager
|
* configure the map with tileManager: null. To create a TileManager with
|
* non-default options, supply the options instead or alternatively supply
|
* an instance of {<OpenLayers.TileManager>}.
|
*/
|
|
/**
|
* APIProperty: fallThrough
|
* {Boolean} Should OpenLayers allow events on the map to fall through to
|
* other elements on the page, or should it swallow them? (#457)
|
* Default is to swallow.
|
*/
|
fallThrough: false,
|
|
/**
|
* APIProperty: autoUpdateSize
|
* {Boolean} Should OpenLayers automatically update the size of the map
|
* when the resize event is fired. Default is true.
|
*/
|
autoUpdateSize: true,
|
|
/**
|
* APIProperty: eventListeners
|
* {Object} If set as an option at construction, the eventListeners
|
* object will be registered with <OpenLayers.Events.on>. Object
|
* structure must be a listeners object as shown in the example for
|
* the events.on method.
|
*/
|
eventListeners: null,
|
|
/**
|
* Property: panTween
|
* {<OpenLayers.Tween>} Animated panning tween object, see panTo()
|
*/
|
panTween: null,
|
|
/**
|
* APIProperty: panMethod
|
* {Function} The Easing function to be used for tweening. Default is
|
* OpenLayers.Easing.Expo.easeOut. Setting this to 'null' turns off
|
* animated panning.
|
*/
|
panMethod: OpenLayers.Easing.Expo.easeOut,
|
|
/**
|
* Property: panDuration
|
* {Integer} The number of steps to be passed to the
|
* OpenLayers.Tween.start() method when the map is
|
* panned.
|
* Default is 50.
|
*/
|
panDuration: 50,
|
|
/**
|
* Property: zoomTween
|
* {<OpenLayers.Tween>} Animated zooming tween object, see zoomTo()
|
*/
|
zoomTween: null,
|
|
/**
|
* APIProperty: zoomMethod
|
* {Function} The Easing function to be used for tweening. Default is
|
* OpenLayers.Easing.Quad.easeOut. Setting this to 'null' turns off
|
* animated zooming.
|
*/
|
zoomMethod: OpenLayers.Easing.Quad.easeOut,
|
|
/**
|
* Property: zoomDuration
|
* {Integer} The number of steps to be passed to the
|
* OpenLayers.Tween.start() method when the map is zoomed.
|
* Default is 20.
|
*/
|
zoomDuration: 20,
|
|
/**
|
* Property: paddingForPopups
|
* {<OpenLayers.Bounds>} Outside margin of the popup. Used to prevent
|
* the popup from getting too close to the map border.
|
*/
|
paddingForPopups : null,
|
|
/**
|
* Property: layerContainerOriginPx
|
* {Object} Cached object representing the layer container origin (in pixels).
|
*/
|
layerContainerOriginPx: null,
|
|
/**
|
* Property: minPx
|
* {Object} An object with a 'x' and 'y' values that is the lower
|
* left of maxExtent in viewport pixel space.
|
* Used to verify in moveByPx that the new location we're moving to
|
* is valid. It is also used in the getLonLatFromViewPortPx function
|
* of Layer.
|
*/
|
minPx: null,
|
|
/**
|
* Property: maxPx
|
* {Object} An object with a 'x' and 'y' values that is the top
|
* right of maxExtent in viewport pixel space.
|
* Used to verify in moveByPx that the new location we're moving to
|
* is valid.
|
*/
|
maxPx: null,
|
|
/**
|
* Constructor: OpenLayers.Map
|
* Constructor for a new OpenLayers.Map instance. There are two possible
|
* ways to call the map constructor. See the examples below.
|
*
|
* Parameters:
|
* div - {DOMElement|String} The element or id of an element in your page
|
* that will contain the map. May be omitted if the <div> option is
|
* provided or if you intend to call the <render> method later.
|
* options - {Object} Optional object with properties to tag onto the map.
|
*
|
* Valid options (in addition to the listed API properties):
|
* center - {<OpenLayers.LonLat>|Array} The default initial center of the map.
|
* If provided as array, the first value is the x coordinate,
|
* and the 2nd value is the y coordinate.
|
* Only specify if <layers> is provided.
|
* Note that if an ArgParser/Permalink control is present,
|
* and the querystring contains coordinates, center will be set
|
* by that, and this option will be ignored.
|
* zoom - {Number} The initial zoom level for the map. Only specify if
|
* <layers> is provided.
|
* Note that if an ArgParser/Permalink control is present,
|
* and the querystring contains a zoom level, zoom will be set
|
* by that, and this option will be ignored.
|
* extent - {<OpenLayers.Bounds>|Array} The initial extent of the map.
|
* If provided as an array, the array should consist of
|
* four values (left, bottom, right, top).
|
* Only specify if <center> and <zoom> are not provided.
|
*
|
* Examples:
|
* (code)
|
* // create a map with default options in an element with the id "map1"
|
* var map = new OpenLayers.Map("map1");
|
*
|
* // create a map with non-default options in an element with id "map2"
|
* var options = {
|
* projection: "EPSG:3857",
|
* maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000),
|
* center: new OpenLayers.LonLat(-12356463.476333, 5621521.4854095)
|
* };
|
* var map = new OpenLayers.Map("map2", options);
|
*
|
* // map with non-default options - same as above but with a single argument,
|
* // a restricted extent, and using arrays for bounds and center
|
* var map = new OpenLayers.Map({
|
* div: "map_id",
|
* projection: "EPSG:3857",
|
* maxExtent: [-18924313.432222, -15538711.094146, 18924313.432222, 15538711.094146],
|
* restrictedExtent: [-13358338.893333, -9608371.5085962, 13358338.893333, 9608371.5085962],
|
* center: [-12356463.476333, 5621521.4854095]
|
* });
|
*
|
* // create a map without a reference to a container - call render later
|
* var map = new OpenLayers.Map({
|
* projection: "EPSG:3857",
|
* maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000)
|
* });
|
* (end)
|
*/
|
initialize: function (div, options) {
|
|
// If only one argument is provided, check if it is an object.
|
if(arguments.length === 1 && typeof div === "object") {
|
options = div;
|
div = options && options.div;
|
}
|
|
// Simple-type defaults are set in class definition.
|
// Now set complex-type defaults
|
this.tileSize = new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH,
|
OpenLayers.Map.TILE_HEIGHT);
|
|
this.paddingForPopups = new OpenLayers.Bounds(15, 15, 15, 15);
|
|
this.theme = OpenLayers._getScriptLocation() +
|
'theme/default/style.css';
|
|
// backup original options
|
this.options = OpenLayers.Util.extend({}, options);
|
|
// now override default options
|
OpenLayers.Util.extend(this, options);
|
|
var projCode = this.projection instanceof OpenLayers.Projection ?
|
this.projection.projCode : this.projection;
|
OpenLayers.Util.applyDefaults(this, OpenLayers.Projection.defaults[projCode]);
|
|
// allow extents and center to be arrays
|
if (this.maxExtent && !(this.maxExtent instanceof OpenLayers.Bounds)) {
|
this.maxExtent = new OpenLayers.Bounds(this.maxExtent);
|
}
|
if (this.minExtent && !(this.minExtent instanceof OpenLayers.Bounds)) {
|
this.minExtent = new OpenLayers.Bounds(this.minExtent);
|
}
|
if (this.restrictedExtent && !(this.restrictedExtent instanceof OpenLayers.Bounds)) {
|
this.restrictedExtent = new OpenLayers.Bounds(this.restrictedExtent);
|
}
|
if (this.center && !(this.center instanceof OpenLayers.LonLat)) {
|
this.center = new OpenLayers.LonLat(this.center);
|
}
|
|
// initialize layers array
|
this.layers = [];
|
|
this.id = OpenLayers.Util.createUniqueID("OpenLayers.Map_");
|
|
this.div = OpenLayers.Util.getElement(div);
|
if(!this.div) {
|
this.div = document.createElement("div");
|
this.div.style.height = "1px";
|
this.div.style.width = "1px";
|
}
|
|
OpenLayers.Element.addClass(this.div, 'olMap');
|
|
// the viewPortDiv is the outermost div we modify
|
var id = this.id + "_OpenLayers_ViewPort";
|
this.viewPortDiv = OpenLayers.Util.createDiv(id, null, null, null,
|
"relative", null,
|
"hidden");
|
this.viewPortDiv.style.width = "100%";
|
this.viewPortDiv.style.height = "100%";
|
this.viewPortDiv.className = "olMapViewport";
|
this.div.appendChild(this.viewPortDiv);
|
|
this.events = new OpenLayers.Events(
|
this, this.viewPortDiv, null, this.fallThrough,
|
{includeXY: true}
|
);
|
|
if (OpenLayers.TileManager && this.tileManager !== null) {
|
if (!(this.tileManager instanceof OpenLayers.TileManager)) {
|
this.tileManager = new OpenLayers.TileManager(this.tileManager);
|
}
|
this.tileManager.addMap(this);
|
}
|
|
// the layerContainerDiv is the one that holds all the layers
|
id = this.id + "_OpenLayers_Container";
|
this.layerContainerDiv = OpenLayers.Util.createDiv(id);
|
this.layerContainerDiv.style.zIndex=this.Z_INDEX_BASE['Popup']-1;
|
this.layerContainerOriginPx = {x: 0, y: 0};
|
this.applyTransform();
|
|
this.viewPortDiv.appendChild(this.layerContainerDiv);
|
|
this.updateSize();
|
if(this.eventListeners instanceof Object) {
|
this.events.on(this.eventListeners);
|
}
|
|
if (this.autoUpdateSize === true) {
|
// updateSize on catching the window's resize
|
// Note that this is ok, as updateSize() does nothing if the
|
// map's size has not actually changed.
|
this.updateSizeDestroy = OpenLayers.Function.bind(this.updateSize,
|
this);
|
OpenLayers.Event.observe(window, 'resize',
|
this.updateSizeDestroy);
|
}
|
|
// only append link stylesheet if the theme property is set
|
if(this.theme) {
|
// check existing links for equivalent url
|
var addNode = true;
|
var nodes = document.getElementsByTagName('link');
|
for(var i=0, len=nodes.length; i<len; ++i) {
|
if(OpenLayers.Util.isEquivalentUrl(nodes.item(i).href,
|
this.theme)) {
|
addNode = false;
|
break;
|
}
|
}
|
// only add a new node if one with an equivalent url hasn't already
|
// been added
|
if(addNode) {
|
var cssNode = document.createElement('link');
|
cssNode.setAttribute('rel', 'stylesheet');
|
cssNode.setAttribute('type', 'text/css');
|
cssNode.setAttribute('href', this.theme);
|
document.getElementsByTagName('head')[0].appendChild(cssNode);
|
}
|
}
|
|
if (this.controls == null) { // default controls
|
this.controls = [];
|
if (OpenLayers.Control != null) { // running full or lite?
|
// Navigation or TouchNavigation depending on what is in build
|
if (OpenLayers.Control.Navigation) {
|
this.controls.push(new OpenLayers.Control.Navigation());
|
} else if (OpenLayers.Control.TouchNavigation) {
|
this.controls.push(new OpenLayers.Control.TouchNavigation());
|
}
|
if (OpenLayers.Control.Zoom) {
|
this.controls.push(new OpenLayers.Control.Zoom());
|
} else if (OpenLayers.Control.PanZoom) {
|
this.controls.push(new OpenLayers.Control.PanZoom());
|
}
|
|
if (OpenLayers.Control.ArgParser) {
|
this.controls.push(new OpenLayers.Control.ArgParser());
|
}
|
if (OpenLayers.Control.Attribution) {
|
this.controls.push(new OpenLayers.Control.Attribution());
|
}
|
}
|
}
|
|
for(var i=0, len=this.controls.length; i<len; i++) {
|
this.addControlToMap(this.controls[i]);
|
}
|
|
this.popups = [];
|
|
this.unloadDestroy = OpenLayers.Function.bind(this.destroy, this);
|
|
|
// always call map.destroy()
|
OpenLayers.Event.observe(window, 'unload', this.unloadDestroy);
|
|
// add any initial layers
|
if (options && options.layers) {
|
/**
|
* If you have set options.center, the map center property will be
|
* set at this point. However, since setCenter has not been called,
|
* addLayers gets confused. So we delete the map center in this
|
* case. Because the check below uses options.center, it will
|
* be properly set below.
|
*/
|
delete this.center;
|
delete this.zoom;
|
this.addLayers(options.layers);
|
// set center (and optionally zoom)
|
if (options.center && !this.getCenter()) {
|
// zoom can be undefined here
|
this.setCenter(options.center, options.zoom);
|
}
|
}
|
|
if (this.panMethod) {
|
this.panTween = new OpenLayers.Tween(this.panMethod);
|
}
|
if (this.zoomMethod && this.applyTransform.transform) {
|
this.zoomTween = new OpenLayers.Tween(this.zoomMethod);
|
}
|
},
|
|
/**
|
* APIMethod: getViewport
|
* Get the DOMElement representing the view port.
|
*
|
* Returns:
|
* {DOMElement}
|
*/
|
getViewport: function() {
|
return this.viewPortDiv;
|
},
|
|
/**
|
* APIMethod: render
|
* Render the map to a specified container.
|
*
|
* Parameters:
|
* div - {String|DOMElement} The container that the map should be rendered
|
* to. If different than the current container, the map viewport
|
* will be moved from the current to the new container.
|
*/
|
render: function(div) {
|
this.div = OpenLayers.Util.getElement(div);
|
OpenLayers.Element.addClass(this.div, 'olMap');
|
this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);
|
this.div.appendChild(this.viewPortDiv);
|
this.updateSize();
|
},
|
|
/**
|
* Method: unloadDestroy
|
* Function that is called to destroy the map on page unload. stored here
|
* so that if map is manually destroyed, we can unregister this.
|
*/
|
unloadDestroy: null,
|
|
/**
|
* Method: updateSizeDestroy
|
* When the map is destroyed, we need to stop listening to updateSize
|
* events: this method stores the function we need to unregister in
|
* non-IE browsers.
|
*/
|
updateSizeDestroy: null,
|
|
/**
|
* APIMethod: destroy
|
* Destroy this map.
|
* Note that if you are using an application which removes a container
|
* of the map from the DOM, you need to ensure that you destroy the
|
* map *before* this happens; otherwise, the page unload handler
|
* will fail because the DOM elements that map.destroy() wants
|
* to clean up will be gone. (See
|
* http://trac.osgeo.org/openlayers/ticket/2277 for more information).
|
* This will apply to GeoExt and also to other applications which
|
* modify the DOM of the container of the OpenLayers Map.
|
*/
|
destroy:function() {
|
// if unloadDestroy is null, we've already been destroyed
|
if (!this.unloadDestroy) {
|
return false;
|
}
|
|
// make sure panning doesn't continue after destruction
|
if(this.panTween) {
|
this.panTween.stop();
|
this.panTween = null;
|
}
|
// make sure zooming doesn't continue after destruction
|
if(this.zoomTween) {
|
this.zoomTween.stop();
|
this.zoomTween = null;
|
}
|
|
// map has been destroyed. dont do it again!
|
OpenLayers.Event.stopObserving(window, 'unload', this.unloadDestroy);
|
this.unloadDestroy = null;
|
|
if (this.updateSizeDestroy) {
|
OpenLayers.Event.stopObserving(window, 'resize',
|
this.updateSizeDestroy);
|
}
|
|
this.paddingForPopups = null;
|
|
if (this.controls != null) {
|
for (var i = this.controls.length - 1; i>=0; --i) {
|
this.controls[i].destroy();
|
}
|
this.controls = null;
|
}
|
if (this.layers != null) {
|
for (var i = this.layers.length - 1; i>=0; --i) {
|
//pass 'false' to destroy so that map wont try to set a new
|
// baselayer after each baselayer is removed
|
this.layers[i].destroy(false);
|
}
|
this.layers = null;
|
}
|
if (this.viewPortDiv && this.viewPortDiv.parentNode) {
|
this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);
|
}
|
this.viewPortDiv = null;
|
|
if (this.tileManager) {
|
this.tileManager.removeMap(this);
|
this.tileManager = null;
|
}
|
|
if(this.eventListeners) {
|
this.events.un(this.eventListeners);
|
this.eventListeners = null;
|
}
|
this.events.destroy();
|
this.events = null;
|
|
this.options = null;
|
},
|
|
/**
|
* APIMethod: setOptions
|
* Change the map options
|
*
|
* Parameters:
|
* options - {Object} Hashtable of options to tag to the map
|
*/
|
setOptions: function(options) {
|
var updatePxExtent = this.minPx &&
|
options.restrictedExtent != this.restrictedExtent;
|
OpenLayers.Util.extend(this, options);
|
// force recalculation of minPx and maxPx
|
updatePxExtent && this.moveTo(this.getCachedCenter(), this.zoom, {
|
forceZoomChange: true
|
});
|
},
|
|
/**
|
* APIMethod: getTileSize
|
* Get the tile size for the map
|
*
|
* Returns:
|
* {<OpenLayers.Size>}
|
*/
|
getTileSize: function() {
|
return this.tileSize;
|
},
|
|
|
/**
|
* APIMethod: getBy
|
* Get a list of objects given a property and a match item.
|
*
|
* Parameters:
|
* array - {String} A property on the map whose value is an array.
|
* property - {String} A property on each item of the given array.
|
* match - {String | Object} A string to match. Can also be a regular
|
* expression literal or object. In addition, it can be any object
|
* with a method named test. For reqular expressions or other, if
|
* match.test(map[array][i][property]) evaluates to true, the item will
|
* be included in the array returned. If no items are found, an empty
|
* array is returned.
|
*
|
* Returns:
|
* {Array} An array of items where the given property matches the given
|
* criteria.
|
*/
|
getBy: function(array, property, match) {
|
var test = (typeof match.test == "function");
|
var found = OpenLayers.Array.filter(this[array], function(item) {
|
return item[property] == match || (test && match.test(item[property]));
|
});
|
return found;
|
},
|
|
/**
|
* APIMethod: getLayersBy
|
* Get a list of layers with properties matching the given criteria.
|
*
|
* Parameters:
|
* property - {String} A layer property to be matched.
|
* match - {String | Object} A string to match. Can also be a regular
|
* expression literal or object. In addition, it can be any object
|
* with a method named test. For reqular expressions or other, if
|
* match.test(layer[property]) evaluates to true, the layer will be
|
* included in the array returned. If no layers are found, an empty
|
* array is returned.
|
*
|
* Returns:
|
* {Array(<OpenLayers.Layer>)} A list of layers matching the given criteria.
|
* An empty array is returned if no matches are found.
|
*/
|
getLayersBy: function(property, match) {
|
return this.getBy("layers", property, match);
|
},
|
|
/**
|
* APIMethod: getLayersByName
|
* Get a list of layers with names matching the given name.
|
*
|
* Parameters:
|
* match - {String | Object} A layer name. The name can also be a regular
|
* expression literal or object. In addition, it can be any object
|
* with a method named test. For reqular expressions or other, if
|
* name.test(layer.name) evaluates to true, the layer will be included
|
* in the list of layers returned. If no layers are found, an empty
|
* array is returned.
|
*
|
* Returns:
|
* {Array(<OpenLayers.Layer>)} A list of layers matching the given name.
|
* An empty array is returned if no matches are found.
|
*/
|
getLayersByName: function(match) {
|
return this.getLayersBy("name", match);
|
},
|
|
/**
|
* APIMethod: getLayersByClass
|
* Get a list of layers of a given class (CLASS_NAME).
|
*
|
* Parameters:
|
* match - {String | Object} A layer class name. The match can also be a
|
* regular expression literal or object. In addition, it can be any
|
* object with a method named test. For reqular expressions or other,
|
* if type.test(layer.CLASS_NAME) evaluates to true, the layer will
|
* be included in the list of layers returned. If no layers are
|
* found, an empty array is returned.
|
*
|
* Returns:
|
* {Array(<OpenLayers.Layer>)} A list of layers matching the given class.
|
* An empty array is returned if no matches are found.
|
*/
|
getLayersByClass: function(match) {
|
return this.getLayersBy("CLASS_NAME", match);
|
},
|
|
/**
|
* APIMethod: getControlsBy
|
* Get a list of controls with properties matching the given criteria.
|
*
|
* Parameters:
|
* property - {String} A control property to be matched.
|
* match - {String | Object} A string to match. Can also be a regular
|
* expression literal or object. In addition, it can be any object
|
* with a method named test. For reqular expressions or other, if
|
* match.test(layer[property]) evaluates to true, the layer will be
|
* included in the array returned. If no layers are found, an empty
|
* array is returned.
|
*
|
* Returns:
|
* {Array(<OpenLayers.Control>)} A list of controls matching the given
|
* criteria. An empty array is returned if no matches are found.
|
*/
|
getControlsBy: function(property, match) {
|
return this.getBy("controls", property, match);
|
},
|
|
/**
|
* APIMethod: getControlsByClass
|
* Get a list of controls of a given class (CLASS_NAME).
|
*
|
* Parameters:
|
* match - {String | Object} A control class name. The match can also be a
|
* regular expression literal or object. In addition, it can be any
|
* object with a method named test. For reqular expressions or other,
|
* if type.test(control.CLASS_NAME) evaluates to true, the control will
|
* be included in the list of controls returned. If no controls are
|
* found, an empty array is returned.
|
*
|
* Returns:
|
* {Array(<OpenLayers.Control>)} A list of controls matching the given class.
|
* An empty array is returned if no matches are found.
|
*/
|
getControlsByClass: function(match) {
|
return this.getControlsBy("CLASS_NAME", match);
|
},
|
|
/********************************************************/
|
/* */
|
/* Layer Functions */
|
/* */
|
/* The following functions deal with adding and */
|
/* removing Layers to and from the Map */
|
/* */
|
/********************************************************/
|
|
/**
|
* APIMethod: getLayer
|
* Get a layer based on its id
|
*
|
* Parameters:
|
* id - {String} A layer id
|
*
|
* Returns:
|
* {<OpenLayers.Layer>} The Layer with the corresponding id from the map's
|
* layer collection, or null if not found.
|
*/
|
getLayer: function(id) {
|
var foundLayer = null;
|
for (var i=0, len=this.layers.length; i<len; i++) {
|
var layer = this.layers[i];
|
if (layer.id == id) {
|
foundLayer = layer;
|
break;
|
}
|
}
|
return foundLayer;
|
},
|
|
/**
|
* Method: setLayerZIndex
|
*
|
* Parameters:
|
* layer - {<OpenLayers.Layer>}
|
* zIdx - {int}
|
*/
|
setLayerZIndex: function (layer, zIdx) {
|
layer.setZIndex(
|
this.Z_INDEX_BASE[layer.isBaseLayer ? 'BaseLayer' : 'Overlay']
|
+ zIdx * 5 );
|
},
|
|
/**
|
* Method: resetLayersZIndex
|
* Reset each layer's z-index based on layer's array index
|
*/
|
resetLayersZIndex: function() {
|
for (var i=0, len=this.layers.length; i<len; i++) {
|
var layer = this.layers[i];
|
this.setLayerZIndex(layer, i);
|
}
|
},
|
|
/**
|
* APIMethod: addLayer
|
*
|
* Parameters:
|
* layer - {<OpenLayers.Layer>}
|
*
|
* Returns:
|
* {Boolean} True if the layer has been added to the map.
|
*/
|
addLayer: function (layer) {
|
for(var i = 0, len = this.layers.length; i < len; i++) {
|
if (this.layers[i] == layer) {
|
return false;
|
}
|
}
|
if (this.events.triggerEvent("preaddlayer", {layer: layer}) === false) {
|
return false;
|
}
|
if(this.allOverlays) {
|
layer.isBaseLayer = false;
|
}
|
|
layer.div.className = "olLayerDiv";
|
layer.div.style.overflow = "";
|
this.setLayerZIndex(layer, this.layers.length);
|
|
if (layer.isFixed) {
|
this.viewPortDiv.appendChild(layer.div);
|
} else {
|
this.layerContainerDiv.appendChild(layer.div);
|
}
|
this.layers.push(layer);
|
layer.setMap(this);
|
|
if (layer.isBaseLayer || (this.allOverlays && !this.baseLayer)) {
|
if (this.baseLayer == null) {
|
// set the first baselaye we add as the baselayer
|
this.setBaseLayer(layer);
|
} else {
|
layer.setVisibility(false);
|
}
|
} else {
|
layer.redraw();
|
}
|
|
this.events.triggerEvent("addlayer", {layer: layer});
|
layer.events.triggerEvent("added", {map: this, layer: layer});
|
layer.afterAdd();
|
|
return true;
|
},
|
|
/**
|
* APIMethod: addLayers
|
*
|
* Parameters:
|
* layers - {Array(<OpenLayers.Layer>)}
|
*/
|
addLayers: function (layers) {
|
for (var i=0, len=layers.length; i<len; i++) {
|
this.addLayer(layers[i]);
|
}
|
},
|
|
/**
|
* APIMethod: removeLayer
|
* Removes a layer from the map by removing its visual element (the
|
* layer.div property), then removing it from the map's internal list
|
* of layers, setting the layer's map property to null.
|
*
|
* a "removelayer" event is triggered.
|
*
|
* very worthy of mention is that simply removing a layer from a map
|
* will not cause the removal of any popups which may have been created
|
* by the layer. this is due to the fact that it was decided at some
|
* point that popups would not belong to layers. thus there is no way
|
* for us to know here to which layer the popup belongs.
|
*
|
* A simple solution to this is simply to call destroy() on the layer.
|
* the default OpenLayers.Layer class's destroy() function
|
* automatically takes care to remove itself from whatever map it has
|
* been attached to.
|
*
|
* The correct solution is for the layer itself to register an
|
* event-handler on "removelayer" and when it is called, if it
|
* recognizes itself as the layer being removed, then it cycles through
|
* its own personal list of popups, removing them from the map.
|
*
|
* Parameters:
|
* layer - {<OpenLayers.Layer>}
|
* setNewBaseLayer - {Boolean} Default is true
|
*/
|
removeLayer: function(layer, setNewBaseLayer) {
|
if (this.events.triggerEvent("preremovelayer", {layer: layer}) === false) {
|
return;
|
}
|
if (setNewBaseLayer == null) {
|
setNewBaseLayer = true;
|
}
|
|
if (layer.isFixed) {
|
this.viewPortDiv.removeChild(layer.div);
|
} else {
|
this.layerContainerDiv.removeChild(layer.div);
|
}
|
OpenLayers.Util.removeItem(this.layers, layer);
|
layer.removeMap(this);
|
layer.map = null;
|
|
// if we removed the base layer, need to set a new one
|
if(this.baseLayer == layer) {
|
this.baseLayer = null;
|
if(setNewBaseLayer) {
|
for(var i=0, len=this.layers.length; i<len; i++) {
|
var iLayer = this.layers[i];
|
if (iLayer.isBaseLayer || this.allOverlays) {
|
this.setBaseLayer(iLayer);
|
break;
|
}
|
}
|
}
|
}
|
|
this.resetLayersZIndex();
|
|
this.events.triggerEvent("removelayer", {layer: layer});
|
layer.events.triggerEvent("removed", {map: this, layer: layer});
|
},
|
|
/**
|
* APIMethod: getNumLayers
|
*
|
* Returns:
|
* {Int} The number of layers attached to the map.
|
*/
|
getNumLayers: function () {
|
return this.layers.length;
|
},
|
|
/**
|
* APIMethod: getLayerIndex
|
*
|
* Parameters:
|
* layer - {<OpenLayers.Layer>}
|
*
|
* Returns:
|
* {Integer} The current (zero-based) index of the given layer in the map's
|
* layer stack. Returns -1 if the layer isn't on the map.
|
*/
|
getLayerIndex: function (layer) {
|
return OpenLayers.Util.indexOf(this.layers, layer);
|
},
|
|
/**
|
* APIMethod: setLayerIndex
|
* Move the given layer to the specified (zero-based) index in the layer
|
* list, changing its z-index in the map display. Use
|
* map.getLayerIndex() to find out the current index of a layer. Note
|
* that this cannot (or at least should not) be effectively used to
|
* raise base layers above overlays.
|
*
|
* Parameters:
|
* layer - {<OpenLayers.Layer>}
|
* idx - {int}
|
*/
|
setLayerIndex: function (layer, idx) {
|
var base = this.getLayerIndex(layer);
|
if (idx < 0) {
|
idx = 0;
|
} else if (idx > this.layers.length) {
|
idx = this.layers.length;
|
}
|
if (base != idx) {
|
this.layers.splice(base, 1);
|
this.layers.splice(idx, 0, layer);
|
for (var i=0, len=this.layers.length; i<len; i++) {
|
this.setLayerZIndex(this.layers[i], i);
|
}
|
this.events.triggerEvent("changelayer", {
|
layer: layer, property: "order"
|
});
|
if(this.allOverlays) {
|
if(idx === 0) {
|
this.setBaseLayer(layer);
|
} else if(this.baseLayer !== this.layers[0]) {
|
this.setBaseLayer(this.layers[0]);
|
}
|
}
|
}
|
},
|
|
/**
|
* APIMethod: raiseLayer
|
* Change the index of the given layer by delta. If delta is positive,
|
* the layer is moved up the map's layer stack; if delta is negative,
|
* the layer is moved down. Again, note that this cannot (or at least
|
* should not) be effectively used to raise base layers above overlays.
|
*
|
* Paremeters:
|
* layer - {<OpenLayers.Layer>}
|
* delta - {int}
|
*/
|
raiseLayer: function (layer, delta) {
|
var idx = this.getLayerIndex(layer) + delta;
|
this.setLayerIndex(layer, idx);
|
},
|
|
/**
|
* APIMethod: setBaseLayer
|
* Allows user to specify one of the currently-loaded layers as the Map's
|
* new base layer.
|
*
|
* Parameters:
|
* newBaseLayer - {<OpenLayers.Layer>}
|
*/
|
setBaseLayer: function(newBaseLayer) {
|
|
if (newBaseLayer != this.baseLayer) {
|
|
// ensure newBaseLayer is already loaded
|
if (OpenLayers.Util.indexOf(this.layers, newBaseLayer) != -1) {
|
|
// preserve center and scale when changing base layers
|
var center = this.getCachedCenter();
|
var newResolution = OpenLayers.Util.getResolutionFromScale(
|
this.getScale(), newBaseLayer.units
|
);
|
|
// make the old base layer invisible
|
if (this.baseLayer != null && !this.allOverlays) {
|
this.baseLayer.setVisibility(false);
|
}
|
|
// set new baselayer
|
this.baseLayer = newBaseLayer;
|
|
if(!this.allOverlays || this.baseLayer.visibility) {
|
this.baseLayer.setVisibility(true);
|
// Layer may previously have been visible but not in range.
|
// In this case we need to redraw it to make it visible.
|
if (this.baseLayer.inRange === false) {
|
this.baseLayer.redraw();
|
}
|
}
|
|
// recenter the map
|
if (center != null) {
|
// new zoom level derived from old scale
|
var newZoom = this.getZoomForResolution(
|
newResolution || this.resolution, true
|
);
|
// zoom and force zoom change
|
this.setCenter(center, newZoom, false, true);
|
}
|
|
this.events.triggerEvent("changebaselayer", {
|
layer: this.baseLayer
|
});
|
}
|
}
|
},
|
|
|
/********************************************************/
|
/* */
|
/* Control Functions */
|
/* */
|
/* The following functions deal with adding and */
|
/* removing Controls to and from the Map */
|
/* */
|
/********************************************************/
|
|
/**
|
* APIMethod: addControl
|
* Add the passed over control to the map. Optionally
|
* position the control at the given pixel.
|
*
|
* Parameters:
|
* control - {<OpenLayers.Control>}
|
* px - {<OpenLayers.Pixel>}
|
*/
|
addControl: function (control, px) {
|
this.controls.push(control);
|
this.addControlToMap(control, px);
|
},
|
|
/**
|
* APIMethod: addControls
|
* Add all of the passed over controls to the map.
|
* You can pass over an optional second array
|
* with pixel-objects to position the controls.
|
* The indices of the two arrays should match and
|
* you can add null as pixel for those controls
|
* you want to be autopositioned.
|
*
|
* Parameters:
|
* controls - {Array(<OpenLayers.Control>)}
|
* pixels - {Array(<OpenLayers.Pixel>)}
|
*/
|
addControls: function (controls, pixels) {
|
var pxs = (arguments.length === 1) ? [] : pixels;
|
for (var i=0, len=controls.length; i<len; i++) {
|
var ctrl = controls[i];
|
var px = (pxs[i]) ? pxs[i] : null;
|
this.addControl( ctrl, px );
|
}
|
},
|
|
/**
|
* Method: addControlToMap
|
*
|
* Parameters:
|
*
|
* control - {<OpenLayers.Control>}
|
* px - {<OpenLayers.Pixel>}
|
*/
|
addControlToMap: function (control, px) {
|
// If a control doesn't have a div at this point, it belongs in the
|
// viewport.
|
control.outsideViewport = (control.div != null);
|
|
// If the map has a displayProjection, and the control doesn't, set
|
// the display projection.
|
if (this.displayProjection && !control.displayProjection) {
|
control.displayProjection = this.displayProjection;
|
}
|
|
control.setMap(this);
|
var div = control.draw(px);
|
if (div) {
|
if(!control.outsideViewport) {
|
div.style.zIndex = this.Z_INDEX_BASE['Control'] +
|
this.controls.length;
|
this.viewPortDiv.appendChild( div );
|
}
|
}
|
if(control.autoActivate) {
|
control.activate();
|
}
|
},
|
|
/**
|
* APIMethod: getControl
|
*
|
* Parameters:
|
* id - {String} ID of the control to return.
|
*
|
* Returns:
|
* {<OpenLayers.Control>} The control from the map's list of controls
|
* which has a matching 'id'. If none found,
|
* returns null.
|
*/
|
getControl: function (id) {
|
var returnControl = null;
|
for(var i=0, len=this.controls.length; i<len; i++) {
|
var control = this.controls[i];
|
if (control.id == id) {
|
returnControl = control;
|
break;
|
}
|
}
|
return returnControl;
|
},
|
|
/**
|
* APIMethod: removeControl
|
* Remove a control from the map. Removes the control both from the map
|
* object's internal array of controls, as well as from the map's
|
* viewPort (assuming the control was not added outsideViewport)
|
*
|
* Parameters:
|
* control - {<OpenLayers.Control>} The control to remove.
|
*/
|
removeControl: function (control) {
|
//make sure control is non-null and actually part of our map
|
if ( (control) && (control == this.getControl(control.id)) ) {
|
if (control.div && (control.div.parentNode == this.viewPortDiv)) {
|
this.viewPortDiv.removeChild(control.div);
|
}
|
OpenLayers.Util.removeItem(this.controls, control);
|
}
|
},
|
|
/********************************************************/
|
/* */
|
/* Popup Functions */
|
/* */
|
/* The following functions deal with adding and */
|
/* removing Popups to and from the Map */
|
/* */
|
/********************************************************/
|
|
/**
|
* APIMethod: addPopup
|
*
|
* Parameters:
|
* popup - {<OpenLayers.Popup>}
|
* exclusive - {Boolean} If true, closes all other popups first
|
*/
|
addPopup: function(popup, exclusive) {
|
|
if (exclusive) {
|
//remove all other popups from screen
|
for (var i = this.popups.length - 1; i >= 0; --i) {
|
this.removePopup(this.popups[i]);
|
}
|
}
|
|
popup.map = this;
|
this.popups.push(popup);
|
var popupDiv = popup.draw();
|
if (popupDiv) {
|
popupDiv.style.zIndex = this.Z_INDEX_BASE['Popup'] +
|
this.popups.length;
|
this.layerContainerDiv.appendChild(popupDiv);
|
}
|
},
|
|
/**
|
* APIMethod: removePopup
|
*
|
* Parameters:
|
* popup - {<OpenLayers.Popup>}
|
*/
|
removePopup: function(popup) {
|
OpenLayers.Util.removeItem(this.popups, popup);
|
if (popup.div) {
|
try { this.layerContainerDiv.removeChild(popup.div); }
|
catch (e) { } // Popups sometimes apparently get disconnected
|
// from the layerContainerDiv, and cause complaints.
|
}
|
popup.map = null;
|
},
|
|
/********************************************************/
|
/* */
|
/* Container Div Functions */
|
/* */
|
/* The following functions deal with the access to */
|
/* and maintenance of the size of the container div */
|
/* */
|
/********************************************************/
|
|
/**
|
* APIMethod: getSize
|
*
|
* Returns:
|
* {<OpenLayers.Size>} An <OpenLayers.Size> object that represents the
|
* size, in pixels, of the div into which OpenLayers
|
* has been loaded.
|
* Note - A clone() of this locally cached variable is
|
* returned, so as not to allow users to modify it.
|
*/
|
getSize: function () {
|
var size = null;
|
if (this.size != null) {
|
size = this.size.clone();
|
}
|
return size;
|
},
|
|
/**
|
* APIMethod: updateSize
|
* This function should be called by any external code which dynamically
|
* changes the size of the map div (because mozilla wont let us catch
|
* the "onresize" for an element)
|
*/
|
updateSize: function() {
|
// the div might have moved on the page, also
|
var newSize = this.getCurrentSize();
|
if (newSize && !isNaN(newSize.h) && !isNaN(newSize.w)) {
|
this.events.clearMouseCache();
|
var oldSize = this.getSize();
|
if (oldSize == null) {
|
this.size = oldSize = newSize;
|
}
|
if (!newSize.equals(oldSize)) {
|
|
// store the new size
|
this.size = newSize;
|
|
//notify layers of mapresize
|
for(var i=0, len=this.layers.length; i<len; i++) {
|
this.layers[i].onMapResize();
|
}
|
|
var center = this.getCachedCenter();
|
|
if (this.baseLayer != null && center != null) {
|
var zoom = this.getZoom();
|
this.zoom = null;
|
this.setCenter(center, zoom);
|
}
|
|
}
|
}
|
this.events.triggerEvent("updatesize");
|
},
|
|
/**
|
* Method: getCurrentSize
|
*
|
* Returns:
|
* {<OpenLayers.Size>} A new <OpenLayers.Size> object with the dimensions
|
* of the map div
|
*/
|
getCurrentSize: function() {
|
|
var size = new OpenLayers.Size(this.div.clientWidth,
|
this.div.clientHeight);
|
|
if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {
|
size.w = this.div.offsetWidth;
|
size.h = this.div.offsetHeight;
|
}
|
if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {
|
size.w = parseInt(this.div.style.width);
|
size.h = parseInt(this.div.style.height);
|
}
|
return size;
|
},
|
|
/**
|
* Method: calculateBounds
|
*
|
* Parameters:
|
* center - {<OpenLayers.LonLat>} Default is this.getCenter()
|
* resolution - {float} Default is this.getResolution()
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} A bounds based on resolution, center, and
|
* current mapsize.
|
*/
|
calculateBounds: function(center, resolution) {
|
|
var extent = null;
|
|
if (center == null) {
|
center = this.getCachedCenter();
|
}
|
if (resolution == null) {
|
resolution = this.getResolution();
|
}
|
|
if ((center != null) && (resolution != null)) {
|
var halfWDeg = (this.size.w * resolution) / 2;
|
var halfHDeg = (this.size.h * resolution) / 2;
|
|
extent = new OpenLayers.Bounds(center.lon - halfWDeg,
|
center.lat - halfHDeg,
|
center.lon + halfWDeg,
|
center.lat + halfHDeg);
|
}
|
|
return extent;
|
},
|
|
|
/********************************************************/
|
/* */
|
/* Zoom, Center, Pan Functions */
|
/* */
|
/* The following functions handle the validation, */
|
/* getting and setting of the Zoom Level and Center */
|
/* as well as the panning of the Map */
|
/* */
|
/********************************************************/
|
/**
|
* APIMethod: getCenter
|
*
|
* Returns:
|
* {<OpenLayers.LonLat>}
|
*/
|
getCenter: function () {
|
var center = null;
|
var cachedCenter = this.getCachedCenter();
|
if (cachedCenter) {
|
center = cachedCenter.clone();
|
}
|
return center;
|
},
|
|
/**
|
* Method: getCachedCenter
|
*
|
* Returns:
|
* {<OpenLayers.LonLat>}
|
*/
|
getCachedCenter: function() {
|
if (!this.center && this.size) {
|
this.center = this.getLonLatFromViewPortPx({
|
x: this.size.w / 2,
|
y: this.size.h / 2
|
});
|
}
|
return this.center;
|
},
|
|
/**
|
* APIMethod: getZoom
|
*
|
* Returns:
|
* {Integer}
|
*/
|
getZoom: function () {
|
return this.zoom;
|
},
|
|
/**
|
* APIMethod: pan
|
* Allows user to pan by a value of screen pixels
|
*
|
* Parameters:
|
* dx - {Integer}
|
* dy - {Integer}
|
* options - {Object} Options to configure panning:
|
* - *animate* {Boolean} Use panTo instead of setCenter. Default is true.
|
* - *dragging* {Boolean} Call setCenter with dragging true. Default is
|
* false.
|
*/
|
pan: function(dx, dy, options) {
|
options = OpenLayers.Util.applyDefaults(options, {
|
animate: true,
|
dragging: false
|
});
|
if (options.dragging) {
|
if (dx != 0 || dy != 0) {
|
this.moveByPx(dx, dy);
|
}
|
} else {
|
// getCenter
|
var centerPx = this.getViewPortPxFromLonLat(this.getCachedCenter());
|
|
// adjust
|
var newCenterPx = centerPx.add(dx, dy);
|
|
if (this.dragging || !newCenterPx.equals(centerPx)) {
|
var newCenterLonLat = this.getLonLatFromViewPortPx(newCenterPx);
|
if (options.animate) {
|
this.panTo(newCenterLonLat);
|
} else {
|
this.moveTo(newCenterLonLat);
|
if(this.dragging) {
|
this.dragging = false;
|
this.events.triggerEvent("moveend");
|
}
|
}
|
}
|
}
|
|
},
|
|
/**
|
* APIMethod: panTo
|
* Allows user to pan to a new lonlat
|
* If the new lonlat is in the current extent the map will slide smoothly
|
*
|
* Parameters:
|
* lonlat - {<OpenLayers.LonLat>}
|
*/
|
panTo: function(lonlat) {
|
if (this.panTween && this.getExtent().scale(this.panRatio).containsLonLat(lonlat)) {
|
var center = this.getCachedCenter();
|
|
// center will not change, don't do nothing
|
if (lonlat.equals(center)) {
|
return;
|
}
|
|
var from = this.getPixelFromLonLat(center);
|
var to = this.getPixelFromLonLat(lonlat);
|
var vector = { x: to.x - from.x, y: to.y - from.y };
|
var last = { x: 0, y: 0 };
|
|
this.panTween.start( { x: 0, y: 0 }, vector, this.panDuration, {
|
callbacks: {
|
eachStep: OpenLayers.Function.bind(function(px) {
|
var x = px.x - last.x,
|
y = px.y - last.y;
|
this.moveByPx(x, y);
|
last.x = Math.round(px.x);
|
last.y = Math.round(px.y);
|
}, this),
|
done: OpenLayers.Function.bind(function(px) {
|
this.moveTo(lonlat);
|
this.dragging = false;
|
this.events.triggerEvent("moveend");
|
}, this)
|
}
|
});
|
} else {
|
this.setCenter(lonlat);
|
}
|
},
|
|
/**
|
* APIMethod: setCenter
|
* Set the map center (and optionally, the zoom level).
|
*
|
* Parameters:
|
* lonlat - {<OpenLayers.LonLat>|Array} The new center location.
|
* If provided as array, the first value is the x coordinate,
|
* and the 2nd value is the y coordinate.
|
* zoom - {Integer} Optional zoom level.
|
* dragging - {Boolean} Specifies whether or not to trigger
|
* movestart/end events
|
* forceZoomChange - {Boolean} Specifies whether or not to trigger zoom
|
* change events (needed on baseLayer change)
|
*
|
* TBD: reconsider forceZoomChange in 3.0
|
*/
|
setCenter: function(lonlat, zoom, dragging, forceZoomChange) {
|
if (this.panTween) {
|
this.panTween.stop();
|
}
|
if (this.zoomTween) {
|
this.zoomTween.stop();
|
}
|
this.moveTo(lonlat, zoom, {
|
'dragging': dragging,
|
'forceZoomChange': forceZoomChange
|
});
|
},
|
|
/**
|
* Method: moveByPx
|
* Drag the map by pixels.
|
*
|
* Parameters:
|
* dx - {Number}
|
* dy - {Number}
|
*/
|
moveByPx: function(dx, dy) {
|
var hw = this.size.w / 2;
|
var hh = this.size.h / 2;
|
var x = hw + dx;
|
var y = hh + dy;
|
var wrapDateLine = this.baseLayer.wrapDateLine;
|
var xRestriction = 0;
|
var yRestriction = 0;
|
if (this.restrictedExtent) {
|
xRestriction = hw;
|
yRestriction = hh;
|
// wrapping the date line makes no sense for restricted extents
|
wrapDateLine = false;
|
}
|
dx = wrapDateLine ||
|
x <= this.maxPx.x - xRestriction &&
|
x >= this.minPx.x + xRestriction ? Math.round(dx) : 0;
|
dy = y <= this.maxPx.y - yRestriction &&
|
y >= this.minPx.y + yRestriction ? Math.round(dy) : 0;
|
if (dx || dy) {
|
if (!this.dragging) {
|
this.dragging = true;
|
this.events.triggerEvent("movestart");
|
}
|
this.center = null;
|
if (dx) {
|
this.layerContainerOriginPx.x -= dx;
|
this.minPx.x -= dx;
|
this.maxPx.x -= dx;
|
}
|
if (dy) {
|
this.layerContainerOriginPx.y -= dy;
|
this.minPx.y -= dy;
|
this.maxPx.y -= dy;
|
}
|
this.applyTransform();
|
var layer, i, len;
|
for (i=0, len=this.layers.length; i<len; ++i) {
|
layer = this.layers[i];
|
if (layer.visibility &&
|
(layer === this.baseLayer || layer.inRange)) {
|
layer.moveByPx(dx, dy);
|
layer.events.triggerEvent("move");
|
}
|
}
|
this.events.triggerEvent("move");
|
}
|
},
|
|
/**
|
* Method: adjustZoom
|
*
|
* Parameters:
|
* zoom - {Number} The zoom level to adjust
|
*
|
* Returns:
|
* {Integer} Adjusted zoom level that shows a map not wider than its
|
* <baseLayer>'s maxExtent.
|
*/
|
adjustZoom: function(zoom) {
|
if (this.baseLayer && this.baseLayer.wrapDateLine) {
|
var resolution, resolutions = this.baseLayer.resolutions,
|
maxResolution = this.getMaxExtent().getWidth() / this.size.w;
|
if (this.getResolutionForZoom(zoom) > maxResolution) {
|
if (this.fractionalZoom) {
|
zoom = this.getZoomForResolution(maxResolution);
|
} else {
|
for (var i=zoom|0, ii=resolutions.length; i<ii; ++i) {
|
if (resolutions[i] <= maxResolution) {
|
zoom = i;
|
break;
|
}
|
}
|
}
|
}
|
}
|
return zoom;
|
},
|
|
/**
|
* APIMethod: getMinZoom
|
* Returns the minimum zoom level for the current map view. If the base
|
* layer is configured with <wrapDateLine> set to true, this will be the
|
* first zoom level that shows no more than one world width in the current
|
* map viewport. Components that rely on this value (e.g. zoom sliders)
|
* should also listen to the map's "updatesize" event and call this method
|
* in the "updatesize" listener.
|
*
|
* Returns:
|
* {Number} Minimum zoom level that shows a map not wider than its
|
* <baseLayer>'s maxExtent. This is an Integer value, unless the map is
|
* configured with <fractionalZoom> set to true.
|
*/
|
getMinZoom: function() {
|
return this.adjustZoom(0);
|
},
|
|
/**
|
* Method: moveTo
|
*
|
* Parameters:
|
* lonlat - {<OpenLayers.LonLat>}
|
* zoom - {Integer}
|
* options - {Object}
|
*/
|
moveTo: function(lonlat, zoom, options) {
|
if (lonlat != null && !(lonlat instanceof OpenLayers.LonLat)) {
|
lonlat = new OpenLayers.LonLat(lonlat);
|
}
|
if (!options) {
|
options = {};
|
}
|
if (zoom != null) {
|
zoom = parseFloat(zoom);
|
if (!this.fractionalZoom) {
|
zoom = Math.round(zoom);
|
}
|
}
|
var requestedZoom = zoom;
|
zoom = this.adjustZoom(zoom);
|
if (zoom !== requestedZoom) {
|
// zoom was adjusted, so keep old lonlat to avoid panning
|
lonlat = this.getCenter();
|
}
|
// dragging is false by default
|
var dragging = options.dragging || this.dragging;
|
// forceZoomChange is false by default
|
var forceZoomChange = options.forceZoomChange;
|
|
if (!this.getCachedCenter() && !this.isValidLonLat(lonlat)) {
|
lonlat = this.maxExtent.getCenterLonLat();
|
this.center = lonlat.clone();
|
}
|
|
if(this.restrictedExtent != null) {
|
// In 3.0, decide if we want to change interpretation of maxExtent.
|
if(lonlat == null) {
|
lonlat = this.center;
|
}
|
if(zoom == null) {
|
zoom = this.getZoom();
|
}
|
var resolution = this.getResolutionForZoom(zoom);
|
var extent = this.calculateBounds(lonlat, resolution);
|
if(!this.restrictedExtent.containsBounds(extent)) {
|
var maxCenter = this.restrictedExtent.getCenterLonLat();
|
if(extent.getWidth() > this.restrictedExtent.getWidth()) {
|
lonlat = new OpenLayers.LonLat(maxCenter.lon, lonlat.lat);
|
} else if(extent.left < this.restrictedExtent.left) {
|
lonlat = lonlat.add(this.restrictedExtent.left -
|
extent.left, 0);
|
} else if(extent.right > this.restrictedExtent.right) {
|
lonlat = lonlat.add(this.restrictedExtent.right -
|
extent.right, 0);
|
}
|
if(extent.getHeight() > this.restrictedExtent.getHeight()) {
|
lonlat = new OpenLayers.LonLat(lonlat.lon, maxCenter.lat);
|
} else if(extent.bottom < this.restrictedExtent.bottom) {
|
lonlat = lonlat.add(0, this.restrictedExtent.bottom -
|
extent.bottom);
|
}
|
else if(extent.top > this.restrictedExtent.top) {
|
lonlat = lonlat.add(0, this.restrictedExtent.top -
|
extent.top);
|
}
|
}
|
}
|
|
var zoomChanged = forceZoomChange || (
|
(this.isValidZoomLevel(zoom)) &&
|
(zoom != this.getZoom()) );
|
|
var centerChanged = (this.isValidLonLat(lonlat)) &&
|
(!lonlat.equals(this.center));
|
|
// if neither center nor zoom will change, no need to do anything
|
if (zoomChanged || centerChanged || dragging) {
|
dragging || this.events.triggerEvent("movestart", {
|
zoomChanged: zoomChanged
|
});
|
|
if (centerChanged) {
|
if (!zoomChanged && this.center) {
|
// if zoom hasnt changed, just slide layerContainer
|
// (must be done before setting this.center to new value)
|
this.centerLayerContainer(lonlat);
|
}
|
this.center = lonlat.clone();
|
}
|
|
var res = zoomChanged ?
|
this.getResolutionForZoom(zoom) : this.getResolution();
|
// (re)set the layerContainerDiv's location
|
if (zoomChanged || this.layerContainerOrigin == null) {
|
this.layerContainerOrigin = this.getCachedCenter();
|
this.layerContainerOriginPx.x = 0;
|
this.layerContainerOriginPx.y = 0;
|
this.applyTransform();
|
var maxExtent = this.getMaxExtent({restricted: true});
|
var maxExtentCenter = maxExtent.getCenterLonLat();
|
var lonDelta = this.center.lon - maxExtentCenter.lon;
|
var latDelta = maxExtentCenter.lat - this.center.lat;
|
var extentWidth = Math.round(maxExtent.getWidth() / res);
|
var extentHeight = Math.round(maxExtent.getHeight() / res);
|
this.minPx = {
|
x: (this.size.w - extentWidth) / 2 - lonDelta / res,
|
y: (this.size.h - extentHeight) / 2 - latDelta / res
|
};
|
this.maxPx = {
|
x: this.minPx.x + Math.round(maxExtent.getWidth() / res),
|
y: this.minPx.y + Math.round(maxExtent.getHeight() / res)
|
};
|
}
|
|
if (zoomChanged) {
|
this.zoom = zoom;
|
this.resolution = res;
|
}
|
|
var bounds = this.getExtent();
|
|
//send the move call to the baselayer and all the overlays
|
|
if(this.baseLayer.visibility) {
|
this.baseLayer.moveTo(bounds, zoomChanged, options.dragging);
|
options.dragging || this.baseLayer.events.triggerEvent(
|
"moveend", {zoomChanged: zoomChanged}
|
);
|
}
|
|
bounds = this.baseLayer.getExtent();
|
|
for (var i=this.layers.length-1; i>=0; --i) {
|
var layer = this.layers[i];
|
if (layer !== this.baseLayer && !layer.isBaseLayer) {
|
var inRange = layer.calculateInRange();
|
if (layer.inRange != inRange) {
|
// the inRange property has changed. If the layer is
|
// no longer in range, we turn it off right away. If
|
// the layer is no longer out of range, the moveTo
|
// call below will turn on the layer.
|
layer.inRange = inRange;
|
if (!inRange) {
|
layer.display(false);
|
}
|
this.events.triggerEvent("changelayer", {
|
layer: layer, property: "visibility"
|
});
|
}
|
if (inRange && layer.visibility) {
|
layer.moveTo(bounds, zoomChanged, options.dragging);
|
options.dragging || layer.events.triggerEvent(
|
"moveend", {zoomChanged: zoomChanged}
|
);
|
}
|
}
|
}
|
|
this.events.triggerEvent("move");
|
dragging || this.events.triggerEvent("moveend");
|
|
if (zoomChanged) {
|
//redraw popups
|
for (var i=0, len=this.popups.length; i<len; i++) {
|
this.popups[i].updatePosition();
|
}
|
this.events.triggerEvent("zoomend");
|
}
|
}
|
},
|
|
/**
|
* Method: centerLayerContainer
|
* This function takes care to recenter the layerContainerDiv.
|
*
|
* Parameters:
|
* lonlat - {<OpenLayers.LonLat>}
|
*/
|
centerLayerContainer: function (lonlat) {
|
var originPx = this.getViewPortPxFromLonLat(this.layerContainerOrigin);
|
var newPx = this.getViewPortPxFromLonLat(lonlat);
|
|
if ((originPx != null) && (newPx != null)) {
|
var oldLeft = this.layerContainerOriginPx.x;
|
var oldTop = this.layerContainerOriginPx.y;
|
var newLeft = Math.round(originPx.x - newPx.x);
|
var newTop = Math.round(originPx.y - newPx.y);
|
this.applyTransform(
|
(this.layerContainerOriginPx.x = newLeft),
|
(this.layerContainerOriginPx.y = newTop));
|
var dx = oldLeft - newLeft;
|
var dy = oldTop - newTop;
|
this.minPx.x -= dx;
|
this.maxPx.x -= dx;
|
this.minPx.y -= dy;
|
this.maxPx.y -= dy;
|
}
|
},
|
|
/**
|
* Method: isValidZoomLevel
|
*
|
* Parameters:
|
* zoomLevel - {Integer}
|
*
|
* Returns:
|
* {Boolean} Whether or not the zoom level passed in is non-null and
|
* within the min/max range of zoom levels.
|
*/
|
isValidZoomLevel: function(zoomLevel) {
|
return ( (zoomLevel != null) &&
|
(zoomLevel >= 0) &&
|
(zoomLevel < this.getNumZoomLevels()) );
|
},
|
|
/**
|
* Method: isValidLonLat
|
*
|
* Parameters:
|
* lonlat - {<OpenLayers.LonLat>}
|
*
|
* Returns:
|
* {Boolean} Whether or not the lonlat passed in is non-null and within
|
* the maxExtent bounds
|
*/
|
isValidLonLat: function(lonlat) {
|
var valid = false;
|
if (lonlat != null) {
|
var maxExtent = this.getMaxExtent();
|
var worldBounds = this.baseLayer.wrapDateLine && maxExtent;
|
valid = maxExtent.containsLonLat(lonlat, {worldBounds: worldBounds});
|
}
|
return valid;
|
},
|
|
/********************************************************/
|
/* */
|
/* Layer Options */
|
/* */
|
/* Accessor functions to Layer Options parameters */
|
/* */
|
/********************************************************/
|
|
/**
|
* APIMethod: getProjection
|
* This method returns a string representing the projection. In
|
* the case of projection support, this will be the srsCode which
|
* is loaded -- otherwise it will simply be the string value that
|
* was passed to the projection at startup.
|
*
|
* FIXME: In 3.0, we will remove getProjectionObject, and instead
|
* return a Projection object from this function.
|
*
|
* Returns:
|
* {String} The Projection string from the base layer or null.
|
*/
|
getProjection: function() {
|
var projection = this.getProjectionObject();
|
return projection ? projection.getCode() : null;
|
},
|
|
/**
|
* APIMethod: getProjectionObject
|
* Returns the projection obect from the baselayer.
|
*
|
* Returns:
|
* {<OpenLayers.Projection>} The Projection of the base layer.
|
*/
|
getProjectionObject: function() {
|
var projection = null;
|
if (this.baseLayer != null) {
|
projection = this.baseLayer.projection;
|
}
|
return projection;
|
},
|
|
/**
|
* APIMethod: getMaxResolution
|
*
|
* Returns:
|
* {String} The Map's Maximum Resolution
|
*/
|
getMaxResolution: function() {
|
var maxResolution = null;
|
if (this.baseLayer != null) {
|
maxResolution = this.baseLayer.maxResolution;
|
}
|
return maxResolution;
|
},
|
|
/**
|
* APIMethod: getMaxExtent
|
*
|
* Parameters:
|
* options - {Object}
|
*
|
* Allowed Options:
|
* restricted - {Boolean} If true, returns restricted extent (if it is
|
* available.)
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} The maxExtent property as set on the current
|
* baselayer, unless the 'restricted' option is set, in which case
|
* the 'restrictedExtent' option from the map is returned (if it
|
* is set).
|
*/
|
getMaxExtent: function (options) {
|
var maxExtent = null;
|
if(options && options.restricted && this.restrictedExtent){
|
maxExtent = this.restrictedExtent;
|
} else if (this.baseLayer != null) {
|
maxExtent = this.baseLayer.maxExtent;
|
}
|
return maxExtent;
|
},
|
|
/**
|
* APIMethod: getNumZoomLevels
|
*
|
* Returns:
|
* {Integer} The total number of zoom levels that can be displayed by the
|
* current baseLayer.
|
*/
|
getNumZoomLevels: function() {
|
var numZoomLevels = null;
|
if (this.baseLayer != null) {
|
numZoomLevels = this.baseLayer.numZoomLevels;
|
}
|
return numZoomLevels;
|
},
|
|
/********************************************************/
|
/* */
|
/* Baselayer Functions */
|
/* */
|
/* The following functions, all publicly exposed */
|
/* in the API?, are all merely wrappers to the */
|
/* the same calls on whatever layer is set as */
|
/* the current base layer */
|
/* */
|
/********************************************************/
|
|
/**
|
* APIMethod: getExtent
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat
|
* bounds of the current viewPort.
|
* If no baselayer is set, returns null.
|
*/
|
getExtent: function () {
|
var extent = null;
|
if (this.baseLayer != null) {
|
extent = this.baseLayer.getExtent();
|
}
|
return extent;
|
},
|
|
/**
|
* APIMethod: getResolution
|
*
|
* Returns:
|
* {Float} The current resolution of the map.
|
* If no baselayer is set, returns null.
|
*/
|
getResolution: function () {
|
var resolution = null;
|
if (this.baseLayer != null) {
|
resolution = this.baseLayer.getResolution();
|
} else if(this.allOverlays === true && this.layers.length > 0) {
|
// while adding the 1st layer to the map in allOverlays mode,
|
// this.baseLayer is not set yet when we need the resolution
|
// for calculateInRange.
|
resolution = this.layers[0].getResolution();
|
}
|
return resolution;
|
},
|
|
/**
|
* APIMethod: getUnits
|
*
|
* Returns:
|
* {Float} The current units of the map.
|
* If no baselayer is set, returns null.
|
*/
|
getUnits: function () {
|
var units = null;
|
if (this.baseLayer != null) {
|
units = this.baseLayer.units;
|
}
|
return units;
|
},
|
|
/**
|
* APIMethod: getScale
|
*
|
* Returns:
|
* {Float} The current scale denominator of the map.
|
* If no baselayer is set, returns null.
|
*/
|
getScale: function () {
|
var scale = null;
|
if (this.baseLayer != null) {
|
var res = this.getResolution();
|
var units = this.baseLayer.units;
|
scale = OpenLayers.Util.getScaleFromResolution(res, units);
|
}
|
return scale;
|
},
|
|
|
/**
|
* APIMethod: getZoomForExtent
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
* closest - {Boolean} Find the zoom level that most closely fits the
|
* specified bounds. Note that this may result in a zoom that does
|
* not exactly contain the entire extent.
|
* Default is false.
|
*
|
* Returns:
|
* {Integer} A suitable zoom level for the specified bounds.
|
* If no baselayer is set, returns null.
|
*/
|
getZoomForExtent: function (bounds, closest) {
|
var zoom = null;
|
if (this.baseLayer != null) {
|
zoom = this.baseLayer.getZoomForExtent(bounds, closest);
|
}
|
return zoom;
|
},
|
|
/**
|
* APIMethod: getResolutionForZoom
|
*
|
* Parameters:
|
* zoom - {Float}
|
*
|
* Returns:
|
* {Float} A suitable resolution for the specified zoom. If no baselayer
|
* is set, returns null.
|
*/
|
getResolutionForZoom: function(zoom) {
|
var resolution = null;
|
if(this.baseLayer) {
|
resolution = this.baseLayer.getResolutionForZoom(zoom);
|
}
|
return resolution;
|
},
|
|
/**
|
* APIMethod: getZoomForResolution
|
*
|
* Parameters:
|
* resolution - {Float}
|
* closest - {Boolean} Find the zoom level that corresponds to the absolute
|
* closest resolution, which may result in a zoom whose corresponding
|
* resolution is actually smaller than we would have desired (if this
|
* is being called from a getZoomForExtent() call, then this means that
|
* the returned zoom index might not actually contain the entire
|
* extent specified... but it'll be close).
|
* Default is false.
|
*
|
* Returns:
|
* {Integer} A suitable zoom level for the specified resolution.
|
* If no baselayer is set, returns null.
|
*/
|
getZoomForResolution: function(resolution, closest) {
|
var zoom = null;
|
if (this.baseLayer != null) {
|
zoom = this.baseLayer.getZoomForResolution(resolution, closest);
|
}
|
return zoom;
|
},
|
|
/********************************************************/
|
/* */
|
/* Zooming Functions */
|
/* */
|
/* The following functions, all publicly exposed */
|
/* in the API, are all merely wrappers to the */
|
/* the setCenter() function */
|
/* */
|
/********************************************************/
|
|
/**
|
* APIMethod: zoomTo
|
* Zoom to a specific zoom level. Zooming will be animated unless the map
|
* is configured with {zoomMethod: null}. To zoom without animation, use
|
* <setCenter> without a lonlat argument.
|
*
|
* Parameters:
|
* zoom - {Integer}
|
*/
|
zoomTo: function(zoom, xy) {
|
// non-API arguments:
|
// xy - {<OpenLayers.Pixel>} optional zoom origin
|
|
var map = this;
|
if (map.isValidZoomLevel(zoom)) {
|
if (map.baseLayer.wrapDateLine) {
|
zoom = map.adjustZoom(zoom);
|
}
|
if (map.zoomTween) {
|
var currentRes = map.getResolution(),
|
targetRes = map.getResolutionForZoom(zoom),
|
start = {scale: 1},
|
end = {scale: currentRes / targetRes};
|
if (map.zoomTween.playing && map.zoomTween.duration < 3 * map.zoomDuration) {
|
// update the end scale, and reuse the running zoomTween
|
map.zoomTween.finish = {
|
scale: map.zoomTween.finish.scale * end.scale
|
};
|
} else {
|
if (!xy) {
|
var size = map.getSize();
|
xy = {x: size.w / 2, y: size.h / 2};
|
}
|
map.zoomTween.start(start, end, map.zoomDuration, {
|
minFrameRate: 50, // don't spend much time zooming
|
callbacks: {
|
eachStep: function(data) {
|
var containerOrigin = map.layerContainerOriginPx,
|
scale = data.scale,
|
dx = ((scale - 1) * (containerOrigin.x - xy.x)) | 0,
|
dy = ((scale - 1) * (containerOrigin.y - xy.y)) | 0;
|
map.applyTransform(containerOrigin.x + dx, containerOrigin.y + dy, scale);
|
},
|
done: function(data) {
|
map.applyTransform();
|
var resolution = map.getResolution() / data.scale,
|
zoom = map.getZoomForResolution(resolution, true)
|
map.moveTo(map.getZoomTargetCenter(xy, resolution), zoom, true);
|
}
|
}
|
});
|
}
|
} else {
|
var center = xy ?
|
map.getZoomTargetCenter(xy, map.getResolutionForZoom(zoom)) :
|
null;
|
map.setCenter(center, zoom);
|
}
|
}
|
},
|
|
/**
|
* APIMethod: zoomIn
|
*
|
*/
|
zoomIn: function() {
|
this.zoomTo(this.getZoom() + 1);
|
},
|
|
/**
|
* APIMethod: zoomOut
|
*
|
*/
|
zoomOut: function() {
|
this.zoomTo(this.getZoom() - 1);
|
},
|
|
/**
|
* APIMethod: zoomToExtent
|
* Zoom to the passed in bounds, recenter
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>|Array} If provided as an array, the array
|
* should consist of four values (left, bottom, right, top).
|
* closest - {Boolean} Find the zoom level that most closely fits the
|
* specified bounds. Note that this may result in a zoom that does
|
* not exactly contain the entire extent.
|
* Default is false.
|
*
|
*/
|
zoomToExtent: function(bounds, closest) {
|
if (!(bounds instanceof OpenLayers.Bounds)) {
|
bounds = new OpenLayers.Bounds(bounds);
|
}
|
var center = bounds.getCenterLonLat();
|
if (this.baseLayer.wrapDateLine) {
|
var maxExtent = this.getMaxExtent();
|
|
//fix straddling bounds (in the case of a bbox that straddles the
|
// dateline, it's left and right boundaries will appear backwards.
|
// we fix this by allowing a right value that is greater than the
|
// max value at the dateline -- this allows us to pass a valid
|
// bounds to calculate zoom)
|
//
|
bounds = bounds.clone();
|
while (bounds.right < bounds.left) {
|
bounds.right += maxExtent.getWidth();
|
}
|
//if the bounds was straddling (see above), then the center point
|
// we got from it was wrong. So we take our new bounds and ask it
|
// for the center.
|
//
|
center = bounds.getCenterLonLat().wrapDateLine(maxExtent);
|
}
|
this.setCenter(center, this.getZoomForExtent(bounds, closest));
|
},
|
|
/**
|
* APIMethod: zoomToMaxExtent
|
* Zoom to the full extent and recenter.
|
*
|
* Parameters:
|
* options - {Object}
|
*
|
* Allowed Options:
|
* restricted - {Boolean} True to zoom to restricted extent if it is
|
* set. Defaults to true.
|
*/
|
zoomToMaxExtent: function(options) {
|
//restricted is true by default
|
var restricted = (options) ? options.restricted : true;
|
|
var maxExtent = this.getMaxExtent({
|
'restricted': restricted
|
});
|
this.zoomToExtent(maxExtent);
|
},
|
|
/**
|
* APIMethod: zoomToScale
|
* Zoom to a specified scale
|
*
|
* Parameters:
|
* scale - {float}
|
* closest - {Boolean} Find the zoom level that most closely fits the
|
* specified scale. Note that this may result in a zoom that does
|
* not exactly contain the entire extent.
|
* Default is false.
|
*
|
*/
|
zoomToScale: function(scale, closest) {
|
var res = OpenLayers.Util.getResolutionFromScale(scale,
|
this.baseLayer.units);
|
|
var halfWDeg = (this.size.w * res) / 2;
|
var halfHDeg = (this.size.h * res) / 2;
|
var center = this.getCachedCenter();
|
|
var extent = new OpenLayers.Bounds(center.lon - halfWDeg,
|
center.lat - halfHDeg,
|
center.lon + halfWDeg,
|
center.lat + halfHDeg);
|
this.zoomToExtent(extent, closest);
|
},
|
|
/********************************************************/
|
/* */
|
/* Translation Functions */
|
/* */
|
/* The following functions translate between */
|
/* LonLat, LayerPx, and ViewPortPx */
|
/* */
|
/********************************************************/
|
|
//
|
// TRANSLATION: LonLat <-> ViewPortPx
|
//
|
|
/**
|
* Method: getLonLatFromViewPortPx
|
*
|
* Parameters:
|
* viewPortPx - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or
|
* an object with a 'x'
|
* and 'y' properties.
|
*
|
* Returns:
|
* {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view
|
* port <OpenLayers.Pixel>, translated into lon/lat
|
* by the current base layer.
|
*/
|
getLonLatFromViewPortPx: function (viewPortPx) {
|
var lonlat = null;
|
if (this.baseLayer != null) {
|
lonlat = this.baseLayer.getLonLatFromViewPortPx(viewPortPx);
|
}
|
return lonlat;
|
},
|
|
/**
|
* APIMethod: getViewPortPxFromLonLat
|
*
|
* Parameters:
|
* lonlat - {<OpenLayers.LonLat>}
|
*
|
* Returns:
|
* {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in
|
* <OpenLayers.LonLat>, translated into view port
|
* pixels by the current base layer.
|
*/
|
getViewPortPxFromLonLat: function (lonlat) {
|
var px = null;
|
if (this.baseLayer != null) {
|
px = this.baseLayer.getViewPortPxFromLonLat(lonlat);
|
}
|
return px;
|
},
|
|
/**
|
* Method: getZoomTargetCenter
|
*
|
* Parameters:
|
* xy - {<OpenLayers.Pixel>} The zoom origin pixel location on the screen
|
* resolution - {Float} The resolution we want to get the center for
|
*
|
* Returns:
|
* {<OpenLayers.LonLat>} The location of the map center after the
|
* transformation described by the origin xy and the target resolution.
|
*/
|
getZoomTargetCenter: function (xy, resolution) {
|
var lonlat = null,
|
size = this.getSize(),
|
deltaX = size.w/2 - xy.x,
|
deltaY = xy.y - size.h/2,
|
zoomPoint = this.getLonLatFromPixel(xy);
|
if (zoomPoint) {
|
lonlat = new OpenLayers.LonLat(
|
zoomPoint.lon + deltaX * resolution,
|
zoomPoint.lat + deltaY * resolution
|
);
|
}
|
return lonlat;
|
},
|
|
//
|
// CONVENIENCE TRANSLATION FUNCTIONS FOR API
|
//
|
|
/**
|
* APIMethod: getLonLatFromPixel
|
*
|
* Parameters:
|
* px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object with
|
* a 'x' and 'y' properties.
|
*
|
* Returns:
|
* {<OpenLayers.LonLat>} An OpenLayers.LonLat corresponding to the given
|
* OpenLayers.Pixel, translated into lon/lat by the
|
* current base layer
|
*/
|
getLonLatFromPixel: function (px) {
|
return this.getLonLatFromViewPortPx(px);
|
},
|
|
/**
|
* APIMethod: getPixelFromLonLat
|
* Returns a pixel location given a map location. The map location is
|
* translated to an integer pixel location (in viewport pixel
|
* coordinates) by the current base layer.
|
*
|
* Parameters:
|
* lonlat - {<OpenLayers.LonLat>} A map location.
|
*
|
* Returns:
|
* {<OpenLayers.Pixel>} An OpenLayers.Pixel corresponding to the
|
* <OpenLayers.LonLat> translated into view port pixels by the current
|
* base layer.
|
*/
|
getPixelFromLonLat: function (lonlat) {
|
var px = this.getViewPortPxFromLonLat(lonlat);
|
px.x = Math.round(px.x);
|
px.y = Math.round(px.y);
|
return px;
|
},
|
|
/**
|
* Method: getGeodesicPixelSize
|
*
|
* Parameters:
|
* px - {<OpenLayers.Pixel>} The pixel to get the geodesic length for. If
|
* not provided, the center pixel of the map viewport will be used.
|
*
|
* Returns:
|
* {<OpenLayers.Size>} The geodesic size of the pixel in kilometers.
|
*/
|
getGeodesicPixelSize: function(px) {
|
var lonlat = px ? this.getLonLatFromPixel(px) : (
|
this.getCachedCenter() || new OpenLayers.LonLat(0, 0));
|
var res = this.getResolution();
|
var left = lonlat.add(-res / 2, 0);
|
var right = lonlat.add(res / 2, 0);
|
var bottom = lonlat.add(0, -res / 2);
|
var top = lonlat.add(0, res / 2);
|
var dest = new OpenLayers.Projection("EPSG:4326");
|
var source = this.getProjectionObject() || dest;
|
if(!source.equals(dest)) {
|
left.transform(source, dest);
|
right.transform(source, dest);
|
bottom.transform(source, dest);
|
top.transform(source, dest);
|
}
|
|
return new OpenLayers.Size(
|
OpenLayers.Util.distVincenty(left, right),
|
OpenLayers.Util.distVincenty(bottom, top)
|
);
|
},
|
|
|
|
//
|
// TRANSLATION: ViewPortPx <-> LayerPx
|
//
|
|
/**
|
* APIMethod: getViewPortPxFromLayerPx
|
*
|
* Parameters:
|
* layerPx - {<OpenLayers.Pixel>}
|
*
|
* Returns:
|
* {<OpenLayers.Pixel>} Layer Pixel translated into ViewPort Pixel
|
* coordinates
|
*/
|
getViewPortPxFromLayerPx:function(layerPx) {
|
var viewPortPx = null;
|
if (layerPx != null) {
|
var dX = this.layerContainerOriginPx.x;
|
var dY = this.layerContainerOriginPx.y;
|
viewPortPx = layerPx.add(dX, dY);
|
}
|
return viewPortPx;
|
},
|
|
/**
|
* APIMethod: getLayerPxFromViewPortPx
|
*
|
* Parameters:
|
* viewPortPx - {<OpenLayers.Pixel>}
|
*
|
* Returns:
|
* {<OpenLayers.Pixel>} ViewPort Pixel translated into Layer Pixel
|
* coordinates
|
*/
|
getLayerPxFromViewPortPx:function(viewPortPx) {
|
var layerPx = null;
|
if (viewPortPx != null) {
|
var dX = -this.layerContainerOriginPx.x;
|
var dY = -this.layerContainerOriginPx.y;
|
layerPx = viewPortPx.add(dX, dY);
|
if (isNaN(layerPx.x) || isNaN(layerPx.y)) {
|
layerPx = null;
|
}
|
}
|
return layerPx;
|
},
|
|
//
|
// TRANSLATION: LonLat <-> LayerPx
|
//
|
|
/**
|
* Method: getLonLatFromLayerPx
|
*
|
* Parameters:
|
* px - {<OpenLayers.Pixel>}
|
*
|
* Returns:
|
* {<OpenLayers.LonLat>}
|
*/
|
getLonLatFromLayerPx: function (px) {
|
//adjust for displacement of layerContainerDiv
|
px = this.getViewPortPxFromLayerPx(px);
|
return this.getLonLatFromViewPortPx(px);
|
},
|
|
/**
|
* APIMethod: getLayerPxFromLonLat
|
*
|
* Parameters:
|
* lonlat - {<OpenLayers.LonLat>} lonlat
|
*
|
* Returns:
|
* {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in
|
* <OpenLayers.LonLat>, translated into layer pixels
|
* by the current base layer
|
*/
|
getLayerPxFromLonLat: function (lonlat) {
|
//adjust for displacement of layerContainerDiv
|
var px = this.getPixelFromLonLat(lonlat);
|
return this.getLayerPxFromViewPortPx(px);
|
},
|
|
/**
|
* Method: applyTransform
|
* Applies the given transform to the <layerContainerDiv>. This method has
|
* a 2-stage fallback from translate3d/scale3d via translate/scale to plain
|
* style.left/style.top, in which case no scaling is supported.
|
*
|
* Parameters:
|
* x - {Number} x parameter for the translation. Defaults to the x value of
|
* the map's <layerContainerOriginPx>
|
* y - {Number} y parameter for the translation. Defaults to the y value of
|
* the map's <layerContainerOriginPx>
|
* scale - {Number} scale. Defaults to 1 if not provided.
|
*/
|
applyTransform: function(x, y, scale) {
|
scale = scale || 1;
|
var origin = this.layerContainerOriginPx,
|
needTransform = scale !== 1;
|
x = x || origin.x;
|
y = y || origin.y;
|
|
var style = this.layerContainerDiv.style,
|
transform = this.applyTransform.transform,
|
template = this.applyTransform.template;
|
|
if (transform === undefined) {
|
transform = OpenLayers.Util.vendorPrefix.style('transform');
|
this.applyTransform.transform = transform;
|
if (transform) {
|
// Try translate3d, but only if the viewPortDiv has a transform
|
// defined in a stylesheet
|
var computedStyle = OpenLayers.Element.getStyle(this.viewPortDiv,
|
OpenLayers.Util.vendorPrefix.css('transform'));
|
if (!computedStyle || computedStyle !== 'none') {
|
template = ['translate3d(', ',0) ', 'scale3d(', ',1)'];
|
style[transform] = [template[0], '0,0', template[1]].join('');
|
}
|
// If no transform is defined in the stylesheet or translate3d
|
// does not stick, use translate and scale
|
if (!template || !~style[transform].indexOf(template[0])) {
|
template = ['translate(', ') ', 'scale(', ')'];
|
}
|
this.applyTransform.template = template;
|
}
|
}
|
|
// If we do 3d transforms, we always want to use them. If we do 2d
|
// transforms, we only use them when we need to.
|
if (transform !== null && (template[0] === 'translate3d(' || needTransform === true)) {
|
// Our 2d transforms are combined with style.left and style.top, so
|
// adjust x and y values and set the origin as left and top
|
if (needTransform === true && template[0] === 'translate(') {
|
x -= origin.x;
|
y -= origin.y;
|
style.left = origin.x + 'px';
|
style.top = origin.y + 'px';
|
}
|
style[transform] = [
|
template[0], x, 'px,', y, 'px', template[1],
|
template[2], scale, ',', scale, template[3]
|
].join('');
|
} else {
|
style.left = x + 'px';
|
style.top = y + 'px';
|
// We previously might have had needTransform, so remove transform
|
if (transform !== null) {
|
style[transform] = '';
|
}
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Map"
|
});
|
|
/**
|
* Constant: TILE_WIDTH
|
* {Integer} 256 Default tile width (unless otherwise specified)
|
*/
|
OpenLayers.Map.TILE_WIDTH = 256;
|
/**
|
* Constant: TILE_HEIGHT
|
* {Integer} 256 Default tile height (unless otherwise specified)
|
*/
|
OpenLayers.Map.TILE_HEIGHT = 256;
|
/* ======================================================================
|
OpenLayers/Handler.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
* @requires OpenLayers/Events.js
|
*/
|
|
/**
|
* Class: OpenLayers.Handler
|
* Base class to construct a higher-level handler for event sequences. All
|
* handlers have activate and deactivate methods. In addition, they have
|
* methods named like browser events. When a handler is activated, any
|
* additional methods named like a browser event is registered as a
|
* listener for the corresponding event. When a handler is deactivated,
|
* those same methods are unregistered as event listeners.
|
*
|
* Handlers also typically have a callbacks object with keys named like
|
* the abstracted events or event sequences that they are in charge of
|
* handling. The controls that wrap handlers define the methods that
|
* correspond to these abstract events - so instead of listening for
|
* individual browser events, they only listen for the abstract events
|
* defined by the handler.
|
*
|
* Handlers are created by controls, which ultimately have the responsibility
|
* of making changes to the the state of the application. Handlers
|
* themselves may make temporary changes, but in general are expected to
|
* return the application in the same state that they found it.
|
*/
|
OpenLayers.Handler = OpenLayers.Class({
|
|
/**
|
* Property: id
|
* {String}
|
*/
|
id: null,
|
|
/**
|
* APIProperty: control
|
* {<OpenLayers.Control>}. The control that initialized this handler. The
|
* control is assumed to have a valid map property - that map is used
|
* in the handler's own setMap method.
|
*/
|
control: null,
|
|
/**
|
* Property: map
|
* {<OpenLayers.Map>}
|
*/
|
map: null,
|
|
/**
|
* APIProperty: keyMask
|
* {Integer} Use bitwise operators and one or more of the OpenLayers.Handler
|
* constants to construct a keyMask. The keyMask is used by
|
* <checkModifiers>. If the keyMask matches the combination of keys
|
* down on an event, checkModifiers returns true.
|
*
|
* Example:
|
* (code)
|
* // handler only responds if the Shift key is down
|
* handler.keyMask = OpenLayers.Handler.MOD_SHIFT;
|
*
|
* // handler only responds if Ctrl-Shift is down
|
* handler.keyMask = OpenLayers.Handler.MOD_SHIFT |
|
* OpenLayers.Handler.MOD_CTRL;
|
* (end)
|
*/
|
keyMask: null,
|
|
/**
|
* Property: active
|
* {Boolean}
|
*/
|
active: false,
|
|
/**
|
* Property: evt
|
* {Event} This property references the last event handled by the handler.
|
* Note that this property is not part of the stable API. Use of the
|
* evt property should be restricted to controls in the library
|
* or other applications that are willing to update with changes to
|
* the OpenLayers code.
|
*/
|
evt: null,
|
|
/**
|
* Property: touch
|
* {Boolean} Indicates the support of touch events. When touch events are
|
* started touch will be true and all mouse related listeners will do
|
* nothing.
|
*/
|
touch: false,
|
|
/**
|
* Constructor: OpenLayers.Handler
|
* Construct a handler.
|
*
|
* Parameters:
|
* control - {<OpenLayers.Control>} The control that initialized this
|
* handler. The control is assumed to have a valid map property; that
|
* map is used in the handler's own setMap method. If a map property
|
* is present in the options argument it will be used instead.
|
* callbacks - {Object} An object whose properties correspond to abstracted
|
* events or sequences of browser events. The values for these
|
* properties are functions defined by the control that get called by
|
* the handler.
|
* options - {Object} An optional object whose properties will be set on
|
* the handler.
|
*/
|
initialize: function(control, callbacks, options) {
|
OpenLayers.Util.extend(this, options);
|
this.control = control;
|
this.callbacks = callbacks;
|
|
var map = this.map || control.map;
|
if (map) {
|
this.setMap(map);
|
}
|
|
this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
|
},
|
|
/**
|
* Method: setMap
|
*/
|
setMap: function (map) {
|
this.map = map;
|
},
|
|
/**
|
* Method: checkModifiers
|
* Check the keyMask on the handler. If no <keyMask> is set, this always
|
* returns true. If a <keyMask> is set and it matches the combination
|
* of keys down on an event, this returns true.
|
*
|
* Returns:
|
* {Boolean} The keyMask matches the keys down on an event.
|
*/
|
checkModifiers: function (evt) {
|
if(this.keyMask == null) {
|
return true;
|
}
|
/* calculate the keyboard modifier mask for this event */
|
var keyModifiers =
|
(evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) |
|
(evt.ctrlKey ? OpenLayers.Handler.MOD_CTRL : 0) |
|
(evt.altKey ? OpenLayers.Handler.MOD_ALT : 0) |
|
(evt.metaKey ? OpenLayers.Handler.MOD_META : 0);
|
|
/* if it differs from the handler object's key mask,
|
bail out of the event handler */
|
return (keyModifiers == this.keyMask);
|
},
|
|
/**
|
* APIMethod: activate
|
* Turn on the handler. Returns false if the handler was already active.
|
*
|
* Returns:
|
* {Boolean} The handler was activated.
|
*/
|
activate: function() {
|
if(this.active) {
|
return false;
|
}
|
// register for event handlers defined on this class.
|
var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
|
for (var i=0, len=events.length; i<len; i++) {
|
if (this[events[i]]) {
|
this.register(events[i], this[events[i]]);
|
}
|
}
|
this.active = true;
|
return true;
|
},
|
|
/**
|
* APIMethod: deactivate
|
* Turn off the handler. Returns false if the handler was already inactive.
|
*
|
* Returns:
|
* {Boolean} The handler was deactivated.
|
*/
|
deactivate: function() {
|
if(!this.active) {
|
return false;
|
}
|
// unregister event handlers defined on this class.
|
var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
|
for (var i=0, len=events.length; i<len; i++) {
|
if (this[events[i]]) {
|
this.unregister(events[i], this[events[i]]);
|
}
|
}
|
this.touch = false;
|
this.active = false;
|
return true;
|
},
|
|
/**
|
* Method: startTouch
|
* Start touch events, this method must be called by subclasses in
|
* "touchstart" method. When touch events are started <touch> will be
|
* true and all mouse related listeners will do nothing.
|
*/
|
startTouch: function() {
|
if (!this.touch) {
|
this.touch = true;
|
var events = [
|
"mousedown", "mouseup", "mousemove", "click", "dblclick",
|
"mouseout"
|
];
|
for (var i=0, len=events.length; i<len; i++) {
|
if (this[events[i]]) {
|
this.unregister(events[i], this[events[i]]);
|
}
|
}
|
}
|
},
|
|
/**
|
* Method: callback
|
* Trigger the control's named callback with the given arguments
|
*
|
* Parameters:
|
* name - {String} The key for the callback that is one of the properties
|
* of the handler's callbacks object.
|
* args - {Array(*)} An array of arguments (any type) with which to call
|
* the callback (defined by the control).
|
*/
|
callback: function (name, args) {
|
if (name && this.callbacks[name]) {
|
this.callbacks[name].apply(this.control, args);
|
}
|
},
|
|
/**
|
* Method: register
|
* register an event on the map
|
*/
|
register: function (name, method) {
|
// TODO: deal with registerPriority in 3.0
|
this.map.events.registerPriority(name, this, method);
|
this.map.events.registerPriority(name, this, this.setEvent);
|
},
|
|
/**
|
* Method: unregister
|
* unregister an event from the map
|
*/
|
unregister: function (name, method) {
|
this.map.events.unregister(name, this, method);
|
this.map.events.unregister(name, this, this.setEvent);
|
},
|
|
/**
|
* Method: setEvent
|
* With each registered browser event, the handler sets its own evt
|
* property. This property can be accessed by controls if needed
|
* to get more information about the event that the handler is
|
* processing.
|
*
|
* This allows modifier keys on the event to be checked (alt, shift, ctrl,
|
* and meta cannot be checked with the keyboard handler). For a
|
* control to determine which modifier keys are associated with the
|
* event that a handler is currently processing, it should access
|
* (code)handler.evt.altKey || handler.evt.shiftKey ||
|
* handler.evt.ctrlKey || handler.evt.metaKey(end).
|
*
|
* Parameters:
|
* evt - {Event} The browser event.
|
*/
|
setEvent: function(evt) {
|
this.evt = evt;
|
return true;
|
},
|
|
/**
|
* Method: destroy
|
* Deconstruct the handler.
|
*/
|
destroy: function () {
|
// unregister event listeners
|
this.deactivate();
|
// eliminate circular references
|
this.control = this.map = null;
|
},
|
|
CLASS_NAME: "OpenLayers.Handler"
|
});
|
|
/**
|
* Constant: OpenLayers.Handler.MOD_NONE
|
* If set as the <keyMask>, <checkModifiers> returns false if any key is down.
|
*/
|
OpenLayers.Handler.MOD_NONE = 0;
|
|
/**
|
* Constant: OpenLayers.Handler.MOD_SHIFT
|
* If set as the <keyMask>, <checkModifiers> returns false if Shift is down.
|
*/
|
OpenLayers.Handler.MOD_SHIFT = 1;
|
|
/**
|
* Constant: OpenLayers.Handler.MOD_CTRL
|
* If set as the <keyMask>, <checkModifiers> returns false if Ctrl is down.
|
*/
|
OpenLayers.Handler.MOD_CTRL = 2;
|
|
/**
|
* Constant: OpenLayers.Handler.MOD_ALT
|
* If set as the <keyMask>, <checkModifiers> returns false if Alt is down.
|
*/
|
OpenLayers.Handler.MOD_ALT = 4;
|
|
/**
|
* Constant: OpenLayers.Handler.MOD_META
|
* If set as the <keyMask>, <checkModifiers> returns false if Cmd is down.
|
*/
|
OpenLayers.Handler.MOD_META = 8;
|
|
|
/* ======================================================================
|
OpenLayers/Handler/Click.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Handler.js
|
*/
|
|
/**
|
* Class: OpenLayers.Handler.Click
|
* A handler for mouse clicks. The intention of this handler is to give
|
* controls more flexibility with handling clicks. Browsers trigger
|
* click events twice for a double-click. In addition, the mousedown,
|
* mousemove, mouseup sequence fires a click event. With this handler,
|
* controls can decide whether to ignore clicks associated with a double
|
* click. By setting a <pixelTolerance>, controls can also ignore clicks
|
* that include a drag. Create a new instance with the
|
* <OpenLayers.Handler.Click> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Handler>
|
*/
|
OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, {
|
/**
|
* APIProperty: delay
|
* {Number} Number of milliseconds between clicks before the event is
|
* considered a double-click.
|
*/
|
delay: 300,
|
|
/**
|
* APIProperty: single
|
* {Boolean} Handle single clicks. Default is true. If false, clicks
|
* will not be reported. If true, single-clicks will be reported.
|
*/
|
single: true,
|
|
/**
|
* APIProperty: double
|
* {Boolean} Handle double-clicks. Default is false.
|
*/
|
'double': false,
|
|
/**
|
* APIProperty: pixelTolerance
|
* {Number} Maximum number of pixels between mouseup and mousedown for an
|
* event to be considered a click. Default is 0. If set to an
|
* integer value, clicks with a drag greater than the value will be
|
* ignored. This property can only be set when the handler is
|
* constructed.
|
*/
|
pixelTolerance: 0,
|
|
/**
|
* APIProperty: dblclickTolerance
|
* {Number} Maximum distance in pixels between clicks for a sequence of
|
* events to be considered a double click. Default is 13. If the
|
* distance between two clicks is greater than this value, a double-
|
* click will not be fired.
|
*/
|
dblclickTolerance: 13,
|
|
/**
|
* APIProperty: stopSingle
|
* {Boolean} Stop other listeners from being notified of clicks. Default
|
* is false. If true, any listeners registered before this one for
|
* click or rightclick events will not be notified.
|
*/
|
stopSingle: false,
|
|
/**
|
* APIProperty: stopDouble
|
* {Boolean} Stop other listeners from being notified of double-clicks.
|
* Default is false. If true, any click listeners registered before
|
* this one will not be notified of *any* double-click events.
|
*
|
* The one caveat with stopDouble is that given a map with two click
|
* handlers, one with stopDouble true and the other with stopSingle
|
* true, the stopSingle handler should be activated last to get
|
* uniform cross-browser performance. Since IE triggers one click
|
* with a dblclick and FF triggers two, if a stopSingle handler is
|
* activated first, all it gets in IE is a single click when the
|
* second handler stops propagation on the dblclick.
|
*/
|
stopDouble: false,
|
|
/**
|
* Property: timerId
|
* {Number} The id of the timeout waiting to clear the <delayedCall>.
|
*/
|
timerId: null,
|
|
/**
|
* Property: down
|
* {Object} Object that store relevant information about the last
|
* mousedown or touchstart. Its 'xy' OpenLayers.Pixel property gives
|
* the average location of the mouse/touch event. Its 'touches'
|
* property records clientX/clientY of each touches.
|
*/
|
down: null,
|
|
/**
|
* Property: last
|
* {Object} Object that store relevant information about the last
|
* mousemove or touchmove. Its 'xy' OpenLayers.Pixel property gives
|
* the average location of the mouse/touch event. Its 'touches'
|
* property records clientX/clientY of each touches.
|
*/
|
last: null,
|
|
/**
|
* Property: first
|
* {Object} When waiting for double clicks, this object will store
|
* information about the first click in a two click sequence.
|
*/
|
first: null,
|
|
/**
|
* Property: rightclickTimerId
|
* {Number} The id of the right mouse timeout waiting to clear the
|
* <delayedEvent>.
|
*/
|
rightclickTimerId: null,
|
|
/**
|
* Constructor: OpenLayers.Handler.Click
|
* Create a new click handler.
|
*
|
* Parameters:
|
* control - {<OpenLayers.Control>} The control that is making use of
|
* this handler. If a handler is being used without a control, the
|
* handler's setMap method must be overridden to deal properly with
|
* the map.
|
* callbacks - {Object} An object with keys corresponding to callbacks
|
* that will be called by the handler. The callbacks should
|
* expect to recieve a single argument, the click event.
|
* Callbacks for 'click' and 'dblclick' are supported.
|
* options - {Object} Optional object whose properties will be set on the
|
* handler.
|
*/
|
|
/**
|
* Method: touchstart
|
* Handle touchstart.
|
*
|
* Returns:
|
* {Boolean} Continue propagating this event.
|
*/
|
touchstart: function(evt) {
|
this.startTouch();
|
this.down = this.getEventInfo(evt);
|
this.last = this.getEventInfo(evt);
|
return true;
|
},
|
|
/**
|
* Method: touchmove
|
* Store position of last move, because touchend event can have
|
* an empty "touches" property.
|
*
|
* Returns:
|
* {Boolean} Continue propagating this event.
|
*/
|
touchmove: function(evt) {
|
this.last = this.getEventInfo(evt);
|
return true;
|
},
|
|
/**
|
* Method: touchend
|
* Correctly set event xy property, and add lastTouches to have
|
* touches property from last touchstart or touchmove
|
*
|
* Returns:
|
* {Boolean} Continue propagating this event.
|
*/
|
touchend: function(evt) {
|
// touchstart may not have been allowed to propagate
|
if (this.down) {
|
evt.xy = this.last.xy;
|
evt.lastTouches = this.last.touches;
|
this.handleSingle(evt);
|
this.down = null;
|
}
|
return true;
|
},
|
|
/**
|
* Method: mousedown
|
* Handle mousedown.
|
*
|
* Returns:
|
* {Boolean} Continue propagating this event.
|
*/
|
mousedown: function(evt) {
|
this.down = this.getEventInfo(evt);
|
this.last = this.getEventInfo(evt);
|
return true;
|
},
|
|
/**
|
* Method: mouseup
|
* Handle mouseup. Installed to support collection of right mouse events.
|
*
|
* Returns:
|
* {Boolean} Continue propagating this event.
|
*/
|
mouseup: function (evt) {
|
var propagate = true;
|
|
// Collect right mouse clicks from the mouseup
|
// IE - ignores the second right click in mousedown so using
|
// mouseup instead
|
if (this.checkModifiers(evt) && this.control.handleRightClicks &&
|
OpenLayers.Event.isRightClick(evt)) {
|
propagate = this.rightclick(evt);
|
}
|
|
return propagate;
|
},
|
|
/**
|
* Method: rightclick
|
* Handle rightclick. For a dblrightclick, we get two clicks so we need
|
* to always register for dblrightclick to properly handle single
|
* clicks.
|
*
|
* Returns:
|
* {Boolean} Continue propagating this event.
|
*/
|
rightclick: function(evt) {
|
if(this.passesTolerance(evt)) {
|
if(this.rightclickTimerId != null) {
|
//Second click received before timeout this must be
|
// a double click
|
this.clearTimer();
|
this.callback('dblrightclick', [evt]);
|
return !this.stopDouble;
|
} else {
|
//Set the rightclickTimerId, send evt only if double is
|
// true else trigger single
|
var clickEvent = this['double'] ?
|
OpenLayers.Util.extend({}, evt) :
|
this.callback('rightclick', [evt]);
|
|
var delayedRightCall = OpenLayers.Function.bind(
|
this.delayedRightCall,
|
this,
|
clickEvent
|
);
|
this.rightclickTimerId = window.setTimeout(
|
delayedRightCall, this.delay
|
);
|
}
|
}
|
return !this.stopSingle;
|
},
|
|
/**
|
* Method: delayedRightCall
|
* Sets <rightclickTimerId> to null. And optionally triggers the
|
* rightclick callback if evt is set.
|
*/
|
delayedRightCall: function(evt) {
|
this.rightclickTimerId = null;
|
if (evt) {
|
this.callback('rightclick', [evt]);
|
}
|
},
|
|
/**
|
* Method: click
|
* Handle click events from the browser. This is registered as a listener
|
* for click events and should not be called from other events in this
|
* handler.
|
*
|
* Returns:
|
* {Boolean} Continue propagating this event.
|
*/
|
click: function(evt) {
|
if (!this.last) {
|
this.last = this.getEventInfo(evt);
|
}
|
this.handleSingle(evt);
|
return !this.stopSingle;
|
},
|
|
/**
|
* Method: dblclick
|
* Handle dblclick. For a dblclick, we get two clicks in some browsers
|
* (FF) and one in others (IE). So we need to always register for
|
* dblclick to properly handle single clicks. This method is registered
|
* as a listener for the dblclick browser event. It should *not* be
|
* called by other methods in this handler.
|
*
|
* Returns:
|
* {Boolean} Continue propagating this event.
|
*/
|
dblclick: function(evt) {
|
this.handleDouble(evt);
|
return !this.stopDouble;
|
},
|
|
/**
|
* Method: handleDouble
|
* Handle double-click sequence.
|
*/
|
handleDouble: function(evt) {
|
if (this.passesDblclickTolerance(evt)) {
|
if (this["double"]) {
|
this.callback("dblclick", [evt]);
|
}
|
// to prevent a dblclick from firing the click callback in IE
|
this.clearTimer();
|
}
|
},
|
|
/**
|
* Method: handleSingle
|
* Handle single click sequence.
|
*/
|
handleSingle: function(evt) {
|
if (this.passesTolerance(evt)) {
|
if (this.timerId != null) {
|
// already received a click
|
if (this.last.touches && this.last.touches.length === 1) {
|
// touch device, no dblclick event - this may be a double
|
if (this["double"]) {
|
// on Android don't let the browser zoom on the page
|
OpenLayers.Event.preventDefault(evt);
|
}
|
this.handleDouble(evt);
|
}
|
// if we're not in a touch environment we clear the click timer
|
// if we've got a second touch, we'll get two touchend events
|
if (!this.last.touches || this.last.touches.length !== 2) {
|
this.clearTimer();
|
}
|
} else {
|
// remember the first click info so we can compare to the second
|
this.first = this.getEventInfo(evt);
|
// set the timer, send evt only if single is true
|
//use a clone of the event object because it will no longer
|
//be a valid event object in IE in the timer callback
|
var clickEvent = this.single ?
|
OpenLayers.Util.extend({}, evt) : null;
|
this.queuePotentialClick(clickEvent);
|
}
|
}
|
},
|
|
/**
|
* Method: queuePotentialClick
|
* This method is separated out largely to make testing easier (so we
|
* don't have to override window.setTimeout)
|
*/
|
queuePotentialClick: function(evt) {
|
this.timerId = window.setTimeout(
|
OpenLayers.Function.bind(this.delayedCall, this, evt),
|
this.delay
|
);
|
},
|
|
/**
|
* Method: passesTolerance
|
* Determine whether the event is within the optional pixel tolerance. Note
|
* that the pixel tolerance check only works if mousedown events get to
|
* the listeners registered here. If they are stopped by other elements,
|
* the <pixelTolerance> will have no effect here (this method will always
|
* return true).
|
*
|
* Returns:
|
* {Boolean} The click is within the pixel tolerance (if specified).
|
*/
|
passesTolerance: function(evt) {
|
var passes = true;
|
if (this.pixelTolerance != null && this.down && this.down.xy) {
|
passes = this.pixelTolerance >= this.down.xy.distanceTo(evt.xy);
|
// for touch environments, we also enforce that all touches
|
// start and end within the given tolerance to be considered a click
|
if (passes && this.touch &&
|
this.down.touches.length === this.last.touches.length) {
|
// the touchend event doesn't come with touches, so we check
|
// down and last
|
for (var i=0, ii=this.down.touches.length; i<ii; ++i) {
|
if (this.getTouchDistance(
|
this.down.touches[i],
|
this.last.touches[i]
|
) > this.pixelTolerance) {
|
passes = false;
|
break;
|
}
|
}
|
}
|
}
|
return passes;
|
},
|
|
/**
|
* Method: getTouchDistance
|
*
|
* Returns:
|
* {Boolean} The pixel displacement between two touches.
|
*/
|
getTouchDistance: function(from, to) {
|
return Math.sqrt(
|
Math.pow(from.clientX - to.clientX, 2) +
|
Math.pow(from.clientY - to.clientY, 2)
|
);
|
},
|
|
/**
|
* Method: passesDblclickTolerance
|
* Determine whether the event is within the optional double-cick pixel
|
* tolerance.
|
*
|
* Returns:
|
* {Boolean} The click is within the double-click pixel tolerance.
|
*/
|
passesDblclickTolerance: function(evt) {
|
var passes = true;
|
if (this.down && this.first) {
|
passes = this.down.xy.distanceTo(this.first.xy) <= this.dblclickTolerance;
|
}
|
return passes;
|
},
|
|
/**
|
* Method: clearTimer
|
* Clear the timer and set <timerId> to null.
|
*/
|
clearTimer: function() {
|
if (this.timerId != null) {
|
window.clearTimeout(this.timerId);
|
this.timerId = null;
|
}
|
if (this.rightclickTimerId != null) {
|
window.clearTimeout(this.rightclickTimerId);
|
this.rightclickTimerId = null;
|
}
|
},
|
|
/**
|
* Method: delayedCall
|
* Sets <timerId> to null. And optionally triggers the click callback if
|
* evt is set.
|
*/
|
delayedCall: function(evt) {
|
this.timerId = null;
|
if (evt) {
|
this.callback("click", [evt]);
|
}
|
},
|
|
/**
|
* Method: getEventInfo
|
* This method allows us to store event information without storing the
|
* actual event. In touch devices (at least), the same event is
|
* modified between touchstart, touchmove, and touchend.
|
*
|
* Returns:
|
* {Object} An object with event related info.
|
*/
|
getEventInfo: function(evt) {
|
var touches;
|
if (evt.touches) {
|
var len = evt.touches.length;
|
touches = new Array(len);
|
var touch;
|
for (var i=0; i<len; i++) {
|
touch = evt.touches[i];
|
touches[i] = {
|
clientX: touch.olClientX,
|
clientY: touch.olClientY
|
};
|
}
|
}
|
return {
|
xy: evt.xy,
|
touches: touches
|
};
|
},
|
|
/**
|
* APIMethod: deactivate
|
* Deactivate the handler.
|
*
|
* Returns:
|
* {Boolean} The handler was successfully deactivated.
|
*/
|
deactivate: function() {
|
var deactivated = false;
|
if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
|
this.clearTimer();
|
this.down = null;
|
this.first = null;
|
this.last = null;
|
deactivated = true;
|
}
|
return deactivated;
|
},
|
|
CLASS_NAME: "OpenLayers.Handler.Click"
|
});
|
/* ======================================================================
|
OpenLayers/Handler/Drag.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Handler.js
|
*/
|
|
/**
|
* Class: OpenLayers.Handler.Drag
|
* The drag handler is used to deal with sequences of browser events related
|
* to dragging. The handler is used by controls that want to know when
|
* a drag sequence begins, when a drag is happening, and when it has
|
* finished.
|
*
|
* Controls that use the drag handler typically construct it with callbacks
|
* for 'down', 'move', and 'done'. Callbacks for these keys are called
|
* when the drag begins, with each move, and when the drag is done. In
|
* addition, controls can have callbacks keyed to 'up' and 'out' if they
|
* care to differentiate between the types of events that correspond with
|
* the end of a drag sequence. If no drag actually occurs (no mouse move)
|
* the 'down' and 'up' callbacks will be called, but not the 'done'
|
* callback.
|
*
|
* Create a new drag handler with the <OpenLayers.Handler.Drag> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Handler>
|
*/
|
OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, {
|
|
/**
|
* Property: started
|
* {Boolean} When a mousedown or touchstart event is received, we want to
|
* record it, but not set 'dragging' until the mouse moves after starting.
|
*/
|
started: false,
|
|
/**
|
* Property: stopDown
|
* {Boolean} Stop propagation of mousedown events from getting to listeners
|
* on the same element. Default is true.
|
*/
|
stopDown: true,
|
|
/**
|
* Property: dragging
|
* {Boolean}
|
*/
|
dragging: false,
|
|
/**
|
* Property: last
|
* {<OpenLayers.Pixel>} The last pixel location of the drag.
|
*/
|
last: null,
|
|
/**
|
* Property: start
|
* {<OpenLayers.Pixel>} The first pixel location of the drag.
|
*/
|
start: null,
|
|
/**
|
* Property: lastMoveEvt
|
* {Object} The last mousemove event that occurred. Used to
|
* position the map correctly when our "delay drag"
|
* timeout expired.
|
*/
|
lastMoveEvt: null,
|
|
/**
|
* Property: oldOnselectstart
|
* {Function}
|
*/
|
oldOnselectstart: null,
|
|
/**
|
* Property: interval
|
* {Integer} In order to increase performance, an interval (in
|
* milliseconds) can be set to reduce the number of drag events
|
* called. If set, a new drag event will not be set until the
|
* interval has passed.
|
* Defaults to 0, meaning no interval.
|
*/
|
interval: 0,
|
|
/**
|
* Property: timeoutId
|
* {String} The id of the timeout used for the mousedown interval.
|
* This is "private", and should be left alone.
|
*/
|
timeoutId: null,
|
|
/**
|
* APIProperty: documentDrag
|
* {Boolean} If set to true, the handler will also handle mouse moves when
|
* the cursor has moved out of the map viewport. Default is false.
|
*/
|
documentDrag: false,
|
|
/**
|
* Property: documentEvents
|
* {Boolean} Are we currently observing document events?
|
*/
|
documentEvents: null,
|
|
/**
|
* Constructor: OpenLayers.Handler.Drag
|
* Returns OpenLayers.Handler.Drag
|
*
|
* Parameters:
|
* control - {<OpenLayers.Control>} The control that is making use of
|
* this handler. If a handler is being used without a control, the
|
* handlers setMap method must be overridden to deal properly with
|
* the map.
|
* callbacks - {Object} An object containing a single function to be
|
* called when the drag operation is finished. The callback should
|
* expect to recieve a single argument, the pixel location of the event.
|
* Callbacks for 'move' and 'done' are supported. You can also speficy
|
* callbacks for 'down', 'up', and 'out' to respond to those events.
|
* options - {Object}
|
*/
|
initialize: function(control, callbacks, options) {
|
OpenLayers.Handler.prototype.initialize.apply(this, arguments);
|
|
if (this.documentDrag === true) {
|
var me = this;
|
this._docMove = function(evt) {
|
me.mousemove({
|
xy: {x: evt.clientX, y: evt.clientY},
|
element: document
|
});
|
};
|
this._docUp = function(evt) {
|
me.mouseup({xy: {x: evt.clientX, y: evt.clientY}});
|
};
|
}
|
},
|
|
|
/**
|
* Method: dragstart
|
* This private method is factorized from mousedown and touchstart methods
|
*
|
* Parameters:
|
* evt - {Event} The event
|
*
|
* Returns:
|
* {Boolean} Let the event propagate.
|
*/
|
dragstart: function (evt) {
|
var propagate = true;
|
this.dragging = false;
|
if (this.checkModifiers(evt) &&
|
(OpenLayers.Event.isLeftClick(evt) ||
|
OpenLayers.Event.isSingleTouch(evt))) {
|
this.started = true;
|
this.start = evt.xy;
|
this.last = evt.xy;
|
OpenLayers.Element.addClass(
|
this.map.viewPortDiv, "olDragDown"
|
);
|
this.down(evt);
|
this.callback("down", [evt.xy]);
|
|
// prevent document dragging
|
OpenLayers.Event.preventDefault(evt);
|
|
if(!this.oldOnselectstart) {
|
this.oldOnselectstart = document.onselectstart ?
|
document.onselectstart : OpenLayers.Function.True;
|
}
|
document.onselectstart = OpenLayers.Function.False;
|
|
propagate = !this.stopDown;
|
} else {
|
this.started = false;
|
this.start = null;
|
this.last = null;
|
}
|
return propagate;
|
},
|
|
/**
|
* Method: dragmove
|
* This private method is factorized from mousemove and touchmove methods
|
*
|
* Parameters:
|
* evt - {Event} The event
|
*
|
* Returns:
|
* {Boolean} Let the event propagate.
|
*/
|
dragmove: function (evt) {
|
this.lastMoveEvt = evt;
|
if (this.started && !this.timeoutId && (evt.xy.x != this.last.x ||
|
evt.xy.y != this.last.y)) {
|
if(this.documentDrag === true && this.documentEvents) {
|
if(evt.element === document) {
|
this.adjustXY(evt);
|
// do setEvent manually because the documentEvents are not
|
// registered with the map
|
this.setEvent(evt);
|
} else {
|
this.removeDocumentEvents();
|
}
|
}
|
if (this.interval > 0) {
|
this.timeoutId = setTimeout(
|
OpenLayers.Function.bind(this.removeTimeout, this),
|
this.interval);
|
}
|
this.dragging = true;
|
|
this.move(evt);
|
this.callback("move", [evt.xy]);
|
if(!this.oldOnselectstart) {
|
this.oldOnselectstart = document.onselectstart;
|
document.onselectstart = OpenLayers.Function.False;
|
}
|
this.last = evt.xy;
|
}
|
return true;
|
},
|
|
/**
|
* Method: dragend
|
* This private method is factorized from mouseup and touchend methods
|
*
|
* Parameters:
|
* evt - {Event} The event
|
*
|
* Returns:
|
* {Boolean} Let the event propagate.
|
*/
|
dragend: function (evt) {
|
if (this.started) {
|
if(this.documentDrag === true && this.documentEvents) {
|
this.adjustXY(evt);
|
this.removeDocumentEvents();
|
}
|
var dragged = (this.start != this.last);
|
this.started = false;
|
this.dragging = false;
|
OpenLayers.Element.removeClass(
|
this.map.viewPortDiv, "olDragDown"
|
);
|
this.up(evt);
|
this.callback("up", [evt.xy]);
|
if(dragged) {
|
this.callback("done", [evt.xy]);
|
}
|
document.onselectstart = this.oldOnselectstart;
|
}
|
return true;
|
},
|
|
/**
|
* The four methods below (down, move, up, and out) are used by subclasses
|
* to do their own processing related to these mouse events.
|
*/
|
|
/**
|
* Method: down
|
* This method is called during the handling of the mouse down event.
|
* Subclasses can do their own processing here.
|
*
|
* Parameters:
|
* evt - {Event} The mouse down event
|
*/
|
down: function(evt) {
|
},
|
|
/**
|
* Method: move
|
* This method is called during the handling of the mouse move event.
|
* Subclasses can do their own processing here.
|
*
|
* Parameters:
|
* evt - {Event} The mouse move event
|
*
|
*/
|
move: function(evt) {
|
},
|
|
/**
|
* Method: up
|
* This method is called during the handling of the mouse up event.
|
* Subclasses can do their own processing here.
|
*
|
* Parameters:
|
* evt - {Event} The mouse up event
|
*/
|
up: function(evt) {
|
},
|
|
/**
|
* Method: out
|
* This method is called during the handling of the mouse out event.
|
* Subclasses can do their own processing here.
|
*
|
* Parameters:
|
* evt - {Event} The mouse out event
|
*/
|
out: function(evt) {
|
},
|
|
/**
|
* The methods below are part of the magic of event handling. Because
|
* they are named like browser events, they are registered as listeners
|
* for the events they represent.
|
*/
|
|
/**
|
* Method: mousedown
|
* Handle mousedown events
|
*
|
* Parameters:
|
* evt - {Event}
|
*
|
* Returns:
|
* {Boolean} Let the event propagate.
|
*/
|
mousedown: function(evt) {
|
return this.dragstart(evt);
|
},
|
|
/**
|
* Method: touchstart
|
* Handle touchstart events
|
*
|
* Parameters:
|
* evt - {Event}
|
*
|
* Returns:
|
* {Boolean} Let the event propagate.
|
*/
|
touchstart: function(evt) {
|
this.startTouch();
|
return this.dragstart(evt);
|
},
|
|
/**
|
* Method: mousemove
|
* Handle mousemove events
|
*
|
* Parameters:
|
* evt - {Event}
|
*
|
* Returns:
|
* {Boolean} Let the event propagate.
|
*/
|
mousemove: function(evt) {
|
return this.dragmove(evt);
|
},
|
|
/**
|
* Method: touchmove
|
* Handle touchmove events
|
*
|
* Parameters:
|
* evt - {Event}
|
*
|
* Returns:
|
* {Boolean} Let the event propagate.
|
*/
|
touchmove: function(evt) {
|
return this.dragmove(evt);
|
},
|
|
/**
|
* Method: removeTimeout
|
* Private. Called by mousemove() to remove the drag timeout.
|
*/
|
removeTimeout: function() {
|
this.timeoutId = null;
|
// if timeout expires while we're still dragging (mouseup
|
// hasn't occurred) then call mousemove to move to the
|
// correct position
|
if(this.dragging) {
|
this.mousemove(this.lastMoveEvt);
|
}
|
},
|
|
/**
|
* Method: mouseup
|
* Handle mouseup events
|
*
|
* Parameters:
|
* evt - {Event}
|
*
|
* Returns:
|
* {Boolean} Let the event propagate.
|
*/
|
mouseup: function(evt) {
|
return this.dragend(evt);
|
},
|
|
/**
|
* Method: touchend
|
* Handle touchend events
|
*
|
* Parameters:
|
* evt - {Event}
|
*
|
* Returns:
|
* {Boolean} Let the event propagate.
|
*/
|
touchend: function(evt) {
|
// override evt.xy with last position since touchend does not have
|
// any touch position
|
evt.xy = this.last;
|
return this.dragend(evt);
|
},
|
|
/**
|
* Method: mouseout
|
* Handle mouseout events
|
*
|
* Parameters:
|
* evt - {Event}
|
*
|
* Returns:
|
* {Boolean} Let the event propagate.
|
*/
|
mouseout: function (evt) {
|
if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {
|
if(this.documentDrag === true) {
|
this.addDocumentEvents();
|
} else {
|
var dragged = (this.start != this.last);
|
this.started = false;
|
this.dragging = false;
|
OpenLayers.Element.removeClass(
|
this.map.viewPortDiv, "olDragDown"
|
);
|
this.out(evt);
|
this.callback("out", []);
|
if(dragged) {
|
this.callback("done", [evt.xy]);
|
}
|
if(document.onselectstart) {
|
document.onselectstart = this.oldOnselectstart;
|
}
|
}
|
}
|
return true;
|
},
|
|
/**
|
* Method: click
|
* The drag handler captures the click event. If something else registers
|
* for clicks on the same element, its listener will not be called
|
* after a drag.
|
*
|
* Parameters:
|
* evt - {Event}
|
*
|
* Returns:
|
* {Boolean} Let the event propagate.
|
*/
|
click: function (evt) {
|
// let the click event propagate only if the mouse moved
|
return (this.start == this.last);
|
},
|
|
/**
|
* Method: activate
|
* Activate the handler.
|
*
|
* Returns:
|
* {Boolean} The handler was successfully activated.
|
*/
|
activate: function() {
|
var activated = false;
|
if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
|
this.dragging = false;
|
activated = true;
|
}
|
return activated;
|
},
|
|
/**
|
* Method: deactivate
|
* Deactivate the handler.
|
*
|
* Returns:
|
* {Boolean} The handler was successfully deactivated.
|
*/
|
deactivate: function() {
|
var deactivated = false;
|
if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
|
this.started = false;
|
this.dragging = false;
|
this.start = null;
|
this.last = null;
|
deactivated = true;
|
OpenLayers.Element.removeClass(
|
this.map.viewPortDiv, "olDragDown"
|
);
|
}
|
return deactivated;
|
},
|
|
/**
|
* Method: adjustXY
|
* Converts event coordinates that are relative to the document body to
|
* ones that are relative to the map viewport. The latter is the default in
|
* OpenLayers.
|
*
|
* Parameters:
|
* evt - {Object}
|
*/
|
adjustXY: function(evt) {
|
var pos = OpenLayers.Util.pagePosition(this.map.viewPortDiv);
|
evt.xy.x -= pos[0];
|
evt.xy.y -= pos[1];
|
},
|
|
/**
|
* Method: addDocumentEvents
|
* Start observing document events when documentDrag is true and the mouse
|
* cursor leaves the map viewport while dragging.
|
*/
|
addDocumentEvents: function() {
|
OpenLayers.Element.addClass(document.body, "olDragDown");
|
this.documentEvents = true;
|
OpenLayers.Event.observe(document, "mousemove", this._docMove);
|
OpenLayers.Event.observe(document, "mouseup", this._docUp);
|
},
|
|
/**
|
* Method: removeDocumentEvents
|
* Stops observing document events when documentDrag is true and the mouse
|
* cursor re-enters the map viewport while dragging.
|
*/
|
removeDocumentEvents: function() {
|
OpenLayers.Element.removeClass(document.body, "olDragDown");
|
this.documentEvents = false;
|
OpenLayers.Event.stopObserving(document, "mousemove", this._docMove);
|
OpenLayers.Event.stopObserving(document, "mouseup", this._docUp);
|
},
|
|
CLASS_NAME: "OpenLayers.Handler.Drag"
|
});
|
/* ======================================================================
|
OpenLayers/Control/OverviewMap.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/BaseTypes.js
|
* @requires OpenLayers/Events/buttonclick.js
|
* @requires OpenLayers/Map.js
|
* @requires OpenLayers/Handler/Click.js
|
* @requires OpenLayers/Handler/Drag.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.OverviewMap
|
* The OverMap control creates a small overview map, useful to display the
|
* extent of a zoomed map and your main map and provide additional
|
* navigation options to the User. By default the overview map is drawn in
|
* the lower right corner of the main map. Create a new overview map with the
|
* <OpenLayers.Control.OverviewMap> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.OverviewMap = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* Property: element
|
* {DOMElement} The DOM element that contains the overview map
|
*/
|
element: null,
|
|
/**
|
* APIProperty: ovmap
|
* {<OpenLayers.Map>} A reference to the overview map itself.
|
*/
|
ovmap: null,
|
|
/**
|
* APIProperty: size
|
* {<OpenLayers.Size>} The overvew map size in pixels. Note that this is
|
* the size of the map itself - the element that contains the map (default
|
* class name olControlOverviewMapElement) may have padding or other style
|
* attributes added via CSS.
|
*/
|
size: {w: 180, h: 90},
|
|
/**
|
* APIProperty: layers
|
* {Array(<OpenLayers.Layer>)} Ordered list of layers in the overview map.
|
* If none are sent at construction, the base layer for the main map is used.
|
*/
|
layers: null,
|
|
/**
|
* APIProperty: minRectSize
|
* {Integer} The minimum width or height (in pixels) of the extent
|
* rectangle on the overview map. When the extent rectangle reaches
|
* this size, it will be replaced depending on the value of the
|
* <minRectDisplayClass> property. Default is 15 pixels.
|
*/
|
minRectSize: 15,
|
|
/**
|
* APIProperty: minRectDisplayClass
|
* {String} Replacement style class name for the extent rectangle when
|
* <minRectSize> is reached. This string will be suffixed on to the
|
* displayClass. Default is "RectReplacement".
|
*
|
* Example CSS declaration:
|
* (code)
|
* .olControlOverviewMapRectReplacement {
|
* overflow: hidden;
|
* cursor: move;
|
* background-image: url("img/overview_replacement.gif");
|
* background-repeat: no-repeat;
|
* background-position: center;
|
* }
|
* (end)
|
*/
|
minRectDisplayClass: "RectReplacement",
|
|
/**
|
* APIProperty: minRatio
|
* {Float} The ratio of the overview map resolution to the main map
|
* resolution at which to zoom farther out on the overview map.
|
*/
|
minRatio: 8,
|
|
/**
|
* APIProperty: maxRatio
|
* {Float} The ratio of the overview map resolution to the main map
|
* resolution at which to zoom farther in on the overview map.
|
*/
|
maxRatio: 32,
|
|
/**
|
* APIProperty: mapOptions
|
* {Object} An object containing any non-default properties to be sent to
|
* the overview map's map constructor. These should include any
|
* non-default options that the main map was constructed with.
|
*/
|
mapOptions: null,
|
|
/**
|
* APIProperty: autoPan
|
* {Boolean} Always pan the overview map, so the extent marker remains in
|
* the center. Default is false. If true, when you drag the extent
|
* marker, the overview map will update itself so the marker returns
|
* to the center.
|
*/
|
autoPan: false,
|
|
/**
|
* Property: handlers
|
* {Object}
|
*/
|
handlers: null,
|
|
/**
|
* Property: resolutionFactor
|
* {Object}
|
*/
|
resolutionFactor: 1,
|
|
/**
|
* APIProperty: maximized
|
* {Boolean} Start as maximized (visible). Defaults to false.
|
*/
|
maximized: false,
|
|
/**
|
* APIProperty: maximizeTitle
|
* {String} This property is used for showing a tooltip over the
|
* maximize div. Defaults to "" (no title).
|
*/
|
maximizeTitle: "",
|
|
/**
|
* APIProperty: minimizeTitle
|
* {String} This property is used for showing a tooltip over the
|
* minimize div. Defaults to "" (no title).
|
*/
|
minimizeTitle: "",
|
|
/**
|
* Constructor: OpenLayers.Control.OverviewMap
|
* Create a new overview map
|
*
|
* Parameters:
|
* options - {Object} Properties of this object will be set on the overview
|
* map object. Note, to set options on the map object contained in this
|
* control, set <mapOptions> as one of the options properties.
|
*/
|
initialize: function(options) {
|
this.layers = [];
|
this.handlers = {};
|
OpenLayers.Control.prototype.initialize.apply(this, [options]);
|
},
|
|
/**
|
* APIMethod: destroy
|
* Deconstruct the control
|
*/
|
destroy: function() {
|
if (!this.mapDiv) { // we've already been destroyed
|
return;
|
}
|
if (this.handlers.click) {
|
this.handlers.click.destroy();
|
}
|
if (this.handlers.drag) {
|
this.handlers.drag.destroy();
|
}
|
|
this.ovmap && this.ovmap.viewPortDiv.removeChild(this.extentRectangle);
|
this.extentRectangle = null;
|
|
if (this.rectEvents) {
|
this.rectEvents.destroy();
|
this.rectEvents = null;
|
}
|
|
if (this.ovmap) {
|
this.ovmap.destroy();
|
this.ovmap = null;
|
}
|
|
this.element.removeChild(this.mapDiv);
|
this.mapDiv = null;
|
|
this.div.removeChild(this.element);
|
this.element = null;
|
|
if (this.maximizeDiv) {
|
this.div.removeChild(this.maximizeDiv);
|
this.maximizeDiv = null;
|
}
|
|
if (this.minimizeDiv) {
|
this.div.removeChild(this.minimizeDiv);
|
this.minimizeDiv = null;
|
}
|
|
this.map.events.un({
|
buttonclick: this.onButtonClick,
|
moveend: this.update,
|
changebaselayer: this.baseLayerDraw,
|
scope: this
|
});
|
|
OpenLayers.Control.prototype.destroy.apply(this, arguments);
|
},
|
|
/**
|
* Method: draw
|
* Render the control in the browser.
|
*/
|
draw: function() {
|
OpenLayers.Control.prototype.draw.apply(this, arguments);
|
if (this.layers.length === 0) {
|
if (this.map.baseLayer) {
|
var layer = this.map.baseLayer.clone();
|
this.layers = [layer];
|
} else {
|
this.map.events.register("changebaselayer", this, this.baseLayerDraw);
|
return this.div;
|
}
|
}
|
|
// create overview map DOM elements
|
this.element = document.createElement('div');
|
this.element.className = this.displayClass + 'Element';
|
this.element.style.display = 'none';
|
|
this.mapDiv = document.createElement('div');
|
this.mapDiv.style.width = this.size.w + 'px';
|
this.mapDiv.style.height = this.size.h + 'px';
|
this.mapDiv.style.position = 'relative';
|
this.mapDiv.style.overflow = 'hidden';
|
this.mapDiv.id = OpenLayers.Util.createUniqueID('overviewMap');
|
|
this.extentRectangle = document.createElement('div');
|
this.extentRectangle.style.position = 'absolute';
|
this.extentRectangle.style.zIndex = 1000; //HACK
|
this.extentRectangle.className = this.displayClass+'ExtentRectangle';
|
|
this.element.appendChild(this.mapDiv);
|
|
this.div.appendChild(this.element);
|
|
// Optionally add min/max buttons if the control will go in the
|
// map viewport.
|
if(!this.outsideViewport) {
|
this.div.className += " " + this.displayClass + 'Container';
|
// maximize button div
|
var img = OpenLayers.Util.getImageLocation('layer-switcher-maximize.png');
|
this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(
|
this.displayClass + 'MaximizeButton',
|
null,
|
null,
|
img,
|
'absolute');
|
this.maximizeDiv.style.display = 'none';
|
this.maximizeDiv.className = this.displayClass + 'MaximizeButton olButton';
|
if (this.maximizeTitle) {
|
this.maximizeDiv.title = this.maximizeTitle;
|
}
|
this.div.appendChild(this.maximizeDiv);
|
|
// minimize button div
|
var img = OpenLayers.Util.getImageLocation('layer-switcher-minimize.png');
|
this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(
|
'OpenLayers_Control_minimizeDiv',
|
null,
|
null,
|
img,
|
'absolute');
|
this.minimizeDiv.style.display = 'none';
|
this.minimizeDiv.className = this.displayClass + 'MinimizeButton olButton';
|
if (this.minimizeTitle) {
|
this.minimizeDiv.title = this.minimizeTitle;
|
}
|
this.div.appendChild(this.minimizeDiv);
|
this.minimizeControl();
|
} else {
|
// show the overview map
|
this.element.style.display = '';
|
}
|
if(this.map.getExtent()) {
|
this.update();
|
}
|
|
this.map.events.on({
|
buttonclick: this.onButtonClick,
|
moveend: this.update,
|
scope: this
|
});
|
|
if (this.maximized) {
|
this.maximizeControl();
|
}
|
return this.div;
|
},
|
|
/**
|
* Method: baseLayerDraw
|
* Draw the base layer - called if unable to complete in the initial draw
|
*/
|
baseLayerDraw: function() {
|
this.draw();
|
this.map.events.unregister("changebaselayer", this, this.baseLayerDraw);
|
},
|
|
/**
|
* Method: rectDrag
|
* Handle extent rectangle drag
|
*
|
* Parameters:
|
* px - {<OpenLayers.Pixel>} The pixel location of the drag.
|
*/
|
rectDrag: function(px) {
|
var deltaX = this.handlers.drag.last.x - px.x;
|
var deltaY = this.handlers.drag.last.y - px.y;
|
if(deltaX != 0 || deltaY != 0) {
|
var rectTop = this.rectPxBounds.top;
|
var rectLeft = this.rectPxBounds.left;
|
var rectHeight = Math.abs(this.rectPxBounds.getHeight());
|
var rectWidth = this.rectPxBounds.getWidth();
|
// don't allow dragging off of parent element
|
var newTop = Math.max(0, (rectTop - deltaY));
|
newTop = Math.min(newTop,
|
this.ovmap.size.h - this.hComp - rectHeight);
|
var newLeft = Math.max(0, (rectLeft - deltaX));
|
newLeft = Math.min(newLeft,
|
this.ovmap.size.w - this.wComp - rectWidth);
|
this.setRectPxBounds(new OpenLayers.Bounds(newLeft,
|
newTop + rectHeight,
|
newLeft + rectWidth,
|
newTop));
|
}
|
},
|
|
/**
|
* Method: mapDivClick
|
* Handle browser events
|
*
|
* Parameters:
|
* evt - {<OpenLayers.Event>} evt
|
*/
|
mapDivClick: function(evt) {
|
var pxCenter = this.rectPxBounds.getCenterPixel();
|
var deltaX = evt.xy.x - pxCenter.x;
|
var deltaY = evt.xy.y - pxCenter.y;
|
var top = this.rectPxBounds.top;
|
var left = this.rectPxBounds.left;
|
var height = Math.abs(this.rectPxBounds.getHeight());
|
var width = this.rectPxBounds.getWidth();
|
var newTop = Math.max(0, (top + deltaY));
|
newTop = Math.min(newTop, this.ovmap.size.h - height);
|
var newLeft = Math.max(0, (left + deltaX));
|
newLeft = Math.min(newLeft, this.ovmap.size.w - width);
|
this.setRectPxBounds(new OpenLayers.Bounds(newLeft,
|
newTop + height,
|
newLeft + width,
|
newTop));
|
this.updateMapToRect();
|
},
|
|
/**
|
* Method: onButtonClick
|
*
|
* Parameters:
|
* evt - {Event}
|
*/
|
onButtonClick: function(evt) {
|
if (evt.buttonElement === this.minimizeDiv) {
|
this.minimizeControl();
|
} else if (evt.buttonElement === this.maximizeDiv) {
|
this.maximizeControl();
|
}
|
},
|
|
/**
|
* Method: maximizeControl
|
* Unhide the control. Called when the control is in the map viewport.
|
*
|
* Parameters:
|
* e - {<OpenLayers.Event>}
|
*/
|
maximizeControl: function(e) {
|
this.element.style.display = '';
|
this.showToggle(false);
|
if (e != null) {
|
OpenLayers.Event.stop(e);
|
}
|
},
|
|
/**
|
* Method: minimizeControl
|
* Hide all the contents of the control, shrink the size,
|
* add the maximize icon
|
*
|
* Parameters:
|
* e - {<OpenLayers.Event>}
|
*/
|
minimizeControl: function(e) {
|
this.element.style.display = 'none';
|
this.showToggle(true);
|
if (e != null) {
|
OpenLayers.Event.stop(e);
|
}
|
},
|
|
/**
|
* Method: showToggle
|
* Hide/Show the toggle depending on whether the control is minimized
|
*
|
* Parameters:
|
* minimize - {Boolean}
|
*/
|
showToggle: function(minimize) {
|
if (this.maximizeDiv) {
|
this.maximizeDiv.style.display = minimize ? '' : 'none';
|
}
|
if (this.minimizeDiv) {
|
this.minimizeDiv.style.display = minimize ? 'none' : '';
|
}
|
},
|
|
/**
|
* Method: update
|
* Update the overview map after layers move.
|
*/
|
update: function() {
|
if(this.ovmap == null) {
|
this.createMap();
|
}
|
|
if(this.autoPan || !this.isSuitableOverview()) {
|
this.updateOverview();
|
}
|
|
// update extent rectangle
|
this.updateRectToMap();
|
},
|
|
/**
|
* Method: isSuitableOverview
|
* Determines if the overview map is suitable given the extent and
|
* resolution of the main map.
|
*/
|
isSuitableOverview: function() {
|
var mapExtent = this.map.getExtent();
|
var maxExtent = this.map.getMaxExtent();
|
var testExtent = new OpenLayers.Bounds(
|
Math.max(mapExtent.left, maxExtent.left),
|
Math.max(mapExtent.bottom, maxExtent.bottom),
|
Math.min(mapExtent.right, maxExtent.right),
|
Math.min(mapExtent.top, maxExtent.top));
|
|
if (this.ovmap.getProjection() != this.map.getProjection()) {
|
testExtent = testExtent.transform(
|
this.map.getProjectionObject(),
|
this.ovmap.getProjectionObject() );
|
}
|
|
var resRatio = this.ovmap.getResolution() / this.map.getResolution();
|
return ((resRatio > this.minRatio) &&
|
(resRatio <= this.maxRatio) &&
|
(this.ovmap.getExtent().containsBounds(testExtent)));
|
},
|
|
/**
|
* Method updateOverview
|
* Called by <update> if <isSuitableOverview> returns true
|
*/
|
updateOverview: function() {
|
var mapRes = this.map.getResolution();
|
var targetRes = this.ovmap.getResolution();
|
var resRatio = targetRes / mapRes;
|
if(resRatio > this.maxRatio) {
|
// zoom in overview map
|
targetRes = this.minRatio * mapRes;
|
} else if(resRatio <= this.minRatio) {
|
// zoom out overview map
|
targetRes = this.maxRatio * mapRes;
|
}
|
var center;
|
if (this.ovmap.getProjection() != this.map.getProjection()) {
|
center = this.map.center.clone();
|
center.transform(this.map.getProjectionObject(),
|
this.ovmap.getProjectionObject() );
|
} else {
|
center = this.map.center;
|
}
|
this.ovmap.setCenter(center, this.ovmap.getZoomForResolution(
|
targetRes * this.resolutionFactor));
|
this.updateRectToMap();
|
},
|
|
/**
|
* Method: createMap
|
* Construct the map that this control contains
|
*/
|
createMap: function() {
|
// create the overview map
|
var options = OpenLayers.Util.extend(
|
{controls: [], maxResolution: 'auto',
|
fallThrough: false}, this.mapOptions);
|
this.ovmap = new OpenLayers.Map(this.mapDiv, options);
|
this.ovmap.viewPortDiv.appendChild(this.extentRectangle);
|
|
// prevent ovmap from being destroyed when the page unloads, because
|
// the OverviewMap control has to do this (and does it).
|
OpenLayers.Event.stopObserving(window, 'unload', this.ovmap.unloadDestroy);
|
|
this.ovmap.addLayers(this.layers);
|
this.ovmap.zoomToMaxExtent();
|
// check extent rectangle border width
|
this.wComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
|
'border-left-width')) +
|
parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
|
'border-right-width'));
|
this.wComp = (this.wComp) ? this.wComp : 2;
|
this.hComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
|
'border-top-width')) +
|
parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
|
'border-bottom-width'));
|
this.hComp = (this.hComp) ? this.hComp : 2;
|
|
this.handlers.drag = new OpenLayers.Handler.Drag(
|
this, {move: this.rectDrag, done: this.updateMapToRect},
|
{map: this.ovmap}
|
);
|
this.handlers.click = new OpenLayers.Handler.Click(
|
this, {
|
"click": this.mapDivClick
|
},{
|
"single": true, "double": false,
|
"stopSingle": true, "stopDouble": true,
|
"pixelTolerance": 1,
|
map: this.ovmap
|
}
|
);
|
this.handlers.click.activate();
|
|
this.rectEvents = new OpenLayers.Events(this, this.extentRectangle,
|
null, true);
|
this.rectEvents.register("mouseover", this, function(e) {
|
if(!this.handlers.drag.active && !this.map.dragging) {
|
this.handlers.drag.activate();
|
}
|
});
|
this.rectEvents.register("mouseout", this, function(e) {
|
if(!this.handlers.drag.dragging) {
|
this.handlers.drag.deactivate();
|
}
|
});
|
|
if (this.ovmap.getProjection() != this.map.getProjection()) {
|
var sourceUnits = this.map.getProjectionObject().getUnits() ||
|
this.map.units || this.map.baseLayer.units;
|
var targetUnits = this.ovmap.getProjectionObject().getUnits() ||
|
this.ovmap.units || this.ovmap.baseLayer.units;
|
this.resolutionFactor = sourceUnits && targetUnits ?
|
OpenLayers.INCHES_PER_UNIT[sourceUnits] /
|
OpenLayers.INCHES_PER_UNIT[targetUnits] : 1;
|
}
|
},
|
|
/**
|
* Method: updateRectToMap
|
* Updates the extent rectangle position and size to match the map extent
|
*/
|
updateRectToMap: function() {
|
// If the projections differ we need to reproject
|
var bounds;
|
if (this.ovmap.getProjection() != this.map.getProjection()) {
|
bounds = this.map.getExtent().transform(
|
this.map.getProjectionObject(),
|
this.ovmap.getProjectionObject() );
|
} else {
|
bounds = this.map.getExtent();
|
}
|
var pxBounds = this.getRectBoundsFromMapBounds(bounds);
|
if (pxBounds) {
|
this.setRectPxBounds(pxBounds);
|
}
|
},
|
|
/**
|
* Method: updateMapToRect
|
* Updates the map extent to match the extent rectangle position and size
|
*/
|
updateMapToRect: function() {
|
var lonLatBounds = this.getMapBoundsFromRectBounds(this.rectPxBounds);
|
if (this.ovmap.getProjection() != this.map.getProjection()) {
|
lonLatBounds = lonLatBounds.transform(
|
this.ovmap.getProjectionObject(),
|
this.map.getProjectionObject() );
|
}
|
this.map.panTo(lonLatBounds.getCenterLonLat());
|
},
|
|
/**
|
* Method: setRectPxBounds
|
* Set extent rectangle pixel bounds.
|
*
|
* Parameters:
|
* pxBounds - {<OpenLayers.Bounds>}
|
*/
|
setRectPxBounds: function(pxBounds) {
|
var top = Math.max(pxBounds.top, 0);
|
var left = Math.max(pxBounds.left, 0);
|
var bottom = Math.min(pxBounds.top + Math.abs(pxBounds.getHeight()),
|
this.ovmap.size.h - this.hComp);
|
var right = Math.min(pxBounds.left + pxBounds.getWidth(),
|
this.ovmap.size.w - this.wComp);
|
var width = Math.max(right - left, 0);
|
var height = Math.max(bottom - top, 0);
|
if(width < this.minRectSize || height < this.minRectSize) {
|
this.extentRectangle.className = this.displayClass +
|
this.minRectDisplayClass;
|
var rLeft = left + (width / 2) - (this.minRectSize / 2);
|
var rTop = top + (height / 2) - (this.minRectSize / 2);
|
this.extentRectangle.style.top = Math.round(rTop) + 'px';
|
this.extentRectangle.style.left = Math.round(rLeft) + 'px';
|
this.extentRectangle.style.height = this.minRectSize + 'px';
|
this.extentRectangle.style.width = this.minRectSize + 'px';
|
} else {
|
this.extentRectangle.className = this.displayClass +
|
'ExtentRectangle';
|
this.extentRectangle.style.top = Math.round(top) + 'px';
|
this.extentRectangle.style.left = Math.round(left) + 'px';
|
this.extentRectangle.style.height = Math.round(height) + 'px';
|
this.extentRectangle.style.width = Math.round(width) + 'px';
|
}
|
this.rectPxBounds = new OpenLayers.Bounds(
|
Math.round(left), Math.round(bottom),
|
Math.round(right), Math.round(top)
|
);
|
},
|
|
/**
|
* Method: getRectBoundsFromMapBounds
|
* Get the rect bounds from the map bounds.
|
*
|
* Parameters:
|
* lonLatBounds - {<OpenLayers.Bounds>}
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>}A bounds which is the passed-in map lon/lat extent
|
* translated into pixel bounds for the overview map
|
*/
|
getRectBoundsFromMapBounds: function(lonLatBounds) {
|
var leftBottomPx = this.getOverviewPxFromLonLat({
|
lon: lonLatBounds.left,
|
lat: lonLatBounds.bottom
|
});
|
var rightTopPx = this.getOverviewPxFromLonLat({
|
lon: lonLatBounds.right,
|
lat: lonLatBounds.top
|
});
|
var bounds = null;
|
if (leftBottomPx && rightTopPx) {
|
bounds = new OpenLayers.Bounds(leftBottomPx.x, leftBottomPx.y,
|
rightTopPx.x, rightTopPx.y);
|
}
|
return bounds;
|
},
|
|
/**
|
* Method: getMapBoundsFromRectBounds
|
* Get the map bounds from the rect bounds.
|
*
|
* Parameters:
|
* pxBounds - {<OpenLayers.Bounds>}
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} Bounds which is the passed-in overview rect bounds
|
* translated into lon/lat bounds for the overview map
|
*/
|
getMapBoundsFromRectBounds: function(pxBounds) {
|
var leftBottomLonLat = this.getLonLatFromOverviewPx({
|
x: pxBounds.left,
|
y: pxBounds.bottom
|
});
|
var rightTopLonLat = this.getLonLatFromOverviewPx({
|
x: pxBounds.right,
|
y: pxBounds.top
|
});
|
return new OpenLayers.Bounds(leftBottomLonLat.lon, leftBottomLonLat.lat,
|
rightTopLonLat.lon, rightTopLonLat.lat);
|
},
|
|
/**
|
* Method: getLonLatFromOverviewPx
|
* Get a map location from a pixel location
|
*
|
* Parameters:
|
* overviewMapPx - {<OpenLayers.Pixel>|Object} OpenLayers.Pixel or
|
* an object with a
|
* 'x' and 'y' properties.
|
*
|
* Returns:
|
* {Object} Location which is the passed-in overview map
|
* OpenLayers.Pixel, translated into lon/lat by the overview
|
* map. An object with a 'lon' and 'lat' properties.
|
*/
|
getLonLatFromOverviewPx: function(overviewMapPx) {
|
var size = this.ovmap.size;
|
var res = this.ovmap.getResolution();
|
var center = this.ovmap.getExtent().getCenterLonLat();
|
|
var deltaX = overviewMapPx.x - (size.w / 2);
|
var deltaY = overviewMapPx.y - (size.h / 2);
|
|
return {
|
lon: center.lon + deltaX * res,
|
lat: center.lat - deltaY * res
|
};
|
},
|
|
/**
|
* Method: getOverviewPxFromLonLat
|
* Get a pixel location from a map location
|
*
|
* Parameters:
|
* lonlat - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an
|
* object with a 'lon' and 'lat' properties.
|
*
|
* Returns:
|
* {Object} Location which is the passed-in OpenLayers.LonLat,
|
* translated into overview map pixels
|
*/
|
getOverviewPxFromLonLat: function(lonlat) {
|
var res = this.ovmap.getResolution();
|
var extent = this.ovmap.getExtent();
|
if (extent) {
|
return {
|
x: Math.round(1/res * (lonlat.lon - extent.left)),
|
y: Math.round(1/res * (extent.top - lonlat.lat))
|
};
|
}
|
},
|
|
CLASS_NAME: 'OpenLayers.Control.OverviewMap'
|
});
|
/* ======================================================================
|
OpenLayers/Layer.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
* @requires OpenLayers/Map.js
|
* @requires OpenLayers/Projection.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer
|
*/
|
OpenLayers.Layer = OpenLayers.Class({
|
|
/**
|
* APIProperty: id
|
* {String}
|
*/
|
id: null,
|
|
/**
|
* APIProperty: name
|
* {String}
|
*/
|
name: null,
|
|
/**
|
* APIProperty: div
|
* {DOMElement}
|
*/
|
div: null,
|
|
/**
|
* APIProperty: opacity
|
* {Float} The layer's opacity. Float number between 0.0 and 1.0. Default
|
* is 1.
|
*/
|
opacity: 1,
|
|
/**
|
* APIProperty: alwaysInRange
|
* {Boolean} If a layer's display should not be scale-based, this should
|
* be set to true. This will cause the layer, as an overlay, to always
|
* be 'active', by always returning true from the calculateInRange()
|
* function.
|
*
|
* If not explicitly specified for a layer, its value will be
|
* determined on startup in initResolutions() based on whether or not
|
* any scale-specific properties have been set as options on the
|
* layer. If no scale-specific options have been set on the layer, we
|
* assume that it should always be in range.
|
*
|
* See #987 for more info.
|
*/
|
alwaysInRange: null,
|
|
/**
|
* Constant: RESOLUTION_PROPERTIES
|
* {Array} The properties that are used for calculating resolutions
|
* information.
|
*/
|
RESOLUTION_PROPERTIES: [
|
'scales', 'resolutions',
|
'maxScale', 'minScale',
|
'maxResolution', 'minResolution',
|
'numZoomLevels', 'maxZoomLevel'
|
],
|
|
/**
|
* APIProperty: events
|
* {<OpenLayers.Events>}
|
*
|
* Register a listener for a particular event with the following syntax:
|
* (code)
|
* layer.events.register(type, obj, listener);
|
* (end)
|
*
|
* Listeners will be called with a reference to an event object. The
|
* properties of this event depends on exactly what happened.
|
*
|
* All event objects have at least the following properties:
|
* object - {Object} A reference to layer.events.object.
|
* element - {DOMElement} A reference to layer.events.element.
|
*
|
* Supported map event types:
|
* loadstart - Triggered when layer loading starts. When using a Vector
|
* layer with a Fixed or BBOX strategy, the event object includes
|
* a *filter* property holding the OpenLayers.Filter used when
|
* calling read on the protocol.
|
* loadend - Triggered when layer loading ends. When using a Vector layer
|
* with a Fixed or BBOX strategy, the event object includes a
|
* *response* property holding an OpenLayers.Protocol.Response object.
|
* visibilitychanged - Triggered when the layer's visibility property is
|
* changed, e.g. by turning the layer on or off in the layer switcher.
|
* Note that the actual visibility of the layer can also change if it
|
* gets out of range (see <calculateInRange>). If you also want to catch
|
* these cases, register for the map's 'changelayer' event instead.
|
* move - Triggered when layer moves (triggered with every mousemove
|
* during a drag).
|
* moveend - Triggered when layer is done moving, object passed as
|
* argument has a zoomChanged boolean property which tells that the
|
* zoom has changed.
|
* added - Triggered after the layer is added to a map. Listeners will
|
* receive an object with a *map* property referencing the map and a
|
* *layer* property referencing the layer.
|
* removed - Triggered after the layer is removed from the map. Listeners
|
* will receive an object with a *map* property referencing the map and
|
* a *layer* property referencing the layer.
|
*/
|
events: null,
|
|
/**
|
* APIProperty: map
|
* {<OpenLayers.Map>} This variable is set when the layer is added to
|
* the map, via the accessor function setMap().
|
*/
|
map: null,
|
|
/**
|
* APIProperty: isBaseLayer
|
* {Boolean} Whether or not the layer is a base layer. This should be set
|
* individually by all subclasses. Default is false
|
*/
|
isBaseLayer: false,
|
|
/**
|
* Property: alpha
|
* {Boolean} The layer's images have an alpha channel. Default is false.
|
*/
|
alpha: false,
|
|
/**
|
* APIProperty: displayInLayerSwitcher
|
* {Boolean} Display the layer's name in the layer switcher. Default is
|
* true.
|
*/
|
displayInLayerSwitcher: true,
|
|
/**
|
* APIProperty: visibility
|
* {Boolean} The layer should be displayed in the map. Default is true.
|
*/
|
visibility: true,
|
|
/**
|
* APIProperty: attribution
|
* {String} Attribution string, displayed when an
|
* <OpenLayers.Control.Attribution> has been added to the map.
|
*/
|
attribution: null,
|
|
/**
|
* Property: inRange
|
* {Boolean} The current map resolution is within the layer's min/max
|
* range. This is set in <OpenLayers.Map.setCenter> whenever the zoom
|
* changes.
|
*/
|
inRange: false,
|
|
/**
|
* Propery: imageSize
|
* {<OpenLayers.Size>} For layers with a gutter, the image is larger than
|
* the tile by twice the gutter in each dimension.
|
*/
|
imageSize: null,
|
|
// OPTIONS
|
|
/**
|
* Property: options
|
* {Object} An optional object whose properties will be set on the layer.
|
* Any of the layer properties can be set as a property of the options
|
* object and sent to the constructor when the layer is created.
|
*/
|
options: null,
|
|
/**
|
* APIProperty: eventListeners
|
* {Object} If set as an option at construction, the eventListeners
|
* object will be registered with <OpenLayers.Events.on>. Object
|
* structure must be a listeners object as shown in the example for
|
* the events.on method.
|
*/
|
eventListeners: null,
|
|
/**
|
* APIProperty: gutter
|
* {Integer} Determines the width (in pixels) of the gutter around image
|
* tiles to ignore. By setting this property to a non-zero value,
|
* images will be requested that are wider and taller than the tile
|
* size by a value of 2 x gutter. This allows artifacts of rendering
|
* at tile edges to be ignored. Set a gutter value that is equal to
|
* half the size of the widest symbol that needs to be displayed.
|
* Defaults to zero. Non-tiled layers always have zero gutter.
|
*/
|
gutter: 0,
|
|
/**
|
* APIProperty: projection
|
* {<OpenLayers.Projection>} or {<String>} Specifies the projection of the layer.
|
* Can be set in the layer options. If not specified in the layer options,
|
* it is set to the default projection specified in the map,
|
* when the layer is added to the map.
|
* Projection along with default maxExtent and resolutions
|
* are set automatically with commercial baselayers in EPSG:3857,
|
* such as Google, Bing and OpenStreetMap, and do not need to be specified.
|
* Otherwise, if specifying projection, also set maxExtent,
|
* maxResolution or resolutions as appropriate.
|
* When using vector layers with strategies, layer projection should be set
|
* to the projection of the source data if that is different from the map default.
|
*
|
* Can be either a string or an <OpenLayers.Projection> object;
|
* if a string is passed, will be converted to an object when
|
* the layer is added to the map.
|
*
|
*/
|
projection: null,
|
|
/**
|
* APIProperty: units
|
* {String} The layer map units. Defaults to null. Possible values
|
* are 'degrees' (or 'dd'), 'm', 'ft', 'km', 'mi', 'inches'.
|
* Normally taken from the projection.
|
* Only required if both map and layers do not define a projection,
|
* or if they define a projection which does not define units.
|
*/
|
units: null,
|
|
/**
|
* APIProperty: scales
|
* {Array} An array of map scales in descending order. The values in the
|
* array correspond to the map scale denominator. Note that these
|
* values only make sense if the display (monitor) resolution of the
|
* client is correctly guessed by whomever is configuring the
|
* application. In addition, the units property must also be set.
|
* Use <resolutions> instead wherever possible.
|
*/
|
scales: null,
|
|
/**
|
* APIProperty: resolutions
|
* {Array} A list of map resolutions (map units per pixel) in descending
|
* order. If this is not set in the layer constructor, it will be set
|
* based on other resolution related properties (maxExtent,
|
* maxResolution, maxScale, etc.).
|
*/
|
resolutions: null,
|
|
/**
|
* APIProperty: maxExtent
|
* {<OpenLayers.Bounds>|Array} If provided as an array, the array
|
* should consist of four values (left, bottom, right, top).
|
* The maximum extent for the layer. Defaults to null.
|
*
|
* The center of these bounds will not stray outside
|
* of the viewport extent during panning. In addition, if
|
* <displayOutsideMaxExtent> is set to false, data will not be
|
* requested that falls completely outside of these bounds.
|
*/
|
maxExtent: null,
|
|
/**
|
* APIProperty: minExtent
|
* {<OpenLayers.Bounds>|Array} If provided as an array, the array
|
* should consist of four values (left, bottom, right, top).
|
* The minimum extent for the layer. Defaults to null.
|
*/
|
minExtent: null,
|
|
/**
|
* APIProperty: maxResolution
|
* {Float} Default max is 360 deg / 256 px, which corresponds to
|
* zoom level 0 on gmaps. Specify a different value in the layer
|
* options if you are not using the default <OpenLayers.Map.tileSize>
|
* and displaying the whole world.
|
*/
|
maxResolution: null,
|
|
/**
|
* APIProperty: minResolution
|
* {Float}
|
*/
|
minResolution: null,
|
|
/**
|
* APIProperty: numZoomLevels
|
* {Integer}
|
*/
|
numZoomLevels: null,
|
|
/**
|
* APIProperty: minScale
|
* {Float}
|
*/
|
minScale: null,
|
|
/**
|
* APIProperty: maxScale
|
* {Float}
|
*/
|
maxScale: null,
|
|
/**
|
* APIProperty: displayOutsideMaxExtent
|
* {Boolean} Request map tiles that are completely outside of the max
|
* extent for this layer. Defaults to false.
|
*/
|
displayOutsideMaxExtent: false,
|
|
/**
|
* APIProperty: wrapDateLine
|
* {Boolean} Wraps the world at the international dateline, so the map can
|
* be panned infinitely in longitudinal direction. Only use this on the
|
* base layer, and only if the layer's maxExtent equals the world bounds.
|
* #487 for more info.
|
*/
|
wrapDateLine: false,
|
|
/**
|
* Property: metadata
|
* {Object} This object can be used to store additional information on a
|
* layer object.
|
*/
|
metadata: null,
|
|
/**
|
* Constructor: OpenLayers.Layer
|
*
|
* Parameters:
|
* name - {String} The layer name
|
* options - {Object} Hashtable of extra options to tag onto the layer
|
*/
|
initialize: function(name, options) {
|
|
this.metadata = {};
|
|
options = OpenLayers.Util.extend({}, options);
|
// make sure we respect alwaysInRange if set on the prototype
|
if (this.alwaysInRange != null) {
|
options.alwaysInRange = this.alwaysInRange;
|
}
|
this.addOptions(options);
|
|
this.name = name;
|
|
if (this.id == null) {
|
|
this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
|
|
this.div = OpenLayers.Util.createDiv(this.id);
|
this.div.style.width = "100%";
|
this.div.style.height = "100%";
|
this.div.dir = "ltr";
|
|
this.events = new OpenLayers.Events(this, this.div);
|
if(this.eventListeners instanceof Object) {
|
this.events.on(this.eventListeners);
|
}
|
|
}
|
},
|
|
/**
|
* Method: destroy
|
* Destroy is a destructor: this is to alleviate cyclic references which
|
* the Javascript garbage cleaner can not take care of on its own.
|
*
|
* Parameters:
|
* setNewBaseLayer - {Boolean} Set a new base layer when this layer has
|
* been destroyed. Default is true.
|
*/
|
destroy: function(setNewBaseLayer) {
|
if (setNewBaseLayer == null) {
|
setNewBaseLayer = true;
|
}
|
if (this.map != null) {
|
this.map.removeLayer(this, setNewBaseLayer);
|
}
|
this.projection = null;
|
this.map = null;
|
this.name = null;
|
this.div = null;
|
this.options = null;
|
|
if (this.events) {
|
if(this.eventListeners) {
|
this.events.un(this.eventListeners);
|
}
|
this.events.destroy();
|
}
|
this.eventListeners = null;
|
this.events = null;
|
},
|
|
/**
|
* Method: clone
|
*
|
* Parameters:
|
* obj - {<OpenLayers.Layer>} The layer to be cloned
|
*
|
* Returns:
|
* {<OpenLayers.Layer>} An exact clone of this <OpenLayers.Layer>
|
*/
|
clone: function (obj) {
|
|
if (obj == null) {
|
obj = new OpenLayers.Layer(this.name, this.getOptions());
|
}
|
|
// catch any randomly tagged-on properties
|
OpenLayers.Util.applyDefaults(obj, this);
|
|
// a cloned layer should never have its map property set
|
// because it has not been added to a map yet.
|
obj.map = null;
|
|
return obj;
|
},
|
|
/**
|
* Method: getOptions
|
* Extracts an object from the layer with the properties that were set as
|
* options, but updates them with the values currently set on the
|
* instance.
|
*
|
* Returns:
|
* {Object} the <options> of the layer, representing the current state.
|
*/
|
getOptions: function() {
|
var options = {};
|
for(var o in this.options) {
|
options[o] = this[o];
|
}
|
return options;
|
},
|
|
/**
|
* APIMethod: setName
|
* Sets the new layer name for this layer. Can trigger a changelayer event
|
* on the map.
|
*
|
* Parameters:
|
* newName - {String} The new name.
|
*/
|
setName: function(newName) {
|
if (newName != this.name) {
|
this.name = newName;
|
if (this.map != null) {
|
this.map.events.triggerEvent("changelayer", {
|
layer: this,
|
property: "name"
|
});
|
}
|
}
|
},
|
|
/**
|
* APIMethod: addOptions
|
*
|
* Parameters:
|
* newOptions - {Object}
|
* reinitialize - {Boolean} If set to true, and if resolution options of the
|
* current baseLayer were changed, the map will be recentered to make
|
* sure that it is displayed with a valid resolution, and a
|
* changebaselayer event will be triggered.
|
*/
|
addOptions: function (newOptions, reinitialize) {
|
|
if (this.options == null) {
|
this.options = {};
|
}
|
|
if (newOptions) {
|
// make sure this.projection references a projection object
|
if(typeof newOptions.projection == "string") {
|
newOptions.projection = new OpenLayers.Projection(newOptions.projection);
|
}
|
if (newOptions.projection) {
|
// get maxResolution, units and maxExtent from projection defaults if
|
// they are not defined already
|
OpenLayers.Util.applyDefaults(newOptions,
|
OpenLayers.Projection.defaults[newOptions.projection.getCode()]);
|
}
|
// allow array for extents
|
if (newOptions.maxExtent && !(newOptions.maxExtent instanceof OpenLayers.Bounds)) {
|
newOptions.maxExtent = new OpenLayers.Bounds(newOptions.maxExtent);
|
}
|
if (newOptions.minExtent && !(newOptions.minExtent instanceof OpenLayers.Bounds)) {
|
newOptions.minExtent = new OpenLayers.Bounds(newOptions.minExtent);
|
}
|
}
|
|
// update our copy for clone
|
OpenLayers.Util.extend(this.options, newOptions);
|
|
// add new options to this
|
OpenLayers.Util.extend(this, newOptions);
|
|
// get the units from the projection, if we have a projection
|
// and it it has units
|
if(this.projection && this.projection.getUnits()) {
|
this.units = this.projection.getUnits();
|
}
|
|
// re-initialize resolutions if necessary, i.e. if any of the
|
// properties of the "properties" array defined below is set
|
// in the new options
|
if(this.map) {
|
// store current resolution so we can try to restore it later
|
var resolution = this.map.getResolution();
|
var properties = this.RESOLUTION_PROPERTIES.concat(
|
["projection", "units", "minExtent", "maxExtent"]
|
);
|
for(var o in newOptions) {
|
if(newOptions.hasOwnProperty(o) &&
|
OpenLayers.Util.indexOf(properties, o) >= 0) {
|
|
this.initResolutions();
|
if (reinitialize && this.map.baseLayer === this) {
|
// update map position, and restore previous resolution
|
this.map.setCenter(this.map.getCenter(),
|
this.map.getZoomForResolution(resolution),
|
false, true
|
);
|
// trigger a changebaselayer event to make sure that
|
// all controls (especially
|
// OpenLayers.Control.PanZoomBar) get notified of the
|
// new options
|
this.map.events.triggerEvent("changebaselayer", {
|
layer: this
|
});
|
}
|
break;
|
}
|
}
|
}
|
},
|
|
/**
|
* APIMethod: onMapResize
|
* This function can be implemented by subclasses
|
*/
|
onMapResize: function() {
|
//this function can be implemented by subclasses
|
},
|
|
/**
|
* APIMethod: redraw
|
* Redraws the layer. Returns true if the layer was redrawn, false if not.
|
*
|
* Returns:
|
* {Boolean} The layer was redrawn.
|
*/
|
redraw: function() {
|
var redrawn = false;
|
if (this.map) {
|
|
// min/max Range may have changed
|
this.inRange = this.calculateInRange();
|
|
// map's center might not yet be set
|
var extent = this.getExtent();
|
|
if (extent && this.inRange && this.visibility) {
|
var zoomChanged = true;
|
this.moveTo(extent, zoomChanged, false);
|
this.events.triggerEvent("moveend",
|
{"zoomChanged": zoomChanged});
|
redrawn = true;
|
}
|
}
|
return redrawn;
|
},
|
|
/**
|
* Method: moveTo
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
* zoomChanged - {Boolean} Tells when zoom has changed, as layers have to
|
* do some init work in that case.
|
* dragging - {Boolean}
|
*/
|
moveTo:function(bounds, zoomChanged, dragging) {
|
var display = this.visibility;
|
if (!this.isBaseLayer) {
|
display = display && this.inRange;
|
}
|
this.display(display);
|
},
|
|
/**
|
* Method: moveByPx
|
* Move the layer based on pixel vector. To be implemented by subclasses.
|
*
|
* Parameters:
|
* dx - {Number} The x coord of the displacement vector.
|
* dy - {Number} The y coord of the displacement vector.
|
*/
|
moveByPx: function(dx, dy) {
|
},
|
|
/**
|
* Method: setMap
|
* Set the map property for the layer. This is done through an accessor
|
* so that subclasses can override this and take special action once
|
* they have their map variable set.
|
*
|
* Here we take care to bring over any of the necessary default
|
* properties from the map.
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
setMap: function(map) {
|
if (this.map == null) {
|
|
this.map = map;
|
|
// grab some essential layer data from the map if it hasn't already
|
// been set
|
this.maxExtent = this.maxExtent || this.map.maxExtent;
|
this.minExtent = this.minExtent || this.map.minExtent;
|
|
this.projection = this.projection || this.map.projection;
|
if (typeof this.projection == "string") {
|
this.projection = new OpenLayers.Projection(this.projection);
|
}
|
|
// Check the projection to see if we can get units -- if not, refer
|
// to properties.
|
this.units = this.projection.getUnits() ||
|
this.units || this.map.units;
|
|
this.initResolutions();
|
|
if (!this.isBaseLayer) {
|
this.inRange = this.calculateInRange();
|
var show = ((this.visibility) && (this.inRange));
|
this.div.style.display = show ? "" : "none";
|
}
|
|
// deal with gutters
|
this.setTileSize();
|
}
|
},
|
|
/**
|
* Method: afterAdd
|
* Called at the end of the map.addLayer sequence. At this point, the map
|
* will have a base layer. To be overridden by subclasses.
|
*/
|
afterAdd: function() {
|
},
|
|
/**
|
* APIMethod: removeMap
|
* Just as setMap() allows each layer the possibility to take a
|
* personalized action on being added to the map, removeMap() allows
|
* each layer to take a personalized action on being removed from it.
|
* For now, this will be mostly unused, except for the EventPane layer,
|
* which needs this hook so that it can remove the special invisible
|
* pane.
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
removeMap: function(map) {
|
//to be overridden by subclasses
|
},
|
|
/**
|
* APIMethod: getImageSize
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>} optional tile bounds, can be used
|
* by subclasses that have to deal with different tile sizes at the
|
* layer extent edges (e.g. Zoomify)
|
*
|
* Returns:
|
* {<OpenLayers.Size>} The size that the image should be, taking into
|
* account gutters.
|
*/
|
getImageSize: function(bounds) {
|
return (this.imageSize || this.tileSize);
|
},
|
|
/**
|
* APIMethod: setTileSize
|
* Set the tile size based on the map size. This also sets layer.imageSize
|
* or use by Tile.Image.
|
*
|
* Parameters:
|
* size - {<OpenLayers.Size>}
|
*/
|
setTileSize: function(size) {
|
var tileSize = (size) ? size :
|
((this.tileSize) ? this.tileSize :
|
this.map.getTileSize());
|
this.tileSize = tileSize;
|
if(this.gutter) {
|
// layers with gutters need non-null tile sizes
|
//if(tileSize == null) {
|
// OpenLayers.console.error("Error in layer.setMap() for " +
|
// this.name + ": layers with " +
|
// "gutters need non-null tile sizes");
|
//}
|
this.imageSize = new OpenLayers.Size(tileSize.w + (2*this.gutter),
|
tileSize.h + (2*this.gutter));
|
}
|
},
|
|
/**
|
* APIMethod: getVisibility
|
*
|
* Returns:
|
* {Boolean} The layer should be displayed (if in range).
|
*/
|
getVisibility: function() {
|
return this.visibility;
|
},
|
|
/**
|
* APIMethod: setVisibility
|
* Set the visibility flag for the layer and hide/show & redraw
|
* accordingly. Fire event unless otherwise specified
|
*
|
* Note that visibility is no longer simply whether or not the layer's
|
* style.display is set to "block". Now we store a 'visibility' state
|
* property on the layer class, this allows us to remember whether or
|
* not we *desire* for a layer to be visible. In the case where the
|
* map's resolution is out of the layer's range, this desire may be
|
* subverted.
|
*
|
* Parameters:
|
* visibility - {Boolean} Whether or not to display the layer (if in range)
|
*/
|
setVisibility: function(visibility) {
|
if (visibility != this.visibility) {
|
this.visibility = visibility;
|
this.display(visibility);
|
this.redraw();
|
if (this.map != null) {
|
this.map.events.triggerEvent("changelayer", {
|
layer: this,
|
property: "visibility"
|
});
|
}
|
this.events.triggerEvent("visibilitychanged");
|
}
|
},
|
|
/**
|
* APIMethod: display
|
* Hide or show the Layer. This is designed to be used internally, and
|
* is not generally the way to enable or disable the layer. For that,
|
* use the setVisibility function instead..
|
*
|
* Parameters:
|
* display - {Boolean}
|
*/
|
display: function(display) {
|
if (display != (this.div.style.display != "none")) {
|
this.div.style.display = (display && this.calculateInRange()) ? "block" : "none";
|
}
|
},
|
|
/**
|
* APIMethod: calculateInRange
|
*
|
* Returns:
|
* {Boolean} The layer is displayable at the current map's current
|
* resolution. Note that if 'alwaysInRange' is true for the layer,
|
* this function will always return true.
|
*/
|
calculateInRange: function() {
|
var inRange = false;
|
|
if (this.alwaysInRange) {
|
inRange = true;
|
} else {
|
if (this.map) {
|
var resolution = this.map.getResolution();
|
inRange = ( (resolution >= this.minResolution) &&
|
(resolution <= this.maxResolution) );
|
}
|
}
|
return inRange;
|
},
|
|
/**
|
* APIMethod: setIsBaseLayer
|
*
|
* Parameters:
|
* isBaseLayer - {Boolean}
|
*/
|
setIsBaseLayer: function(isBaseLayer) {
|
if (isBaseLayer != this.isBaseLayer) {
|
this.isBaseLayer = isBaseLayer;
|
if (this.map != null) {
|
this.map.events.triggerEvent("changebaselayer", {
|
layer: this
|
});
|
}
|
}
|
},
|
|
/********************************************************/
|
/* */
|
/* Baselayer Functions */
|
/* */
|
/********************************************************/
|
|
/**
|
* Method: initResolutions
|
* This method's responsibility is to set up the 'resolutions' array
|
* for the layer -- this array is what the layer will use to interface
|
* between the zoom levels of the map and the resolution display
|
* of the layer.
|
*
|
* The user has several options that determine how the array is set up.
|
*
|
* For a detailed explanation, see the following wiki from the
|
* openlayers.org homepage:
|
* http://trac.openlayers.org/wiki/SettingZoomLevels
|
*/
|
initResolutions: function() {
|
|
// ok we want resolutions, here's our strategy:
|
//
|
// 1. if resolutions are defined in the layer config, use them
|
// 2. else, if scales are defined in the layer config then derive
|
// resolutions from these scales
|
// 3. else, attempt to calculate resolutions from maxResolution,
|
// minResolution, numZoomLevels, maxZoomLevel set in the
|
// layer config
|
// 4. if we still don't have resolutions, and if resolutions
|
// are defined in the same, use them
|
// 5. else, if scales are defined in the map then derive
|
// resolutions from these scales
|
// 6. else, attempt to calculate resolutions from maxResolution,
|
// minResolution, numZoomLevels, maxZoomLevel set in the
|
// map
|
// 7. hope for the best!
|
|
var i, len, p;
|
var props = {}, alwaysInRange = true;
|
|
// get resolution data from layer config
|
// (we also set alwaysInRange in the layer as appropriate)
|
for(i=0, len=this.RESOLUTION_PROPERTIES.length; i<len; i++) {
|
p = this.RESOLUTION_PROPERTIES[i];
|
props[p] = this.options[p];
|
if(alwaysInRange && this.options[p]) {
|
alwaysInRange = false;
|
}
|
}
|
if(this.options.alwaysInRange == null) {
|
this.alwaysInRange = alwaysInRange;
|
}
|
|
// if we don't have resolutions then attempt to derive them from scales
|
if(props.resolutions == null) {
|
props.resolutions = this.resolutionsFromScales(props.scales);
|
}
|
|
// if we still don't have resolutions then attempt to calculate them
|
if(props.resolutions == null) {
|
props.resolutions = this.calculateResolutions(props);
|
}
|
|
// if we couldn't calculate resolutions then we look at we have
|
// in the map
|
if(props.resolutions == null) {
|
for(i=0, len=this.RESOLUTION_PROPERTIES.length; i<len; i++) {
|
p = this.RESOLUTION_PROPERTIES[i];
|
props[p] = this.options[p] != null ?
|
this.options[p] : this.map[p];
|
}
|
if(props.resolutions == null) {
|
props.resolutions = this.resolutionsFromScales(props.scales);
|
}
|
if(props.resolutions == null) {
|
props.resolutions = this.calculateResolutions(props);
|
}
|
}
|
|
// ok, we new need to set properties in the instance
|
|
// get maxResolution from the config if it's defined there
|
var maxResolution;
|
if(this.options.maxResolution &&
|
this.options.maxResolution !== "auto") {
|
maxResolution = this.options.maxResolution;
|
}
|
if(this.options.minScale) {
|
maxResolution = OpenLayers.Util.getResolutionFromScale(
|
this.options.minScale, this.units);
|
}
|
|
// get minResolution from the config if it's defined there
|
var minResolution;
|
if(this.options.minResolution &&
|
this.options.minResolution !== "auto") {
|
minResolution = this.options.minResolution;
|
}
|
if(this.options.maxScale) {
|
minResolution = OpenLayers.Util.getResolutionFromScale(
|
this.options.maxScale, this.units);
|
}
|
|
if(props.resolutions) {
|
|
//sort resolutions array descendingly
|
props.resolutions.sort(function(a, b) {
|
return (b - a);
|
});
|
|
// if we still don't have a maxResolution get it from the
|
// resolutions array
|
if(!maxResolution) {
|
maxResolution = props.resolutions[0];
|
}
|
|
// if we still don't have a minResolution get it from the
|
// resolutions array
|
if(!minResolution) {
|
var lastIdx = props.resolutions.length - 1;
|
minResolution = props.resolutions[lastIdx];
|
}
|
}
|
|
this.resolutions = props.resolutions;
|
if(this.resolutions) {
|
len = this.resolutions.length;
|
this.scales = new Array(len);
|
for(i=0; i<len; i++) {
|
this.scales[i] = OpenLayers.Util.getScaleFromResolution(
|
this.resolutions[i], this.units);
|
}
|
this.numZoomLevels = len;
|
}
|
this.minResolution = minResolution;
|
if(minResolution) {
|
this.maxScale = OpenLayers.Util.getScaleFromResolution(
|
minResolution, this.units);
|
}
|
this.maxResolution = maxResolution;
|
if(maxResolution) {
|
this.minScale = OpenLayers.Util.getScaleFromResolution(
|
maxResolution, this.units);
|
}
|
},
|
|
/**
|
* Method: resolutionsFromScales
|
* Derive resolutions from scales.
|
*
|
* Parameters:
|
* scales - {Array(Number)} Scales
|
*
|
* Returns
|
* {Array(Number)} Resolutions
|
*/
|
resolutionsFromScales: function(scales) {
|
if(scales == null) {
|
return;
|
}
|
var resolutions, i, len;
|
len = scales.length;
|
resolutions = new Array(len);
|
for(i=0; i<len; i++) {
|
resolutions[i] = OpenLayers.Util.getResolutionFromScale(
|
scales[i], this.units);
|
}
|
return resolutions;
|
},
|
|
/**
|
* Method: calculateResolutions
|
* Calculate resolutions based on the provided properties.
|
*
|
* Parameters:
|
* props - {Object} Properties
|
*
|
* Returns:
|
* {Array({Number})} Array of resolutions.
|
*/
|
calculateResolutions: function(props) {
|
|
var viewSize, wRes, hRes;
|
|
// determine maxResolution
|
var maxResolution = props.maxResolution;
|
if(props.minScale != null) {
|
maxResolution =
|
OpenLayers.Util.getResolutionFromScale(props.minScale,
|
this.units);
|
} else if(maxResolution == "auto" && this.maxExtent != null) {
|
viewSize = this.map.getSize();
|
wRes = this.maxExtent.getWidth() / viewSize.w;
|
hRes = this.maxExtent.getHeight() / viewSize.h;
|
maxResolution = Math.max(wRes, hRes);
|
}
|
|
// determine minResolution
|
var minResolution = props.minResolution;
|
if(props.maxScale != null) {
|
minResolution =
|
OpenLayers.Util.getResolutionFromScale(props.maxScale,
|
this.units);
|
} else if(props.minResolution == "auto" && this.minExtent != null) {
|
viewSize = this.map.getSize();
|
wRes = this.minExtent.getWidth() / viewSize.w;
|
hRes = this.minExtent.getHeight()/ viewSize.h;
|
minResolution = Math.max(wRes, hRes);
|
}
|
|
if(typeof maxResolution !== "number" &&
|
typeof minResolution !== "number" &&
|
this.maxExtent != null) {
|
// maxResolution for default grid sets assumes that at zoom
|
// level zero, the whole world fits on one tile.
|
var tileSize = this.map.getTileSize();
|
maxResolution = Math.max(
|
this.maxExtent.getWidth() / tileSize.w,
|
this.maxExtent.getHeight() / tileSize.h
|
);
|
}
|
|
// determine numZoomLevels
|
var maxZoomLevel = props.maxZoomLevel;
|
var numZoomLevels = props.numZoomLevels;
|
if(typeof minResolution === "number" &&
|
typeof maxResolution === "number" && numZoomLevels === undefined) {
|
var ratio = maxResolution / minResolution;
|
numZoomLevels = Math.floor(Math.log(ratio) / Math.log(2)) + 1;
|
} else if(numZoomLevels === undefined && maxZoomLevel != null) {
|
numZoomLevels = maxZoomLevel + 1;
|
}
|
|
// are we able to calculate resolutions?
|
if(typeof numZoomLevels !== "number" || numZoomLevels <= 0 ||
|
(typeof maxResolution !== "number" &&
|
typeof minResolution !== "number")) {
|
return;
|
}
|
|
// now we have numZoomLevels and at least one of maxResolution
|
// or minResolution, we can populate the resolutions array
|
|
var resolutions = new Array(numZoomLevels);
|
var base = 2;
|
if(typeof minResolution == "number" &&
|
typeof maxResolution == "number") {
|
// if maxResolution and minResolution are set, we calculate
|
// the base for exponential scaling that starts at
|
// maxResolution and ends at minResolution in numZoomLevels
|
// steps.
|
base = Math.pow(
|
(maxResolution / minResolution),
|
(1 / (numZoomLevels - 1))
|
);
|
}
|
|
var i;
|
if(typeof maxResolution === "number") {
|
for(i=0; i<numZoomLevels; i++) {
|
resolutions[i] = maxResolution / Math.pow(base, i);
|
}
|
} else {
|
for(i=0; i<numZoomLevels; i++) {
|
resolutions[numZoomLevels - 1 - i] =
|
minResolution * Math.pow(base, i);
|
}
|
}
|
|
return resolutions;
|
},
|
|
/**
|
* APIMethod: getResolution
|
*
|
* Returns:
|
* {Float} The currently selected resolution of the map, taken from the
|
* resolutions array, indexed by current zoom level.
|
*/
|
getResolution: function() {
|
var zoom = this.map.getZoom();
|
return this.getResolutionForZoom(zoom);
|
},
|
|
/**
|
* APIMethod: getExtent
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat
|
* bounds of the current viewPort.
|
*/
|
getExtent: function() {
|
// just use stock map calculateBounds function -- passing no arguments
|
// means it will user map's current center & resolution
|
//
|
return this.map.calculateBounds();
|
},
|
|
/**
|
* APIMethod: getZoomForExtent
|
*
|
* Parameters:
|
* extent - {<OpenLayers.Bounds>}
|
* closest - {Boolean} Find the zoom level that most closely fits the
|
* specified bounds. Note that this may result in a zoom that does
|
* not exactly contain the entire extent.
|
* Default is false.
|
*
|
* Returns:
|
* {Integer} The index of the zoomLevel (entry in the resolutions array)
|
* for the passed-in extent. We do this by calculating the ideal
|
* resolution for the given extent (based on the map size) and then
|
* calling getZoomForResolution(), passing along the 'closest'
|
* parameter.
|
*/
|
getZoomForExtent: function(extent, closest) {
|
var viewSize = this.map.getSize();
|
var idealResolution = Math.max( extent.getWidth() / viewSize.w,
|
extent.getHeight() / viewSize.h );
|
|
return this.getZoomForResolution(idealResolution, closest);
|
},
|
|
/**
|
* Method: getDataExtent
|
* Calculates the max extent which includes all of the data for the layer.
|
* This function is to be implemented by subclasses.
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>}
|
*/
|
getDataExtent: function () {
|
//to be implemented by subclasses
|
},
|
|
/**
|
* APIMethod: getResolutionForZoom
|
*
|
* Parameters:
|
* zoom - {Float}
|
*
|
* Returns:
|
* {Float} A suitable resolution for the specified zoom.
|
*/
|
getResolutionForZoom: function(zoom) {
|
zoom = Math.max(0, Math.min(zoom, this.resolutions.length - 1));
|
var resolution;
|
if(this.map.fractionalZoom) {
|
var low = Math.floor(zoom);
|
var high = Math.ceil(zoom);
|
resolution = this.resolutions[low] -
|
((zoom-low) * (this.resolutions[low]-this.resolutions[high]));
|
} else {
|
resolution = this.resolutions[Math.round(zoom)];
|
}
|
return resolution;
|
},
|
|
/**
|
* APIMethod: getZoomForResolution
|
*
|
* Parameters:
|
* resolution - {Float}
|
* closest - {Boolean} Find the zoom level that corresponds to the absolute
|
* closest resolution, which may result in a zoom whose corresponding
|
* resolution is actually smaller than we would have desired (if this
|
* is being called from a getZoomForExtent() call, then this means that
|
* the returned zoom index might not actually contain the entire
|
* extent specified... but it'll be close).
|
* Default is false.
|
*
|
* Returns:
|
* {Integer} The index of the zoomLevel (entry in the resolutions array)
|
* that corresponds to the best fit resolution given the passed in
|
* value and the 'closest' specification.
|
*/
|
getZoomForResolution: function(resolution, closest) {
|
var zoom, i, len;
|
if(this.map.fractionalZoom) {
|
var lowZoom = 0;
|
var highZoom = this.resolutions.length - 1;
|
var highRes = this.resolutions[lowZoom];
|
var lowRes = this.resolutions[highZoom];
|
var res;
|
for(i=0, len=this.resolutions.length; i<len; ++i) {
|
res = this.resolutions[i];
|
if(res >= resolution) {
|
highRes = res;
|
lowZoom = i;
|
}
|
if(res <= resolution) {
|
lowRes = res;
|
highZoom = i;
|
break;
|
}
|
}
|
var dRes = highRes - lowRes;
|
if(dRes > 0) {
|
zoom = lowZoom + ((highRes - resolution) / dRes);
|
} else {
|
zoom = lowZoom;
|
}
|
} else {
|
var diff;
|
var minDiff = Number.POSITIVE_INFINITY;
|
for(i=0, len=this.resolutions.length; i<len; i++) {
|
if (closest) {
|
diff = Math.abs(this.resolutions[i] - resolution);
|
if (diff > minDiff) {
|
break;
|
}
|
minDiff = diff;
|
} else {
|
if (this.resolutions[i] < resolution) {
|
break;
|
}
|
}
|
}
|
zoom = Math.max(0, i-1);
|
}
|
return zoom;
|
},
|
|
/**
|
* APIMethod: getLonLatFromViewPortPx
|
*
|
* Parameters:
|
* viewPortPx - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or
|
* an object with a 'x'
|
* and 'y' properties.
|
*
|
* Returns:
|
* {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in
|
* view port <OpenLayers.Pixel>, translated into lon/lat by the layer.
|
*/
|
getLonLatFromViewPortPx: function (viewPortPx) {
|
var lonlat = null;
|
var map = this.map;
|
if (viewPortPx != null && map.minPx) {
|
var res = map.getResolution();
|
var maxExtent = map.getMaxExtent({restricted: true});
|
var lon = (viewPortPx.x - map.minPx.x) * res + maxExtent.left;
|
var lat = (map.minPx.y - viewPortPx.y) * res + maxExtent.top;
|
lonlat = new OpenLayers.LonLat(lon, lat);
|
|
if (this.wrapDateLine) {
|
lonlat = lonlat.wrapDateLine(this.maxExtent);
|
}
|
}
|
return lonlat;
|
},
|
|
/**
|
* APIMethod: getViewPortPxFromLonLat
|
* Returns a pixel location given a map location. This method will return
|
* fractional pixel values.
|
*
|
* Parameters:
|
* lonlat - {<OpenLayers.LonLat>|Object} An OpenLayers.LonLat or
|
* an object with a 'lon'
|
* and 'lat' properties.
|
*
|
* Returns:
|
* {<OpenLayers.Pixel>} An <OpenLayers.Pixel> which is the passed-in
|
* lonlat translated into view port pixels.
|
*/
|
getViewPortPxFromLonLat: function (lonlat, resolution) {
|
var px = null;
|
if (lonlat != null) {
|
resolution = resolution || this.map.getResolution();
|
var extent = this.map.calculateBounds(null, resolution);
|
px = new OpenLayers.Pixel(
|
(1/resolution * (lonlat.lon - extent.left)),
|
(1/resolution * (extent.top - lonlat.lat))
|
);
|
}
|
return px;
|
},
|
|
/**
|
* APIMethod: setOpacity
|
* Sets the opacity for the entire layer (all images)
|
*
|
* Parameters:
|
* opacity - {Float}
|
*/
|
setOpacity: function(opacity) {
|
if (opacity != this.opacity) {
|
this.opacity = opacity;
|
var childNodes = this.div.childNodes;
|
for(var i = 0, len = childNodes.length; i < len; ++i) {
|
var element = childNodes[i].firstChild || childNodes[i];
|
var lastChild = childNodes[i].lastChild;
|
//TODO de-uglify this
|
if (lastChild && lastChild.nodeName.toLowerCase() === "iframe") {
|
element = lastChild.parentNode;
|
}
|
OpenLayers.Util.modifyDOMElement(element, null, null, null,
|
null, null, null, opacity);
|
}
|
if (this.map != null) {
|
this.map.events.triggerEvent("changelayer", {
|
layer: this,
|
property: "opacity"
|
});
|
}
|
}
|
},
|
|
/**
|
* Method: getZIndex
|
*
|
* Returns:
|
* {Integer} the z-index of this layer
|
*/
|
getZIndex: function () {
|
return this.div.style.zIndex;
|
},
|
|
/**
|
* Method: setZIndex
|
*
|
* Parameters:
|
* zIndex - {Integer}
|
*/
|
setZIndex: function (zIndex) {
|
this.div.style.zIndex = zIndex;
|
},
|
|
/**
|
* Method: adjustBounds
|
* This function will take a bounds, and if wrapDateLine option is set
|
* on the layer, it will return a bounds which is wrapped around the
|
* world. We do not wrap for bounds which *cross* the
|
* maxExtent.left/right, only bounds which are entirely to the left
|
* or entirely to the right.
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
*/
|
adjustBounds: function (bounds) {
|
|
if (this.gutter) {
|
// Adjust the extent of a bounds in map units by the
|
// layer's gutter in pixels.
|
var mapGutter = this.gutter * this.map.getResolution();
|
bounds = new OpenLayers.Bounds(bounds.left - mapGutter,
|
bounds.bottom - mapGutter,
|
bounds.right + mapGutter,
|
bounds.top + mapGutter);
|
}
|
|
if (this.wrapDateLine) {
|
// wrap around the date line, within the limits of rounding error
|
var wrappingOptions = {
|
'rightTolerance':this.getResolution(),
|
'leftTolerance':this.getResolution()
|
};
|
bounds = bounds.wrapDateLine(this.maxExtent, wrappingOptions);
|
|
}
|
return bounds;
|
},
|
|
CLASS_NAME: "OpenLayers.Layer"
|
});
|
/* ======================================================================
|
OpenLayers/Layer/SphericalMercator.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Layer.js
|
* @requires OpenLayers/Projection.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.SphericalMercator
|
* A mixin for layers that wraps up the pieces neccesary to have a coordinate
|
* conversion for working with commercial APIs which use a spherical
|
* mercator projection. Using this layer as a base layer, additional
|
* layers can be used as overlays if they are in the same projection.
|
*
|
* A layer is given properties of this object by setting the sphericalMercator
|
* property to true.
|
*
|
* More projection information:
|
* - http://spatialreference.org/ref/user/google-projection/
|
*
|
* Proj4 Text:
|
* +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0
|
* +k=1.0 +units=m +nadgrids=@null +no_defs
|
*
|
* WKT:
|
* 900913=PROJCS["WGS84 / Simple Mercator", GEOGCS["WGS 84",
|
* DATUM["WGS_1984", SPHEROID["WGS_1984", 6378137.0, 298.257223563]],
|
* PRIMEM["Greenwich", 0.0], UNIT["degree", 0.017453292519943295],
|
* AXIS["Longitude", EAST], AXIS["Latitude", NORTH]],
|
* PROJECTION["Mercator_1SP_Google"],
|
* PARAMETER["latitude_of_origin", 0.0], PARAMETER["central_meridian", 0.0],
|
* PARAMETER["scale_factor", 1.0], PARAMETER["false_easting", 0.0],
|
* PARAMETER["false_northing", 0.0], UNIT["m", 1.0], AXIS["x", EAST],
|
* AXIS["y", NORTH], AUTHORITY["EPSG","900913"]]
|
*/
|
OpenLayers.Layer.SphericalMercator = {
|
|
/**
|
* Method: getExtent
|
* Get the map's extent.
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} The map extent.
|
*/
|
getExtent: function() {
|
var extent = null;
|
if (this.sphericalMercator) {
|
extent = this.map.calculateBounds();
|
} else {
|
extent = OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this);
|
}
|
return extent;
|
},
|
|
/**
|
* Method: getLonLatFromViewPortPx
|
* Get a map location from a pixel location
|
*
|
* Parameters:
|
* viewPortPx - {<OpenLayers.Pixel>}
|
*
|
* Returns:
|
* {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view
|
* port OpenLayers.Pixel, translated into lon/lat by map lib
|
* If the map lib is not loaded or not centered, returns null
|
*/
|
getLonLatFromViewPortPx: function (viewPortPx) {
|
return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this, arguments);
|
},
|
|
/**
|
* Method: getViewPortPxFromLonLat
|
* Get a pixel location from a map location
|
*
|
* Parameters:
|
* lonlat - {<OpenLayers.LonLat>}
|
*
|
* Returns:
|
* {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in
|
* OpenLayers.LonLat, translated into view port pixels by map lib
|
* If map lib is not loaded or not centered, returns null
|
*/
|
getViewPortPxFromLonLat: function (lonlat) {
|
return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this, arguments);
|
},
|
|
/**
|
* Method: initMercatorParameters
|
* Set up the mercator parameters on the layer: resolutions,
|
* projection, units.
|
*/
|
initMercatorParameters: function() {
|
// set up properties for Mercator - assume EPSG:900913
|
this.RESOLUTIONS = [];
|
var maxResolution = 156543.03390625;
|
for(var zoom=0; zoom<=this.MAX_ZOOM_LEVEL; ++zoom) {
|
this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom);
|
}
|
this.units = "m";
|
this.projection = this.projection || "EPSG:900913";
|
},
|
|
/**
|
* APIMethod: forwardMercator
|
* Given a lon,lat in EPSG:4326, return a point in Spherical Mercator.
|
*
|
* Parameters:
|
* lon - {float}
|
* lat - {float}
|
*
|
* Returns:
|
* {<OpenLayers.LonLat>} The coordinates transformed to Mercator.
|
*/
|
forwardMercator: (function() {
|
var gg = new OpenLayers.Projection("EPSG:4326");
|
var sm = new OpenLayers.Projection("EPSG:900913");
|
return function(lon, lat) {
|
var point = OpenLayers.Projection.transform({x: lon, y: lat}, gg, sm);
|
return new OpenLayers.LonLat(point.x, point.y);
|
};
|
})(),
|
|
/**
|
* APIMethod: inverseMercator
|
* Given a x,y in Spherical Mercator, return a point in EPSG:4326.
|
*
|
* Parameters:
|
* x - {float} A map x in Spherical Mercator.
|
* y - {float} A map y in Spherical Mercator.
|
*
|
* Returns:
|
* {<OpenLayers.LonLat>} The coordinates transformed to EPSG:4326.
|
*/
|
inverseMercator: (function() {
|
var gg = new OpenLayers.Projection("EPSG:4326");
|
var sm = new OpenLayers.Projection("EPSG:900913");
|
return function(x, y) {
|
var point = OpenLayers.Projection.transform({x: x, y: y}, sm, gg);
|
return new OpenLayers.LonLat(point.x, point.y);
|
};
|
})()
|
|
};
|
/* ======================================================================
|
OpenLayers/Layer/EventPane.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Layer.js
|
* @requires OpenLayers/Util.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.EventPane
|
* Base class for 3rd party layers, providing a DOM element which isolates
|
* the 3rd-party layer from mouse events.
|
* Only used by Google layers.
|
*
|
* Automatically instantiated by the Google constructor, and not usually instantiated directly.
|
*
|
* Create a new event pane layer with the
|
* <OpenLayers.Layer.EventPane> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer>
|
*/
|
OpenLayers.Layer.EventPane = OpenLayers.Class(OpenLayers.Layer, {
|
|
/**
|
* APIProperty: smoothDragPan
|
* {Boolean} smoothDragPan determines whether non-public/internal API
|
* methods are used for better performance while dragging EventPane
|
* layers. When not in sphericalMercator mode, the smoother dragging
|
* doesn't actually move north/south directly with the number of
|
* pixels moved, resulting in a slight offset when you drag your mouse
|
* north south with this option on. If this visual disparity bothers
|
* you, you should turn this option off, or use spherical mercator.
|
* Default is on.
|
*/
|
smoothDragPan: true,
|
|
/**
|
* Property: isBaseLayer
|
* {Boolean} EventPaned layers are always base layers, by necessity.
|
*/
|
isBaseLayer: true,
|
|
/**
|
* APIProperty: isFixed
|
* {Boolean} EventPaned layers are fixed by default.
|
*/
|
isFixed: true,
|
|
/**
|
* Property: pane
|
* {DOMElement} A reference to the element that controls the events.
|
*/
|
pane: null,
|
|
|
/**
|
* Property: mapObject
|
* {Object} This is the object which will be used to load the 3rd party library
|
* in the case of the google layer, this will be of type GMap,
|
* in the case of the ve layer, this will be of type VEMap
|
*/
|
mapObject: null,
|
|
|
/**
|
* Constructor: OpenLayers.Layer.EventPane
|
* Create a new event pane layer
|
*
|
* Parameters:
|
* name - {String}
|
* options - {Object} Hashtable of extra options to tag onto the layer
|
*/
|
initialize: function(name, options) {
|
OpenLayers.Layer.prototype.initialize.apply(this, arguments);
|
if (this.pane == null) {
|
this.pane = OpenLayers.Util.createDiv(this.div.id + "_EventPane");
|
}
|
},
|
|
/**
|
* APIMethod: destroy
|
* Deconstruct this layer.
|
*/
|
destroy: function() {
|
this.mapObject = null;
|
this.pane = null;
|
OpenLayers.Layer.prototype.destroy.apply(this, arguments);
|
},
|
|
|
/**
|
* Method: setMap
|
* Set the map property for the layer. This is done through an accessor
|
* so that subclasses can override this and take special action once
|
* they have their map variable set.
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
setMap: function(map) {
|
OpenLayers.Layer.prototype.setMap.apply(this, arguments);
|
|
this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;
|
this.pane.style.display = this.div.style.display;
|
this.pane.style.width="100%";
|
this.pane.style.height="100%";
|
if (OpenLayers.BROWSER_NAME == "msie") {
|
this.pane.style.background =
|
"url(" + OpenLayers.Util.getImageLocation("blank.gif") + ")";
|
}
|
|
if (this.isFixed) {
|
this.map.viewPortDiv.appendChild(this.pane);
|
} else {
|
this.map.layerContainerDiv.appendChild(this.pane);
|
}
|
|
// once our layer has been added to the map, we can load it
|
this.loadMapObject();
|
|
// if map didn't load, display warning
|
if (this.mapObject == null) {
|
this.loadWarningMessage();
|
}
|
},
|
|
/**
|
* APIMethod: removeMap
|
* On being removed from the map, we'll like to remove the invisible 'pane'
|
* div that we added to it on creation.
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
removeMap: function(map) {
|
if (this.pane && this.pane.parentNode) {
|
this.pane.parentNode.removeChild(this.pane);
|
}
|
OpenLayers.Layer.prototype.removeMap.apply(this, arguments);
|
},
|
|
/**
|
* Method: loadWarningMessage
|
* If we can't load the map lib, then display an error message to the
|
* user and tell them where to go for help.
|
*
|
* This function sets up the layout for the warning message. Each 3rd
|
* party layer must implement its own getWarningHTML() function to
|
* provide the actual warning message.
|
*/
|
loadWarningMessage:function() {
|
|
this.div.style.backgroundColor = "darkblue";
|
|
var viewSize = this.map.getSize();
|
|
var msgW = Math.min(viewSize.w, 300);
|
var msgH = Math.min(viewSize.h, 200);
|
var size = new OpenLayers.Size(msgW, msgH);
|
|
var centerPx = new OpenLayers.Pixel(viewSize.w/2, viewSize.h/2);
|
|
var topLeft = centerPx.add(-size.w/2, -size.h/2);
|
|
var div = OpenLayers.Util.createDiv(this.name + "_warning",
|
topLeft,
|
size,
|
null,
|
null,
|
null,
|
"auto");
|
|
div.style.padding = "7px";
|
div.style.backgroundColor = "yellow";
|
|
div.innerHTML = this.getWarningHTML();
|
this.div.appendChild(div);
|
},
|
|
/**
|
* Method: getWarningHTML
|
* To be implemented by subclasses.
|
*
|
* Returns:
|
* {String} String with information on why layer is broken, how to get
|
* it working.
|
*/
|
getWarningHTML:function() {
|
//should be implemented by subclasses
|
return "";
|
},
|
|
/**
|
* Method: display
|
* Set the display on the pane
|
*
|
* Parameters:
|
* display - {Boolean}
|
*/
|
display: function(display) {
|
OpenLayers.Layer.prototype.display.apply(this, arguments);
|
this.pane.style.display = this.div.style.display;
|
},
|
|
/**
|
* Method: setZIndex
|
* Set the z-index order for the pane.
|
*
|
* Parameters:
|
* zIndex - {int}
|
*/
|
setZIndex: function (zIndex) {
|
OpenLayers.Layer.prototype.setZIndex.apply(this, arguments);
|
this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;
|
},
|
|
/**
|
* Method: moveByPx
|
* Move the layer based on pixel vector. To be implemented by subclasses.
|
*
|
* Parameters:
|
* dx - {Number} The x coord of the displacement vector.
|
* dy - {Number} The y coord of the displacement vector.
|
*/
|
moveByPx: function(dx, dy) {
|
OpenLayers.Layer.prototype.moveByPx.apply(this, arguments);
|
|
if (this.dragPanMapObject) {
|
this.dragPanMapObject(dx, -dy);
|
} else {
|
this.moveTo(this.map.getCachedCenter());
|
}
|
},
|
|
/**
|
* Method: moveTo
|
* Handle calls to move the layer.
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
* zoomChanged - {Boolean}
|
* dragging - {Boolean}
|
*/
|
moveTo:function(bounds, zoomChanged, dragging) {
|
OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
|
|
if (this.mapObject != null) {
|
|
var newCenter = this.map.getCenter();
|
var newZoom = this.map.getZoom();
|
|
if (newCenter != null) {
|
|
var moOldCenter = this.getMapObjectCenter();
|
var oldCenter = this.getOLLonLatFromMapObjectLonLat(moOldCenter);
|
|
var moOldZoom = this.getMapObjectZoom();
|
var oldZoom= this.getOLZoomFromMapObjectZoom(moOldZoom);
|
|
if (!(newCenter.equals(oldCenter)) || newZoom != oldZoom) {
|
|
if (!zoomChanged && oldCenter && this.dragPanMapObject &&
|
this.smoothDragPan) {
|
var oldPx = this.map.getViewPortPxFromLonLat(oldCenter);
|
var newPx = this.map.getViewPortPxFromLonLat(newCenter);
|
this.dragPanMapObject(newPx.x-oldPx.x, oldPx.y-newPx.y);
|
} else {
|
var center = this.getMapObjectLonLatFromOLLonLat(newCenter);
|
var zoom = this.getMapObjectZoomFromOLZoom(newZoom);
|
this.setMapObjectCenter(center, zoom, dragging);
|
}
|
}
|
}
|
}
|
},
|
|
|
/********************************************************/
|
/* */
|
/* Baselayer Functions */
|
/* */
|
/********************************************************/
|
|
/**
|
* Method: getLonLatFromViewPortPx
|
* Get a map location from a pixel location
|
*
|
* Parameters:
|
* viewPortPx - {<OpenLayers.Pixel>}
|
*
|
* Returns:
|
* {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view
|
* port OpenLayers.Pixel, translated into lon/lat by map lib
|
* If the map lib is not loaded or not centered, returns null
|
*/
|
getLonLatFromViewPortPx: function (viewPortPx) {
|
var lonlat = null;
|
if ( (this.mapObject != null) &&
|
(this.getMapObjectCenter() != null) ) {
|
var moPixel = this.getMapObjectPixelFromOLPixel(viewPortPx);
|
var moLonLat = this.getMapObjectLonLatFromMapObjectPixel(moPixel);
|
lonlat = this.getOLLonLatFromMapObjectLonLat(moLonLat);
|
}
|
return lonlat;
|
},
|
|
|
/**
|
* Method: getViewPortPxFromLonLat
|
* Get a pixel location from a map location
|
*
|
* Parameters:
|
* lonlat - {<OpenLayers.LonLat>}
|
*
|
* Returns:
|
* {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in
|
* OpenLayers.LonLat, translated into view port pixels by map lib
|
* If map lib is not loaded or not centered, returns null
|
*/
|
getViewPortPxFromLonLat: function (lonlat) {
|
var viewPortPx = null;
|
if ( (this.mapObject != null) &&
|
(this.getMapObjectCenter() != null) ) {
|
|
var moLonLat = this.getMapObjectLonLatFromOLLonLat(lonlat);
|
var moPixel = this.getMapObjectPixelFromMapObjectLonLat(moLonLat);
|
|
viewPortPx = this.getOLPixelFromMapObjectPixel(moPixel);
|
}
|
return viewPortPx;
|
},
|
|
/********************************************************/
|
/* */
|
/* Translation Functions */
|
/* */
|
/* The following functions translate Map Object and */
|
/* OL formats for Pixel, LonLat */
|
/* */
|
/********************************************************/
|
|
//
|
// TRANSLATION: MapObject LatLng <-> OpenLayers.LonLat
|
//
|
|
/**
|
* Method: getOLLonLatFromMapObjectLonLat
|
* Get an OL style map location from a 3rd party style map location
|
*
|
* Parameters
|
* moLonLat - {Object}
|
*
|
* Returns:
|
* {<OpenLayers.LonLat>} An OpenLayers.LonLat, translated from the passed in
|
* MapObject LonLat
|
* Returns null if null value is passed in
|
*/
|
getOLLonLatFromMapObjectLonLat: function(moLonLat) {
|
var olLonLat = null;
|
if (moLonLat != null) {
|
var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);
|
var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);
|
olLonLat = new OpenLayers.LonLat(lon, lat);
|
}
|
return olLonLat;
|
},
|
|
/**
|
* Method: getMapObjectLonLatFromOLLonLat
|
* Get a 3rd party map location from an OL map location.
|
*
|
* Parameters:
|
* olLonLat - {<OpenLayers.LonLat>}
|
*
|
* Returns:
|
* {Object} A MapObject LonLat, translated from the passed in
|
* OpenLayers.LonLat
|
* Returns null if null value is passed in
|
*/
|
getMapObjectLonLatFromOLLonLat: function(olLonLat) {
|
var moLatLng = null;
|
if (olLonLat != null) {
|
moLatLng = this.getMapObjectLonLatFromLonLat(olLonLat.lon,
|
olLonLat.lat);
|
}
|
return moLatLng;
|
},
|
|
|
//
|
// TRANSLATION: MapObject Pixel <-> OpenLayers.Pixel
|
//
|
|
/**
|
* Method: getOLPixelFromMapObjectPixel
|
* Get an OL pixel location from a 3rd party pixel location.
|
*
|
* Parameters:
|
* moPixel - {Object}
|
*
|
* Returns:
|
* {<OpenLayers.Pixel>} An OpenLayers.Pixel, translated from the passed in
|
* MapObject Pixel
|
* Returns null if null value is passed in
|
*/
|
getOLPixelFromMapObjectPixel: function(moPixel) {
|
var olPixel = null;
|
if (moPixel != null) {
|
var x = this.getXFromMapObjectPixel(moPixel);
|
var y = this.getYFromMapObjectPixel(moPixel);
|
olPixel = new OpenLayers.Pixel(x, y);
|
}
|
return olPixel;
|
},
|
|
/**
|
* Method: getMapObjectPixelFromOLPixel
|
* Get a 3rd party pixel location from an OL pixel location
|
*
|
* Parameters:
|
* olPixel - {<OpenLayers.Pixel>}
|
*
|
* Returns:
|
* {Object} A MapObject Pixel, translated from the passed in
|
* OpenLayers.Pixel
|
* Returns null if null value is passed in
|
*/
|
getMapObjectPixelFromOLPixel: function(olPixel) {
|
var moPixel = null;
|
if (olPixel != null) {
|
moPixel = this.getMapObjectPixelFromXY(olPixel.x, olPixel.y);
|
}
|
return moPixel;
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.EventPane"
|
});
|
/* ======================================================================
|
OpenLayers/Layer/FixedZoomLevels.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Layer.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.FixedZoomLevels
|
* Some Layers will already have established zoom levels (like google
|
* or ve). Instead of trying to determine them and populate a resolutions[]
|
* Array with those values, we will hijack the resolution functionality
|
* here.
|
*
|
* When you subclass FixedZoomLevels:
|
*
|
* The initResolutions() call gets nullified, meaning no resolutions[] array
|
* is set up. Which would be a big problem getResolution() in Layer, since
|
* it merely takes map.zoom and indexes into resolutions[]... but....
|
*
|
* The getResolution() call is also overridden. Instead of using the
|
* resolutions[] array, we simply calculate the current resolution based
|
* on the current extent and the current map size. But how will we be able
|
* to calculate the current extent without knowing the resolution...?
|
*
|
* The getExtent() function is also overridden. Instead of calculating extent
|
* based on the center point and the current resolution, we instead
|
* calculate the extent by getting the lonlats at the top-left and
|
* bottom-right by using the getLonLatFromViewPortPx() translation function,
|
* taken from the pixel locations (0,0) and the size of the map. But how
|
* will we be able to do lonlat-px translation without resolution....?
|
*
|
* The getZoomForResolution() method is overridden. Instead of indexing into
|
* the resolutions[] array, we call OpenLayers.Layer.getExent(), passing in
|
* the desired resolution. With this extent, we then call getZoomForExtent()
|
*
|
*
|
* Whenever you implement a layer using OpenLayers.Layer.FixedZoomLevels,
|
* it is your responsibility to provide the following three functions:
|
*
|
* - getLonLatFromViewPortPx
|
* - getViewPortPxFromLonLat
|
* - getZoomForExtent
|
*
|
* ...those three functions should generally be provided by any reasonable
|
* API that you might be working from.
|
*
|
*/
|
OpenLayers.Layer.FixedZoomLevels = OpenLayers.Class({
|
|
/********************************************************/
|
/* */
|
/* Baselayer Functions */
|
/* */
|
/* The following functions must all be implemented */
|
/* by all base layers */
|
/* */
|
/********************************************************/
|
|
/**
|
* Constructor: OpenLayers.Layer.FixedZoomLevels
|
* Create a new fixed zoom levels layer.
|
*/
|
initialize: function() {
|
//this class is only just to add the following functions...
|
// nothing to actually do here... but it is probably a good
|
// idea to have layers that use these functions call this
|
// inititalize() anyways, in case at some point we decide we
|
// do want to put some functionality or state in here.
|
},
|
|
/**
|
* Method: initResolutions
|
* Populate the resolutions array
|
*/
|
initResolutions: function() {
|
|
var props = ['minZoomLevel', 'maxZoomLevel', 'numZoomLevels'];
|
|
for(var i=0, len=props.length; i<len; i++) {
|
var property = props[i];
|
this[property] = (this.options[property] != null)
|
? this.options[property]
|
: this.map[property];
|
}
|
|
if ( (this.minZoomLevel == null) ||
|
(this.minZoomLevel < this.MIN_ZOOM_LEVEL) ){
|
this.minZoomLevel = this.MIN_ZOOM_LEVEL;
|
}
|
|
//
|
// At this point, we know what the minimum desired zoom level is, and
|
// we must calculate the total number of zoom levels.
|
//
|
// Because we allow for the setting of either the 'numZoomLevels'
|
// or the 'maxZoomLevel' properties... on either the layer or the
|
// map, we have to define some rules to see which we take into
|
// account first in this calculation.
|
//
|
// The following is the precedence list for these properties:
|
//
|
// (1) numZoomLevels set on layer
|
// (2) maxZoomLevel set on layer
|
// (3) numZoomLevels set on map
|
// (4) maxZoomLevel set on map*
|
// (5) none of the above*
|
//
|
// *Note that options (4) and (5) are only possible if the user
|
// _explicitly_ sets the 'numZoomLevels' property on the map to
|
// null, since it is set by default to 16.
|
//
|
|
//
|
// Note to future: In 3.0, I think we should remove the default
|
// value of 16 for map.numZoomLevels. Rather, I think that value
|
// should be set as a default on the Layer.WMS class. If someone
|
// creates a 3rd party layer and does not specify any 'minZoomLevel',
|
// 'maxZoomLevel', or 'numZoomLevels', and has not explicitly
|
// specified any of those on the map object either.. then I think
|
// it is fair to say that s/he wants all the zoom levels available.
|
//
|
// By making map.numZoomLevels *null* by default, that will be the
|
// case. As it is, I don't feel comfortable changing that right now
|
// as it would be a glaring API change and actually would probably
|
// break many peoples' codes.
|
//
|
|
//the number of zoom levels we'd like to have.
|
var desiredZoomLevels;
|
|
//this is the maximum number of zoom levels the layer will allow,
|
// given the specified starting minimum zoom level.
|
var limitZoomLevels = this.MAX_ZOOM_LEVEL - this.minZoomLevel + 1;
|
|
if ( ((this.options.numZoomLevels == null) &&
|
(this.options.maxZoomLevel != null)) // (2)
|
||
|
((this.numZoomLevels == null) &&
|
(this.maxZoomLevel != null)) // (4)
|
) {
|
//calculate based on specified maxZoomLevel (on layer or map)
|
desiredZoomLevels = this.maxZoomLevel - this.minZoomLevel + 1;
|
} else {
|
//calculate based on specified numZoomLevels (on layer or map)
|
// this covers cases (1) and (3)
|
desiredZoomLevels = this.numZoomLevels;
|
}
|
|
if (desiredZoomLevels != null) {
|
//Now that we know what we would *like* the number of zoom levels
|
// to be, based on layer or map options, we have to make sure that
|
// it does not conflict with the actual limit, as specified by
|
// the constants on the layer itself (and calculated into the
|
// 'limitZoomLevels' variable).
|
this.numZoomLevels = Math.min(desiredZoomLevels, limitZoomLevels);
|
} else {
|
// case (5) -- neither 'numZoomLevels' not 'maxZoomLevel' was
|
// set on either the layer or the map. So we just use the
|
// maximum limit as calculated by the layer's constants.
|
this.numZoomLevels = limitZoomLevels;
|
}
|
|
//now that the 'numZoomLevels' is appropriately, safely set,
|
// we go back and re-calculate the 'maxZoomLevel'.
|
this.maxZoomLevel = this.minZoomLevel + this.numZoomLevels - 1;
|
|
if (this.RESOLUTIONS != null) {
|
var resolutionsIndex = 0;
|
this.resolutions = [];
|
for(var i= this.minZoomLevel; i <= this.maxZoomLevel; i++) {
|
this.resolutions[resolutionsIndex++] = this.RESOLUTIONS[i];
|
}
|
this.maxResolution = this.resolutions[0];
|
this.minResolution = this.resolutions[this.resolutions.length - 1];
|
}
|
},
|
|
/**
|
* APIMethod: getResolution
|
* Get the current map resolution
|
*
|
* Returns:
|
* {Float} Map units per Pixel
|
*/
|
getResolution: function() {
|
|
if (this.resolutions != null) {
|
return OpenLayers.Layer.prototype.getResolution.apply(this, arguments);
|
} else {
|
var resolution = null;
|
|
var viewSize = this.map.getSize();
|
var extent = this.getExtent();
|
|
if ((viewSize != null) && (extent != null)) {
|
resolution = Math.max( extent.getWidth() / viewSize.w,
|
extent.getHeight() / viewSize.h );
|
}
|
return resolution;
|
}
|
},
|
|
/**
|
* APIMethod: getExtent
|
* Calculates using px-> lonlat translation functions on tl and br
|
* corners of viewport
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat
|
* bounds of the current viewPort.
|
*/
|
getExtent: function () {
|
var size = this.map.getSize();
|
var tl = this.getLonLatFromViewPortPx({
|
x: 0, y: 0
|
});
|
var br = this.getLonLatFromViewPortPx({
|
x: size.w, y: size.h
|
});
|
|
if ((tl != null) && (br != null)) {
|
return new OpenLayers.Bounds(tl.lon, br.lat, br.lon, tl.lat);
|
} else {
|
return null;
|
}
|
},
|
|
/**
|
* Method: getZoomForResolution
|
* Get the zoom level for a given resolution
|
*
|
* Parameters:
|
* resolution - {Float}
|
*
|
* Returns:
|
* {Integer} A suitable zoom level for the specified resolution.
|
* If no baselayer is set, returns null.
|
*/
|
getZoomForResolution: function(resolution) {
|
|
if (this.resolutions != null) {
|
return OpenLayers.Layer.prototype.getZoomForResolution.apply(this, arguments);
|
} else {
|
var extent = OpenLayers.Layer.prototype.getExtent.apply(this, []);
|
return this.getZoomForExtent(extent);
|
}
|
},
|
|
|
|
|
/********************************************************/
|
/* */
|
/* Translation Functions */
|
/* */
|
/* The following functions translate GMaps and OL */
|
/* formats for Pixel, LonLat, Bounds, and Zoom */
|
/* */
|
/********************************************************/
|
|
|
//
|
// TRANSLATION: MapObject Zoom <-> OpenLayers Zoom
|
//
|
|
/**
|
* Method: getOLZoomFromMapObjectZoom
|
* Get the OL zoom index from the map object zoom level
|
*
|
* Parameters:
|
* moZoom - {Integer}
|
*
|
* Returns:
|
* {Integer} An OpenLayers Zoom level, translated from the passed in zoom
|
* Returns null if null value is passed in
|
*/
|
getOLZoomFromMapObjectZoom: function(moZoom) {
|
var zoom = null;
|
if (moZoom != null) {
|
zoom = moZoom - this.minZoomLevel;
|
if (this.map.baseLayer !== this) {
|
zoom = this.map.baseLayer.getZoomForResolution(
|
this.getResolutionForZoom(zoom)
|
);
|
}
|
}
|
return zoom;
|
},
|
|
/**
|
* Method: getMapObjectZoomFromOLZoom
|
* Get the map object zoom level from the OL zoom level
|
*
|
* Parameters:
|
* olZoom - {Integer}
|
*
|
* Returns:
|
* {Integer} A MapObject level, translated from the passed in olZoom
|
* Returns null if null value is passed in
|
*/
|
getMapObjectZoomFromOLZoom: function(olZoom) {
|
var zoom = null;
|
if (olZoom != null) {
|
zoom = olZoom + this.minZoomLevel;
|
if (this.map.baseLayer !== this) {
|
zoom = this.getZoomForResolution(
|
this.map.baseLayer.getResolutionForZoom(zoom)
|
);
|
}
|
}
|
return zoom;
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.FixedZoomLevels"
|
});
|
|
/* ======================================================================
|
OpenLayers/Layer/Google.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Layer/SphericalMercator.js
|
* @requires OpenLayers/Layer/EventPane.js
|
* @requires OpenLayers/Layer/FixedZoomLevels.js
|
* @requires OpenLayers/Lang.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.Google
|
*
|
* Provides a wrapper for Google's Maps API
|
* Normally the Terms of Use for this API do not allow wrapping, but Google
|
* have provided written consent to OpenLayers for this - see email in
|
* http://osgeo-org.1560.n6.nabble.com/Google-Maps-API-Terms-of-Use-changes-tp4910013p4911981.html
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer.SphericalMercator>
|
* - <OpenLayers.Layer.EventPane>
|
* - <OpenLayers.Layer.FixedZoomLevels>
|
*/
|
OpenLayers.Layer.Google = OpenLayers.Class(
|
OpenLayers.Layer.EventPane,
|
OpenLayers.Layer.FixedZoomLevels, {
|
|
/**
|
* Constant: MIN_ZOOM_LEVEL
|
* {Integer} 0
|
*/
|
MIN_ZOOM_LEVEL: 0,
|
|
/**
|
* Constant: MAX_ZOOM_LEVEL
|
* {Integer} 21
|
*/
|
MAX_ZOOM_LEVEL: 21,
|
|
/**
|
* Constant: RESOLUTIONS
|
* {Array(Float)} Hardcode these resolutions so that they are more closely
|
* tied with the standard wms projection
|
*/
|
RESOLUTIONS: [
|
1.40625,
|
0.703125,
|
0.3515625,
|
0.17578125,
|
0.087890625,
|
0.0439453125,
|
0.02197265625,
|
0.010986328125,
|
0.0054931640625,
|
0.00274658203125,
|
0.001373291015625,
|
0.0006866455078125,
|
0.00034332275390625,
|
0.000171661376953125,
|
0.0000858306884765625,
|
0.00004291534423828125,
|
0.00002145767211914062,
|
0.00001072883605957031,
|
0.00000536441802978515,
|
0.00000268220901489257,
|
0.0000013411045074462891,
|
0.00000067055225372314453
|
],
|
|
/**
|
* APIProperty: type
|
* {GMapType}
|
*/
|
type: null,
|
|
/**
|
* APIProperty: wrapDateLine
|
* {Boolean} Allow user to pan forever east/west. Default is true.
|
* Setting this to false only restricts panning if
|
* <sphericalMercator> is true.
|
*/
|
wrapDateLine: true,
|
|
/**
|
* APIProperty: sphericalMercator
|
* {Boolean} Should the map act as a mercator-projected map? This will
|
* cause all interactions with the map to be in the actual map
|
* projection, which allows support for vector drawing, overlaying
|
* other maps, etc.
|
*/
|
sphericalMercator: false,
|
|
/**
|
* Property: version
|
* {Number} The version of the Google Maps API
|
*/
|
version: null,
|
|
/**
|
* Constructor: OpenLayers.Layer.Google
|
*
|
* Parameters:
|
* name - {String} A name for the layer.
|
* options - {Object} An optional object whose properties will be set
|
* on the layer.
|
*/
|
initialize: function(name, options) {
|
options = options || {};
|
if(!options.version) {
|
options.version = typeof GMap2 === "function" ? "2" : "3";
|
}
|
var mixin = OpenLayers.Layer.Google["v" +
|
options.version.replace(/\./g, "_")];
|
if (mixin) {
|
OpenLayers.Util.applyDefaults(options, mixin);
|
} else {
|
throw "Unsupported Google Maps API version: " + options.version;
|
}
|
|
OpenLayers.Util.applyDefaults(options, mixin.DEFAULTS);
|
if (options.maxExtent) {
|
options.maxExtent = options.maxExtent.clone();
|
}
|
|
OpenLayers.Layer.EventPane.prototype.initialize.apply(this,
|
[name, options]);
|
OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,
|
[name, options]);
|
|
if (this.sphericalMercator) {
|
OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
|
this.initMercatorParameters();
|
}
|
},
|
|
/**
|
* Method: clone
|
* Create a clone of this layer
|
*
|
* Returns:
|
* {<OpenLayers.Layer.Google>} An exact clone of this layer
|
*/
|
clone: function() {
|
/**
|
* This method isn't intended to be called by a subclass and it
|
* doesn't call the same method on the superclass. We don't call
|
* the super's clone because we don't want properties that are set
|
* on this layer after initialize (i.e. this.mapObject etc.).
|
*/
|
return new OpenLayers.Layer.Google(
|
this.name, this.getOptions()
|
);
|
},
|
|
/**
|
* APIMethod: setVisibility
|
* Set the visibility flag for the layer and hide/show & redraw
|
* accordingly. Fire event unless otherwise specified
|
*
|
* Note that visibility is no longer simply whether or not the layer's
|
* style.display is set to "block". Now we store a 'visibility' state
|
* property on the layer class, this allows us to remember whether or
|
* not we *desire* for a layer to be visible. In the case where the
|
* map's resolution is out of the layer's range, this desire may be
|
* subverted.
|
*
|
* Parameters:
|
* visible - {Boolean} Display the layer (if in range)
|
*/
|
setVisibility: function(visible) {
|
// sharing a map container, opacity has to be set per layer
|
var opacity = this.opacity == null ? 1 : this.opacity;
|
OpenLayers.Layer.EventPane.prototype.setVisibility.apply(this, arguments);
|
this.setOpacity(opacity);
|
},
|
|
/**
|
* APIMethod: display
|
* Hide or show the Layer
|
*
|
* Parameters:
|
* visible - {Boolean}
|
*/
|
display: function(visible) {
|
if (!this._dragging) {
|
this.setGMapVisibility(visible);
|
}
|
OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments);
|
},
|
|
/**
|
* Method: moveTo
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
* zoomChanged - {Boolean} Tells when zoom has changed, as layers have to
|
* do some init work in that case.
|
* dragging - {Boolean}
|
*/
|
moveTo: function(bounds, zoomChanged, dragging) {
|
this._dragging = dragging;
|
OpenLayers.Layer.EventPane.prototype.moveTo.apply(this, arguments);
|
delete this._dragging;
|
},
|
|
/**
|
* APIMethod: setOpacity
|
* Sets the opacity for the entire layer (all images)
|
*
|
* Parameters:
|
* opacity - {Float}
|
*/
|
setOpacity: function(opacity) {
|
if (opacity !== this.opacity) {
|
if (this.map != null) {
|
this.map.events.triggerEvent("changelayer", {
|
layer: this,
|
property: "opacity"
|
});
|
}
|
this.opacity = opacity;
|
}
|
// Though this layer's opacity may not change, we're sharing a container
|
// and need to update the opacity for the entire container.
|
if (this.getVisibility()) {
|
var container = this.getMapContainer();
|
OpenLayers.Util.modifyDOMElement(
|
container, null, null, null, null, null, null, opacity
|
);
|
}
|
},
|
|
/**
|
* APIMethod: destroy
|
* Clean up this layer.
|
*/
|
destroy: function() {
|
/**
|
* We have to override this method because the event pane destroy
|
* deletes the mapObject reference before removing this layer from
|
* the map.
|
*/
|
if (this.map) {
|
this.setGMapVisibility(false);
|
var cache = OpenLayers.Layer.Google.cache[this.map.id];
|
if (cache && cache.count <= 1) {
|
this.removeGMapElements();
|
}
|
}
|
OpenLayers.Layer.EventPane.prototype.destroy.apply(this, arguments);
|
},
|
|
/**
|
* Method: removeGMapElements
|
* Remove all elements added to the dom. This should only be called if
|
* this is the last of the Google layers for the given map.
|
*/
|
removeGMapElements: function() {
|
var cache = OpenLayers.Layer.Google.cache[this.map.id];
|
if (cache) {
|
// remove shared elements from dom
|
var container = this.mapObject && this.getMapContainer();
|
if (container && container.parentNode) {
|
container.parentNode.removeChild(container);
|
}
|
var termsOfUse = cache.termsOfUse;
|
if (termsOfUse && termsOfUse.parentNode) {
|
termsOfUse.parentNode.removeChild(termsOfUse);
|
}
|
var poweredBy = cache.poweredBy;
|
if (poweredBy && poweredBy.parentNode) {
|
poweredBy.parentNode.removeChild(poweredBy);
|
}
|
if (this.mapObject && window.google && google.maps &&
|
google.maps.event && google.maps.event.clearListeners) {
|
google.maps.event.clearListeners(this.mapObject, 'tilesloaded');
|
}
|
}
|
},
|
|
/**
|
* APIMethod: removeMap
|
* On being removed from the map, also remove termsOfUse and poweredBy divs
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
removeMap: function(map) {
|
// hide layer before removing
|
if (this.visibility && this.mapObject) {
|
this.setGMapVisibility(false);
|
}
|
// check to see if last Google layer in this map
|
var cache = OpenLayers.Layer.Google.cache[map.id];
|
if (cache) {
|
if (cache.count <= 1) {
|
this.removeGMapElements();
|
delete OpenLayers.Layer.Google.cache[map.id];
|
} else {
|
// decrement the layer count
|
--cache.count;
|
}
|
}
|
// remove references to gmap elements
|
delete this.termsOfUse;
|
delete this.poweredBy;
|
delete this.mapObject;
|
delete this.dragObject;
|
OpenLayers.Layer.EventPane.prototype.removeMap.apply(this, arguments);
|
},
|
|
//
|
// TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds
|
//
|
|
/**
|
* APIMethod: getOLBoundsFromMapObjectBounds
|
*
|
* Parameters:
|
* moBounds - {Object}
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} An <OpenLayers.Bounds>, translated from the
|
* passed-in MapObject Bounds.
|
* Returns null if null value is passed in.
|
*/
|
getOLBoundsFromMapObjectBounds: function(moBounds) {
|
var olBounds = null;
|
if (moBounds != null) {
|
var sw = moBounds.getSouthWest();
|
var ne = moBounds.getNorthEast();
|
if (this.sphericalMercator) {
|
sw = this.forwardMercator(sw.lng(), sw.lat());
|
ne = this.forwardMercator(ne.lng(), ne.lat());
|
} else {
|
sw = new OpenLayers.LonLat(sw.lng(), sw.lat());
|
ne = new OpenLayers.LonLat(ne.lng(), ne.lat());
|
}
|
olBounds = new OpenLayers.Bounds(sw.lon,
|
sw.lat,
|
ne.lon,
|
ne.lat );
|
}
|
return olBounds;
|
},
|
|
/**
|
* APIMethod: getWarningHTML
|
*
|
* Returns:
|
* {String} String with information on why layer is broken, how to get
|
* it working.
|
*/
|
getWarningHTML:function() {
|
return OpenLayers.i18n("googleWarning");
|
},
|
|
|
/************************************
|
* *
|
* MapObject Interface Controls *
|
* *
|
************************************/
|
|
|
// Get&Set Center, Zoom
|
|
/**
|
* APIMethod: getMapObjectCenter
|
*
|
* Returns:
|
* {Object} The mapObject's current center in Map Object format
|
*/
|
getMapObjectCenter: function() {
|
return this.mapObject.getCenter();
|
},
|
|
/**
|
* APIMethod: getMapObjectZoom
|
*
|
* Returns:
|
* {Integer} The mapObject's current zoom, in Map Object format
|
*/
|
getMapObjectZoom: function() {
|
return this.mapObject.getZoom();
|
},
|
|
|
/************************************
|
* *
|
* MapObject Primitives *
|
* *
|
************************************/
|
|
|
// LonLat
|
|
/**
|
* APIMethod: getLongitudeFromMapObjectLonLat
|
*
|
* Parameters:
|
* moLonLat - {Object} MapObject LonLat format
|
*
|
* Returns:
|
* {Float} Longitude of the given MapObject LonLat
|
*/
|
getLongitudeFromMapObjectLonLat: function(moLonLat) {
|
return this.sphericalMercator ?
|
this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lon :
|
moLonLat.lng();
|
},
|
|
/**
|
* APIMethod: getLatitudeFromMapObjectLonLat
|
*
|
* Parameters:
|
* moLonLat - {Object} MapObject LonLat format
|
*
|
* Returns:
|
* {Float} Latitude of the given MapObject LonLat
|
*/
|
getLatitudeFromMapObjectLonLat: function(moLonLat) {
|
var lat = this.sphericalMercator ?
|
this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lat :
|
moLonLat.lat();
|
return lat;
|
},
|
|
// Pixel
|
|
/**
|
* APIMethod: getXFromMapObjectPixel
|
*
|
* Parameters:
|
* moPixel - {Object} MapObject Pixel format
|
*
|
* Returns:
|
* {Integer} X value of the MapObject Pixel
|
*/
|
getXFromMapObjectPixel: function(moPixel) {
|
return moPixel.x;
|
},
|
|
/**
|
* APIMethod: getYFromMapObjectPixel
|
*
|
* Parameters:
|
* moPixel - {Object} MapObject Pixel format
|
*
|
* Returns:
|
* {Integer} Y value of the MapObject Pixel
|
*/
|
getYFromMapObjectPixel: function(moPixel) {
|
return moPixel.y;
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.Google"
|
});
|
|
/**
|
* Property: OpenLayers.Layer.Google.cache
|
* {Object} Cache for elements that should only be created once per map.
|
*/
|
OpenLayers.Layer.Google.cache = {};
|
|
|
/**
|
* Constant: OpenLayers.Layer.Google.v2
|
*
|
* Mixin providing functionality specific to the Google Maps API v2.
|
*
|
* This API has been deprecated by Google.
|
* Developers are encouraged to migrate to v3 of the API; support for this
|
* is provided by <OpenLayers.Layer.Google.v3>
|
*/
|
OpenLayers.Layer.Google.v2 = {
|
|
/**
|
* Property: termsOfUse
|
* {DOMElement} Div for Google's copyright and terms of use link
|
*/
|
termsOfUse: null,
|
|
/**
|
* Property: poweredBy
|
* {DOMElement} Div for Google's powered by logo and link
|
*/
|
poweredBy: null,
|
|
/**
|
* Property: dragObject
|
* {GDraggableObject} Since 2.93, Google has exposed the ability to get
|
* the maps GDraggableObject. We can now use this for smooth panning
|
*/
|
dragObject: null,
|
|
/**
|
* Method: loadMapObject
|
* Load the GMap and register appropriate event listeners. If we can't
|
* load GMap2, then display a warning message.
|
*/
|
loadMapObject:function() {
|
if (!this.type) {
|
this.type = G_NORMAL_MAP;
|
}
|
var mapObject, termsOfUse, poweredBy;
|
var cache = OpenLayers.Layer.Google.cache[this.map.id];
|
if (cache) {
|
// there are already Google layers added to this map
|
mapObject = cache.mapObject;
|
termsOfUse = cache.termsOfUse;
|
poweredBy = cache.poweredBy;
|
// increment the layer count
|
++cache.count;
|
} else {
|
// this is the first Google layer for this map
|
|
var container = this.map.viewPortDiv;
|
var div = document.createElement("div");
|
div.id = this.map.id + "_GMap2Container";
|
div.style.position = "absolute";
|
div.style.width = "100%";
|
div.style.height = "100%";
|
container.appendChild(div);
|
|
// create GMap and shuffle elements
|
try {
|
mapObject = new GMap2(div);
|
|
// move the ToS and branding stuff up to the container div
|
termsOfUse = div.lastChild;
|
container.appendChild(termsOfUse);
|
termsOfUse.style.zIndex = "1100";
|
termsOfUse.style.right = "";
|
termsOfUse.style.bottom = "";
|
termsOfUse.className = "olLayerGoogleCopyright";
|
|
poweredBy = div.lastChild;
|
container.appendChild(poweredBy);
|
poweredBy.style.zIndex = "1100";
|
poweredBy.style.right = "";
|
poweredBy.style.bottom = "";
|
poweredBy.className = "olLayerGooglePoweredBy gmnoprint";
|
|
} catch (e) {
|
throw(e);
|
}
|
// cache elements for use by any other google layers added to
|
// this same map
|
OpenLayers.Layer.Google.cache[this.map.id] = {
|
mapObject: mapObject,
|
termsOfUse: termsOfUse,
|
poweredBy: poweredBy,
|
count: 1
|
};
|
}
|
|
this.mapObject = mapObject;
|
this.termsOfUse = termsOfUse;
|
this.poweredBy = poweredBy;
|
|
// ensure this layer type is one of the mapObject types
|
if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(),
|
this.type) === -1) {
|
this.mapObject.addMapType(this.type);
|
}
|
|
//since v 2.93 getDragObject is now available.
|
if(typeof mapObject.getDragObject == "function") {
|
this.dragObject = mapObject.getDragObject();
|
} else {
|
this.dragPanMapObject = null;
|
}
|
|
if(this.isBaseLayer === false) {
|
this.setGMapVisibility(this.div.style.display !== "none");
|
}
|
|
},
|
|
/**
|
* APIMethod: onMapResize
|
*/
|
onMapResize: function() {
|
// workaround for resizing of invisible or not yet fully loaded layers
|
// where GMap2.checkResize() does not work. We need to load the GMap
|
// for the old div size, then checkResize(), and then call
|
// layer.moveTo() to trigger GMap.setCenter() (which will finish
|
// the GMap initialization).
|
if(this.visibility && this.mapObject.isLoaded()) {
|
this.mapObject.checkResize();
|
} else {
|
if(!this._resized) {
|
var layer = this;
|
var handle = GEvent.addListener(this.mapObject, "load", function() {
|
GEvent.removeListener(handle);
|
delete layer._resized;
|
layer.mapObject.checkResize();
|
layer.moveTo(layer.map.getCenter(), layer.map.getZoom());
|
});
|
}
|
this._resized = true;
|
}
|
},
|
|
/**
|
* Method: setGMapVisibility
|
* Display the GMap container and associated elements.
|
*
|
* Parameters:
|
* visible - {Boolean} Display the GMap elements.
|
*/
|
setGMapVisibility: function(visible) {
|
var cache = OpenLayers.Layer.Google.cache[this.map.id];
|
if (cache) {
|
var container = this.mapObject.getContainer();
|
if (visible === true) {
|
this.mapObject.setMapType(this.type);
|
container.style.display = "";
|
this.termsOfUse.style.left = "";
|
this.termsOfUse.style.display = "";
|
this.poweredBy.style.display = "";
|
cache.displayed = this.id;
|
} else {
|
if (cache.displayed === this.id) {
|
delete cache.displayed;
|
}
|
if (!cache.displayed) {
|
container.style.display = "none";
|
this.termsOfUse.style.display = "none";
|
// move ToU far to the left in addition to setting display
|
// to "none", because at the end of the GMap2 load
|
// sequence, display: none will be unset and ToU would be
|
// visible after loading a map with a google layer that is
|
// initially hidden.
|
this.termsOfUse.style.left = "-9999px";
|
this.poweredBy.style.display = "none";
|
}
|
}
|
}
|
},
|
|
/**
|
* Method: getMapContainer
|
*
|
* Returns:
|
* {DOMElement} the GMap container's div
|
*/
|
getMapContainer: function() {
|
return this.mapObject.getContainer();
|
},
|
|
//
|
// TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds
|
//
|
|
/**
|
* APIMethod: getMapObjectBoundsFromOLBounds
|
*
|
* Parameters:
|
* olBounds - {<OpenLayers.Bounds>}
|
*
|
* Returns:
|
* {Object} A MapObject Bounds, translated from olBounds
|
* Returns null if null value is passed in
|
*/
|
getMapObjectBoundsFromOLBounds: function(olBounds) {
|
var moBounds = null;
|
if (olBounds != null) {
|
var sw = this.sphericalMercator ?
|
this.inverseMercator(olBounds.bottom, olBounds.left) :
|
new OpenLayers.LonLat(olBounds.bottom, olBounds.left);
|
var ne = this.sphericalMercator ?
|
this.inverseMercator(olBounds.top, olBounds.right) :
|
new OpenLayers.LonLat(olBounds.top, olBounds.right);
|
moBounds = new GLatLngBounds(new GLatLng(sw.lat, sw.lon),
|
new GLatLng(ne.lat, ne.lon));
|
}
|
return moBounds;
|
},
|
|
|
/************************************
|
* *
|
* MapObject Interface Controls *
|
* *
|
************************************/
|
|
|
// Get&Set Center, Zoom
|
|
/**
|
* APIMethod: setMapObjectCenter
|
* Set the mapObject to the specified center and zoom
|
*
|
* Parameters:
|
* center - {Object} MapObject LonLat format
|
* zoom - {int} MapObject zoom format
|
*/
|
setMapObjectCenter: function(center, zoom) {
|
this.mapObject.setCenter(center, zoom);
|
},
|
|
/**
|
* APIMethod: dragPanMapObject
|
*
|
* Parameters:
|
* dX - {Integer}
|
* dY - {Integer}
|
*/
|
dragPanMapObject: function(dX, dY) {
|
this.dragObject.moveBy(new GSize(-dX, dY));
|
},
|
|
|
// LonLat - Pixel Translation
|
|
/**
|
* APIMethod: getMapObjectLonLatFromMapObjectPixel
|
*
|
* Parameters:
|
* moPixel - {Object} MapObject Pixel format
|
*
|
* Returns:
|
* {Object} MapObject LonLat translated from MapObject Pixel
|
*/
|
getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
|
return this.mapObject.fromContainerPixelToLatLng(moPixel);
|
},
|
|
/**
|
* APIMethod: getMapObjectPixelFromMapObjectLonLat
|
*
|
* Parameters:
|
* moLonLat - {Object} MapObject LonLat format
|
*
|
* Returns:
|
* {Object} MapObject Pixel transtlated from MapObject LonLat
|
*/
|
getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
|
return this.mapObject.fromLatLngToContainerPixel(moLonLat);
|
},
|
|
|
// Bounds
|
|
/**
|
* APIMethod: getMapObjectZoomFromMapObjectBounds
|
*
|
* Parameters:
|
* moBounds - {Object} MapObject Bounds format
|
*
|
* Returns:
|
* {Object} MapObject Zoom for specified MapObject Bounds
|
*/
|
getMapObjectZoomFromMapObjectBounds: function(moBounds) {
|
return this.mapObject.getBoundsZoomLevel(moBounds);
|
},
|
|
/************************************
|
* *
|
* MapObject Primitives *
|
* *
|
************************************/
|
|
|
// LonLat
|
|
/**
|
* APIMethod: getMapObjectLonLatFromLonLat
|
*
|
* Parameters:
|
* lon - {Float}
|
* lat - {Float}
|
*
|
* Returns:
|
* {Object} MapObject LonLat built from lon and lat params
|
*/
|
getMapObjectLonLatFromLonLat: function(lon, lat) {
|
var gLatLng;
|
if(this.sphericalMercator) {
|
var lonlat = this.inverseMercator(lon, lat);
|
gLatLng = new GLatLng(lonlat.lat, lonlat.lon);
|
} else {
|
gLatLng = new GLatLng(lat, lon);
|
}
|
return gLatLng;
|
},
|
|
// Pixel
|
|
/**
|
* APIMethod: getMapObjectPixelFromXY
|
*
|
* Parameters:
|
* x - {Integer}
|
* y - {Integer}
|
*
|
* Returns:
|
* {Object} MapObject Pixel from x and y parameters
|
*/
|
getMapObjectPixelFromXY: function(x, y) {
|
return new GPoint(x, y);
|
}
|
|
};
|
/* ======================================================================
|
OpenLayers/Format/XML.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.XML
|
* Read and write XML. For cross-browser XML generation, use methods on an
|
* instance of the XML format class instead of on <code>document<end>.
|
* The DOM creation and traversing methods exposed here all mimic the
|
* W3C XML DOM methods. Create a new parser with the
|
* <OpenLayers.Format.XML> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format>
|
*/
|
OpenLayers.Format.XML = OpenLayers.Class(OpenLayers.Format, {
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs. Properties
|
* of this object should not be set individually. Read-only. All
|
* XML subclasses should have their own namespaces object. Use
|
* <setNamespace> to add or set a namespace alias after construction.
|
*/
|
namespaces: null,
|
|
/**
|
* Property: namespaceAlias
|
* {Object} Mapping of namespace URI to namespace alias. This object
|
* is read-only. Use <setNamespace> to add or set a namespace alias.
|
*/
|
namespaceAlias: null,
|
|
/**
|
* Property: defaultPrefix
|
* {String} The default namespace alias for creating element nodes.
|
*/
|
defaultPrefix: null,
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {},
|
|
/**
|
* Property: writers
|
* As a compliment to the <readers> property, this structure contains public
|
* writing functions grouped by namespace alias and named like the
|
* node names they produce.
|
*/
|
writers: {},
|
|
/**
|
* Property: xmldom
|
* {XMLDom} If this browser uses ActiveX, this will be set to a XMLDOM
|
* object. It is not intended to be a browser sniffing property.
|
* Instead, the xmldom property is used instead of <code>document<end>
|
* where namespaced node creation methods are not supported. In all
|
* other browsers, this remains null.
|
*/
|
xmldom: null,
|
|
/**
|
* Constructor: OpenLayers.Format.XML
|
* Construct an XML parser. The parser is used to read and write XML.
|
* Reading XML from a string returns a DOM element. Writing XML from
|
* a DOM element returns a string.
|
*
|
* Parameters:
|
* options - {Object} Optional object whose properties will be set on
|
* the object.
|
*/
|
initialize: function(options) {
|
if(window.ActiveXObject) {
|
this.xmldom = new ActiveXObject("Microsoft.XMLDOM");
|
}
|
OpenLayers.Format.prototype.initialize.apply(this, [options]);
|
// clone the namespace object and set all namespace aliases
|
this.namespaces = OpenLayers.Util.extend({}, this.namespaces);
|
this.namespaceAlias = {};
|
for(var alias in this.namespaces) {
|
this.namespaceAlias[this.namespaces[alias]] = alias;
|
}
|
},
|
|
/**
|
* APIMethod: destroy
|
* Clean up.
|
*/
|
destroy: function() {
|
this.xmldom = null;
|
OpenLayers.Format.prototype.destroy.apply(this, arguments);
|
},
|
|
/**
|
* Method: setNamespace
|
* Set a namespace alias and URI for the format.
|
*
|
* Parameters:
|
* alias - {String} The namespace alias (prefix).
|
* uri - {String} The namespace URI.
|
*/
|
setNamespace: function(alias, uri) {
|
this.namespaces[alias] = uri;
|
this.namespaceAlias[uri] = alias;
|
},
|
|
/**
|
* APIMethod: read
|
* Deserialize a XML string and return a DOM node.
|
*
|
* Parameters:
|
* text - {String} A XML string
|
|
* Returns:
|
* {DOMElement} A DOM node
|
*/
|
read: function(text) {
|
var index = text.indexOf('<');
|
if(index > 0) {
|
text = text.substring(index);
|
}
|
var node = OpenLayers.Util.Try(
|
OpenLayers.Function.bind((
|
function() {
|
var xmldom;
|
/**
|
* Since we want to be able to call this method on the prototype
|
* itself, this.xmldom may not exist even if in IE.
|
*/
|
if(window.ActiveXObject && !this.xmldom) {
|
xmldom = new ActiveXObject("Microsoft.XMLDOM");
|
} else {
|
xmldom = this.xmldom;
|
|
}
|
xmldom.loadXML(text);
|
return xmldom;
|
}
|
), this),
|
function() {
|
return new DOMParser().parseFromString(text, 'text/xml');
|
},
|
function() {
|
var req = new XMLHttpRequest();
|
req.open("GET", "data:" + "text/xml" +
|
";charset=utf-8," + encodeURIComponent(text), false);
|
if(req.overrideMimeType) {
|
req.overrideMimeType("text/xml");
|
}
|
req.send(null);
|
return req.responseXML;
|
}
|
);
|
|
if(this.keepData) {
|
this.data = node;
|
}
|
|
return node;
|
},
|
|
/**
|
* APIMethod: write
|
* Serialize a DOM node into a XML string.
|
*
|
* Parameters:
|
* node - {DOMElement} A DOM node.
|
*
|
* Returns:
|
* {String} The XML string representation of the input node.
|
*/
|
write: function(node) {
|
var data;
|
if(this.xmldom) {
|
data = node.xml;
|
} else {
|
var serializer = new XMLSerializer();
|
if (node.nodeType == 1) {
|
// Add nodes to a document before serializing. Everything else
|
// is serialized as is. This may need more work. See #1218 .
|
var doc = document.implementation.createDocument("", "", null);
|
if (doc.importNode) {
|
node = doc.importNode(node, true);
|
}
|
doc.appendChild(node);
|
data = serializer.serializeToString(doc);
|
} else {
|
data = serializer.serializeToString(node);
|
}
|
}
|
return data;
|
},
|
|
/**
|
* APIMethod: createElementNS
|
* Create a new element with namespace. This node can be appended to
|
* another node with the standard node.appendChild method. For
|
* cross-browser support, this method must be used instead of
|
* document.createElementNS.
|
*
|
* Parameters:
|
* uri - {String} Namespace URI for the element.
|
* name - {String} The qualified name of the element (prefix:localname).
|
*
|
* Returns:
|
* {Element} A DOM element with namespace.
|
*/
|
createElementNS: function(uri, name) {
|
var element;
|
if(this.xmldom) {
|
if(typeof uri == "string") {
|
element = this.xmldom.createNode(1, name, uri);
|
} else {
|
element = this.xmldom.createNode(1, name, "");
|
}
|
} else {
|
element = document.createElementNS(uri, name);
|
}
|
return element;
|
},
|
|
/**
|
* APIMethod: createDocumentFragment
|
* Create a document fragment node that can be appended to another node
|
* created by createElementNS. This will call
|
* document.createDocumentFragment outside of IE. In IE, the ActiveX
|
* object's createDocumentFragment method is used.
|
*
|
* Returns:
|
* {Element} A document fragment.
|
*/
|
createDocumentFragment: function() {
|
var element;
|
if (this.xmldom) {
|
element = this.xmldom.createDocumentFragment();
|
} else {
|
element = document.createDocumentFragment();
|
}
|
return element;
|
},
|
|
/**
|
* APIMethod: createTextNode
|
* Create a text node. This node can be appended to another node with
|
* the standard node.appendChild method. For cross-browser support,
|
* this method must be used instead of document.createTextNode.
|
*
|
* Parameters:
|
* text - {String} The text of the node.
|
*
|
* Returns:
|
* {DOMElement} A DOM text node.
|
*/
|
createTextNode: function(text) {
|
var node;
|
if (typeof text !== "string") {
|
text = String(text);
|
}
|
if(this.xmldom) {
|
node = this.xmldom.createTextNode(text);
|
} else {
|
node = document.createTextNode(text);
|
}
|
return node;
|
},
|
|
/**
|
* APIMethod: getElementsByTagNameNS
|
* Get a list of elements on a node given the namespace URI and local name.
|
* To return all nodes in a given namespace, use '*' for the name
|
* argument. To return all nodes of a given (local) name, regardless
|
* of namespace, use '*' for the uri argument.
|
*
|
* Parameters:
|
* node - {Element} Node on which to search for other nodes.
|
* uri - {String} Namespace URI.
|
* name - {String} Local name of the tag (without the prefix).
|
*
|
* Returns:
|
* {NodeList} A node list or array of elements.
|
*/
|
getElementsByTagNameNS: function(node, uri, name) {
|
var elements = [];
|
if(node.getElementsByTagNameNS) {
|
elements = node.getElementsByTagNameNS(uri, name);
|
} else {
|
// brute force method
|
var allNodes = node.getElementsByTagName("*");
|
var potentialNode, fullName;
|
for(var i=0, len=allNodes.length; i<len; ++i) {
|
potentialNode = allNodes[i];
|
fullName = (potentialNode.prefix) ?
|
(potentialNode.prefix + ":" + name) : name;
|
if((name == "*") || (fullName == potentialNode.nodeName)) {
|
if((uri == "*") || (uri == potentialNode.namespaceURI)) {
|
elements.push(potentialNode);
|
}
|
}
|
}
|
}
|
return elements;
|
},
|
|
/**
|
* APIMethod: getAttributeNodeNS
|
* Get an attribute node given the namespace URI and local name.
|
*
|
* Parameters:
|
* node - {Element} Node on which to search for attribute nodes.
|
* uri - {String} Namespace URI.
|
* name - {String} Local name of the attribute (without the prefix).
|
*
|
* Returns:
|
* {DOMElement} An attribute node or null if none found.
|
*/
|
getAttributeNodeNS: function(node, uri, name) {
|
var attributeNode = null;
|
if(node.getAttributeNodeNS) {
|
attributeNode = node.getAttributeNodeNS(uri, name);
|
} else {
|
var attributes = node.attributes;
|
var potentialNode, fullName;
|
for(var i=0, len=attributes.length; i<len; ++i) {
|
potentialNode = attributes[i];
|
if(potentialNode.namespaceURI == uri) {
|
fullName = (potentialNode.prefix) ?
|
(potentialNode.prefix + ":" + name) : name;
|
if(fullName == potentialNode.nodeName) {
|
attributeNode = potentialNode;
|
break;
|
}
|
}
|
}
|
}
|
return attributeNode;
|
},
|
|
/**
|
* APIMethod: getAttributeNS
|
* Get an attribute value given the namespace URI and local name.
|
*
|
* Parameters:
|
* node - {Element} Node on which to search for an attribute.
|
* uri - {String} Namespace URI.
|
* name - {String} Local name of the attribute (without the prefix).
|
*
|
* Returns:
|
* {String} An attribute value or and empty string if none found.
|
*/
|
getAttributeNS: function(node, uri, name) {
|
var attributeValue = "";
|
if(node.getAttributeNS) {
|
attributeValue = node.getAttributeNS(uri, name) || "";
|
} else {
|
var attributeNode = this.getAttributeNodeNS(node, uri, name);
|
if(attributeNode) {
|
attributeValue = attributeNode.nodeValue;
|
}
|
}
|
return attributeValue;
|
},
|
|
/**
|
* APIMethod: getChildValue
|
* Get the textual value of the node if it exists, or return an
|
* optional default string. Returns an empty string if no first child
|
* exists and no default value is supplied.
|
*
|
* Parameters:
|
* node - {DOMElement} The element used to look for a first child value.
|
* def - {String} Optional string to return in the event that no
|
* first child value exists.
|
*
|
* Returns:
|
* {String} The value of the first child of the given node.
|
*/
|
getChildValue: function(node, def) {
|
var value = def || "";
|
if(node) {
|
for(var child=node.firstChild; child; child=child.nextSibling) {
|
switch(child.nodeType) {
|
case 3: // text node
|
case 4: // cdata section
|
value += child.nodeValue;
|
}
|
}
|
}
|
return value;
|
},
|
|
/**
|
* APIMethod: isSimpleContent
|
* Test if the given node has only simple content (i.e. no child element
|
* nodes).
|
*
|
* Parameters:
|
* node - {DOMElement} An element node.
|
*
|
* Returns:
|
* {Boolean} The node has no child element nodes (nodes of type 1).
|
*/
|
isSimpleContent: function(node) {
|
var simple = true;
|
for(var child=node.firstChild; child; child=child.nextSibling) {
|
if(child.nodeType === 1) {
|
simple = false;
|
break;
|
}
|
}
|
return simple;
|
},
|
|
/**
|
* APIMethod: contentType
|
* Determine the content type for a given node.
|
*
|
* Parameters:
|
* node - {DOMElement}
|
*
|
* Returns:
|
* {Integer} One of OpenLayers.Format.XML.CONTENT_TYPE.{EMPTY,SIMPLE,COMPLEX,MIXED}
|
* if the node has no, simple, complex, or mixed content.
|
*/
|
contentType: function(node) {
|
var simple = false,
|
complex = false;
|
|
var type = OpenLayers.Format.XML.CONTENT_TYPE.EMPTY;
|
|
for(var child=node.firstChild; child; child=child.nextSibling) {
|
switch(child.nodeType) {
|
case 1: // element
|
complex = true;
|
break;
|
case 8: // comment
|
break;
|
default:
|
simple = true;
|
}
|
if(complex && simple) {
|
break;
|
}
|
}
|
|
if(complex && simple) {
|
type = OpenLayers.Format.XML.CONTENT_TYPE.MIXED;
|
} else if(complex) {
|
return OpenLayers.Format.XML.CONTENT_TYPE.COMPLEX;
|
} else if(simple) {
|
return OpenLayers.Format.XML.CONTENT_TYPE.SIMPLE;
|
}
|
return type;
|
},
|
|
/**
|
* APIMethod: hasAttributeNS
|
* Determine whether a node has a particular attribute matching the given
|
* name and namespace.
|
*
|
* Parameters:
|
* node - {Element} Node on which to search for an attribute.
|
* uri - {String} Namespace URI.
|
* name - {String} Local name of the attribute (without the prefix).
|
*
|
* Returns:
|
* {Boolean} The node has an attribute matching the name and namespace.
|
*/
|
hasAttributeNS: function(node, uri, name) {
|
var found = false;
|
if(node.hasAttributeNS) {
|
found = node.hasAttributeNS(uri, name);
|
} else {
|
found = !!this.getAttributeNodeNS(node, uri, name);
|
}
|
return found;
|
},
|
|
/**
|
* APIMethod: setAttributeNS
|
* Adds a new attribute or changes the value of an attribute with the given
|
* namespace and name.
|
*
|
* Parameters:
|
* node - {Element} Element node on which to set the attribute.
|
* uri - {String} Namespace URI for the attribute.
|
* name - {String} Qualified name (prefix:localname) for the attribute.
|
* value - {String} Attribute value.
|
*/
|
setAttributeNS: function(node, uri, name, value) {
|
if(node.setAttributeNS) {
|
node.setAttributeNS(uri, name, value);
|
} else {
|
if(this.xmldom) {
|
if(uri) {
|
var attribute = node.ownerDocument.createNode(
|
2, name, uri
|
);
|
attribute.nodeValue = value;
|
node.setAttributeNode(attribute);
|
} else {
|
node.setAttribute(name, value);
|
}
|
} else {
|
throw "setAttributeNS not implemented";
|
}
|
}
|
},
|
|
/**
|
* Method: createElementNSPlus
|
* Shorthand for creating namespaced elements with optional attributes and
|
* child text nodes.
|
*
|
* Parameters:
|
* name - {String} The qualified node name.
|
* options - {Object} Optional object for node configuration.
|
*
|
* Valid options:
|
* uri - {String} Optional namespace uri for the element - supply a prefix
|
* instead if the namespace uri is a property of the format's namespace
|
* object.
|
* attributes - {Object} Optional attributes to be set using the
|
* <setAttributes> method.
|
* value - {String} Optional text to be appended as a text node.
|
*
|
* Returns:
|
* {Element} An element node.
|
*/
|
createElementNSPlus: function(name, options) {
|
options = options || {};
|
// order of prefix preference
|
// 1. in the uri option
|
// 2. in the prefix option
|
// 3. in the qualified name
|
// 4. from the defaultPrefix
|
var uri = options.uri || this.namespaces[options.prefix];
|
if(!uri) {
|
var loc = name.indexOf(":");
|
uri = this.namespaces[name.substring(0, loc)];
|
}
|
if(!uri) {
|
uri = this.namespaces[this.defaultPrefix];
|
}
|
var node = this.createElementNS(uri, name);
|
if(options.attributes) {
|
this.setAttributes(node, options.attributes);
|
}
|
var value = options.value;
|
if(value != null) {
|
node.appendChild(this.createTextNode(value));
|
}
|
return node;
|
},
|
|
/**
|
* Method: setAttributes
|
* Set multiple attributes given key value pairs from an object.
|
*
|
* Parameters:
|
* node - {Element} An element node.
|
* obj - {Object || Array} An object whose properties represent attribute
|
* names and values represent attribute values. If an attribute name
|
* is a qualified name ("prefix:local"), the prefix will be looked up
|
* in the parsers {namespaces} object. If the prefix is found,
|
* setAttributeNS will be used instead of setAttribute.
|
*/
|
setAttributes: function(node, obj) {
|
var value, uri;
|
for(var name in obj) {
|
if(obj[name] != null && obj[name].toString) {
|
value = obj[name].toString();
|
// check for qualified attribute name ("prefix:local")
|
uri = this.namespaces[name.substring(0, name.indexOf(":"))] || null;
|
this.setAttributeNS(node, uri, name, value);
|
}
|
}
|
},
|
|
/**
|
* Method: readNode
|
* Shorthand for applying one of the named readers given the node
|
* namespace and local name. Readers take two args (node, obj) and
|
* generally extend or modify the second.
|
*
|
* Parameters:
|
* node - {DOMElement} The node to be read (required).
|
* obj - {Object} The object to be modified (optional).
|
*
|
* Returns:
|
* {Object} The input object, modified (or a new one if none was provided).
|
*/
|
readNode: function(node, obj) {
|
if(!obj) {
|
obj = {};
|
}
|
var group = this.readers[node.namespaceURI ? this.namespaceAlias[node.namespaceURI]: this.defaultPrefix];
|
if(group) {
|
var local = node.localName || node.nodeName.split(":").pop();
|
var reader = group[local] || group["*"];
|
if(reader) {
|
reader.apply(this, [node, obj]);
|
}
|
}
|
return obj;
|
},
|
|
/**
|
* Method: readChildNodes
|
* Shorthand for applying the named readers to all children of a node.
|
* For each child of type 1 (element), <readSelf> is called.
|
*
|
* Parameters:
|
* node - {DOMElement} The node to be read (required).
|
* obj - {Object} The object to be modified (optional).
|
*
|
* Returns:
|
* {Object} The input object, modified.
|
*/
|
readChildNodes: function(node, obj) {
|
if(!obj) {
|
obj = {};
|
}
|
var children = node.childNodes;
|
var child;
|
for(var i=0, len=children.length; i<len; ++i) {
|
child = children[i];
|
if(child.nodeType == 1) {
|
this.readNode(child, obj);
|
}
|
}
|
return obj;
|
},
|
|
/**
|
* Method: writeNode
|
* Shorthand for applying one of the named writers and appending the
|
* results to a node. If a qualified name is not provided for the
|
* second argument (and a local name is used instead), the namespace
|
* of the parent node will be assumed.
|
*
|
* Parameters:
|
* name - {String} The name of a node to generate. If a qualified name
|
* (e.g. "pre:Name") is used, the namespace prefix is assumed to be
|
* in the <writers> group. If a local name is used (e.g. "Name") then
|
* the namespace of the parent is assumed. If a local name is used
|
* and no parent is supplied, then the default namespace is assumed.
|
* obj - {Object} Structure containing data for the writer.
|
* parent - {DOMElement} Result will be appended to this node. If no parent
|
* is supplied, the node will not be appended to anything.
|
*
|
* Returns:
|
* {DOMElement} The child node.
|
*/
|
writeNode: function(name, obj, parent) {
|
var prefix, local;
|
var split = name.indexOf(":");
|
if(split > 0) {
|
prefix = name.substring(0, split);
|
local = name.substring(split + 1);
|
} else {
|
if(parent) {
|
prefix = this.namespaceAlias[parent.namespaceURI];
|
} else {
|
prefix = this.defaultPrefix;
|
}
|
local = name;
|
}
|
var child = this.writers[prefix][local].apply(this, [obj]);
|
if(parent) {
|
parent.appendChild(child);
|
}
|
return child;
|
},
|
|
/**
|
* APIMethod: getChildEl
|
* Get the first child element. Optionally only return the first child
|
* if it matches the given name and namespace URI.
|
*
|
* Parameters:
|
* node - {DOMElement} The parent node.
|
* name - {String} Optional node name (local) to search for.
|
* uri - {String} Optional namespace URI to search for.
|
*
|
* Returns:
|
* {DOMElement} The first child. Returns null if no element is found, if
|
* something significant besides an element is found, or if the element
|
* found does not match the optional name and uri.
|
*/
|
getChildEl: function(node, name, uri) {
|
return node && this.getThisOrNextEl(node.firstChild, name, uri);
|
},
|
|
/**
|
* APIMethod: getNextEl
|
* Get the next sibling element. Optionally get the first sibling only
|
* if it matches the given local name and namespace URI.
|
*
|
* Parameters:
|
* node - {DOMElement} The node.
|
* name - {String} Optional local name of the sibling to search for.
|
* uri - {String} Optional namespace URI of the sibling to search for.
|
*
|
* Returns:
|
* {DOMElement} The next sibling element. Returns null if no element is
|
* found, something significant besides an element is found, or the
|
* found element does not match the optional name and uri.
|
*/
|
getNextEl: function(node, name, uri) {
|
return node && this.getThisOrNextEl(node.nextSibling, name, uri);
|
},
|
|
/**
|
* Method: getThisOrNextEl
|
* Return this node or the next element node. Optionally get the first
|
* sibling with the given local name or namespace URI.
|
*
|
* Parameters:
|
* node - {DOMElement} The node.
|
* name - {String} Optional local name of the sibling to search for.
|
* uri - {String} Optional namespace URI of the sibling to search for.
|
*
|
* Returns:
|
* {DOMElement} The next sibling element. Returns null if no element is
|
* found, something significant besides an element is found, or the
|
* found element does not match the query.
|
*/
|
getThisOrNextEl: function(node, name, uri) {
|
outer: for(var sibling=node; sibling; sibling=sibling.nextSibling) {
|
switch(sibling.nodeType) {
|
case 1: // Element
|
if((!name || name === (sibling.localName || sibling.nodeName.split(":").pop())) &&
|
(!uri || uri === sibling.namespaceURI)) {
|
// matches
|
break outer;
|
}
|
sibling = null;
|
break outer;
|
case 3: // Text
|
if(/^\s*$/.test(sibling.nodeValue)) {
|
break;
|
}
|
case 4: // CDATA
|
case 6: // ENTITY_NODE
|
case 12: // NOTATION_NODE
|
case 10: // DOCUMENT_TYPE_NODE
|
case 11: // DOCUMENT_FRAGMENT_NODE
|
sibling = null;
|
break outer;
|
} // ignore comments and processing instructions
|
}
|
return sibling || null;
|
},
|
|
/**
|
* APIMethod: lookupNamespaceURI
|
* Takes a prefix and returns the namespace URI associated with it on the given
|
* node if found (and null if not). Supplying null for the prefix will
|
* return the default namespace.
|
*
|
* For browsers that support it, this calls the native lookupNamesapceURI
|
* function. In other browsers, this is an implementation of
|
* http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI.
|
*
|
* For browsers that don't support the attribute.ownerElement property, this
|
* method cannot be called on attribute nodes.
|
*
|
* Parameters:
|
* node - {DOMElement} The node from which to start looking.
|
* prefix - {String} The prefix to lookup or null to lookup the default namespace.
|
*
|
* Returns:
|
* {String} The namespace URI for the given prefix. Returns null if the prefix
|
* cannot be found or the node is the wrong type.
|
*/
|
lookupNamespaceURI: function(node, prefix) {
|
var uri = null;
|
if(node) {
|
if(node.lookupNamespaceURI) {
|
uri = node.lookupNamespaceURI(prefix);
|
} else {
|
outer: switch(node.nodeType) {
|
case 1: // ELEMENT_NODE
|
if(node.namespaceURI !== null && node.prefix === prefix) {
|
uri = node.namespaceURI;
|
break outer;
|
}
|
var len = node.attributes.length;
|
if(len) {
|
var attr;
|
for(var i=0; i<len; ++i) {
|
attr = node.attributes[i];
|
if(attr.prefix === "xmlns" && attr.name === "xmlns:" + prefix) {
|
uri = attr.value || null;
|
break outer;
|
} else if(attr.name === "xmlns" && prefix === null) {
|
uri = attr.value || null;
|
break outer;
|
}
|
}
|
}
|
uri = this.lookupNamespaceURI(node.parentNode, prefix);
|
break outer;
|
case 2: // ATTRIBUTE_NODE
|
uri = this.lookupNamespaceURI(node.ownerElement, prefix);
|
break outer;
|
case 9: // DOCUMENT_NODE
|
uri = this.lookupNamespaceURI(node.documentElement, prefix);
|
break outer;
|
case 6: // ENTITY_NODE
|
case 12: // NOTATION_NODE
|
case 10: // DOCUMENT_TYPE_NODE
|
case 11: // DOCUMENT_FRAGMENT_NODE
|
break outer;
|
default:
|
// TEXT_NODE (3), CDATA_SECTION_NODE (4), ENTITY_REFERENCE_NODE (5),
|
// PROCESSING_INSTRUCTION_NODE (7), COMMENT_NODE (8)
|
uri = this.lookupNamespaceURI(node.parentNode, prefix);
|
break outer;
|
}
|
}
|
}
|
return uri;
|
},
|
|
/**
|
* Method: getXMLDoc
|
* Get an XML document for nodes that are not supported in HTML (e.g.
|
* createCDATASection). On IE, this will either return an existing or
|
* create a new <xmldom> on the instance. On other browsers, this will
|
* either return an existing or create a new shared document (see
|
* <OpenLayers.Format.XML.document>).
|
*
|
* Returns:
|
* {XMLDocument}
|
*/
|
getXMLDoc: function() {
|
if (!OpenLayers.Format.XML.document && !this.xmldom) {
|
if (document.implementation && document.implementation.createDocument) {
|
OpenLayers.Format.XML.document =
|
document.implementation.createDocument("", "", null);
|
} else if (!this.xmldom && window.ActiveXObject) {
|
this.xmldom = new ActiveXObject("Microsoft.XMLDOM");
|
}
|
}
|
return OpenLayers.Format.XML.document || this.xmldom;
|
},
|
|
CLASS_NAME: "OpenLayers.Format.XML"
|
|
});
|
|
OpenLayers.Format.XML.CONTENT_TYPE = {EMPTY: 0, SIMPLE: 1, COMPLEX: 2, MIXED: 3};
|
|
/**
|
* APIFunction: OpenLayers.Format.XML.lookupNamespaceURI
|
* Takes a prefix and returns the namespace URI associated with it on the given
|
* node if found (and null if not). Supplying null for the prefix will
|
* return the default namespace.
|
*
|
* For browsers that support it, this calls the native lookupNamesapceURI
|
* function. In other browsers, this is an implementation of
|
* http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI.
|
*
|
* For browsers that don't support the attribute.ownerElement property, this
|
* method cannot be called on attribute nodes.
|
*
|
* Parameters:
|
* node - {DOMElement} The node from which to start looking.
|
* prefix - {String} The prefix to lookup or null to lookup the default namespace.
|
*
|
* Returns:
|
* {String} The namespace URI for the given prefix. Returns null if the prefix
|
* cannot be found or the node is the wrong type.
|
*/
|
OpenLayers.Format.XML.lookupNamespaceURI = OpenLayers.Function.bind(
|
OpenLayers.Format.XML.prototype.lookupNamespaceURI,
|
OpenLayers.Format.XML.prototype
|
);
|
|
/**
|
* Property: OpenLayers.Format.XML.document
|
* {XMLDocument} XML document to reuse for creating non-HTML compliant nodes,
|
* like document.createCDATASection.
|
*/
|
OpenLayers.Format.XML.document = null;
|
/* ======================================================================
|
OpenLayers/Format/WFST.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format.js
|
*/
|
|
/**
|
* Function: OpenLayers.Format.WFST
|
* Used to create a versioned WFS protocol. Default version is 1.0.0.
|
*
|
* Returns:
|
* {<OpenLayers.Format>} A WFST format of the given version.
|
*/
|
OpenLayers.Format.WFST = function(options) {
|
options = OpenLayers.Util.applyDefaults(
|
options, OpenLayers.Format.WFST.DEFAULTS
|
);
|
var cls = OpenLayers.Format.WFST["v"+options.version.replace(/\./g, "_")];
|
if(!cls) {
|
throw "Unsupported WFST version: " + options.version;
|
}
|
return new cls(options);
|
};
|
|
/**
|
* Constant: OpenLayers.Format.WFST.DEFAULTS
|
* {Object} Default properties for the WFST format.
|
*/
|
OpenLayers.Format.WFST.DEFAULTS = {
|
"version": "1.0.0"
|
};
|
/* ======================================================================
|
OpenLayers/Feature.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
* @requires OpenLayers/Util.js
|
*/
|
|
/**
|
* Class: OpenLayers.Feature
|
* Features are combinations of geography and attributes. The OpenLayers.Feature
|
* class specifically combines a marker and a lonlat.
|
*/
|
OpenLayers.Feature = OpenLayers.Class({
|
|
/**
|
* Property: layer
|
* {<OpenLayers.Layer>}
|
*/
|
layer: null,
|
|
/**
|
* Property: id
|
* {String}
|
*/
|
id: null,
|
|
/**
|
* Property: lonlat
|
* {<OpenLayers.LonLat>}
|
*/
|
lonlat: null,
|
|
/**
|
* Property: data
|
* {Object}
|
*/
|
data: null,
|
|
/**
|
* Property: marker
|
* {<OpenLayers.Marker>}
|
*/
|
marker: null,
|
|
/**
|
* APIProperty: popupClass
|
* {<OpenLayers.Class>} The class which will be used to instantiate
|
* a new Popup. Default is <OpenLayers.Popup.Anchored>.
|
*/
|
popupClass: null,
|
|
/**
|
* Property: popup
|
* {<OpenLayers.Popup>}
|
*/
|
popup: null,
|
|
/**
|
* Constructor: OpenLayers.Feature
|
* Constructor for features.
|
*
|
* Parameters:
|
* layer - {<OpenLayers.Layer>}
|
* lonlat - {<OpenLayers.LonLat>}
|
* data - {Object}
|
*
|
* Returns:
|
* {<OpenLayers.Feature>}
|
*/
|
initialize: function(layer, lonlat, data) {
|
this.layer = layer;
|
this.lonlat = lonlat;
|
this.data = (data != null) ? data : {};
|
this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
|
},
|
|
/**
|
* Method: destroy
|
* nullify references to prevent circular references and memory leaks
|
*/
|
destroy: function() {
|
|
//remove the popup from the map
|
if ((this.layer != null) && (this.layer.map != null)) {
|
if (this.popup != null) {
|
this.layer.map.removePopup(this.popup);
|
}
|
}
|
// remove the marker from the layer
|
if (this.layer != null && this.marker != null) {
|
this.layer.removeMarker(this.marker);
|
}
|
|
this.layer = null;
|
this.id = null;
|
this.lonlat = null;
|
this.data = null;
|
if (this.marker != null) {
|
this.destroyMarker(this.marker);
|
this.marker = null;
|
}
|
if (this.popup != null) {
|
this.destroyPopup(this.popup);
|
this.popup = null;
|
}
|
},
|
|
/**
|
* Method: onScreen
|
*
|
* Returns:
|
* {Boolean} Whether or not the feature is currently visible on screen
|
* (based on its 'lonlat' property)
|
*/
|
onScreen:function() {
|
|
var onScreen = false;
|
if ((this.layer != null) && (this.layer.map != null)) {
|
var screenBounds = this.layer.map.getExtent();
|
onScreen = screenBounds.containsLonLat(this.lonlat);
|
}
|
return onScreen;
|
},
|
|
|
/**
|
* Method: createMarker
|
* Based on the data associated with the Feature, create and return a marker object.
|
*
|
* Returns:
|
* {<OpenLayers.Marker>} A Marker Object created from the 'lonlat' and 'icon' properties
|
* set in this.data. If no 'lonlat' is set, returns null. If no
|
* 'icon' is set, OpenLayers.Marker() will load the default image.
|
*
|
* Note - this.marker is set to return value
|
*
|
*/
|
createMarker: function() {
|
|
if (this.lonlat != null) {
|
this.marker = new OpenLayers.Marker(this.lonlat, this.data.icon);
|
}
|
return this.marker;
|
},
|
|
/**
|
* Method: destroyMarker
|
* Destroys marker.
|
* If user overrides the createMarker() function, s/he should be able
|
* to also specify an alternative function for destroying it
|
*/
|
destroyMarker: function() {
|
this.marker.destroy();
|
},
|
|
/**
|
* Method: createPopup
|
* Creates a popup object created from the 'lonlat', 'popupSize',
|
* and 'popupContentHTML' properties set in this.data. It uses
|
* this.marker.icon as default anchor.
|
*
|
* If no 'lonlat' is set, returns null.
|
* If no this.marker has been created, no anchor is sent.
|
*
|
* Note - the returned popup object is 'owned' by the feature, so you
|
* cannot use the popup's destroy method to discard the popup.
|
* Instead, you must use the feature's destroyPopup
|
*
|
* Note - this.popup is set to return value
|
*
|
* Parameters:
|
* closeBox - {Boolean} create popup with closebox or not
|
*
|
* Returns:
|
* {<OpenLayers.Popup>} Returns the created popup, which is also set
|
* as 'popup' property of this feature. Will be of whatever type
|
* specified by this feature's 'popupClass' property, but must be
|
* of type <OpenLayers.Popup>.
|
*
|
*/
|
createPopup: function(closeBox) {
|
|
if (this.lonlat != null) {
|
if (!this.popup) {
|
var anchor = (this.marker) ? this.marker.icon : null;
|
var popupClass = this.popupClass ?
|
this.popupClass : OpenLayers.Popup.Anchored;
|
this.popup = new popupClass(this.id + "_popup",
|
this.lonlat,
|
this.data.popupSize,
|
this.data.popupContentHTML,
|
anchor,
|
closeBox);
|
}
|
if (this.data.overflow != null) {
|
this.popup.contentDiv.style.overflow = this.data.overflow;
|
}
|
|
this.popup.feature = this;
|
}
|
return this.popup;
|
},
|
|
|
/**
|
* Method: destroyPopup
|
* Destroys the popup created via createPopup.
|
*
|
* As with the marker, if user overrides the createPopup() function, s/he
|
* should also be able to override the destruction
|
*/
|
destroyPopup: function() {
|
if (this.popup) {
|
this.popup.feature = null;
|
this.popup.destroy();
|
this.popup = null;
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Feature"
|
});
|
/* ======================================================================
|
OpenLayers/Feature/Vector.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
// TRASH THIS
|
OpenLayers.State = {
|
/** states */
|
UNKNOWN: 'Unknown',
|
INSERT: 'Insert',
|
UPDATE: 'Update',
|
DELETE: 'Delete'
|
};
|
|
/**
|
* @requires OpenLayers/Feature.js
|
* @requires OpenLayers/Util.js
|
*/
|
|
/**
|
* Class: OpenLayers.Feature.Vector
|
* Vector features use the OpenLayers.Geometry classes as geometry description.
|
* They have an 'attributes' property, which is the data object, and a 'style'
|
* property, the default values of which are defined in the
|
* <OpenLayers.Feature.Vector.style> objects.
|
*
|
* Inherits from:
|
* - <OpenLayers.Feature>
|
*/
|
OpenLayers.Feature.Vector = OpenLayers.Class(OpenLayers.Feature, {
|
|
/**
|
* Property: fid
|
* {String}
|
*/
|
fid: null,
|
|
/**
|
* APIProperty: geometry
|
* {<OpenLayers.Geometry>}
|
*/
|
geometry: null,
|
|
/**
|
* APIProperty: attributes
|
* {Object} This object holds arbitrary, serializable properties that
|
* describe the feature.
|
*/
|
attributes: null,
|
|
/**
|
* Property: bounds
|
* {<OpenLayers.Bounds>} The box bounding that feature's geometry, that
|
* property can be set by an <OpenLayers.Format> object when
|
* deserializing the feature, so in most cases it represents an
|
* information set by the server.
|
*/
|
bounds: null,
|
|
/**
|
* Property: state
|
* {String}
|
*/
|
state: null,
|
|
/**
|
* APIProperty: style
|
* {Object}
|
*/
|
style: null,
|
|
/**
|
* APIProperty: url
|
* {String} If this property is set it will be taken into account by
|
* {<OpenLayers.HTTP>} when upadting or deleting the feature.
|
*/
|
url: null,
|
|
/**
|
* Property: renderIntent
|
* {String} rendering intent currently being used
|
*/
|
renderIntent: "default",
|
|
/**
|
* APIProperty: modified
|
* {Object} An object with the originals of the geometry and attributes of
|
* the feature, if they were changed. Currently this property is only read
|
* by <OpenLayers.Format.WFST.v1>, and written by
|
* <OpenLayers.Control.ModifyFeature>, which sets the geometry property.
|
* Applications can set the originals of modified attributes in the
|
* attributes property. Note that applications have to check if this
|
* object and the attributes property is already created before using it.
|
* After a change made with ModifyFeature, this object could look like
|
*
|
* (code)
|
* {
|
* geometry: >Object
|
* }
|
* (end)
|
*
|
* When an application has made changes to feature attributes, it could
|
* have set the attributes to something like this:
|
*
|
* (code)
|
* {
|
* attributes: {
|
* myAttribute: "original"
|
* }
|
* }
|
* (end)
|
*
|
* Note that <OpenLayers.Format.WFST.v1> only checks for truthy values in
|
* *modified.geometry* and the attribute names in *modified.attributes*,
|
* but it is recommended to set the original values (and not just true) as
|
* attribute value, so applications could use this information to undo
|
* changes.
|
*/
|
modified: null,
|
|
/**
|
* Constructor: OpenLayers.Feature.Vector
|
* Create a vector feature.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>} The geometry that this feature
|
* represents.
|
* attributes - {Object} An optional object that will be mapped to the
|
* <attributes> property.
|
* style - {Object} An optional style object.
|
*/
|
initialize: function(geometry, attributes, style) {
|
OpenLayers.Feature.prototype.initialize.apply(this,
|
[null, null, attributes]);
|
this.lonlat = null;
|
this.geometry = geometry ? geometry : null;
|
this.state = null;
|
this.attributes = {};
|
if (attributes) {
|
this.attributes = OpenLayers.Util.extend(this.attributes,
|
attributes);
|
}
|
this.style = style ? style : null;
|
},
|
|
/**
|
* Method: destroy
|
* nullify references to prevent circular references and memory leaks
|
*/
|
destroy: function() {
|
if (this.layer) {
|
this.layer.removeFeatures(this);
|
this.layer = null;
|
}
|
|
this.geometry = null;
|
this.modified = null;
|
OpenLayers.Feature.prototype.destroy.apply(this, arguments);
|
},
|
|
/**
|
* Method: clone
|
* Create a clone of this vector feature. Does not set any non-standard
|
* properties.
|
*
|
* Returns:
|
* {<OpenLayers.Feature.Vector>} An exact clone of this vector feature.
|
*/
|
clone: function () {
|
return new OpenLayers.Feature.Vector(
|
this.geometry ? this.geometry.clone() : null,
|
this.attributes,
|
this.style);
|
},
|
|
/**
|
* Method: onScreen
|
* Determine whether the feature is within the map viewport. This method
|
* tests for an intersection between the geometry and the viewport
|
* bounds. If a more effecient but less precise geometry bounds
|
* intersection is desired, call the method with the boundsOnly
|
* parameter true.
|
*
|
* Parameters:
|
* boundsOnly - {Boolean} Only test whether a feature's bounds intersects
|
* the viewport bounds. Default is false. If false, the feature's
|
* geometry must intersect the viewport for onScreen to return true.
|
*
|
* Returns:
|
* {Boolean} The feature is currently visible on screen (optionally
|
* based on its bounds if boundsOnly is true).
|
*/
|
onScreen:function(boundsOnly) {
|
var onScreen = false;
|
if(this.layer && this.layer.map) {
|
var screenBounds = this.layer.map.getExtent();
|
if(boundsOnly) {
|
var featureBounds = this.geometry.getBounds();
|
onScreen = screenBounds.intersectsBounds(featureBounds);
|
} else {
|
var screenPoly = screenBounds.toGeometry();
|
onScreen = screenPoly.intersects(this.geometry);
|
}
|
}
|
return onScreen;
|
},
|
|
/**
|
* Method: getVisibility
|
* Determine whether the feature is displayed or not. It may not displayed
|
* because:
|
* - its style display property is set to 'none',
|
* - it doesn't belong to any layer,
|
* - the styleMap creates a symbolizer with display property set to 'none'
|
* for it,
|
* - the layer which it belongs to is not visible.
|
*
|
* Returns:
|
* {Boolean} The feature is currently displayed.
|
*/
|
getVisibility: function() {
|
return !(this.style && this.style.display == 'none' ||
|
!this.layer ||
|
this.layer && this.layer.styleMap &&
|
this.layer.styleMap.createSymbolizer(this, this.renderIntent).display == 'none' ||
|
this.layer && !this.layer.getVisibility());
|
},
|
|
/**
|
* Method: createMarker
|
* HACK - we need to decide if all vector features should be able to
|
* create markers
|
*
|
* Returns:
|
* {<OpenLayers.Marker>} For now just returns null
|
*/
|
createMarker: function() {
|
return null;
|
},
|
|
/**
|
* Method: destroyMarker
|
* HACK - we need to decide if all vector features should be able to
|
* delete markers
|
*
|
* If user overrides the createMarker() function, s/he should be able
|
* to also specify an alternative function for destroying it
|
*/
|
destroyMarker: function() {
|
// pass
|
},
|
|
/**
|
* Method: createPopup
|
* HACK - we need to decide if all vector features should be able to
|
* create popups
|
*
|
* Returns:
|
* {<OpenLayers.Popup>} For now just returns null
|
*/
|
createPopup: function() {
|
return null;
|
},
|
|
/**
|
* Method: atPoint
|
* Determins whether the feature intersects with the specified location.
|
*
|
* Parameters:
|
* lonlat - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an
|
* object with a 'lon' and 'lat' properties.
|
* toleranceLon - {float} Optional tolerance in Geometric Coords
|
* toleranceLat - {float} Optional tolerance in Geographic Coords
|
*
|
* Returns:
|
* {Boolean} Whether or not the feature is at the specified location
|
*/
|
atPoint: function(lonlat, toleranceLon, toleranceLat) {
|
var atPoint = false;
|
if(this.geometry) {
|
atPoint = this.geometry.atPoint(lonlat, toleranceLon,
|
toleranceLat);
|
}
|
return atPoint;
|
},
|
|
/**
|
* Method: destroyPopup
|
* HACK - we need to decide if all vector features should be able to
|
* delete popups
|
*/
|
destroyPopup: function() {
|
// pass
|
},
|
|
/**
|
* Method: move
|
* Moves the feature and redraws it at its new location
|
*
|
* Parameters:
|
* location - {<OpenLayers.LonLat> or <OpenLayers.Pixel>} the
|
* location to which to move the feature.
|
*/
|
move: function(location) {
|
|
if(!this.layer || !this.geometry.move){
|
//do nothing if no layer or immoveable geometry
|
return undefined;
|
}
|
|
var pixel;
|
if (location.CLASS_NAME == "OpenLayers.LonLat") {
|
pixel = this.layer.getViewPortPxFromLonLat(location);
|
} else {
|
pixel = location;
|
}
|
|
var lastPixel = this.layer.getViewPortPxFromLonLat(this.geometry.getBounds().getCenterLonLat());
|
var res = this.layer.map.getResolution();
|
this.geometry.move(res * (pixel.x - lastPixel.x),
|
res * (lastPixel.y - pixel.y));
|
this.layer.drawFeature(this);
|
return lastPixel;
|
},
|
|
/**
|
* Method: toState
|
* Sets the new state
|
*
|
* Parameters:
|
* state - {String}
|
*/
|
toState: function(state) {
|
if (state == OpenLayers.State.UPDATE) {
|
switch (this.state) {
|
case OpenLayers.State.UNKNOWN:
|
case OpenLayers.State.DELETE:
|
this.state = state;
|
break;
|
case OpenLayers.State.UPDATE:
|
case OpenLayers.State.INSERT:
|
break;
|
}
|
} else if (state == OpenLayers.State.INSERT) {
|
switch (this.state) {
|
case OpenLayers.State.UNKNOWN:
|
break;
|
default:
|
this.state = state;
|
break;
|
}
|
} else if (state == OpenLayers.State.DELETE) {
|
switch (this.state) {
|
case OpenLayers.State.INSERT:
|
// the feature should be destroyed
|
break;
|
case OpenLayers.State.DELETE:
|
break;
|
case OpenLayers.State.UNKNOWN:
|
case OpenLayers.State.UPDATE:
|
this.state = state;
|
break;
|
}
|
} else if (state == OpenLayers.State.UNKNOWN) {
|
this.state = state;
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Feature.Vector"
|
});
|
|
|
/**
|
* Constant: OpenLayers.Feature.Vector.style
|
* OpenLayers features can have a number of style attributes. The 'default'
|
* style will typically be used if no other style is specified. These
|
* styles correspond for the most part, to the styling properties defined
|
* by the SVG standard.
|
* Information on fill properties: http://www.w3.org/TR/SVG/painting.html#FillProperties
|
* Information on stroke properties: http://www.w3.org/TR/SVG/painting.html#StrokeProperties
|
*
|
* Symbolizer properties:
|
* fill - {Boolean} Set to false if no fill is desired.
|
* fillColor - {String} Hex fill color. Default is "#ee9900".
|
* fillOpacity - {Number} Fill opacity (0-1). Default is 0.4
|
* stroke - {Boolean} Set to false if no stroke is desired.
|
* strokeColor - {String} Hex stroke color. Default is "#ee9900".
|
* strokeOpacity - {Number} Stroke opacity (0-1). Default is 1.
|
* strokeWidth - {Number} Pixel stroke width. Default is 1.
|
* strokeLinecap - {String} Stroke cap type. Default is "round". [butt | round | square]
|
* strokeDashstyle - {String} Stroke dash style. Default is "solid". [dot | dash | dashdot | longdash | longdashdot | solid]
|
* graphic - {Boolean} Set to false if no graphic is desired.
|
* pointRadius - {Number} Pixel point radius. Default is 6.
|
* pointerEvents - {String} Default is "visiblePainted".
|
* cursor - {String} Default is "".
|
* externalGraphic - {String} Url to an external graphic that will be used for rendering points.
|
* graphicWidth - {Number} Pixel width for sizing an external graphic.
|
* graphicHeight - {Number} Pixel height for sizing an external graphic.
|
* graphicOpacity - {Number} Opacity (0-1) for an external graphic.
|
* graphicXOffset - {Number} Pixel offset along the positive x axis for displacing an external graphic.
|
* graphicYOffset - {Number} Pixel offset along the positive y axis for displacing an external graphic.
|
* 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).
|
* graphicZIndex - {Number} The integer z-index value to use in rendering.
|
* graphicName - {String} Named graphic to use when rendering points. Supported values include "circle" (default),
|
* "square", "star", "x", "cross", "triangle".
|
* graphicTitle - {String} Tooltip when hovering over a feature. *deprecated*, use title instead
|
* title - {String} Tooltip when hovering over a feature. Not supported by the canvas renderer.
|
* backgroundGraphic - {String} Url to a graphic to be used as the background under an externalGraphic.
|
* backgroundGraphicZIndex - {Number} The integer z-index value to use in rendering the background graphic.
|
* backgroundXOffset - {Number} The x offset (in pixels) for the background graphic.
|
* backgroundYOffset - {Number} The y offset (in pixels) for the background graphic.
|
* backgroundHeight - {Number} The height of the background graphic. If not provided, the graphicHeight will be used.
|
* backgroundWidth - {Number} The width of the background width. If not provided, the graphicWidth will be used.
|
* label - {String} The text for an optional label. For browsers that use the canvas renderer, this requires either
|
* fillText or mozDrawText to be available.
|
* labelAlign - {String} Label alignment. This specifies the insertion point relative to the text. It is a string
|
* composed of two characters. The first character is for the horizontal alignment, the second for the vertical
|
* alignment. Valid values for horizontal alignment: "l"=left, "c"=center, "r"=right. Valid values for vertical
|
* alignment: "t"=top, "m"=middle, "b"=bottom. Example values: "lt", "cm", "rb". Default is "cm".
|
* labelXOffset - {Number} Pixel offset along the positive x axis for displacing the label. Not supported by the canvas renderer.
|
* labelYOffset - {Number} Pixel offset along the positive y axis for displacing the label. Not supported by the canvas renderer.
|
* labelSelect - {Boolean} If set to true, labels will be selectable using SelectFeature or similar controls.
|
* Default is false.
|
* labelOutlineColor - {String} The color of the label outline. Default is 'white'. Only supported by the canvas & SVG renderers.
|
* labelOutlineWidth - {Number} The width of the label outline. Default is 3, set to 0 or null to disable. Only supported by the SVG renderers.
|
* labelOutlineOpacity - {Number} The opacity (0-1) of the label outline. Default is fontOpacity. Only supported by the canvas & SVG renderers.
|
* fontColor - {String} The font color for the label, to be provided like CSS.
|
* fontOpacity - {Number} Opacity (0-1) for the label
|
* fontFamily - {String} The font family for the label, to be provided like in CSS.
|
* fontSize - {String} The font size for the label, to be provided like in CSS.
|
* fontStyle - {String} The font style for the label, to be provided like in CSS.
|
* fontWeight - {String} The font weight for the label, to be provided like in CSS.
|
* display - {String} Symbolizers will have no effect if display is set to "none". All other values have no effect.
|
*/
|
OpenLayers.Feature.Vector.style = {
|
'default': {
|
fillColor: "#ee9900",
|
fillOpacity: 0.4,
|
hoverFillColor: "white",
|
hoverFillOpacity: 0.8,
|
strokeColor: "#ee9900",
|
strokeOpacity: 1,
|
strokeWidth: 1,
|
strokeLinecap: "round",
|
strokeDashstyle: "solid",
|
hoverStrokeColor: "red",
|
hoverStrokeOpacity: 1,
|
hoverStrokeWidth: 0.2,
|
pointRadius: 6,
|
hoverPointRadius: 1,
|
hoverPointUnit: "%",
|
pointerEvents: "visiblePainted",
|
cursor: "inherit",
|
fontColor: "#000000",
|
labelAlign: "cm",
|
labelOutlineColor: "white",
|
labelOutlineWidth: 3
|
},
|
'select': {
|
fillColor: "blue",
|
fillOpacity: 0.4,
|
hoverFillColor: "white",
|
hoverFillOpacity: 0.8,
|
strokeColor: "blue",
|
strokeOpacity: 1,
|
strokeWidth: 2,
|
strokeLinecap: "round",
|
strokeDashstyle: "solid",
|
hoverStrokeColor: "red",
|
hoverStrokeOpacity: 1,
|
hoverStrokeWidth: 0.2,
|
pointRadius: 6,
|
hoverPointRadius: 1,
|
hoverPointUnit: "%",
|
pointerEvents: "visiblePainted",
|
cursor: "pointer",
|
fontColor: "#000000",
|
labelAlign: "cm",
|
labelOutlineColor: "white",
|
labelOutlineWidth: 3
|
|
},
|
'temporary': {
|
fillColor: "#66cccc",
|
fillOpacity: 0.2,
|
hoverFillColor: "white",
|
hoverFillOpacity: 0.8,
|
strokeColor: "#66cccc",
|
strokeOpacity: 1,
|
strokeLinecap: "round",
|
strokeWidth: 2,
|
strokeDashstyle: "solid",
|
hoverStrokeColor: "red",
|
hoverStrokeOpacity: 1,
|
hoverStrokeWidth: 0.2,
|
pointRadius: 6,
|
hoverPointRadius: 1,
|
hoverPointUnit: "%",
|
pointerEvents: "visiblePainted",
|
cursor: "inherit",
|
fontColor: "#000000",
|
labelAlign: "cm",
|
labelOutlineColor: "white",
|
labelOutlineWidth: 3
|
|
},
|
'delete': {
|
display: "none"
|
}
|
};
|
/* ======================================================================
|
OpenLayers/Style.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
* @requires OpenLayers/Util.js
|
* @requires OpenLayers/Feature/Vector.js
|
*/
|
|
/**
|
* Class: OpenLayers.Style
|
* This class represents a UserStyle obtained
|
* from a SLD, containing styling rules.
|
*/
|
OpenLayers.Style = OpenLayers.Class({
|
|
/**
|
* Property: id
|
* {String} A unique id for this session.
|
*/
|
id: null,
|
|
/**
|
* APIProperty: name
|
* {String}
|
*/
|
name: null,
|
|
/**
|
* Property: title
|
* {String} Title of this style (set if included in SLD)
|
*/
|
title: null,
|
|
/**
|
* Property: description
|
* {String} Description of this style (set if abstract is included in SLD)
|
*/
|
description: null,
|
|
/**
|
* APIProperty: layerName
|
* {<String>} name of the layer that this style belongs to, usually
|
* according to the NamedLayer attribute of an SLD document.
|
*/
|
layerName: null,
|
|
/**
|
* APIProperty: isDefault
|
* {Boolean}
|
*/
|
isDefault: false,
|
|
/**
|
* Property: rules
|
* {Array(<OpenLayers.Rule>)}
|
*/
|
rules: null,
|
|
/**
|
* APIProperty: context
|
* {Object} An optional object with properties that symbolizers' property
|
* values should be evaluated against. If no context is specified,
|
* feature.attributes will be used
|
*/
|
context: null,
|
|
/**
|
* Property: defaultStyle
|
* {Object} hash of style properties to use as default for merging
|
* rule-based style symbolizers onto. If no rules are defined,
|
* createSymbolizer will return this style. If <defaultsPerSymbolizer> is set to
|
* true, the defaultStyle will only be taken into account if there are
|
* rules defined.
|
*/
|
defaultStyle: null,
|
|
/**
|
* Property: defaultsPerSymbolizer
|
* {Boolean} If set to true, the <defaultStyle> will extend the symbolizer
|
* of every rule. Properties of the <defaultStyle> will also be used to set
|
* missing symbolizer properties if the symbolizer has stroke, fill or
|
* graphic set to true. Default is false.
|
*/
|
defaultsPerSymbolizer: false,
|
|
/**
|
* Property: propertyStyles
|
* {Hash of Boolean} cache of style properties that need to be parsed for
|
* propertyNames. Property names are keys, values won't be used.
|
*/
|
propertyStyles: null,
|
|
|
/**
|
* Constructor: OpenLayers.Style
|
* Creates a UserStyle.
|
*
|
* Parameters:
|
* style - {Object} Optional hash of style properties that will be
|
* used as default style for this style object. This style
|
* applies if no rules are specified. Symbolizers defined in
|
* rules will extend this default style.
|
* options - {Object} An optional object with properties to set on the
|
* style.
|
*
|
* Valid options:
|
* rules - {Array(<OpenLayers.Rule>)} List of rules to be added to the
|
* style.
|
*
|
* Returns:
|
* {<OpenLayers.Style>}
|
*/
|
initialize: function(style, options) {
|
|
OpenLayers.Util.extend(this, options);
|
this.rules = [];
|
if(options && options.rules) {
|
this.addRules(options.rules);
|
}
|
|
// use the default style from OpenLayers.Feature.Vector if no style
|
// was given in the constructor
|
this.setDefaultStyle(style ||
|
OpenLayers.Feature.Vector.style["default"]);
|
|
this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
|
},
|
|
/**
|
* APIMethod: destroy
|
* nullify references to prevent circular references and memory leaks
|
*/
|
destroy: function() {
|
for (var i=0, len=this.rules.length; i<len; i++) {
|
this.rules[i].destroy();
|
this.rules[i] = null;
|
}
|
this.rules = null;
|
this.defaultStyle = null;
|
},
|
|
/**
|
* Method: createSymbolizer
|
* creates a style by applying all feature-dependent rules to the base
|
* style.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature>} feature to evaluate rules for
|
*
|
* Returns:
|
* {Object} symbolizer hash
|
*/
|
createSymbolizer: function(feature) {
|
var style = this.defaultsPerSymbolizer ? {} : this.createLiterals(
|
OpenLayers.Util.extend({}, this.defaultStyle), feature);
|
|
var rules = this.rules;
|
|
var rule, context;
|
var elseRules = [];
|
var appliedRules = false;
|
for(var i=0, len=rules.length; i<len; i++) {
|
rule = rules[i];
|
// does the rule apply?
|
var applies = rule.evaluate(feature);
|
|
if(applies) {
|
if(rule instanceof OpenLayers.Rule && rule.elseFilter) {
|
elseRules.push(rule);
|
} else {
|
appliedRules = true;
|
this.applySymbolizer(rule, style, feature);
|
}
|
}
|
}
|
|
// if no other rules apply, apply the rules with else filters
|
if(appliedRules == false && elseRules.length > 0) {
|
appliedRules = true;
|
for(var i=0, len=elseRules.length; i<len; i++) {
|
this.applySymbolizer(elseRules[i], style, feature);
|
}
|
}
|
|
// don't display if there were rules but none applied
|
if(rules.length > 0 && appliedRules == false) {
|
style.display = "none";
|
}
|
|
if (style.label != null && typeof style.label !== "string") {
|
style.label = String(style.label);
|
}
|
|
return style;
|
},
|
|
/**
|
* Method: applySymbolizer
|
*
|
* Parameters:
|
* rule - {<OpenLayers.Rule>}
|
* style - {Object}
|
* feature - {<OpenLayer.Feature.Vector>}
|
*
|
* Returns:
|
* {Object} A style with new symbolizer applied.
|
*/
|
applySymbolizer: function(rule, style, feature) {
|
var symbolizerPrefix = feature.geometry ?
|
this.getSymbolizerPrefix(feature.geometry) :
|
OpenLayers.Style.SYMBOLIZER_PREFIXES[0];
|
|
var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer;
|
|
if(this.defaultsPerSymbolizer === true) {
|
var defaults = this.defaultStyle;
|
OpenLayers.Util.applyDefaults(symbolizer, {
|
pointRadius: defaults.pointRadius
|
});
|
if(symbolizer.stroke === true || symbolizer.graphic === true) {
|
OpenLayers.Util.applyDefaults(symbolizer, {
|
strokeWidth: defaults.strokeWidth,
|
strokeColor: defaults.strokeColor,
|
strokeOpacity: defaults.strokeOpacity,
|
strokeDashstyle: defaults.strokeDashstyle,
|
strokeLinecap: defaults.strokeLinecap
|
});
|
}
|
if(symbolizer.fill === true || symbolizer.graphic === true) {
|
OpenLayers.Util.applyDefaults(symbolizer, {
|
fillColor: defaults.fillColor,
|
fillOpacity: defaults.fillOpacity
|
});
|
}
|
if(symbolizer.graphic === true) {
|
OpenLayers.Util.applyDefaults(symbolizer, {
|
pointRadius: this.defaultStyle.pointRadius,
|
externalGraphic: this.defaultStyle.externalGraphic,
|
graphicName: this.defaultStyle.graphicName,
|
graphicOpacity: this.defaultStyle.graphicOpacity,
|
graphicWidth: this.defaultStyle.graphicWidth,
|
graphicHeight: this.defaultStyle.graphicHeight,
|
graphicXOffset: this.defaultStyle.graphicXOffset,
|
graphicYOffset: this.defaultStyle.graphicYOffset
|
});
|
}
|
}
|
|
// merge the style with the current style
|
return this.createLiterals(
|
OpenLayers.Util.extend(style, symbolizer), feature);
|
},
|
|
/**
|
* Method: createLiterals
|
* creates literals for all style properties that have an entry in
|
* <this.propertyStyles>.
|
*
|
* Parameters:
|
* style - {Object} style to create literals for. Will be modified
|
* inline.
|
* feature - {Object}
|
*
|
* Returns:
|
* {Object} the modified style
|
*/
|
createLiterals: function(style, feature) {
|
var context = OpenLayers.Util.extend({}, feature.attributes || feature.data);
|
OpenLayers.Util.extend(context, this.context);
|
|
for (var i in this.propertyStyles) {
|
style[i] = OpenLayers.Style.createLiteral(style[i], context, feature, i);
|
}
|
return style;
|
},
|
|
/**
|
* Method: findPropertyStyles
|
* Looks into all rules for this style and the defaultStyle to collect
|
* all the style hash property names containing ${...} strings that have
|
* to be replaced using the createLiteral method before returning them.
|
*
|
* Returns:
|
* {Object} hash of property names that need createLiteral parsing. The
|
* name of the property is the key, and the value is true;
|
*/
|
findPropertyStyles: function() {
|
var propertyStyles = {};
|
|
// check the default style
|
var style = this.defaultStyle;
|
this.addPropertyStyles(propertyStyles, style);
|
|
// walk through all rules to check for properties in their symbolizer
|
var rules = this.rules;
|
var symbolizer, value;
|
for (var i=0, len=rules.length; i<len; i++) {
|
symbolizer = rules[i].symbolizer;
|
for (var key in symbolizer) {
|
value = symbolizer[key];
|
if (typeof value == "object") {
|
// symbolizer key is "Point", "Line" or "Polygon"
|
this.addPropertyStyles(propertyStyles, value);
|
} else {
|
// symbolizer is a hash of style properties
|
this.addPropertyStyles(propertyStyles, symbolizer);
|
break;
|
}
|
}
|
}
|
return propertyStyles;
|
},
|
|
/**
|
* Method: addPropertyStyles
|
*
|
* Parameters:
|
* propertyStyles - {Object} hash to add new property styles to. Will be
|
* modified inline
|
* symbolizer - {Object} search this symbolizer for property styles
|
*
|
* Returns:
|
* {Object} propertyStyles hash
|
*/
|
addPropertyStyles: function(propertyStyles, symbolizer) {
|
var property;
|
for (var key in symbolizer) {
|
property = symbolizer[key];
|
if (typeof property == "string" &&
|
property.match(/\$\{\w+\}/)) {
|
propertyStyles[key] = true;
|
}
|
}
|
return propertyStyles;
|
},
|
|
/**
|
* APIMethod: addRules
|
* Adds rules to this style.
|
*
|
* Parameters:
|
* rules - {Array(<OpenLayers.Rule>)}
|
*/
|
addRules: function(rules) {
|
Array.prototype.push.apply(this.rules, rules);
|
this.propertyStyles = this.findPropertyStyles();
|
},
|
|
/**
|
* APIMethod: setDefaultStyle
|
* Sets the default style for this style object.
|
*
|
* Parameters:
|
* style - {Object} Hash of style properties
|
*/
|
setDefaultStyle: function(style) {
|
this.defaultStyle = style;
|
this.propertyStyles = this.findPropertyStyles();
|
},
|
|
/**
|
* Method: getSymbolizerPrefix
|
* Returns the correct symbolizer prefix according to the
|
* geometry type of the passed geometry
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {String} key of the according symbolizer
|
*/
|
getSymbolizerPrefix: function(geometry) {
|
var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES;
|
for (var i=0, len=prefixes.length; i<len; i++) {
|
if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) {
|
return prefixes[i];
|
}
|
}
|
},
|
|
/**
|
* APIMethod: clone
|
* Clones this style.
|
*
|
* Returns:
|
* {<OpenLayers.Style>} Clone of this style.
|
*/
|
clone: function() {
|
var options = OpenLayers.Util.extend({}, this);
|
// clone rules
|
if(this.rules) {
|
options.rules = [];
|
for(var i=0, len=this.rules.length; i<len; ++i) {
|
options.rules.push(this.rules[i].clone());
|
}
|
}
|
// clone context
|
options.context = this.context && OpenLayers.Util.extend({}, this.context);
|
//clone default style
|
var defaultStyle = OpenLayers.Util.extend({}, this.defaultStyle);
|
return new OpenLayers.Style(defaultStyle, options);
|
},
|
|
CLASS_NAME: "OpenLayers.Style"
|
});
|
|
|
/**
|
* Function: createLiteral
|
* converts a style value holding a combination of PropertyName and Literal
|
* into a Literal, taking the property values from the passed features.
|
*
|
* Parameters:
|
* value - {String} value to parse. If this string contains a construct like
|
* "foo ${bar}", then "foo " will be taken as literal, and "${bar}"
|
* will be replaced by the value of the "bar" attribute of the passed
|
* feature.
|
* context - {Object} context to take attribute values from
|
* feature - {<OpenLayers.Feature.Vector>} optional feature to pass to
|
* <OpenLayers.String.format> for evaluating functions in the
|
* context.
|
* property - {String} optional, name of the property for which the literal is
|
* being created for evaluating functions in the context.
|
*
|
* Returns:
|
* {String} the parsed value. In the example of the value parameter above, the
|
* result would be "foo valueOfBar", assuming that the passed feature has an
|
* attribute named "bar" with the value "valueOfBar".
|
*/
|
OpenLayers.Style.createLiteral = function(value, context, feature, property) {
|
if (typeof value == "string" && value.indexOf("${") != -1) {
|
value = OpenLayers.String.format(value, context, [feature, property]);
|
value = (isNaN(value) || !value) ? value : parseFloat(value);
|
}
|
return value;
|
};
|
|
/**
|
* Constant: OpenLayers.Style.SYMBOLIZER_PREFIXES
|
* {Array} prefixes of the sld symbolizers. These are the
|
* same as the main geometry types
|
*/
|
OpenLayers.Style.SYMBOLIZER_PREFIXES = ['Point', 'Line', 'Polygon', 'Text',
|
'Raster'];
|
/* ======================================================================
|
OpenLayers/Filter.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
* @requires OpenLayers/Util.js
|
* @requires OpenLayers/Style.js
|
*/
|
|
/**
|
* Class: OpenLayers.Filter
|
* This class represents an OGC Filter.
|
*/
|
OpenLayers.Filter = OpenLayers.Class({
|
|
/**
|
* Constructor: OpenLayers.Filter
|
* This class represents a generic filter.
|
*
|
* Parameters:
|
* options - {Object} Optional object whose properties will be set on the
|
* instance.
|
*
|
* Returns:
|
* {<OpenLayers.Filter>}
|
*/
|
initialize: function(options) {
|
OpenLayers.Util.extend(this, options);
|
},
|
|
/**
|
* APIMethod: destroy
|
* Remove reference to anything added.
|
*/
|
destroy: function() {
|
},
|
|
/**
|
* APIMethod: evaluate
|
* Evaluates this filter in a specific context. Instances or subclasses
|
* are supposed to override this method.
|
*
|
* Parameters:
|
* context - {Object} Context to use in evaluating the filter. If a vector
|
* feature is provided, the feature.attributes will be used as context.
|
*
|
* Returns:
|
* {Boolean} The filter applies.
|
*/
|
evaluate: function(context) {
|
return true;
|
},
|
|
/**
|
* APIMethod: clone
|
* Clones this filter. Should be implemented by subclasses.
|
*
|
* Returns:
|
* {<OpenLayers.Filter>} Clone of this filter.
|
*/
|
clone: function() {
|
return null;
|
},
|
|
/**
|
* APIMethod: toString
|
*
|
* Returns:
|
* {String} Include <OpenLayers.Format.CQL> in your build to get a CQL
|
* representation of the filter returned. Otherwise "[Object object]"
|
* will be returned.
|
*/
|
toString: function() {
|
var string;
|
if (OpenLayers.Format && OpenLayers.Format.CQL) {
|
string = OpenLayers.Format.CQL.prototype.write(this);
|
} else {
|
string = Object.prototype.toString.call(this);
|
}
|
return string;
|
},
|
|
CLASS_NAME: "OpenLayers.Filter"
|
});
|
/* ======================================================================
|
OpenLayers/Filter/Spatial.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Filter.js
|
*/
|
|
/**
|
* Class: OpenLayers.Filter.Spatial
|
* This class represents a spatial filter.
|
* Currently implemented: BBOX, DWithin and Intersects
|
*
|
* Inherits from:
|
* - <OpenLayers.Filter>
|
*/
|
OpenLayers.Filter.Spatial = OpenLayers.Class(OpenLayers.Filter, {
|
|
/**
|
* APIProperty: type
|
* {String} Type of spatial filter.
|
*
|
* The type should be one of:
|
* - OpenLayers.Filter.Spatial.BBOX
|
* - OpenLayers.Filter.Spatial.INTERSECTS
|
* - OpenLayers.Filter.Spatial.DWITHIN
|
* - OpenLayers.Filter.Spatial.WITHIN
|
* - OpenLayers.Filter.Spatial.CONTAINS
|
*/
|
type: null,
|
|
/**
|
* APIProperty: property
|
* {String} Name of the context property to compare.
|
*/
|
property: null,
|
|
/**
|
* APIProperty: value
|
* {<OpenLayers.Bounds> || <OpenLayers.Geometry>} The bounds or geometry
|
* to be used by the filter. Use bounds for BBOX filters and geometry
|
* for INTERSECTS or DWITHIN filters.
|
*/
|
value: null,
|
|
/**
|
* APIProperty: distance
|
* {Number} The distance to use in a DWithin spatial filter.
|
*/
|
distance: null,
|
|
/**
|
* APIProperty: distanceUnits
|
* {String} The units to use for the distance, e.g. 'm'.
|
*/
|
distanceUnits: null,
|
|
/**
|
* Constructor: OpenLayers.Filter.Spatial
|
* Creates a spatial filter.
|
*
|
* Parameters:
|
* options - {Object} An optional object with properties to set on the
|
* filter.
|
*
|
* Returns:
|
* {<OpenLayers.Filter.Spatial>}
|
*/
|
|
/**
|
* Method: evaluate
|
* Evaluates this filter for a specific feature.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>} feature to apply the filter to.
|
*
|
* Returns:
|
* {Boolean} The feature meets filter criteria.
|
*/
|
evaluate: function(feature) {
|
var intersect = false;
|
switch(this.type) {
|
case OpenLayers.Filter.Spatial.BBOX:
|
case OpenLayers.Filter.Spatial.INTERSECTS:
|
if(feature.geometry) {
|
var geom = this.value;
|
if(this.value.CLASS_NAME == "OpenLayers.Bounds") {
|
geom = this.value.toGeometry();
|
}
|
if(feature.geometry.intersects(geom)) {
|
intersect = true;
|
}
|
}
|
break;
|
default:
|
throw new Error('evaluate is not implemented for this filter type.');
|
}
|
return intersect;
|
},
|
|
/**
|
* APIMethod: clone
|
* Clones this filter.
|
*
|
* Returns:
|
* {<OpenLayers.Filter.Spatial>} Clone of this filter.
|
*/
|
clone: function() {
|
var options = OpenLayers.Util.applyDefaults({
|
value: this.value && this.value.clone && this.value.clone()
|
}, this);
|
return new OpenLayers.Filter.Spatial(options);
|
},
|
CLASS_NAME: "OpenLayers.Filter.Spatial"
|
});
|
|
OpenLayers.Filter.Spatial.BBOX = "BBOX";
|
OpenLayers.Filter.Spatial.INTERSECTS = "INTERSECTS";
|
OpenLayers.Filter.Spatial.DWITHIN = "DWITHIN";
|
OpenLayers.Filter.Spatial.WITHIN = "WITHIN";
|
OpenLayers.Filter.Spatial.CONTAINS = "CONTAINS";
|
/* ======================================================================
|
OpenLayers/Filter/FeatureId.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Filter.js
|
*/
|
|
/**
|
* Class: OpenLayers.Filter.FeatureId
|
* This class represents a ogc:FeatureId Filter, as being used for rule-based SLD
|
* styling
|
*
|
* Inherits from:
|
* - <OpenLayers.Filter>
|
*/
|
OpenLayers.Filter.FeatureId = OpenLayers.Class(OpenLayers.Filter, {
|
|
/**
|
* APIProperty: fids
|
* {Array(String)} Feature Ids to evaluate this rule against.
|
* To be passed inside the params object.
|
*/
|
fids: null,
|
|
/**
|
* Property: type
|
* {String} Type to identify this filter.
|
*/
|
type: "FID",
|
|
/**
|
* Constructor: OpenLayers.Filter.FeatureId
|
* Creates an ogc:FeatureId rule.
|
*
|
* Parameters:
|
* options - {Object} An optional object with properties to set on the
|
* rule
|
*
|
* Returns:
|
* {<OpenLayers.Filter.FeatureId>}
|
*/
|
initialize: function(options) {
|
this.fids = [];
|
OpenLayers.Filter.prototype.initialize.apply(this, [options]);
|
},
|
|
/**
|
* APIMethod: evaluate
|
* evaluates this rule for a specific feature
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature>} feature to apply the rule to.
|
* For vector features, the check is run against the fid,
|
* for plain features against the id.
|
*
|
* Returns:
|
* {Boolean} true if the rule applies, false if it does not
|
*/
|
evaluate: function(feature) {
|
for (var i=0, len=this.fids.length; i<len; i++) {
|
var fid = feature.fid || feature.id;
|
if (fid == this.fids[i]) {
|
return true;
|
}
|
}
|
return false;
|
},
|
|
/**
|
* APIMethod: clone
|
* Clones this filter.
|
*
|
* Returns:
|
* {<OpenLayers.Filter.FeatureId>} Clone of this filter.
|
*/
|
clone: function() {
|
var filter = new OpenLayers.Filter.FeatureId();
|
OpenLayers.Util.extend(filter, this);
|
filter.fids = this.fids.slice();
|
return filter;
|
},
|
|
CLASS_NAME: "OpenLayers.Filter.FeatureId"
|
});
|
/* ======================================================================
|
OpenLayers/Format/WFST/v1.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML.js
|
* @requires OpenLayers/Format/WFST.js
|
* @requires OpenLayers/Filter/Spatial.js
|
* @requires OpenLayers/Filter/FeatureId.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WFST.v1
|
* Superclass for WFST parsers.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.WFST.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
xlink: "http://www.w3.org/1999/xlink",
|
xsi: "http://www.w3.org/2001/XMLSchema-instance",
|
wfs: "http://www.opengis.net/wfs",
|
gml: "http://www.opengis.net/gml",
|
ogc: "http://www.opengis.net/ogc",
|
ows: "http://www.opengis.net/ows"
|
},
|
|
/**
|
* Property: defaultPrefix
|
*/
|
defaultPrefix: "wfs",
|
|
/**
|
* Property: version
|
* {String} WFS version number.
|
*/
|
version: null,
|
|
/**
|
* Property: schemaLocation
|
* {String} Schema location for a particular minor version.
|
*/
|
schemaLocations: null,
|
|
/**
|
* APIProperty: srsName
|
* {String} URI for spatial reference system.
|
*/
|
srsName: null,
|
|
/**
|
* APIProperty: extractAttributes
|
* {Boolean} Extract attributes from GML. Default is true.
|
*/
|
extractAttributes: true,
|
|
/**
|
* APIProperty: xy
|
* {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
|
* Changing is not recommended, a new Format should be instantiated.
|
*/
|
xy: true,
|
|
/**
|
* Property: stateName
|
* {Object} Maps feature states to node names.
|
*/
|
stateName: null,
|
|
/**
|
* Constructor: OpenLayers.Format.WFST.v1
|
* Instances of this class are not created directly. Use the
|
* <OpenLayers.Format.WFST.v1_0_0> or <OpenLayers.Format.WFST.v1_1_0>
|
* constructor instead.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
initialize: function(options) {
|
// set state name mapping
|
this.stateName = {};
|
this.stateName[OpenLayers.State.INSERT] = "wfs:Insert";
|
this.stateName[OpenLayers.State.UPDATE] = "wfs:Update";
|
this.stateName[OpenLayers.State.DELETE] = "wfs:Delete";
|
OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
|
},
|
|
/**
|
* Method: getSrsName
|
*/
|
getSrsName: function(feature, options) {
|
var srsName = options && options.srsName;
|
if(!srsName) {
|
if(feature && feature.layer) {
|
srsName = feature.layer.projection.getCode();
|
} else {
|
srsName = this.srsName;
|
}
|
}
|
return srsName;
|
},
|
|
/**
|
* APIMethod: read
|
* Parse the response from a transaction. Because WFS is split into
|
* Transaction requests (create, update, and delete) and GetFeature
|
* requests (read), this method handles parsing of both types of
|
* responses.
|
*
|
* Parameters:
|
* data - {String | Document} The WFST document to read
|
* options - {Object} Options for the reader
|
*
|
* Valid options properties:
|
* output - {String} either "features" or "object". The default is
|
* "features", which means that the method will return an array of
|
* features. If set to "object", an object with a "features" property
|
* and other properties read by the parser will be returned.
|
*
|
* Returns:
|
* {Array | Object} Output depending on the output option.
|
*/
|
read: function(data, options) {
|
options = options || {};
|
OpenLayers.Util.applyDefaults(options, {
|
output: "features"
|
});
|
|
if(typeof data == "string") {
|
data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
|
}
|
if(data && data.nodeType == 9) {
|
data = data.documentElement;
|
}
|
var obj = {};
|
if(data) {
|
this.readNode(data, obj, true);
|
}
|
if(obj.features && options.output === "features") {
|
obj = obj.features;
|
}
|
return obj;
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"wfs": {
|
"FeatureCollection": function(node, obj) {
|
obj.features = [];
|
this.readChildNodes(node, obj);
|
}
|
}
|
},
|
|
/**
|
* Method: write
|
* Given an array of features, write a WFS transaction. This assumes
|
* the features have a state property that determines the operation
|
* type - insert, update, or delete.
|
*
|
* Parameters:
|
* features - {Array(<OpenLayers.Feature.Vector>)} A list of features. See
|
* below for a more detailed description of the influence of the
|
* feature's *modified* property.
|
* options - {Object}
|
*
|
* feature.modified rules:
|
* If a feature has a modified property set, the following checks will be
|
* made before a feature's geometry or attribute is included in an Update
|
* transaction:
|
* - *modified* is not set at all: The geometry and all attributes will be
|
* included.
|
* - *modified.geometry* is set (null or a geometry): The geometry will be
|
* included. If *modified.attributes* is not set, all attributes will
|
* be included.
|
* - *modified.attributes* is set: Only the attributes set (i.e. to null or
|
* a value) in *modified.attributes* will be included.
|
* If *modified.geometry* is not set, the geometry will not be included.
|
*
|
* Valid options include:
|
* - *multi* {Boolean} If set to true, geometries will be casted to
|
* Multi geometries before writing.
|
*
|
* Returns:
|
* {String} A serialized WFS transaction.
|
*/
|
write: function(features, options) {
|
var node = this.writeNode("wfs:Transaction", {
|
features:features,
|
options: options
|
});
|
var value = this.schemaLocationAttr();
|
if(value) {
|
this.setAttributeNS(
|
node, this.namespaces["xsi"], "xsi:schemaLocation", value
|
);
|
}
|
return OpenLayers.Format.XML.prototype.write.apply(this, [node]);
|
},
|
|
/**
|
* Property: writers
|
* As a compliment to the readers property, this structure contains public
|
* writing functions grouped by namespace alias and named like the
|
* node names they produce.
|
*/
|
writers: {
|
"wfs": {
|
"GetFeature": function(options) {
|
var node = this.createElementNSPlus("wfs:GetFeature", {
|
attributes: {
|
service: "WFS",
|
version: this.version,
|
handle: options && options.handle,
|
outputFormat: options && options.outputFormat,
|
maxFeatures: options && options.maxFeatures,
|
"xsi:schemaLocation": this.schemaLocationAttr(options)
|
}
|
});
|
if (typeof this.featureType == "string") {
|
this.writeNode("Query", options, node);
|
} else {
|
for (var i=0,len = this.featureType.length; i<len; i++) {
|
options.featureType = this.featureType[i];
|
this.writeNode("Query", options, node);
|
}
|
}
|
return node;
|
},
|
"Transaction": function(obj) {
|
obj = obj || {};
|
var options = obj.options || {};
|
var node = this.createElementNSPlus("wfs:Transaction", {
|
attributes: {
|
service: "WFS",
|
version: this.version,
|
handle: options.handle
|
}
|
});
|
var i, len;
|
var features = obj.features;
|
if(features) {
|
// temporarily re-assigning geometry types
|
if (options.multi === true) {
|
OpenLayers.Util.extend(this.geometryTypes, {
|
"OpenLayers.Geometry.Point": "MultiPoint",
|
"OpenLayers.Geometry.LineString": (this.multiCurve === true) ? "MultiCurve": "MultiLineString",
|
"OpenLayers.Geometry.Polygon": (this.multiSurface === true) ? "MultiSurface" : "MultiPolygon"
|
});
|
}
|
var name, feature;
|
for(i=0, len=features.length; i<len; ++i) {
|
feature = features[i];
|
name = this.stateName[feature.state];
|
if(name) {
|
this.writeNode(name, {
|
feature: feature,
|
options: options
|
}, node);
|
}
|
}
|
// switch back to original geometry types assignment
|
if (options.multi === true) {
|
this.setGeometryTypes();
|
}
|
}
|
if (options.nativeElements) {
|
for (i=0, len=options.nativeElements.length; i<len; ++i) {
|
this.writeNode("wfs:Native",
|
options.nativeElements[i], node);
|
}
|
}
|
return node;
|
},
|
"Native": function(nativeElement) {
|
var node = this.createElementNSPlus("wfs:Native", {
|
attributes: {
|
vendorId: nativeElement.vendorId,
|
safeToIgnore: nativeElement.safeToIgnore
|
},
|
value: nativeElement.value
|
});
|
return node;
|
},
|
"Insert": function(obj) {
|
var feature = obj.feature;
|
var options = obj.options;
|
var node = this.createElementNSPlus("wfs:Insert", {
|
attributes: {
|
handle: options && options.handle
|
}
|
});
|
this.srsName = this.getSrsName(feature);
|
this.writeNode("feature:_typeName", feature, node);
|
return node;
|
},
|
"Update": function(obj) {
|
var feature = obj.feature;
|
var options = obj.options;
|
var node = this.createElementNSPlus("wfs:Update", {
|
attributes: {
|
handle: options && options.handle,
|
typeName: (this.featureNS ? this.featurePrefix + ":" : "") +
|
this.featureType
|
}
|
});
|
if(this.featureNS) {
|
node.setAttribute("xmlns:" + this.featurePrefix, this.featureNS);
|
}
|
|
// add in geometry
|
var modified = feature.modified;
|
if (this.geometryName !== null && (!modified || modified.geometry !== undefined)) {
|
this.srsName = this.getSrsName(feature);
|
this.writeNode(
|
"Property", {name: this.geometryName, value: feature.geometry}, node
|
);
|
}
|
|
// add in attributes
|
for(var key in feature.attributes) {
|
if(feature.attributes[key] !== undefined &&
|
(!modified || !modified.attributes ||
|
(modified.attributes && modified.attributes[key] !== undefined))) {
|
this.writeNode(
|
"Property", {name: key, value: feature.attributes[key]}, node
|
);
|
}
|
}
|
|
// add feature id filter
|
this.writeNode("ogc:Filter", new OpenLayers.Filter.FeatureId({
|
fids: [feature.fid]
|
}), node);
|
|
return node;
|
},
|
"Property": function(obj) {
|
var node = this.createElementNSPlus("wfs:Property");
|
this.writeNode("Name", obj.name, node);
|
if(obj.value !== null) {
|
this.writeNode("Value", obj.value, node);
|
}
|
return node;
|
},
|
"Name": function(name) {
|
return this.createElementNSPlus("wfs:Name", {value: name});
|
},
|
"Value": function(obj) {
|
var node;
|
if(obj instanceof OpenLayers.Geometry) {
|
node = this.createElementNSPlus("wfs:Value");
|
var geom = this.writeNode("feature:_geometry", obj).firstChild;
|
node.appendChild(geom);
|
} else {
|
node = this.createElementNSPlus("wfs:Value", {value: obj});
|
}
|
return node;
|
},
|
"Delete": function(obj) {
|
var feature = obj.feature;
|
var options = obj.options;
|
var node = this.createElementNSPlus("wfs:Delete", {
|
attributes: {
|
handle: options && options.handle,
|
typeName: (this.featureNS ? this.featurePrefix + ":" : "") +
|
this.featureType
|
}
|
});
|
if(this.featureNS) {
|
node.setAttribute("xmlns:" + this.featurePrefix, this.featureNS);
|
}
|
this.writeNode("ogc:Filter", new OpenLayers.Filter.FeatureId({
|
fids: [feature.fid]
|
}), node);
|
return node;
|
}
|
}
|
},
|
|
/**
|
* Method: schemaLocationAttr
|
* Generate the xsi:schemaLocation attribute value.
|
*
|
* Returns:
|
* {String} The xsi:schemaLocation attribute or undefined if none.
|
*/
|
schemaLocationAttr: function(options) {
|
options = OpenLayers.Util.extend({
|
featurePrefix: this.featurePrefix,
|
schema: this.schema
|
}, options);
|
var schemaLocations = OpenLayers.Util.extend({}, this.schemaLocations);
|
if(options.schema) {
|
schemaLocations[options.featurePrefix] = options.schema;
|
}
|
var parts = [];
|
var uri;
|
for(var key in schemaLocations) {
|
uri = this.namespaces[key];
|
if(uri) {
|
parts.push(uri + " " + schemaLocations[key]);
|
}
|
}
|
var value = parts.join(" ") || undefined;
|
return value;
|
},
|
|
/**
|
* Method: setFilterProperty
|
* Set the property of each spatial filter.
|
*
|
* Parameters:
|
* filter - {<OpenLayers.Filter>}
|
*/
|
setFilterProperty: function(filter) {
|
if(filter.filters) {
|
for(var i=0, len=filter.filters.length; i<len; ++i) {
|
OpenLayers.Format.WFST.v1.prototype.setFilterProperty.call(this, filter.filters[i]);
|
}
|
} else {
|
if(filter instanceof OpenLayers.Filter.Spatial && !filter.property) {
|
// got a spatial filter without property, so set it
|
filter.property = this.geometryName;
|
}
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WFST.v1"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/OGCExceptionReport.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.OGCExceptionReport
|
* Class to read exception reports for various OGC services and versions.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.OGCExceptionReport = OpenLayers.Class(OpenLayers.Format.XML, {
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
ogc: "http://www.opengis.net/ogc"
|
},
|
|
/**
|
* Property: regExes
|
* Compiled regular expressions for manipulating strings.
|
*/
|
regExes: {
|
trimSpace: (/^\s*|\s*$/g),
|
removeSpace: (/\s*/g),
|
splitSpace: (/\s+/),
|
trimComma: (/\s*,\s*/g)
|
},
|
|
/**
|
* Property: defaultPrefix
|
*/
|
defaultPrefix: "ogc",
|
|
/**
|
* Constructor: OpenLayers.Format.OGCExceptionReport
|
* Create a new parser for OGC exception reports.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* APIMethod: read
|
* Read OGC exception report data from a string, and return an object with
|
* information about the exceptions.
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
*
|
* Returns:
|
* {Object} Information about the exceptions that occurred.
|
*/
|
read: function(data) {
|
var result;
|
if(typeof data == "string") {
|
data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
|
}
|
var root = data.documentElement;
|
var exceptionInfo = {exceptionReport: null};
|
if (root) {
|
this.readChildNodes(data, exceptionInfo);
|
if (exceptionInfo.exceptionReport === null) {
|
// fall-back to OWSCommon since this is a common output format for exceptions
|
// we cannot easily use the ows readers directly since they differ for 1.0 and 1.1
|
exceptionInfo = new OpenLayers.Format.OWSCommon().read(data);
|
}
|
}
|
return exceptionInfo;
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"ogc": {
|
"ServiceExceptionReport": function(node, obj) {
|
obj.exceptionReport = {exceptions: []};
|
this.readChildNodes(node, obj.exceptionReport);
|
},
|
"ServiceException": function(node, exceptionReport) {
|
var exception = {
|
code: node.getAttribute("code"),
|
locator: node.getAttribute("locator"),
|
text: this.getChildValue(node)
|
};
|
exceptionReport.exceptions.push(exception);
|
}
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Format.OGCExceptionReport"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/XML/VersionedOGC.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML.js
|
* @requires OpenLayers/Format/OGCExceptionReport.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.XML.VersionedOGC
|
* Base class for versioned formats, i.e. a format which supports multiple
|
* versions.
|
*
|
* To enable checking if parsing succeeded, you will need to define a property
|
* called errorProperty on the parser you want to check. The parser will then
|
* check the returned object to see if that property is present. If it is, it
|
* assumes the parsing was successful. If it is not present (or is null), it will
|
* pass the document through an OGCExceptionReport parser.
|
*
|
* If errorProperty is undefined for the parser, this error checking mechanism
|
* will be disabled.
|
*
|
*
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.XML.VersionedOGC = OpenLayers.Class(OpenLayers.Format.XML, {
|
|
/**
|
* APIProperty: defaultVersion
|
* {String} Version number to assume if none found.
|
*/
|
defaultVersion: null,
|
|
/**
|
* APIProperty: version
|
* {String} Specify a version string if one is known.
|
*/
|
version: null,
|
|
/**
|
* APIProperty: profile
|
* {String} If provided, use a custom profile.
|
*/
|
profile: null,
|
|
/**
|
* APIProperty: allowFallback
|
* {Boolean} If a profiled parser cannot be found for the returned version,
|
* use a non-profiled parser as the fallback. Application code using this
|
* should take into account that the return object structure might be
|
* missing the specifics of the profile. Defaults to false.
|
*/
|
allowFallback: false,
|
|
/**
|
* Property: name
|
* {String} The name of this parser, this is the part of the CLASS_NAME
|
* except for "OpenLayers.Format."
|
*/
|
name: null,
|
|
/**
|
* APIProperty: stringifyOutput
|
* {Boolean} If true, write will return a string otherwise a DOMElement.
|
* Default is false.
|
*/
|
stringifyOutput: false,
|
|
/**
|
* Property: parser
|
* {Object} Instance of the versioned parser. Cached for multiple read and
|
* write calls of the same version.
|
*/
|
parser: null,
|
|
/**
|
* Constructor: OpenLayers.Format.XML.VersionedOGC.
|
* Constructor.
|
*
|
* Parameters:
|
* options - {Object} Optional object whose properties will be set on
|
* the object.
|
*/
|
initialize: function(options) {
|
OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
|
var className = this.CLASS_NAME;
|
this.name = className.substring(className.lastIndexOf(".")+1);
|
},
|
|
/**
|
* Method: getVersion
|
* Returns the version to use. Subclasses can override this function
|
* if a different version detection is needed.
|
*
|
* Parameters:
|
* root - {DOMElement}
|
* options - {Object} Optional configuration object.
|
*
|
* Returns:
|
* {String} The version to use.
|
*/
|
getVersion: function(root, options) {
|
var version;
|
// read
|
if (root) {
|
version = this.version;
|
if(!version) {
|
version = root.getAttribute("version");
|
if(!version) {
|
version = this.defaultVersion;
|
}
|
}
|
} else { // write
|
version = (options && options.version) ||
|
this.version || this.defaultVersion;
|
}
|
return version;
|
},
|
|
/**
|
* Method: getParser
|
* Get an instance of the cached parser if available, otherwise create one.
|
*
|
* Parameters:
|
* version - {String}
|
*
|
* Returns:
|
* {<OpenLayers.Format>}
|
*/
|
getParser: function(version) {
|
version = version || this.defaultVersion;
|
var profile = this.profile ? "_" + this.profile : "";
|
if(!this.parser || this.parser.VERSION != version) {
|
var format = OpenLayers.Format[this.name][
|
"v" + version.replace(/\./g, "_") + profile
|
];
|
if(!format) {
|
if (profile !== "" && this.allowFallback) {
|
// fallback to the non-profiled version of the parser
|
profile = "";
|
format = OpenLayers.Format[this.name][
|
"v" + version.replace(/\./g, "_")
|
];
|
}
|
if (!format) {
|
throw "Can't find a " + this.name + " parser for version " +
|
version + profile;
|
}
|
}
|
this.parser = new format(this.options);
|
}
|
return this.parser;
|
},
|
|
/**
|
* APIMethod: write
|
* Write a document.
|
*
|
* Parameters:
|
* obj - {Object} An object representing the document.
|
* options - {Object} Optional configuration object.
|
*
|
* Returns:
|
* {String} The document as a string
|
*/
|
write: function(obj, options) {
|
var version = this.getVersion(null, options);
|
this.parser = this.getParser(version);
|
var root = this.parser.write(obj, options);
|
if (this.stringifyOutput === false) {
|
return root;
|
} else {
|
return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
|
}
|
},
|
|
/**
|
* APIMethod: read
|
* Read a doc and return an object representing the document.
|
*
|
* Parameters:
|
* data - {String | DOMElement} Data to read.
|
* options - {Object} Options for the reader.
|
*
|
* Returns:
|
* {Object} An object representing the document.
|
*/
|
read: function(data, options) {
|
if(typeof data == "string") {
|
data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
|
}
|
var root = data.documentElement;
|
var version = this.getVersion(root);
|
this.parser = this.getParser(version); // Select the parser
|
var obj = this.parser.read(data, options); // Parse the data
|
|
var errorProperty = this.parser.errorProperty || null;
|
if (errorProperty !== null && obj[errorProperty] === undefined) {
|
// an error must have happened, so parse it and report back
|
var format = new OpenLayers.Format.OGCExceptionReport();
|
obj.error = format.read(data);
|
}
|
obj.version = version;
|
return obj;
|
},
|
|
CLASS_NAME: "OpenLayers.Format.XML.VersionedOGC"
|
});
|
/* ======================================================================
|
OpenLayers/Filter/Logical.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Filter.js
|
*/
|
|
/**
|
* Class: OpenLayers.Filter.Logical
|
* This class represents ogc:And, ogc:Or and ogc:Not rules.
|
*
|
* Inherits from:
|
* - <OpenLayers.Filter>
|
*/
|
OpenLayers.Filter.Logical = OpenLayers.Class(OpenLayers.Filter, {
|
|
/**
|
* APIProperty: filters
|
* {Array(<OpenLayers.Filter>)} Child filters for this filter.
|
*/
|
filters: null,
|
|
/**
|
* APIProperty: type
|
* {String} type of logical operator. Available types are:
|
* - OpenLayers.Filter.Logical.AND = "&&";
|
* - OpenLayers.Filter.Logical.OR = "||";
|
* - OpenLayers.Filter.Logical.NOT = "!";
|
*/
|
type: null,
|
|
/**
|
* Constructor: OpenLayers.Filter.Logical
|
* Creates a logical filter (And, Or, Not).
|
*
|
* Parameters:
|
* options - {Object} An optional object with properties to set on the
|
* filter.
|
*
|
* Returns:
|
* {<OpenLayers.Filter.Logical>}
|
*/
|
initialize: function(options) {
|
this.filters = [];
|
OpenLayers.Filter.prototype.initialize.apply(this, [options]);
|
},
|
|
/**
|
* APIMethod: destroy
|
* Remove reference to child filters.
|
*/
|
destroy: function() {
|
this.filters = null;
|
OpenLayers.Filter.prototype.destroy.apply(this);
|
},
|
|
/**
|
* APIMethod: evaluate
|
* Evaluates this filter in a specific context.
|
*
|
* Parameters:
|
* context - {Object} Context to use in evaluating the filter. A vector
|
* feature may also be provided to evaluate feature attributes in
|
* comparison filters or geometries in spatial filters.
|
*
|
* Returns:
|
* {Boolean} The filter applies.
|
*/
|
evaluate: function(context) {
|
var i, len;
|
switch(this.type) {
|
case OpenLayers.Filter.Logical.AND:
|
for (i=0, len=this.filters.length; i<len; i++) {
|
if (this.filters[i].evaluate(context) == false) {
|
return false;
|
}
|
}
|
return true;
|
|
case OpenLayers.Filter.Logical.OR:
|
for (i=0, len=this.filters.length; i<len; i++) {
|
if (this.filters[i].evaluate(context) == true) {
|
return true;
|
}
|
}
|
return false;
|
|
case OpenLayers.Filter.Logical.NOT:
|
return (!this.filters[0].evaluate(context));
|
}
|
return undefined;
|
},
|
|
/**
|
* APIMethod: clone
|
* Clones this filter.
|
*
|
* Returns:
|
* {<OpenLayers.Filter.Logical>} Clone of this filter.
|
*/
|
clone: function() {
|
var filters = [];
|
for(var i=0, len=this.filters.length; i<len; ++i) {
|
filters.push(this.filters[i].clone());
|
}
|
return new OpenLayers.Filter.Logical({
|
type: this.type,
|
filters: filters
|
});
|
},
|
|
CLASS_NAME: "OpenLayers.Filter.Logical"
|
});
|
|
|
OpenLayers.Filter.Logical.AND = "&&";
|
OpenLayers.Filter.Logical.OR = "||";
|
OpenLayers.Filter.Logical.NOT = "!";
|
/* ======================================================================
|
OpenLayers/Filter/Comparison.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Filter.js
|
*/
|
|
/**
|
* Class: OpenLayers.Filter.Comparison
|
* This class represents a comparison filter.
|
*
|
* Inherits from:
|
* - <OpenLayers.Filter>
|
*/
|
OpenLayers.Filter.Comparison = OpenLayers.Class(OpenLayers.Filter, {
|
|
/**
|
* APIProperty: type
|
* {String} type: type of the comparison. This is one of
|
* - OpenLayers.Filter.Comparison.EQUAL_TO = "==";
|
* - OpenLayers.Filter.Comparison.NOT_EQUAL_TO = "!=";
|
* - OpenLayers.Filter.Comparison.LESS_THAN = "<";
|
* - OpenLayers.Filter.Comparison.GREATER_THAN = ">";
|
* - OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = "<=";
|
* - OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">=";
|
* - OpenLayers.Filter.Comparison.BETWEEN = "..";
|
* - OpenLayers.Filter.Comparison.LIKE = "~";
|
* - OpenLayers.Filter.Comparison.IS_NULL = "NULL";
|
*/
|
type: null,
|
|
/**
|
* APIProperty: property
|
* {String}
|
* name of the context property to compare
|
*/
|
property: null,
|
|
/**
|
* APIProperty: value
|
* {Number} or {String}
|
* comparison value for binary comparisons. In the case of a String, this
|
* can be a combination of text and propertyNames in the form
|
* "literal ${propertyName}"
|
*/
|
value: null,
|
|
/**
|
* Property: matchCase
|
* {Boolean} Force case sensitive searches for EQUAL_TO and NOT_EQUAL_TO
|
* comparisons. The Filter Encoding 1.1 specification added a matchCase
|
* attribute to ogc:PropertyIsEqualTo and ogc:PropertyIsNotEqualTo
|
* elements. This property will be serialized with those elements only
|
* if using the v1.1.0 filter format. However, when evaluating filters
|
* here, the matchCase property will always be respected (for EQUAL_TO
|
* and NOT_EQUAL_TO). Default is true.
|
*/
|
matchCase: true,
|
|
/**
|
* APIProperty: lowerBoundary
|
* {Number} or {String}
|
* lower boundary for between comparisons. In the case of a String, this
|
* can be a combination of text and propertyNames in the form
|
* "literal ${propertyName}"
|
*/
|
lowerBoundary: null,
|
|
/**
|
* APIProperty: upperBoundary
|
* {Number} or {String}
|
* upper boundary for between comparisons. In the case of a String, this
|
* can be a combination of text and propertyNames in the form
|
* "literal ${propertyName}"
|
*/
|
upperBoundary: null,
|
|
/**
|
* Constructor: OpenLayers.Filter.Comparison
|
* Creates a comparison rule.
|
*
|
* Parameters:
|
* options - {Object} An optional object with properties to set on the
|
* rule
|
*
|
* Returns:
|
* {<OpenLayers.Filter.Comparison>}
|
*/
|
initialize: function(options) {
|
OpenLayers.Filter.prototype.initialize.apply(this, [options]);
|
// since matchCase on PropertyIsLike is not schema compliant, we only
|
// want to use this if explicitly asked for
|
if (this.type === OpenLayers.Filter.Comparison.LIKE
|
&& options.matchCase === undefined) {
|
this.matchCase = null;
|
}
|
},
|
|
/**
|
* APIMethod: evaluate
|
* Evaluates this filter in a specific context.
|
*
|
* Parameters:
|
* context - {Object} Context to use in evaluating the filter. If a vector
|
* feature is provided, the feature.attributes will be used as context.
|
*
|
* Returns:
|
* {Boolean} The filter applies.
|
*/
|
evaluate: function(context) {
|
if (context instanceof OpenLayers.Feature.Vector) {
|
context = context.attributes;
|
}
|
var result = false;
|
var got = context[this.property];
|
var exp;
|
switch(this.type) {
|
case OpenLayers.Filter.Comparison.EQUAL_TO:
|
exp = this.value;
|
if(!this.matchCase &&
|
typeof got == "string" && typeof exp == "string") {
|
result = (got.toUpperCase() == exp.toUpperCase());
|
} else {
|
result = (got == exp);
|
}
|
break;
|
case OpenLayers.Filter.Comparison.NOT_EQUAL_TO:
|
exp = this.value;
|
if(!this.matchCase &&
|
typeof got == "string" && typeof exp == "string") {
|
result = (got.toUpperCase() != exp.toUpperCase());
|
} else {
|
result = (got != exp);
|
}
|
break;
|
case OpenLayers.Filter.Comparison.LESS_THAN:
|
result = got < this.value;
|
break;
|
case OpenLayers.Filter.Comparison.GREATER_THAN:
|
result = got > this.value;
|
break;
|
case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO:
|
result = got <= this.value;
|
break;
|
case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO:
|
result = got >= this.value;
|
break;
|
case OpenLayers.Filter.Comparison.BETWEEN:
|
result = (got >= this.lowerBoundary) &&
|
(got <= this.upperBoundary);
|
break;
|
case OpenLayers.Filter.Comparison.LIKE:
|
var regexp = new RegExp(this.value, "gi");
|
result = regexp.test(got);
|
break;
|
case OpenLayers.Filter.Comparison.IS_NULL:
|
result = (got === null);
|
break;
|
}
|
return result;
|
},
|
|
/**
|
* APIMethod: value2regex
|
* Converts the value of this rule into a regular expression string,
|
* according to the wildcard characters specified. This method has to
|
* be called after instantiation of this class, if the value is not a
|
* regular expression already.
|
*
|
* Parameters:
|
* wildCard - {Char} wildcard character in the above value, default
|
* is "*"
|
* singleChar - {Char} single-character wildcard in the above value
|
* default is "."
|
* escapeChar - {Char} escape character in the above value, default is
|
* "!"
|
*
|
* Returns:
|
* {String} regular expression string
|
*/
|
value2regex: function(wildCard, singleChar, escapeChar) {
|
if (wildCard == ".") {
|
throw new Error("'.' is an unsupported wildCard character for " +
|
"OpenLayers.Filter.Comparison");
|
}
|
|
|
// set UMN MapServer defaults for unspecified parameters
|
wildCard = wildCard ? wildCard : "*";
|
singleChar = singleChar ? singleChar : ".";
|
escapeChar = escapeChar ? escapeChar : "!";
|
|
this.value = this.value.replace(
|
new RegExp("\\"+escapeChar+"(.|$)", "g"), "\\$1");
|
this.value = this.value.replace(
|
new RegExp("\\"+singleChar, "g"), ".");
|
this.value = this.value.replace(
|
new RegExp("\\"+wildCard, "g"), ".*");
|
this.value = this.value.replace(
|
new RegExp("\\\\.\\*", "g"), "\\"+wildCard);
|
this.value = this.value.replace(
|
new RegExp("\\\\\\.", "g"), "\\"+singleChar);
|
|
return this.value;
|
},
|
|
/**
|
* Method: regex2value
|
* Convert the value of this rule from a regular expression string into an
|
* ogc literal string using a wildCard of *, a singleChar of ., and an
|
* escape of !. Leaves the <value> property unmodified.
|
*
|
* Returns:
|
* {String} A string value.
|
*/
|
regex2value: function() {
|
|
var value = this.value;
|
|
// replace ! with !!
|
value = value.replace(/!/g, "!!");
|
|
// replace \. with !. (watching out for \\.)
|
value = value.replace(/(\\)?\\\./g, function($0, $1) {
|
return $1 ? $0 : "!.";
|
});
|
|
// replace \* with #* (watching out for \\*)
|
value = value.replace(/(\\)?\\\*/g, function($0, $1) {
|
return $1 ? $0 : "!*";
|
});
|
|
// replace \\ with \
|
value = value.replace(/\\\\/g, "\\");
|
|
// convert .* to * (the sequence #.* is not allowed)
|
value = value.replace(/\.\*/g, "*");
|
|
return value;
|
},
|
|
/**
|
* APIMethod: clone
|
* Clones this filter.
|
*
|
* Returns:
|
* {<OpenLayers.Filter.Comparison>} Clone of this filter.
|
*/
|
clone: function() {
|
return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison(), this);
|
},
|
|
CLASS_NAME: "OpenLayers.Filter.Comparison"
|
});
|
|
|
OpenLayers.Filter.Comparison.EQUAL_TO = "==";
|
OpenLayers.Filter.Comparison.NOT_EQUAL_TO = "!=";
|
OpenLayers.Filter.Comparison.LESS_THAN = "<";
|
OpenLayers.Filter.Comparison.GREATER_THAN = ">";
|
OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = "<=";
|
OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">=";
|
OpenLayers.Filter.Comparison.BETWEEN = "..";
|
OpenLayers.Filter.Comparison.LIKE = "~";
|
OpenLayers.Filter.Comparison.IS_NULL = "NULL";
|
/* ======================================================================
|
OpenLayers/Format/Filter.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML/VersionedOGC.js
|
* @requires OpenLayers/Filter/FeatureId.js
|
* @requires OpenLayers/Filter/Logical.js
|
* @requires OpenLayers/Filter/Comparison.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.Filter
|
* Read/Write ogc:Filter. Create a new instance with the <OpenLayers.Format.Filter>
|
* constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML.VersionedOGC>
|
*/
|
OpenLayers.Format.Filter = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
|
|
/**
|
* APIProperty: defaultVersion
|
* {String} Version number to assume if none found. Default is "1.0.0".
|
*/
|
defaultVersion: "1.0.0",
|
|
/**
|
* APIMethod: write
|
* Write an ogc:Filter given a filter object.
|
*
|
* Parameters:
|
* filter - {<OpenLayers.Filter>} An filter.
|
* options - {Object} Optional configuration object.
|
*
|
* Returns:
|
* {Elment} An ogc:Filter element node.
|
*/
|
|
/**
|
* APIMethod: read
|
* Read and Filter doc and return an object representing the Filter.
|
*
|
* Parameters:
|
* data - {String | DOMElement} Data to read.
|
*
|
* Returns:
|
* {<OpenLayers.Filter>} A filter object.
|
*/
|
|
CLASS_NAME: "OpenLayers.Format.Filter"
|
});
|
/* ======================================================================
|
OpenLayers/Filter/Function.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Filter.js
|
*/
|
|
/**
|
* Class: OpenLayers.Filter.Function
|
* This class represents a filter function.
|
* We are using this class for creation of complex
|
* filters that can contain filter functions as values.
|
* Nesting function as other functions parameter is supported.
|
*
|
* Inherits from:
|
* - <OpenLayers.Filter>
|
*/
|
OpenLayers.Filter.Function = OpenLayers.Class(OpenLayers.Filter, {
|
|
/**
|
* APIProperty: name
|
* {String} Name of the function.
|
*/
|
name: null,
|
|
/**
|
* APIProperty: params
|
* {Array(<OpenLayers.Filter.Function> || String || Number)} Function parameters
|
* For now support only other Functions, String or Number
|
*/
|
params: null,
|
|
/**
|
* Constructor: OpenLayers.Filter.Function
|
* Creates a filter function.
|
*
|
* Parameters:
|
* options - {Object} An optional object with properties to set on the
|
* function.
|
*
|
* Returns:
|
* {<OpenLayers.Filter.Function>}
|
*/
|
|
CLASS_NAME: "OpenLayers.Filter.Function"
|
});
|
|
/* ======================================================================
|
OpenLayers/BaseTypes/Date.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/SingleFile.js
|
*/
|
|
/**
|
* Namespace: OpenLayers.Date
|
* Contains implementations of Date.parse and date.toISOString that match the
|
* ECMAScript 5 specification for parsing RFC 3339 dates.
|
* http://tools.ietf.org/html/rfc3339
|
*/
|
OpenLayers.Date = {
|
|
/**
|
* APIProperty: dateRegEx
|
* The regex to be used for validating dates. You can provide your own
|
* regex for instance for adding support for years before BC. Default
|
* value is: /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:(?:T(\d{1,2}):(\d{2}):(\d{2}(?:\.\d+)?)(Z|(?:[+-]\d{1,2}(?::(\d{2}))?)))|Z)?$/
|
*/
|
dateRegEx: /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:(?:T(\d{1,2}):(\d{2}):(\d{2}(?:\.\d+)?)(Z|(?:[+-]\d{1,2}(?::(\d{2}))?)))|Z)?$/,
|
|
/**
|
* APIMethod: toISOString
|
* Generates a string representing a date. The format of the string follows
|
* the profile of ISO 8601 for date and time on the Internet (see
|
* http://tools.ietf.org/html/rfc3339). If the toISOString method is
|
* available on the Date prototype, that is used. The toISOString
|
* method for Date instances is defined in ECMA-262.
|
*
|
* Parameters:
|
* date - {Date} A date object.
|
*
|
* Returns:
|
* {String} A string representing the date (e.g.
|
* "2010-08-07T16:58:23.123Z"). If the date does not have a valid time
|
* (i.e. isNaN(date.getTime())) this method returns the string "Invalid
|
* Date". The ECMA standard says the toISOString method should throw
|
* RangeError in this case, but Firefox returns a string instead. For
|
* best results, use isNaN(date.getTime()) to determine date validity
|
* before generating date strings.
|
*/
|
toISOString: (function() {
|
if ("toISOString" in Date.prototype) {
|
return function(date) {
|
return date.toISOString();
|
};
|
} else {
|
return function(date) {
|
var str;
|
if (isNaN(date.getTime())) {
|
// ECMA-262 says throw RangeError, Firefox returns
|
// "Invalid Date"
|
str = "Invalid Date";
|
} else {
|
str =
|
date.getUTCFullYear() + "-" +
|
OpenLayers.Number.zeroPad(date.getUTCMonth() + 1, 2) + "-" +
|
OpenLayers.Number.zeroPad(date.getUTCDate(), 2) + "T" +
|
OpenLayers.Number.zeroPad(date.getUTCHours(), 2) + ":" +
|
OpenLayers.Number.zeroPad(date.getUTCMinutes(), 2) + ":" +
|
OpenLayers.Number.zeroPad(date.getUTCSeconds(), 2) + "." +
|
OpenLayers.Number.zeroPad(date.getUTCMilliseconds(), 3) + "Z";
|
}
|
return str;
|
};
|
}
|
|
})(),
|
|
/**
|
* APIMethod: parse
|
* Generate a date object from a string. The format for the string follows
|
* the profile of ISO 8601 for date and time on the Internet (see
|
* http://tools.ietf.org/html/rfc3339). We don't call the native
|
* Date.parse because of inconsistency between implmentations. In
|
* Chrome, calling Date.parse with a string that doesn't contain any
|
* indication of the timezone (e.g. "2011"), the date is interpreted
|
* in local time. On Firefox, the assumption is UTC.
|
*
|
* Parameters:
|
* str - {String} A string representing the date (e.g.
|
* "2010", "2010-08", "2010-08-07", "2010-08-07T16:58:23.123Z",
|
* "2010-08-07T11:58:23.123-06").
|
*
|
* Returns:
|
* {Date} A date object. If the string could not be parsed, an invalid
|
* date is returned (i.e. isNaN(date.getTime())).
|
*/
|
parse: function(str) {
|
var date;
|
var match = str.match(this.dateRegEx);
|
if (match && (match[1] || match[7])) { // must have at least year or time
|
var year = parseInt(match[1], 10) || 0;
|
var month = (parseInt(match[2], 10) - 1) || 0;
|
var day = parseInt(match[3], 10) || 1;
|
date = new Date(Date.UTC(year, month, day));
|
// optional time
|
var type = match[7];
|
if (type) {
|
var hours = parseInt(match[4], 10);
|
var minutes = parseInt(match[5], 10);
|
var secFrac = parseFloat(match[6]);
|
var seconds = secFrac | 0;
|
var milliseconds = Math.round(1000 * (secFrac - seconds));
|
date.setUTCHours(hours, minutes, seconds, milliseconds);
|
// check offset
|
if (type !== "Z") {
|
var hoursOffset = parseInt(type, 10);
|
var minutesOffset = parseInt(match[8], 10) || 0;
|
var offset = -1000 * (60 * (hoursOffset * 60) + minutesOffset * 60);
|
date = new Date(date.getTime() + offset);
|
}
|
}
|
} else {
|
date = new Date("invalid");
|
}
|
return date;
|
}
|
};
|
/* ======================================================================
|
OpenLayers/Format/Filter/v1.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
/**
|
* @requires OpenLayers/Format/Filter.js
|
* @requires OpenLayers/Format/XML.js
|
* @requires OpenLayers/Filter/Function.js
|
* @requires OpenLayers/BaseTypes/Date.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.Filter.v1
|
* Superclass for Filter version 1 parsers.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.Filter.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
ogc: "http://www.opengis.net/ogc",
|
gml: "http://www.opengis.net/gml",
|
xlink: "http://www.w3.org/1999/xlink",
|
xsi: "http://www.w3.org/2001/XMLSchema-instance"
|
},
|
|
/**
|
* Property: defaultPrefix
|
*/
|
defaultPrefix: "ogc",
|
|
/**
|
* Property: schemaLocation
|
* {String} Schema location for a particular minor version.
|
*/
|
schemaLocation: null,
|
|
/**
|
* Constructor: OpenLayers.Format.Filter.v1
|
* Instances of this class are not created directly. Use the
|
* <OpenLayers.Format.Filter> constructor instead.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
initialize: function(options) {
|
OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
|
},
|
|
/**
|
* Method: read
|
*
|
* Parameters:
|
* data - {DOMElement} A Filter document element.
|
*
|
* Returns:
|
* {<OpenLayers.Filter>} A filter object.
|
*/
|
read: function(data) {
|
var obj = {};
|
this.readers.ogc["Filter"].apply(this, [data, obj]);
|
return obj.filter;
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"ogc": {
|
"_expression": function(node) {
|
// only the simplest of ogc:expression handled
|
// "some text and an <PropertyName>attribute</PropertyName>"}
|
var obj, value = "";
|
for(var child=node.firstChild; child; child=child.nextSibling) {
|
switch(child.nodeType) {
|
case 1:
|
obj = this.readNode(child);
|
if (obj.property) {
|
value += "${" + obj.property + "}";
|
} else if (obj.value !== undefined) {
|
value += obj.value;
|
}
|
break;
|
case 3: // text node
|
case 4: // cdata section
|
value += child.nodeValue;
|
}
|
}
|
return value;
|
},
|
"Filter": function(node, parent) {
|
// Filters correspond to subclasses of OpenLayers.Filter.
|
// Since they contain information we don't persist, we
|
// create a temporary object and then pass on the filter
|
// (ogc:Filter) to the parent obj.
|
var obj = {
|
fids: [],
|
filters: []
|
};
|
this.readChildNodes(node, obj);
|
if(obj.fids.length > 0) {
|
parent.filter = new OpenLayers.Filter.FeatureId({
|
fids: obj.fids
|
});
|
} else if(obj.filters.length > 0) {
|
parent.filter = obj.filters[0];
|
}
|
},
|
"FeatureId": function(node, obj) {
|
var fid = node.getAttribute("fid");
|
if(fid) {
|
obj.fids.push(fid);
|
}
|
},
|
"And": function(node, obj) {
|
var filter = new OpenLayers.Filter.Logical({
|
type: OpenLayers.Filter.Logical.AND
|
});
|
this.readChildNodes(node, filter);
|
obj.filters.push(filter);
|
},
|
"Or": function(node, obj) {
|
var filter = new OpenLayers.Filter.Logical({
|
type: OpenLayers.Filter.Logical.OR
|
});
|
this.readChildNodes(node, filter);
|
obj.filters.push(filter);
|
},
|
"Not": function(node, obj) {
|
var filter = new OpenLayers.Filter.Logical({
|
type: OpenLayers.Filter.Logical.NOT
|
});
|
this.readChildNodes(node, filter);
|
obj.filters.push(filter);
|
},
|
"PropertyIsLessThan": function(node, obj) {
|
var filter = new OpenLayers.Filter.Comparison({
|
type: OpenLayers.Filter.Comparison.LESS_THAN
|
});
|
this.readChildNodes(node, filter);
|
obj.filters.push(filter);
|
},
|
"PropertyIsGreaterThan": function(node, obj) {
|
var filter = new OpenLayers.Filter.Comparison({
|
type: OpenLayers.Filter.Comparison.GREATER_THAN
|
});
|
this.readChildNodes(node, filter);
|
obj.filters.push(filter);
|
},
|
"PropertyIsLessThanOrEqualTo": function(node, obj) {
|
var filter = new OpenLayers.Filter.Comparison({
|
type: OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO
|
});
|
this.readChildNodes(node, filter);
|
obj.filters.push(filter);
|
},
|
"PropertyIsGreaterThanOrEqualTo": function(node, obj) {
|
var filter = new OpenLayers.Filter.Comparison({
|
type: OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO
|
});
|
this.readChildNodes(node, filter);
|
obj.filters.push(filter);
|
},
|
"PropertyIsBetween": function(node, obj) {
|
var filter = new OpenLayers.Filter.Comparison({
|
type: OpenLayers.Filter.Comparison.BETWEEN
|
});
|
this.readChildNodes(node, filter);
|
obj.filters.push(filter);
|
},
|
"Literal": function(node, obj) {
|
obj.value = OpenLayers.String.numericIf(
|
this.getChildValue(node), true);
|
},
|
"PropertyName": function(node, filter) {
|
filter.property = this.getChildValue(node);
|
},
|
"LowerBoundary": function(node, filter) {
|
filter.lowerBoundary = OpenLayers.String.numericIf(
|
this.readers.ogc._expression.call(this, node), true);
|
},
|
"UpperBoundary": function(node, filter) {
|
filter.upperBoundary = OpenLayers.String.numericIf(
|
this.readers.ogc._expression.call(this, node), true);
|
},
|
"Intersects": function(node, obj) {
|
this.readSpatial(node, obj, OpenLayers.Filter.Spatial.INTERSECTS);
|
},
|
"Within": function(node, obj) {
|
this.readSpatial(node, obj, OpenLayers.Filter.Spatial.WITHIN);
|
},
|
"Contains": function(node, obj) {
|
this.readSpatial(node, obj, OpenLayers.Filter.Spatial.CONTAINS);
|
},
|
"DWithin": function(node, obj) {
|
this.readSpatial(node, obj, OpenLayers.Filter.Spatial.DWITHIN);
|
},
|
"Distance": function(node, obj) {
|
obj.distance = parseInt(this.getChildValue(node));
|
obj.distanceUnits = node.getAttribute("units");
|
},
|
"Function": function(node, obj) {
|
//TODO write decoder for it
|
return;
|
},
|
"PropertyIsNull": function(node, obj) {
|
var filter = new OpenLayers.Filter.Comparison({
|
type: OpenLayers.Filter.Comparison.IS_NULL
|
});
|
this.readChildNodes(node, filter);
|
obj.filters.push(filter);
|
}
|
}
|
},
|
|
/**
|
* Method: readSpatial
|
*
|
* Read a {<OpenLayers.Filter.Spatial>} filter.
|
*
|
* Parameters:
|
* node - {DOMElement} A DOM element that contains an ogc:expression.
|
* obj - {Object} The target object.
|
* type - {String} One of the OpenLayers.Filter.Spatial.* constants.
|
*
|
* Returns:
|
* {<OpenLayers.Filter.Spatial>} The created filter.
|
*/
|
readSpatial: function(node, obj, type) {
|
var filter = new OpenLayers.Filter.Spatial({
|
type: type
|
});
|
this.readChildNodes(node, filter);
|
filter.value = filter.components[0];
|
delete filter.components;
|
obj.filters.push(filter);
|
},
|
|
/**
|
* APIMethod: encodeLiteral
|
* Generates the string representation of a value for use in <Literal>
|
* elements. The default encoder writes Date values as ISO 8601
|
* strings.
|
*
|
* Parameters:
|
* value - {Object} Literal value to encode
|
*
|
* Returns:
|
* {String} String representation of the provided value.
|
*/
|
encodeLiteral: function(value) {
|
if (value instanceof Date) {
|
value = OpenLayers.Date.toISOString(value);
|
}
|
return value;
|
},
|
|
/**
|
* Method: writeOgcExpression
|
* Limited support for writing OGC expressions. Currently it supports
|
* (<OpenLayers.Filter.Function> || String || Number)
|
*
|
* Parameters:
|
* value - (<OpenLayers.Filter.Function> || String || Number)
|
* node - {DOMElement} A parent DOM element
|
*
|
* Returns:
|
* {DOMElement} Updated node element.
|
*/
|
writeOgcExpression: function(value, node) {
|
if (value instanceof OpenLayers.Filter.Function){
|
this.writeNode("Function", value, node);
|
} else {
|
this.writeNode("Literal", value, node);
|
}
|
return node;
|
},
|
|
/**
|
* Method: write
|
*
|
* Parameters:
|
* filter - {<OpenLayers.Filter>} A filter object.
|
*
|
* Returns:
|
* {DOMElement} An ogc:Filter element.
|
*/
|
write: function(filter) {
|
return this.writers.ogc["Filter"].apply(this, [filter]);
|
},
|
|
/**
|
* Property: writers
|
* As a compliment to the readers property, this structure contains public
|
* writing functions grouped by namespace alias and named like the
|
* node names they produce.
|
*/
|
writers: {
|
"ogc": {
|
"Filter": function(filter) {
|
var node = this.createElementNSPlus("ogc:Filter");
|
this.writeNode(this.getFilterType(filter), filter, node);
|
return node;
|
},
|
"_featureIds": function(filter) {
|
var node = this.createDocumentFragment();
|
for (var i=0, ii=filter.fids.length; i<ii; ++i) {
|
this.writeNode("ogc:FeatureId", filter.fids[i], node);
|
}
|
return node;
|
},
|
"FeatureId": function(fid) {
|
return this.createElementNSPlus("ogc:FeatureId", {
|
attributes: {fid: fid}
|
});
|
},
|
"And": function(filter) {
|
var node = this.createElementNSPlus("ogc:And");
|
var childFilter;
|
for (var i=0, ii=filter.filters.length; i<ii; ++i) {
|
childFilter = filter.filters[i];
|
this.writeNode(
|
this.getFilterType(childFilter), childFilter, node
|
);
|
}
|
return node;
|
},
|
"Or": function(filter) {
|
var node = this.createElementNSPlus("ogc:Or");
|
var childFilter;
|
for (var i=0, ii=filter.filters.length; i<ii; ++i) {
|
childFilter = filter.filters[i];
|
this.writeNode(
|
this.getFilterType(childFilter), childFilter, node
|
);
|
}
|
return node;
|
},
|
"Not": function(filter) {
|
var node = this.createElementNSPlus("ogc:Not");
|
var childFilter = filter.filters[0];
|
this.writeNode(
|
this.getFilterType(childFilter), childFilter, node
|
);
|
return node;
|
},
|
"PropertyIsLessThan": function(filter) {
|
var node = this.createElementNSPlus("ogc:PropertyIsLessThan");
|
// no ogc:expression handling for PropertyName for now
|
this.writeNode("PropertyName", filter, node);
|
// handle Literals or Functions for now
|
this.writeOgcExpression(filter.value, node);
|
return node;
|
},
|
"PropertyIsGreaterThan": function(filter) {
|
var node = this.createElementNSPlus("ogc:PropertyIsGreaterThan");
|
// no ogc:expression handling for PropertyName for now
|
this.writeNode("PropertyName", filter, node);
|
// handle Literals or Functions for now
|
this.writeOgcExpression(filter.value, node);
|
return node;
|
},
|
"PropertyIsLessThanOrEqualTo": function(filter) {
|
var node = this.createElementNSPlus("ogc:PropertyIsLessThanOrEqualTo");
|
// no ogc:expression handling for PropertyName for now
|
this.writeNode("PropertyName", filter, node);
|
// handle Literals or Functions for now
|
this.writeOgcExpression(filter.value, node);
|
return node;
|
},
|
"PropertyIsGreaterThanOrEqualTo": function(filter) {
|
var node = this.createElementNSPlus("ogc:PropertyIsGreaterThanOrEqualTo");
|
// no ogc:expression handling for PropertyName for now
|
this.writeNode("PropertyName", filter, node);
|
// handle Literals or Functions for now
|
this.writeOgcExpression(filter.value, node);
|
return node;
|
},
|
"PropertyIsBetween": function(filter) {
|
var node = this.createElementNSPlus("ogc:PropertyIsBetween");
|
// no ogc:expression handling for PropertyName for now
|
this.writeNode("PropertyName", filter, node);
|
this.writeNode("LowerBoundary", filter, node);
|
this.writeNode("UpperBoundary", filter, node);
|
return node;
|
},
|
"PropertyName": function(filter) {
|
// no ogc:expression handling for now
|
return this.createElementNSPlus("ogc:PropertyName", {
|
value: filter.property
|
});
|
},
|
"Literal": function(value) {
|
var encode = this.encodeLiteral ||
|
OpenLayers.Format.Filter.v1.prototype.encodeLiteral;
|
return this.createElementNSPlus("ogc:Literal", {
|
value: encode(value)
|
});
|
},
|
"LowerBoundary": function(filter) {
|
// handle Literals or Functions for now
|
var node = this.createElementNSPlus("ogc:LowerBoundary");
|
this.writeOgcExpression(filter.lowerBoundary, node);
|
return node;
|
},
|
"UpperBoundary": function(filter) {
|
// handle Literals or Functions for now
|
var node = this.createElementNSPlus("ogc:UpperBoundary");
|
this.writeNode("Literal", filter.upperBoundary, node);
|
return node;
|
},
|
"INTERSECTS": function(filter) {
|
return this.writeSpatial(filter, "Intersects");
|
},
|
"WITHIN": function(filter) {
|
return this.writeSpatial(filter, "Within");
|
},
|
"CONTAINS": function(filter) {
|
return this.writeSpatial(filter, "Contains");
|
},
|
"DWITHIN": function(filter) {
|
var node = this.writeSpatial(filter, "DWithin");
|
this.writeNode("Distance", filter, node);
|
return node;
|
},
|
"Distance": function(filter) {
|
return this.createElementNSPlus("ogc:Distance", {
|
attributes: {
|
units: filter.distanceUnits
|
},
|
value: filter.distance
|
});
|
},
|
"Function": function(filter) {
|
var node = this.createElementNSPlus("ogc:Function", {
|
attributes: {
|
name: filter.name
|
}
|
});
|
var params = filter.params;
|
for(var i=0, len=params.length; i<len; i++){
|
this.writeOgcExpression(params[i], node);
|
}
|
return node;
|
},
|
"PropertyIsNull": function(filter) {
|
var node = this.createElementNSPlus("ogc:PropertyIsNull");
|
this.writeNode("PropertyName", filter, node);
|
return node;
|
}
|
}
|
},
|
|
/**
|
* Method: getFilterType
|
*/
|
getFilterType: function(filter) {
|
var filterType = this.filterMap[filter.type];
|
if(!filterType) {
|
throw "Filter writing not supported for rule type: " + filter.type;
|
}
|
return filterType;
|
},
|
|
/**
|
* Property: filterMap
|
* {Object} Contains a member for each filter type. Values are node names
|
* for corresponding OGC Filter child elements.
|
*/
|
filterMap: {
|
"&&": "And",
|
"||": "Or",
|
"!": "Not",
|
"==": "PropertyIsEqualTo",
|
"!=": "PropertyIsNotEqualTo",
|
"<": "PropertyIsLessThan",
|
">": "PropertyIsGreaterThan",
|
"<=": "PropertyIsLessThanOrEqualTo",
|
">=": "PropertyIsGreaterThanOrEqualTo",
|
"..": "PropertyIsBetween",
|
"~": "PropertyIsLike",
|
"NULL": "PropertyIsNull",
|
"BBOX": "BBOX",
|
"DWITHIN": "DWITHIN",
|
"WITHIN": "WITHIN",
|
"CONTAINS": "CONTAINS",
|
"INTERSECTS": "INTERSECTS",
|
"FID": "_featureIds"
|
},
|
|
CLASS_NAME: "OpenLayers.Format.Filter.v1"
|
|
});
|
/* ======================================================================
|
OpenLayers/Geometry.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
*/
|
|
/**
|
* Class: OpenLayers.Geometry
|
* A Geometry is a description of a geographic object. Create an instance of
|
* this class with the <OpenLayers.Geometry> constructor. This is a base class,
|
* typical geometry types are described by subclasses of this class.
|
*
|
* Note that if you use the <OpenLayers.Geometry.fromWKT> method, you must
|
* explicitly include the OpenLayers.Format.WKT in your build.
|
*/
|
OpenLayers.Geometry = OpenLayers.Class({
|
|
/**
|
* Property: id
|
* {String} A unique identifier for this geometry.
|
*/
|
id: null,
|
|
/**
|
* Property: parent
|
* {<OpenLayers.Geometry>}This is set when a Geometry is added as component
|
* of another geometry
|
*/
|
parent: null,
|
|
/**
|
* Property: bounds
|
* {<OpenLayers.Bounds>} The bounds of this geometry
|
*/
|
bounds: null,
|
|
/**
|
* Constructor: OpenLayers.Geometry
|
* Creates a geometry object.
|
*/
|
initialize: function() {
|
this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME+ "_");
|
},
|
|
/**
|
* Method: destroy
|
* Destroy this geometry.
|
*/
|
destroy: function() {
|
this.id = null;
|
this.bounds = null;
|
},
|
|
/**
|
* APIMethod: clone
|
* Create a clone of this geometry. Does not set any non-standard
|
* properties of the cloned geometry.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry>} An exact clone of this geometry.
|
*/
|
clone: function() {
|
return new OpenLayers.Geometry();
|
},
|
|
/**
|
* Method: setBounds
|
* Set the bounds for this Geometry.
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
*/
|
setBounds: function(bounds) {
|
if (bounds) {
|
this.bounds = bounds.clone();
|
}
|
},
|
|
/**
|
* Method: clearBounds
|
* Nullify this components bounds and that of its parent as well.
|
*/
|
clearBounds: function() {
|
this.bounds = null;
|
if (this.parent) {
|
this.parent.clearBounds();
|
}
|
},
|
|
/**
|
* Method: extendBounds
|
* Extend the existing bounds to include the new bounds.
|
* If geometry's bounds is not yet set, then set a new Bounds.
|
*
|
* Parameters:
|
* newBounds - {<OpenLayers.Bounds>}
|
*/
|
extendBounds: function(newBounds){
|
var bounds = this.getBounds();
|
if (!bounds) {
|
this.setBounds(newBounds);
|
} else {
|
this.bounds.extend(newBounds);
|
}
|
},
|
|
/**
|
* APIMethod: getBounds
|
* Get the bounds for this Geometry. If bounds is not set, it
|
* is calculated again, this makes queries faster.
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>}
|
*/
|
getBounds: function() {
|
if (this.bounds == null) {
|
this.calculateBounds();
|
}
|
return this.bounds;
|
},
|
|
/**
|
* APIMethod: calculateBounds
|
* Recalculate the bounds for the geometry.
|
*/
|
calculateBounds: function() {
|
//
|
// This should be overridden by subclasses.
|
//
|
},
|
|
/**
|
* APIMethod: distanceTo
|
* Calculate the closest distance between two geometries (on the x-y plane).
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>} The target geometry.
|
* options - {Object} Optional properties for configuring the distance
|
* calculation.
|
*
|
* Valid options depend on the specific geometry type.
|
*
|
* Returns:
|
* {Number | Object} The distance between this geometry and the target.
|
* If details is true, the return will be an object with distance,
|
* x0, y0, x1, and x2 properties. The x0 and y0 properties represent
|
* the coordinates of the closest point on this geometry. The x1 and y1
|
* properties represent the coordinates of the closest point on the
|
* target geometry.
|
*/
|
distanceTo: function(geometry, options) {
|
},
|
|
/**
|
* APIMethod: getVertices
|
* Return a list of all points in this geometry.
|
*
|
* Parameters:
|
* nodes - {Boolean} For lines, only return vertices that are
|
* endpoints. If false, for lines, only vertices that are not
|
* endpoints will be returned. If not provided, all vertices will
|
* be returned.
|
*
|
* Returns:
|
* {Array} A list of all vertices in the geometry.
|
*/
|
getVertices: function(nodes) {
|
},
|
|
/**
|
* Method: atPoint
|
* Note - This is only an approximation based on the bounds of the
|
* geometry.
|
*
|
* Parameters:
|
* lonlat - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an
|
* object with a 'lon' and 'lat' properties.
|
* toleranceLon - {float} Optional tolerance in Geometric Coords
|
* toleranceLat - {float} Optional tolerance in Geographic Coords
|
*
|
* Returns:
|
* {Boolean} Whether or not the geometry is at the specified location
|
*/
|
atPoint: function(lonlat, toleranceLon, toleranceLat) {
|
var atPoint = false;
|
var bounds = this.getBounds();
|
if ((bounds != null) && (lonlat != null)) {
|
|
var dX = (toleranceLon != null) ? toleranceLon : 0;
|
var dY = (toleranceLat != null) ? toleranceLat : 0;
|
|
var toleranceBounds =
|
new OpenLayers.Bounds(this.bounds.left - dX,
|
this.bounds.bottom - dY,
|
this.bounds.right + dX,
|
this.bounds.top + dY);
|
|
atPoint = toleranceBounds.containsLonLat(lonlat);
|
}
|
return atPoint;
|
},
|
|
/**
|
* Method: getLength
|
* Calculate the length of this geometry. This method is defined in
|
* subclasses.
|
*
|
* Returns:
|
* {Float} The length of the collection by summing its parts
|
*/
|
getLength: function() {
|
//to be overridden by geometries that actually have a length
|
//
|
return 0.0;
|
},
|
|
/**
|
* Method: getArea
|
* Calculate the area of this geometry. This method is defined in subclasses.
|
*
|
* Returns:
|
* {Float} The area of the collection by summing its parts
|
*/
|
getArea: function() {
|
//to be overridden by geometries that actually have an area
|
//
|
return 0.0;
|
},
|
|
/**
|
* APIMethod: getCentroid
|
* Calculate the centroid of this geometry. This method is defined in subclasses.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry.Point>} The centroid of the collection
|
*/
|
getCentroid: function() {
|
return null;
|
},
|
|
/**
|
* Method: toString
|
* Returns a text representation of the geometry. If the WKT format is
|
* included in a build, this will be the Well-Known Text
|
* representation.
|
*
|
* Returns:
|
* {String} String representation of this geometry.
|
*/
|
toString: function() {
|
var string;
|
if (OpenLayers.Format && OpenLayers.Format.WKT) {
|
string = OpenLayers.Format.WKT.prototype.write(
|
new OpenLayers.Feature.Vector(this)
|
);
|
} else {
|
string = Object.prototype.toString.call(this);
|
}
|
return string;
|
},
|
|
CLASS_NAME: "OpenLayers.Geometry"
|
});
|
|
/**
|
* Function: OpenLayers.Geometry.fromWKT
|
* Generate a geometry given a Well-Known Text string. For this method to
|
* work, you must include the OpenLayers.Format.WKT in your build
|
* explicitly.
|
*
|
* Parameters:
|
* wkt - {String} A string representing the geometry in Well-Known Text.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry>} A geometry of the appropriate class.
|
*/
|
OpenLayers.Geometry.fromWKT = function(wkt) {
|
var geom;
|
if (OpenLayers.Format && OpenLayers.Format.WKT) {
|
var format = OpenLayers.Geometry.fromWKT.format;
|
if (!format) {
|
format = new OpenLayers.Format.WKT();
|
OpenLayers.Geometry.fromWKT.format = format;
|
}
|
var result = format.read(wkt);
|
if (result instanceof OpenLayers.Feature.Vector) {
|
geom = result.geometry;
|
} else if (OpenLayers.Util.isArray(result)) {
|
var len = result.length;
|
var components = new Array(len);
|
for (var i=0; i<len; ++i) {
|
components[i] = result[i].geometry;
|
}
|
geom = new OpenLayers.Geometry.Collection(components);
|
}
|
}
|
return geom;
|
};
|
|
/**
|
* Method: OpenLayers.Geometry.segmentsIntersect
|
* Determine whether two line segments intersect. Optionally calculates
|
* and returns the intersection point. This function is optimized for
|
* cases where seg1.x2 >= seg2.x1 || seg2.x2 >= seg1.x1. In those
|
* obvious cases where there is no intersection, the function should
|
* not be called.
|
*
|
* Parameters:
|
* seg1 - {Object} Object representing a segment with properties x1, y1, x2,
|
* and y2. The start point is represented by x1 and y1. The end point
|
* is represented by x2 and y2. Start and end are ordered so that x1 < x2.
|
* seg2 - {Object} Object representing a segment with properties x1, y1, x2,
|
* and y2. The start point is represented by x1 and y1. The end point
|
* is represented by x2 and y2. Start and end are ordered so that x1 < x2.
|
* options - {Object} Optional properties for calculating the intersection.
|
*
|
* Valid options:
|
* point - {Boolean} Return the intersection point. If false, the actual
|
* intersection point will not be calculated. If true and the segments
|
* intersect, the intersection point will be returned. If true and
|
* the segments do not intersect, false will be returned. If true and
|
* the segments are coincident, true will be returned.
|
* tolerance - {Number} If a non-null value is provided, if the segments are
|
* within the tolerance distance, this will be considered an intersection.
|
* In addition, if the point option is true and the calculated intersection
|
* is within the tolerance distance of an end point, the endpoint will be
|
* returned instead of the calculated intersection. Further, if the
|
* intersection is within the tolerance of endpoints on both segments, or
|
* if two segment endpoints are within the tolerance distance of eachother
|
* (but no intersection is otherwise calculated), an endpoint on the
|
* first segment provided will be returned.
|
*
|
* Returns:
|
* {Boolean | <OpenLayers.Geometry.Point>} The two segments intersect.
|
* If the point argument is true, the return will be the intersection
|
* point or false if none exists. If point is true and the segments
|
* are coincident, return will be true (and the instersection is equal
|
* to the shorter segment).
|
*/
|
OpenLayers.Geometry.segmentsIntersect = function(seg1, seg2, options) {
|
var point = options && options.point;
|
var tolerance = options && options.tolerance;
|
var intersection = false;
|
var x11_21 = seg1.x1 - seg2.x1;
|
var y11_21 = seg1.y1 - seg2.y1;
|
var x12_11 = seg1.x2 - seg1.x1;
|
var y12_11 = seg1.y2 - seg1.y1;
|
var y22_21 = seg2.y2 - seg2.y1;
|
var x22_21 = seg2.x2 - seg2.x1;
|
var d = (y22_21 * x12_11) - (x22_21 * y12_11);
|
var n1 = (x22_21 * y11_21) - (y22_21 * x11_21);
|
var n2 = (x12_11 * y11_21) - (y12_11 * x11_21);
|
if(d == 0) {
|
// parallel
|
if(n1 == 0 && n2 == 0) {
|
// coincident
|
intersection = true;
|
}
|
} else {
|
var along1 = n1 / d;
|
var along2 = n2 / d;
|
if(along1 >= 0 && along1 <= 1 && along2 >=0 && along2 <= 1) {
|
// intersect
|
if(!point) {
|
intersection = true;
|
} else {
|
// calculate the intersection point
|
var x = seg1.x1 + (along1 * x12_11);
|
var y = seg1.y1 + (along1 * y12_11);
|
intersection = new OpenLayers.Geometry.Point(x, y);
|
}
|
}
|
}
|
if(tolerance) {
|
var dist;
|
if(intersection) {
|
if(point) {
|
var segs = [seg1, seg2];
|
var seg, x, y;
|
// check segment endpoints for proximity to intersection
|
// set intersection to first endpoint within the tolerance
|
outer: for(var i=0; i<2; ++i) {
|
seg = segs[i];
|
for(var j=1; j<3; ++j) {
|
x = seg["x" + j];
|
y = seg["y" + j];
|
dist = Math.sqrt(
|
Math.pow(x - intersection.x, 2) +
|
Math.pow(y - intersection.y, 2)
|
);
|
if(dist < tolerance) {
|
intersection.x = x;
|
intersection.y = y;
|
break outer;
|
}
|
}
|
}
|
|
}
|
} else {
|
// no calculated intersection, but segments could be within
|
// the tolerance of one another
|
var segs = [seg1, seg2];
|
var source, target, x, y, p, result;
|
// check segment endpoints for proximity to intersection
|
// set intersection to first endpoint within the tolerance
|
outer: for(var i=0; i<2; ++i) {
|
source = segs[i];
|
target = segs[(i+1)%2];
|
for(var j=1; j<3; ++j) {
|
p = {x: source["x"+j], y: source["y"+j]};
|
result = OpenLayers.Geometry.distanceToSegment(p, target);
|
if(result.distance < tolerance) {
|
if(point) {
|
intersection = new OpenLayers.Geometry.Point(p.x, p.y);
|
} else {
|
intersection = true;
|
}
|
break outer;
|
}
|
}
|
}
|
}
|
}
|
return intersection;
|
};
|
|
/**
|
* Function: OpenLayers.Geometry.distanceToSegment
|
*
|
* Parameters:
|
* point - {Object} An object with x and y properties representing the
|
* point coordinates.
|
* segment - {Object} An object with x1, y1, x2, and y2 properties
|
* representing endpoint coordinates.
|
*
|
* Returns:
|
* {Object} An object with distance, along, x, and y properties. The distance
|
* will be the shortest distance between the input point and segment.
|
* The x and y properties represent the coordinates along the segment
|
* where the shortest distance meets the segment. The along attribute
|
* describes how far between the two segment points the given point is.
|
*/
|
OpenLayers.Geometry.distanceToSegment = function(point, segment) {
|
var result = OpenLayers.Geometry.distanceSquaredToSegment(point, segment);
|
result.distance = Math.sqrt(result.distance);
|
return result;
|
};
|
|
/**
|
* Function: OpenLayers.Geometry.distanceSquaredToSegment
|
*
|
* Usually the distanceToSegment function should be used. This variant however
|
* can be used for comparisons where the exact distance is not important.
|
*
|
* Parameters:
|
* point - {Object} An object with x and y properties representing the
|
* point coordinates.
|
* segment - {Object} An object with x1, y1, x2, and y2 properties
|
* representing endpoint coordinates.
|
*
|
* Returns:
|
* {Object} An object with squared distance, along, x, and y properties.
|
* The distance will be the shortest distance between the input point and
|
* segment. The x and y properties represent the coordinates along the
|
* segment where the shortest distance meets the segment. The along
|
* attribute describes how far between the two segment points the given
|
* point is.
|
*/
|
OpenLayers.Geometry.distanceSquaredToSegment = function(point, segment) {
|
var x0 = point.x;
|
var y0 = point.y;
|
var x1 = segment.x1;
|
var y1 = segment.y1;
|
var x2 = segment.x2;
|
var y2 = segment.y2;
|
var dx = x2 - x1;
|
var dy = y2 - y1;
|
var along = ((dx * (x0 - x1)) + (dy * (y0 - y1))) /
|
(Math.pow(dx, 2) + Math.pow(dy, 2));
|
var x, y;
|
if(along <= 0.0) {
|
x = x1;
|
y = y1;
|
} else if(along >= 1.0) {
|
x = x2;
|
y = y2;
|
} else {
|
x = x1 + along * dx;
|
y = y1 + along * dy;
|
}
|
return {
|
distance: Math.pow(x - x0, 2) + Math.pow(y - y0, 2),
|
x: x, y: y,
|
along: along
|
};
|
};
|
/* ======================================================================
|
OpenLayers/Geometry/Point.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Geometry.js
|
*/
|
|
/**
|
* Class: OpenLayers.Geometry.Point
|
* Point geometry class.
|
*
|
* Inherits from:
|
* - <OpenLayers.Geometry>
|
*/
|
OpenLayers.Geometry.Point = OpenLayers.Class(OpenLayers.Geometry, {
|
|
/**
|
* APIProperty: x
|
* {float}
|
*/
|
x: null,
|
|
/**
|
* APIProperty: y
|
* {float}
|
*/
|
y: null,
|
|
/**
|
* Constructor: OpenLayers.Geometry.Point
|
* Construct a point geometry.
|
*
|
* Parameters:
|
* x - {float}
|
* y - {float}
|
*
|
*/
|
initialize: function(x, y) {
|
OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
|
|
this.x = parseFloat(x);
|
this.y = parseFloat(y);
|
},
|
|
/**
|
* APIMethod: clone
|
*
|
* Returns:
|
* {<OpenLayers.Geometry.Point>} An exact clone of this OpenLayers.Geometry.Point
|
*/
|
clone: function(obj) {
|
if (obj == null) {
|
obj = new OpenLayers.Geometry.Point(this.x, this.y);
|
}
|
|
// catch any randomly tagged-on properties
|
OpenLayers.Util.applyDefaults(obj, this);
|
|
return obj;
|
},
|
|
/**
|
* Method: calculateBounds
|
* Create a new Bounds based on the lon/lat
|
*/
|
calculateBounds: function () {
|
this.bounds = new OpenLayers.Bounds(this.x, this.y,
|
this.x, this.y);
|
},
|
|
/**
|
* APIMethod: distanceTo
|
* Calculate the closest distance between two geometries (on the x-y plane).
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>} The target geometry.
|
* options - {Object} Optional properties for configuring the distance
|
* calculation.
|
*
|
* Valid options:
|
* details - {Boolean} Return details from the distance calculation.
|
* Default is false.
|
* edge - {Boolean} Calculate the distance from this geometry to the
|
* nearest edge of the target geometry. Default is true. If true,
|
* calling distanceTo from a geometry that is wholly contained within
|
* the target will result in a non-zero distance. If false, whenever
|
* geometries intersect, calling distanceTo will return 0. If false,
|
* details cannot be returned.
|
*
|
* Returns:
|
* {Number | Object} The distance between this geometry and the target.
|
* If details is true, the return will be an object with distance,
|
* x0, y0, x1, and x2 properties. The x0 and y0 properties represent
|
* the coordinates of the closest point on this geometry. The x1 and y1
|
* properties represent the coordinates of the closest point on the
|
* target geometry.
|
*/
|
distanceTo: function(geometry, options) {
|
var edge = !(options && options.edge === false);
|
var details = edge && options && options.details;
|
var distance, x0, y0, x1, y1, result;
|
if(geometry instanceof OpenLayers.Geometry.Point) {
|
x0 = this.x;
|
y0 = this.y;
|
x1 = geometry.x;
|
y1 = geometry.y;
|
distance = Math.sqrt(Math.pow(x0 - x1, 2) + Math.pow(y0 - y1, 2));
|
result = !details ?
|
distance : {x0: x0, y0: y0, x1: x1, y1: y1, distance: distance};
|
} else {
|
result = geometry.distanceTo(this, options);
|
if(details) {
|
// switch coord order since this geom is target
|
result = {
|
x0: result.x1, y0: result.y1,
|
x1: result.x0, y1: result.y0,
|
distance: result.distance
|
};
|
}
|
}
|
return result;
|
},
|
|
/**
|
* APIMethod: equals
|
* Determine whether another geometry is equivalent to this one. Geometries
|
* are considered equivalent if all components have the same coordinates.
|
*
|
* Parameters:
|
* geom - {<OpenLayers.Geometry.Point>} The geometry to test.
|
*
|
* Returns:
|
* {Boolean} The supplied geometry is equivalent to this geometry.
|
*/
|
equals: function(geom) {
|
var equals = false;
|
if (geom != null) {
|
equals = ((this.x == geom.x && this.y == geom.y) ||
|
(isNaN(this.x) && isNaN(this.y) && isNaN(geom.x) && isNaN(geom.y)));
|
}
|
return equals;
|
},
|
|
/**
|
* Method: toShortString
|
*
|
* Returns:
|
* {String} Shortened String representation of Point object.
|
* (ex. <i>"5, 42"</i>)
|
*/
|
toShortString: function() {
|
return (this.x + ", " + this.y);
|
},
|
|
/**
|
* APIMethod: move
|
* Moves a geometry by the given displacement along positive x and y axes.
|
* This modifies the position of the geometry and clears the cached
|
* bounds.
|
*
|
* Parameters:
|
* x - {Float} Distance to move geometry in positive x direction.
|
* y - {Float} Distance to move geometry in positive y direction.
|
*/
|
move: function(x, y) {
|
this.x = this.x + x;
|
this.y = this.y + y;
|
this.clearBounds();
|
},
|
|
/**
|
* APIMethod: rotate
|
* Rotate a point around another.
|
*
|
* Parameters:
|
* angle - {Float} Rotation angle in degrees (measured counterclockwise
|
* from the positive x-axis)
|
* origin - {<OpenLayers.Geometry.Point>} Center point for the rotation
|
*/
|
rotate: function(angle, origin) {
|
angle *= Math.PI / 180;
|
var radius = this.distanceTo(origin);
|
var theta = angle + Math.atan2(this.y - origin.y, this.x - origin.x);
|
this.x = origin.x + (radius * Math.cos(theta));
|
this.y = origin.y + (radius * Math.sin(theta));
|
this.clearBounds();
|
},
|
|
/**
|
* APIMethod: getCentroid
|
*
|
* Returns:
|
* {<OpenLayers.Geometry.Point>} The centroid of the collection
|
*/
|
getCentroid: function() {
|
return new OpenLayers.Geometry.Point(this.x, this.y);
|
},
|
|
/**
|
* APIMethod: resize
|
* Resize a point relative to some origin. For points, this has the effect
|
* of scaling a vector (from the origin to the point). This method is
|
* more useful on geometry collection subclasses.
|
*
|
* Parameters:
|
* scale - {Float} Ratio of the new distance from the origin to the old
|
* distance from the origin. A scale of 2 doubles the
|
* distance between the point and origin.
|
* origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing
|
* ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry>} - The current geometry.
|
*/
|
resize: function(scale, origin, ratio) {
|
ratio = (ratio == undefined) ? 1 : ratio;
|
this.x = origin.x + (scale * ratio * (this.x - origin.x));
|
this.y = origin.y + (scale * (this.y - origin.y));
|
this.clearBounds();
|
return this;
|
},
|
|
/**
|
* APIMethod: intersects
|
* Determine if the input geometry intersects this one.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>} Any type of geometry.
|
*
|
* Returns:
|
* {Boolean} The input geometry intersects this one.
|
*/
|
intersects: function(geometry) {
|
var intersect = false;
|
if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
|
intersect = this.equals(geometry);
|
} else {
|
intersect = geometry.intersects(this);
|
}
|
return intersect;
|
},
|
|
/**
|
* APIMethod: transform
|
* Translate the x,y properties of the point from source to dest.
|
*
|
* Parameters:
|
* source - {<OpenLayers.Projection>}
|
* dest - {<OpenLayers.Projection>}
|
*
|
* Returns:
|
* {<OpenLayers.Geometry>}
|
*/
|
transform: function(source, dest) {
|
if ((source && dest)) {
|
OpenLayers.Projection.transform(
|
this, source, dest);
|
this.bounds = null;
|
}
|
return this;
|
},
|
|
/**
|
* APIMethod: getVertices
|
* Return a list of all points in this geometry.
|
*
|
* Parameters:
|
* nodes - {Boolean} For lines, only return vertices that are
|
* endpoints. If false, for lines, only vertices that are not
|
* endpoints will be returned. If not provided, all vertices will
|
* be returned.
|
*
|
* Returns:
|
* {Array} A list of all vertices in the geometry.
|
*/
|
getVertices: function(nodes) {
|
return [this];
|
},
|
|
CLASS_NAME: "OpenLayers.Geometry.Point"
|
});
|
/* ======================================================================
|
OpenLayers/Geometry/Collection.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Geometry.js
|
*/
|
|
/**
|
* Class: OpenLayers.Geometry.Collection
|
* A Collection is exactly what it sounds like: A collection of different
|
* Geometries. These are stored in the local parameter <components> (which
|
* can be passed as a parameter to the constructor).
|
*
|
* As new geometries are added to the collection, they are NOT cloned.
|
* When removing geometries, they need to be specified by reference (ie you
|
* have to pass in the *exact* geometry to be removed).
|
*
|
* The <getArea> and <getLength> functions here merely iterate through
|
* the components, summing their respective areas and lengths.
|
*
|
* Create a new instance with the <OpenLayers.Geometry.Collection> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Geometry>
|
*/
|
OpenLayers.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, {
|
|
/**
|
* APIProperty: components
|
* {Array(<OpenLayers.Geometry>)} The component parts of this geometry
|
*/
|
components: null,
|
|
/**
|
* Property: componentTypes
|
* {Array(String)} An array of class names representing the types of
|
* components that the collection can include. A null value means the
|
* component types are not restricted.
|
*/
|
componentTypes: null,
|
|
/**
|
* Constructor: OpenLayers.Geometry.Collection
|
* Creates a Geometry Collection -- a list of geoms.
|
*
|
* Parameters:
|
* components - {Array(<OpenLayers.Geometry>)} Optional array of geometries
|
*
|
*/
|
initialize: function (components) {
|
OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
|
this.components = [];
|
if (components != null) {
|
this.addComponents(components);
|
}
|
},
|
|
/**
|
* APIMethod: destroy
|
* Destroy this geometry.
|
*/
|
destroy: function () {
|
this.components.length = 0;
|
this.components = null;
|
OpenLayers.Geometry.prototype.destroy.apply(this, arguments);
|
},
|
|
/**
|
* APIMethod: clone
|
* Clone this geometry.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry.Collection>} An exact clone of this collection
|
*/
|
clone: function() {
|
var geometry = eval("new " + this.CLASS_NAME + "()");
|
for(var i=0, len=this.components.length; i<len; i++) {
|
geometry.addComponent(this.components[i].clone());
|
}
|
|
// catch any randomly tagged-on properties
|
OpenLayers.Util.applyDefaults(geometry, this);
|
|
return geometry;
|
},
|
|
/**
|
* Method: getComponentsString
|
* Get a string representing the components for this collection
|
*
|
* Returns:
|
* {String} A string representation of the components of this geometry
|
*/
|
getComponentsString: function(){
|
var strings = [];
|
for(var i=0, len=this.components.length; i<len; i++) {
|
strings.push(this.components[i].toShortString());
|
}
|
return strings.join(",");
|
},
|
|
/**
|
* APIMethod: calculateBounds
|
* Recalculate the bounds by iterating through the components and
|
* calling calling extendBounds() on each item.
|
*/
|
calculateBounds: function() {
|
this.bounds = null;
|
var bounds = new OpenLayers.Bounds();
|
var components = this.components;
|
if (components) {
|
for (var i=0, len=components.length; i<len; i++) {
|
bounds.extend(components[i].getBounds());
|
}
|
}
|
// to preserve old behavior, we only set bounds if non-null
|
// in the future, we could add bounds.isEmpty()
|
if (bounds.left != null && bounds.bottom != null &&
|
bounds.right != null && bounds.top != null) {
|
this.setBounds(bounds);
|
}
|
},
|
|
/**
|
* APIMethod: addComponents
|
* Add components to this geometry.
|
*
|
* Parameters:
|
* components - {Array(<OpenLayers.Geometry>)} An array of geometries to add
|
*/
|
addComponents: function(components){
|
if(!(OpenLayers.Util.isArray(components))) {
|
components = [components];
|
}
|
for(var i=0, len=components.length; i<len; i++) {
|
this.addComponent(components[i]);
|
}
|
},
|
|
/**
|
* Method: addComponent
|
* Add a new component (geometry) to the collection. If this.componentTypes
|
* is set, then the component class name must be in the componentTypes array.
|
*
|
* The bounds cache is reset.
|
*
|
* Parameters:
|
* component - {<OpenLayers.Geometry>} A geometry to add
|
* index - {int} Optional index into the array to insert the component
|
*
|
* Returns:
|
* {Boolean} The component geometry was successfully added
|
*/
|
addComponent: function(component, index) {
|
var added = false;
|
if(component) {
|
if(this.componentTypes == null ||
|
(OpenLayers.Util.indexOf(this.componentTypes,
|
component.CLASS_NAME) > -1)) {
|
|
if(index != null && (index < this.components.length)) {
|
var components1 = this.components.slice(0, index);
|
var components2 = this.components.slice(index,
|
this.components.length);
|
components1.push(component);
|
this.components = components1.concat(components2);
|
} else {
|
this.components.push(component);
|
}
|
component.parent = this;
|
this.clearBounds();
|
added = true;
|
}
|
}
|
return added;
|
},
|
|
/**
|
* APIMethod: removeComponents
|
* Remove components from this geometry.
|
*
|
* Parameters:
|
* components - {Array(<OpenLayers.Geometry>)} The components to be removed
|
*
|
* Returns:
|
* {Boolean} A component was removed.
|
*/
|
removeComponents: function(components) {
|
var removed = false;
|
|
if(!(OpenLayers.Util.isArray(components))) {
|
components = [components];
|
}
|
for(var i=components.length-1; i>=0; --i) {
|
removed = this.removeComponent(components[i]) || removed;
|
}
|
return removed;
|
},
|
|
/**
|
* Method: removeComponent
|
* Remove a component from this geometry.
|
*
|
* Parameters:
|
* component - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {Boolean} The component was removed.
|
*/
|
removeComponent: function(component) {
|
|
OpenLayers.Util.removeItem(this.components, component);
|
|
// clearBounds() so that it gets recalculated on the next call
|
// to this.getBounds();
|
this.clearBounds();
|
return true;
|
},
|
|
/**
|
* APIMethod: getLength
|
* Calculate the length of this geometry
|
*
|
* Returns:
|
* {Float} The length of the geometry
|
*/
|
getLength: function() {
|
var length = 0.0;
|
for (var i=0, len=this.components.length; i<len; i++) {
|
length += this.components[i].getLength();
|
}
|
return length;
|
},
|
|
/**
|
* APIMethod: getArea
|
* Calculate the area of this geometry. Note how this function is overridden
|
* in <OpenLayers.Geometry.Polygon>.
|
*
|
* Returns:
|
* {Float} The area of the collection by summing its parts
|
*/
|
getArea: function() {
|
var area = 0.0;
|
for (var i=0, len=this.components.length; i<len; i++) {
|
area += this.components[i].getArea();
|
}
|
return area;
|
},
|
|
/**
|
* APIMethod: getGeodesicArea
|
* Calculate the approximate area of the polygon were it projected onto
|
* the earth.
|
*
|
* Parameters:
|
* projection - {<OpenLayers.Projection>} The spatial reference system
|
* for the geometry coordinates. If not provided, Geographic/WGS84 is
|
* assumed.
|
*
|
* Reference:
|
* Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
|
* Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
|
* Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
|
*
|
* Returns:
|
* {float} The approximate geodesic area of the geometry in square meters.
|
*/
|
getGeodesicArea: function(projection) {
|
var area = 0.0;
|
for(var i=0, len=this.components.length; i<len; i++) {
|
area += this.components[i].getGeodesicArea(projection);
|
}
|
return area;
|
},
|
|
/**
|
* APIMethod: getCentroid
|
*
|
* Compute the centroid for this geometry collection.
|
*
|
* Parameters:
|
* weighted - {Boolean} Perform the getCentroid computation recursively,
|
* returning an area weighted average of all geometries in this collection.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry.Point>} The centroid of the collection
|
*/
|
getCentroid: function(weighted) {
|
if (!weighted) {
|
return this.components.length && this.components[0].getCentroid();
|
}
|
var len = this.components.length;
|
if (!len) {
|
return false;
|
}
|
|
var areas = [];
|
var centroids = [];
|
var areaSum = 0;
|
var minArea = Number.MAX_VALUE;
|
var component;
|
for (var i=0; i<len; ++i) {
|
component = this.components[i];
|
var area = component.getArea();
|
var centroid = component.getCentroid(true);
|
if (isNaN(area) || isNaN(centroid.x) || isNaN(centroid.y)) {
|
continue;
|
}
|
areas.push(area);
|
areaSum += area;
|
minArea = (area < minArea && area > 0) ? area : minArea;
|
centroids.push(centroid);
|
}
|
len = areas.length;
|
if (areaSum === 0) {
|
// all the components in this collection have 0 area
|
// probably a collection of points -- weight all the points the same
|
for (var i=0; i<len; ++i) {
|
areas[i] = 1;
|
}
|
areaSum = areas.length;
|
} else {
|
// normalize all the areas where the smallest area will get
|
// a value of 1
|
for (var i=0; i<len; ++i) {
|
areas[i] /= minArea;
|
}
|
areaSum /= minArea;
|
}
|
|
var xSum = 0, ySum = 0, centroid, area;
|
for (var i=0; i<len; ++i) {
|
centroid = centroids[i];
|
area = areas[i];
|
xSum += centroid.x * area;
|
ySum += centroid.y * area;
|
}
|
|
return new OpenLayers.Geometry.Point(xSum/areaSum, ySum/areaSum);
|
},
|
|
/**
|
* APIMethod: getGeodesicLength
|
* Calculate the approximate length of the geometry were it projected onto
|
* the earth.
|
*
|
* projection - {<OpenLayers.Projection>} The spatial reference system
|
* for the geometry coordinates. If not provided, Geographic/WGS84 is
|
* assumed.
|
*
|
* Returns:
|
* {Float} The appoximate geodesic length of the geometry in meters.
|
*/
|
getGeodesicLength: function(projection) {
|
var length = 0.0;
|
for(var i=0, len=this.components.length; i<len; i++) {
|
length += this.components[i].getGeodesicLength(projection);
|
}
|
return length;
|
},
|
|
/**
|
* APIMethod: move
|
* Moves a geometry by the given displacement along positive x and y axes.
|
* This modifies the position of the geometry and clears the cached
|
* bounds.
|
*
|
* Parameters:
|
* x - {Float} Distance to move geometry in positive x direction.
|
* y - {Float} Distance to move geometry in positive y direction.
|
*/
|
move: function(x, y) {
|
for(var i=0, len=this.components.length; i<len; i++) {
|
this.components[i].move(x, y);
|
}
|
},
|
|
/**
|
* APIMethod: rotate
|
* Rotate a geometry around some origin
|
*
|
* Parameters:
|
* angle - {Float} Rotation angle in degrees (measured counterclockwise
|
* from the positive x-axis)
|
* origin - {<OpenLayers.Geometry.Point>} Center point for the rotation
|
*/
|
rotate: function(angle, origin) {
|
for(var i=0, len=this.components.length; i<len; ++i) {
|
this.components[i].rotate(angle, origin);
|
}
|
},
|
|
/**
|
* APIMethod: resize
|
* Resize a geometry relative to some origin. Use this method to apply
|
* a uniform scaling to a geometry.
|
*
|
* Parameters:
|
* scale - {Float} Factor by which to scale the geometry. A scale of 2
|
* doubles the size of the geometry in each dimension
|
* (lines, for example, will be twice as long, and polygons
|
* will have four times the area).
|
* origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing
|
* ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry>} - The current geometry.
|
*/
|
resize: function(scale, origin, ratio) {
|
for(var i=0; i<this.components.length; ++i) {
|
this.components[i].resize(scale, origin, ratio);
|
}
|
return this;
|
},
|
|
/**
|
* APIMethod: distanceTo
|
* Calculate the closest distance between two geometries (on the x-y plane).
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>} The target geometry.
|
* options - {Object} Optional properties for configuring the distance
|
* calculation.
|
*
|
* Valid options:
|
* details - {Boolean} Return details from the distance calculation.
|
* Default is false.
|
* edge - {Boolean} Calculate the distance from this geometry to the
|
* nearest edge of the target geometry. Default is true. If true,
|
* calling distanceTo from a geometry that is wholly contained within
|
* the target will result in a non-zero distance. If false, whenever
|
* geometries intersect, calling distanceTo will return 0. If false,
|
* details cannot be returned.
|
*
|
* Returns:
|
* {Number | Object} The distance between this geometry and the target.
|
* If details is true, the return will be an object with distance,
|
* x0, y0, x1, and y1 properties. The x0 and y0 properties represent
|
* the coordinates of the closest point on this geometry. The x1 and y1
|
* properties represent the coordinates of the closest point on the
|
* target geometry.
|
*/
|
distanceTo: function(geometry, options) {
|
var edge = !(options && options.edge === false);
|
var details = edge && options && options.details;
|
var result, best, distance;
|
var min = Number.POSITIVE_INFINITY;
|
for(var i=0, len=this.components.length; i<len; ++i) {
|
result = this.components[i].distanceTo(geometry, options);
|
distance = details ? result.distance : result;
|
if(distance < min) {
|
min = distance;
|
best = result;
|
if(min == 0) {
|
break;
|
}
|
}
|
}
|
return best;
|
},
|
|
/**
|
* APIMethod: equals
|
* Determine whether another geometry is equivalent to this one. Geometries
|
* are considered equivalent if all components have the same coordinates.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>} The geometry to test.
|
*
|
* Returns:
|
* {Boolean} The supplied geometry is equivalent to this geometry.
|
*/
|
equals: function(geometry) {
|
var equivalent = true;
|
if(!geometry || !geometry.CLASS_NAME ||
|
(this.CLASS_NAME != geometry.CLASS_NAME)) {
|
equivalent = false;
|
} else if(!(OpenLayers.Util.isArray(geometry.components)) ||
|
(geometry.components.length != this.components.length)) {
|
equivalent = false;
|
} else {
|
for(var i=0, len=this.components.length; i<len; ++i) {
|
if(!this.components[i].equals(geometry.components[i])) {
|
equivalent = false;
|
break;
|
}
|
}
|
}
|
return equivalent;
|
},
|
|
/**
|
* APIMethod: transform
|
* Reproject the components geometry from source to dest.
|
*
|
* Parameters:
|
* source - {<OpenLayers.Projection>}
|
* dest - {<OpenLayers.Projection>}
|
*
|
* Returns:
|
* {<OpenLayers.Geometry>}
|
*/
|
transform: function(source, dest) {
|
if (source && dest) {
|
for (var i=0, len=this.components.length; i<len; i++) {
|
var component = this.components[i];
|
component.transform(source, dest);
|
}
|
this.bounds = null;
|
}
|
return this;
|
},
|
|
/**
|
* APIMethod: intersects
|
* Determine if the input geometry intersects this one.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>} Any type of geometry.
|
*
|
* Returns:
|
* {Boolean} The input geometry intersects this one.
|
*/
|
intersects: function(geometry) {
|
var intersect = false;
|
for(var i=0, len=this.components.length; i<len; ++ i) {
|
intersect = geometry.intersects(this.components[i]);
|
if(intersect) {
|
break;
|
}
|
}
|
return intersect;
|
},
|
|
/**
|
* APIMethod: getVertices
|
* Return a list of all points in this geometry.
|
*
|
* Parameters:
|
* nodes - {Boolean} For lines, only return vertices that are
|
* endpoints. If false, for lines, only vertices that are not
|
* endpoints will be returned. If not provided, all vertices will
|
* be returned.
|
*
|
* Returns:
|
* {Array} A list of all vertices in the geometry.
|
*/
|
getVertices: function(nodes) {
|
var vertices = [];
|
for(var i=0, len=this.components.length; i<len; ++i) {
|
Array.prototype.push.apply(
|
vertices, this.components[i].getVertices(nodes)
|
);
|
}
|
return vertices;
|
},
|
|
|
CLASS_NAME: "OpenLayers.Geometry.Collection"
|
});
|
/* ======================================================================
|
OpenLayers/Geometry/MultiPoint.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Geometry/Collection.js
|
* @requires OpenLayers/Geometry/Point.js
|
*/
|
|
/**
|
* Class: OpenLayers.Geometry.MultiPoint
|
* MultiPoint is a collection of Points. Create a new instance with the
|
* <OpenLayers.Geometry.MultiPoint> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Geometry.Collection>
|
* - <OpenLayers.Geometry>
|
*/
|
OpenLayers.Geometry.MultiPoint = OpenLayers.Class(
|
OpenLayers.Geometry.Collection, {
|
|
/**
|
* Property: componentTypes
|
* {Array(String)} An array of class names representing the types of
|
* components that the collection can include. A null value means the
|
* component types are not restricted.
|
*/
|
componentTypes: ["OpenLayers.Geometry.Point"],
|
|
/**
|
* Constructor: OpenLayers.Geometry.MultiPoint
|
* Create a new MultiPoint Geometry
|
*
|
* Parameters:
|
* components - {Array(<OpenLayers.Geometry.Point>)}
|
*
|
* Returns:
|
* {<OpenLayers.Geometry.MultiPoint>}
|
*/
|
|
/**
|
* APIMethod: addPoint
|
* Wrapper for <OpenLayers.Geometry.Collection.addComponent>
|
*
|
* Parameters:
|
* point - {<OpenLayers.Geometry.Point>} Point to be added
|
* index - {Integer} Optional index
|
*/
|
addPoint: function(point, index) {
|
this.addComponent(point, index);
|
},
|
|
/**
|
* APIMethod: removePoint
|
* Wrapper for <OpenLayers.Geometry.Collection.removeComponent>
|
*
|
* Parameters:
|
* point - {<OpenLayers.Geometry.Point>} Point to be removed
|
*/
|
removePoint: function(point){
|
this.removeComponent(point);
|
},
|
|
CLASS_NAME: "OpenLayers.Geometry.MultiPoint"
|
});
|
/* ======================================================================
|
OpenLayers/Geometry/Curve.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Geometry/MultiPoint.js
|
*/
|
|
/**
|
* Class: OpenLayers.Geometry.Curve
|
* A Curve is a MultiPoint, whose points are assumed to be connected. To
|
* this end, we provide a "getLength()" function, which iterates through
|
* the points, summing the distances between them.
|
*
|
* Inherits:
|
* - <OpenLayers.Geometry.MultiPoint>
|
*/
|
OpenLayers.Geometry.Curve = OpenLayers.Class(OpenLayers.Geometry.MultiPoint, {
|
|
/**
|
* Property: componentTypes
|
* {Array(String)} An array of class names representing the types of
|
* components that the collection can include. A null
|
* value means the component types are not restricted.
|
*/
|
componentTypes: ["OpenLayers.Geometry.Point"],
|
|
/**
|
* Constructor: OpenLayers.Geometry.Curve
|
*
|
* Parameters:
|
* point - {Array(<OpenLayers.Geometry.Point>)}
|
*/
|
|
/**
|
* APIMethod: getLength
|
*
|
* Returns:
|
* {Float} The length of the curve
|
*/
|
getLength: function() {
|
var length = 0.0;
|
if ( this.components && (this.components.length > 1)) {
|
for(var i=1, len=this.components.length; i<len; i++) {
|
length += this.components[i-1].distanceTo(this.components[i]);
|
}
|
}
|
return length;
|
},
|
|
/**
|
* APIMethod: getGeodesicLength
|
* Calculate the approximate length of the geometry were it projected onto
|
* the earth.
|
*
|
* projection - {<OpenLayers.Projection>} The spatial reference system
|
* for the geometry coordinates. If not provided, Geographic/WGS84 is
|
* assumed.
|
*
|
* Returns:
|
* {Float} The appoximate geodesic length of the geometry in meters.
|
*/
|
getGeodesicLength: function(projection) {
|
var geom = this; // so we can work with a clone if needed
|
if(projection) {
|
var gg = new OpenLayers.Projection("EPSG:4326");
|
if(!gg.equals(projection)) {
|
geom = this.clone().transform(projection, gg);
|
}
|
}
|
var length = 0.0;
|
if(geom.components && (geom.components.length > 1)) {
|
var p1, p2;
|
for(var i=1, len=geom.components.length; i<len; i++) {
|
p1 = geom.components[i-1];
|
p2 = geom.components[i];
|
// this returns km and requires lon/lat properties
|
length += OpenLayers.Util.distVincenty(
|
{lon: p1.x, lat: p1.y}, {lon: p2.x, lat: p2.y}
|
);
|
}
|
}
|
// convert to m
|
return length * 1000;
|
},
|
|
CLASS_NAME: "OpenLayers.Geometry.Curve"
|
});
|
/* ======================================================================
|
OpenLayers/Geometry/LineString.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Geometry/Curve.js
|
*/
|
|
/**
|
* Class: OpenLayers.Geometry.LineString
|
* A LineString is a Curve which, once two points have been added to it, can
|
* never be less than two points long.
|
*
|
* Inherits from:
|
* - <OpenLayers.Geometry.Curve>
|
*/
|
OpenLayers.Geometry.LineString = OpenLayers.Class(OpenLayers.Geometry.Curve, {
|
|
/**
|
* Constructor: OpenLayers.Geometry.LineString
|
* Create a new LineString geometry
|
*
|
* Parameters:
|
* points - {Array(<OpenLayers.Geometry.Point>)} An array of points used to
|
* generate the linestring
|
*
|
*/
|
|
/**
|
* APIMethod: removeComponent
|
* Only allows removal of a point if there are three or more points in
|
* the linestring. (otherwise the result would be just a single point)
|
*
|
* Parameters:
|
* point - {<OpenLayers.Geometry.Point>} The point to be removed
|
*
|
* Returns:
|
* {Boolean} The component was removed.
|
*/
|
removeComponent: function(point) {
|
var removed = this.components && (this.components.length > 2);
|
if (removed) {
|
OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this,
|
arguments);
|
}
|
return removed;
|
},
|
|
/**
|
* APIMethod: intersects
|
* Test for instersection between two geometries. This is a cheapo
|
* implementation of the Bently-Ottmann algorigithm. It doesn't
|
* really keep track of a sweep line data structure. It is closer
|
* to the brute force method, except that segments are sorted and
|
* potential intersections are only calculated when bounding boxes
|
* intersect.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {Boolean} The input geometry intersects this geometry.
|
*/
|
intersects: function(geometry) {
|
var intersect = false;
|
var type = geometry.CLASS_NAME;
|
if(type == "OpenLayers.Geometry.LineString" ||
|
type == "OpenLayers.Geometry.LinearRing" ||
|
type == "OpenLayers.Geometry.Point") {
|
var segs1 = this.getSortedSegments();
|
var segs2;
|
if(type == "OpenLayers.Geometry.Point") {
|
segs2 = [{
|
x1: geometry.x, y1: geometry.y,
|
x2: geometry.x, y2: geometry.y
|
}];
|
} else {
|
segs2 = geometry.getSortedSegments();
|
}
|
var seg1, seg1x1, seg1x2, seg1y1, seg1y2,
|
seg2, seg2y1, seg2y2;
|
// sweep right
|
outer: for(var i=0, len=segs1.length; i<len; ++i) {
|
seg1 = segs1[i];
|
seg1x1 = seg1.x1;
|
seg1x2 = seg1.x2;
|
seg1y1 = seg1.y1;
|
seg1y2 = seg1.y2;
|
inner: for(var j=0, jlen=segs2.length; j<jlen; ++j) {
|
seg2 = segs2[j];
|
if(seg2.x1 > seg1x2) {
|
// seg1 still left of seg2
|
break;
|
}
|
if(seg2.x2 < seg1x1) {
|
// seg2 still left of seg1
|
continue;
|
}
|
seg2y1 = seg2.y1;
|
seg2y2 = seg2.y2;
|
if(Math.min(seg2y1, seg2y2) > Math.max(seg1y1, seg1y2)) {
|
// seg2 above seg1
|
continue;
|
}
|
if(Math.max(seg2y1, seg2y2) < Math.min(seg1y1, seg1y2)) {
|
// seg2 below seg1
|
continue;
|
}
|
if(OpenLayers.Geometry.segmentsIntersect(seg1, seg2)) {
|
intersect = true;
|
break outer;
|
}
|
}
|
}
|
} else {
|
intersect = geometry.intersects(this);
|
}
|
return intersect;
|
},
|
|
/**
|
* Method: getSortedSegments
|
*
|
* Returns:
|
* {Array} An array of segment objects. Segment objects have properties
|
* x1, y1, x2, and y2. The start point is represented by x1 and y1.
|
* The end point is represented by x2 and y2. Start and end are
|
* ordered so that x1 < x2.
|
*/
|
getSortedSegments: function() {
|
var numSeg = this.components.length - 1;
|
var segments = new Array(numSeg), point1, point2;
|
for(var i=0; i<numSeg; ++i) {
|
point1 = this.components[i];
|
point2 = this.components[i + 1];
|
if(point1.x < point2.x) {
|
segments[i] = {
|
x1: point1.x,
|
y1: point1.y,
|
x2: point2.x,
|
y2: point2.y
|
};
|
} else {
|
segments[i] = {
|
x1: point2.x,
|
y1: point2.y,
|
x2: point1.x,
|
y2: point1.y
|
};
|
}
|
}
|
// more efficient to define this somewhere static
|
function byX1(seg1, seg2) {
|
return seg1.x1 - seg2.x1;
|
}
|
return segments.sort(byX1);
|
},
|
|
/**
|
* Method: splitWithSegment
|
* Split this geometry with the given segment.
|
*
|
* Parameters:
|
* seg - {Object} An object with x1, y1, x2, and y2 properties referencing
|
* segment endpoint coordinates.
|
* options - {Object} Properties of this object will be used to determine
|
* how the split is conducted.
|
*
|
* Valid options:
|
* edge - {Boolean} Allow splitting when only edges intersect. Default is
|
* true. If false, a vertex on the source segment must be within the
|
* tolerance distance of the intersection to be considered a split.
|
* tolerance - {Number} If a non-null value is provided, intersections
|
* within the tolerance distance of one of the source segment's
|
* endpoints will be assumed to occur at the endpoint.
|
*
|
* Returns:
|
* {Object} An object with *lines* and *points* properties. If the given
|
* segment intersects this linestring, the lines array will reference
|
* geometries that result from the split. The points array will contain
|
* all intersection points. Intersection points are sorted along the
|
* segment (in order from x1,y1 to x2,y2).
|
*/
|
splitWithSegment: function(seg, options) {
|
var edge = !(options && options.edge === false);
|
var tolerance = options && options.tolerance;
|
var lines = [];
|
var verts = this.getVertices();
|
var points = [];
|
var intersections = [];
|
var split = false;
|
var vert1, vert2, point;
|
var node, vertex, target;
|
var interOptions = {point: true, tolerance: tolerance};
|
var result = null;
|
for(var i=0, stop=verts.length-2; i<=stop; ++i) {
|
vert1 = verts[i];
|
points.push(vert1.clone());
|
vert2 = verts[i+1];
|
target = {x1: vert1.x, y1: vert1.y, x2: vert2.x, y2: vert2.y};
|
point = OpenLayers.Geometry.segmentsIntersect(
|
seg, target, interOptions
|
);
|
if(point instanceof OpenLayers.Geometry.Point) {
|
if((point.x === seg.x1 && point.y === seg.y1) ||
|
(point.x === seg.x2 && point.y === seg.y2) ||
|
point.equals(vert1) || point.equals(vert2)) {
|
vertex = true;
|
} else {
|
vertex = false;
|
}
|
if(vertex || edge) {
|
// push intersections different than the previous
|
if(!point.equals(intersections[intersections.length-1])) {
|
intersections.push(point.clone());
|
}
|
if(i === 0) {
|
if(point.equals(vert1)) {
|
continue;
|
}
|
}
|
if(point.equals(vert2)) {
|
continue;
|
}
|
split = true;
|
if(!point.equals(vert1)) {
|
points.push(point);
|
}
|
lines.push(new OpenLayers.Geometry.LineString(points));
|
points = [point.clone()];
|
}
|
}
|
}
|
if(split) {
|
points.push(vert2.clone());
|
lines.push(new OpenLayers.Geometry.LineString(points));
|
}
|
if(intersections.length > 0) {
|
// sort intersections along segment
|
var xDir = seg.x1 < seg.x2 ? 1 : -1;
|
var yDir = seg.y1 < seg.y2 ? 1 : -1;
|
result = {
|
lines: lines,
|
points: intersections.sort(function(p1, p2) {
|
return (xDir * p1.x - xDir * p2.x) || (yDir * p1.y - yDir * p2.y);
|
})
|
};
|
}
|
return result;
|
},
|
|
/**
|
* Method: split
|
* Use this geometry (the source) to attempt to split a target geometry.
|
*
|
* Parameters:
|
* target - {<OpenLayers.Geometry>} The target geometry.
|
* options - {Object} Properties of this object will be used to determine
|
* how the split is conducted.
|
*
|
* Valid options:
|
* mutual - {Boolean} Split the source geometry in addition to the target
|
* geometry. Default is false.
|
* edge - {Boolean} Allow splitting when only edges intersect. Default is
|
* true. If false, a vertex on the source must be within the tolerance
|
* distance of the intersection to be considered a split.
|
* tolerance - {Number} If a non-null value is provided, intersections
|
* within the tolerance distance of an existing vertex on the source
|
* will be assumed to occur at the vertex.
|
*
|
* Returns:
|
* {Array} A list of geometries (of this same type as the target) that
|
* result from splitting the target with the source geometry. The
|
* source and target geometry will remain unmodified. If no split
|
* results, null will be returned. If mutual is true and a split
|
* results, return will be an array of two arrays - the first will be
|
* all geometries that result from splitting the source geometry and
|
* the second will be all geometries that result from splitting the
|
* target geometry.
|
*/
|
split: function(target, options) {
|
var results = null;
|
var mutual = options && options.mutual;
|
var sourceSplit, targetSplit, sourceParts, targetParts;
|
if(target instanceof OpenLayers.Geometry.LineString) {
|
var verts = this.getVertices();
|
var vert1, vert2, seg, splits, lines, point;
|
var points = [];
|
sourceParts = [];
|
for(var i=0, stop=verts.length-2; i<=stop; ++i) {
|
vert1 = verts[i];
|
vert2 = verts[i+1];
|
seg = {
|
x1: vert1.x, y1: vert1.y,
|
x2: vert2.x, y2: vert2.y
|
};
|
targetParts = targetParts || [target];
|
if(mutual) {
|
points.push(vert1.clone());
|
}
|
for(var j=0; j<targetParts.length; ++j) {
|
splits = targetParts[j].splitWithSegment(seg, options);
|
if(splits) {
|
// splice in new features
|
lines = splits.lines;
|
if(lines.length > 0) {
|
lines.unshift(j, 1);
|
Array.prototype.splice.apply(targetParts, lines);
|
j += lines.length - 2;
|
}
|
if(mutual) {
|
for(var k=0, len=splits.points.length; k<len; ++k) {
|
point = splits.points[k];
|
if(!point.equals(vert1)) {
|
points.push(point);
|
sourceParts.push(new OpenLayers.Geometry.LineString(points));
|
if(point.equals(vert2)) {
|
points = [];
|
} else {
|
points = [point.clone()];
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
if(mutual && sourceParts.length > 0 && points.length > 0) {
|
points.push(vert2.clone());
|
sourceParts.push(new OpenLayers.Geometry.LineString(points));
|
}
|
} else {
|
results = target.splitWith(this, options);
|
}
|
if(targetParts && targetParts.length > 1) {
|
targetSplit = true;
|
} else {
|
targetParts = [];
|
}
|
if(sourceParts && sourceParts.length > 1) {
|
sourceSplit = true;
|
} else {
|
sourceParts = [];
|
}
|
if(targetSplit || sourceSplit) {
|
if(mutual) {
|
results = [sourceParts, targetParts];
|
} else {
|
results = targetParts;
|
}
|
}
|
return results;
|
},
|
|
/**
|
* Method: splitWith
|
* Split this geometry (the target) with the given geometry (the source).
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>} A geometry used to split this
|
* geometry (the source).
|
* options - {Object} Properties of this object will be used to determine
|
* how the split is conducted.
|
*
|
* Valid options:
|
* mutual - {Boolean} Split the source geometry in addition to the target
|
* geometry. Default is false.
|
* edge - {Boolean} Allow splitting when only edges intersect. Default is
|
* true. If false, a vertex on the source must be within the tolerance
|
* distance of the intersection to be considered a split.
|
* tolerance - {Number} If a non-null value is provided, intersections
|
* within the tolerance distance of an existing vertex on the source
|
* will be assumed to occur at the vertex.
|
*
|
* Returns:
|
* {Array} A list of geometries (of this same type as the target) that
|
* result from splitting the target with the source geometry. The
|
* source and target geometry will remain unmodified. If no split
|
* results, null will be returned. If mutual is true and a split
|
* results, return will be an array of two arrays - the first will be
|
* all geometries that result from splitting the source geometry and
|
* the second will be all geometries that result from splitting the
|
* target geometry.
|
*/
|
splitWith: function(geometry, options) {
|
return geometry.split(this, options);
|
|
},
|
|
/**
|
* APIMethod: getVertices
|
* Return a list of all points in this geometry.
|
*
|
* Parameters:
|
* nodes - {Boolean} For lines, only return vertices that are
|
* endpoints. If false, for lines, only vertices that are not
|
* endpoints will be returned. If not provided, all vertices will
|
* be returned.
|
*
|
* Returns:
|
* {Array} A list of all vertices in the geometry.
|
*/
|
getVertices: function(nodes) {
|
var vertices;
|
if(nodes === true) {
|
vertices = [
|
this.components[0],
|
this.components[this.components.length-1]
|
];
|
} else if (nodes === false) {
|
vertices = this.components.slice(1, this.components.length-1);
|
} else {
|
vertices = this.components.slice();
|
}
|
return vertices;
|
},
|
|
/**
|
* APIMethod: distanceTo
|
* Calculate the closest distance between two geometries (on the x-y plane).
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>} The target geometry.
|
* options - {Object} Optional properties for configuring the distance
|
* calculation.
|
*
|
* Valid options:
|
* details - {Boolean} Return details from the distance calculation.
|
* Default is false.
|
* edge - {Boolean} Calculate the distance from this geometry to the
|
* nearest edge of the target geometry. Default is true. If true,
|
* calling distanceTo from a geometry that is wholly contained within
|
* the target will result in a non-zero distance. If false, whenever
|
* geometries intersect, calling distanceTo will return 0. If false,
|
* details cannot be returned.
|
*
|
* Returns:
|
* {Number | Object} The distance between this geometry and the target.
|
* If details is true, the return will be an object with distance,
|
* x0, y0, x1, and x2 properties. The x0 and y0 properties represent
|
* the coordinates of the closest point on this geometry. The x1 and y1
|
* properties represent the coordinates of the closest point on the
|
* target geometry.
|
*/
|
distanceTo: function(geometry, options) {
|
var edge = !(options && options.edge === false);
|
var details = edge && options && options.details;
|
var result, best = {};
|
var min = Number.POSITIVE_INFINITY;
|
if(geometry instanceof OpenLayers.Geometry.Point) {
|
var segs = this.getSortedSegments();
|
var x = geometry.x;
|
var y = geometry.y;
|
var seg;
|
for(var i=0, len=segs.length; i<len; ++i) {
|
seg = segs[i];
|
result = OpenLayers.Geometry.distanceToSegment(geometry, seg);
|
if(result.distance < min) {
|
min = result.distance;
|
best = result;
|
if(min === 0) {
|
break;
|
}
|
} else {
|
// if distance increases and we cross y0 to the right of x0, no need to keep looking.
|
if(seg.x2 > x && ((y > seg.y1 && y < seg.y2) || (y < seg.y1 && y > seg.y2))) {
|
break;
|
}
|
}
|
}
|
if(details) {
|
best = {
|
distance: best.distance,
|
x0: best.x, y0: best.y,
|
x1: x, y1: y
|
};
|
} else {
|
best = best.distance;
|
}
|
} else if(geometry instanceof OpenLayers.Geometry.LineString) {
|
var segs0 = this.getSortedSegments();
|
var segs1 = geometry.getSortedSegments();
|
var seg0, seg1, intersection, x0, y0;
|
var len1 = segs1.length;
|
var interOptions = {point: true};
|
outer: for(var i=0, len=segs0.length; i<len; ++i) {
|
seg0 = segs0[i];
|
x0 = seg0.x1;
|
y0 = seg0.y1;
|
for(var j=0; j<len1; ++j) {
|
seg1 = segs1[j];
|
intersection = OpenLayers.Geometry.segmentsIntersect(seg0, seg1, interOptions);
|
if(intersection) {
|
min = 0;
|
best = {
|
distance: 0,
|
x0: intersection.x, y0: intersection.y,
|
x1: intersection.x, y1: intersection.y
|
};
|
break outer;
|
} else {
|
result = OpenLayers.Geometry.distanceToSegment({x: x0, y: y0}, seg1);
|
if(result.distance < min) {
|
min = result.distance;
|
best = {
|
distance: min,
|
x0: x0, y0: y0,
|
x1: result.x, y1: result.y
|
};
|
}
|
}
|
}
|
}
|
if(!details) {
|
best = best.distance;
|
}
|
if(min !== 0) {
|
// check the final vertex in this line's sorted segments
|
if(seg0) {
|
result = geometry.distanceTo(
|
new OpenLayers.Geometry.Point(seg0.x2, seg0.y2),
|
options
|
);
|
var dist = details ? result.distance : result;
|
if(dist < min) {
|
if(details) {
|
best = {
|
distance: min,
|
x0: result.x1, y0: result.y1,
|
x1: result.x0, y1: result.y0
|
};
|
} else {
|
best = dist;
|
}
|
}
|
}
|
}
|
} else {
|
best = geometry.distanceTo(this, options);
|
// swap since target comes from this line
|
if(details) {
|
best = {
|
distance: best.distance,
|
x0: best.x1, y0: best.y1,
|
x1: best.x0, y1: best.y0
|
};
|
}
|
}
|
return best;
|
},
|
|
/**
|
* APIMethod: simplify
|
* This function will return a simplified LineString.
|
* Simplification is based on the Douglas-Peucker algorithm.
|
*
|
*
|
* Parameters:
|
* tolerance - {number} threshhold for simplification in map units
|
*
|
* Returns:
|
* {OpenLayers.Geometry.LineString} the simplified LineString
|
*/
|
simplify: function(tolerance){
|
if (this && this !== null) {
|
var points = this.getVertices();
|
if (points.length < 3) {
|
return this;
|
}
|
|
var compareNumbers = function(a, b){
|
return (a-b);
|
};
|
|
/**
|
* Private function doing the Douglas-Peucker reduction
|
*/
|
var douglasPeuckerReduction = function(points, firstPoint, lastPoint, tolerance){
|
var maxDistance = 0;
|
var indexFarthest = 0;
|
|
for (var index = firstPoint, distance; index < lastPoint; index++) {
|
distance = perpendicularDistance(points[firstPoint], points[lastPoint], points[index]);
|
if (distance > maxDistance) {
|
maxDistance = distance;
|
indexFarthest = index;
|
}
|
}
|
|
if (maxDistance > tolerance && indexFarthest != firstPoint) {
|
//Add the largest point that exceeds the tolerance
|
pointIndexsToKeep.push(indexFarthest);
|
douglasPeuckerReduction(points, firstPoint, indexFarthest, tolerance);
|
douglasPeuckerReduction(points, indexFarthest, lastPoint, tolerance);
|
}
|
};
|
|
/**
|
* Private function calculating the perpendicular distance
|
* TODO: check whether OpenLayers.Geometry.LineString::distanceTo() is faster or slower
|
*/
|
var perpendicularDistance = function(point1, point2, point){
|
//Area = |(1/2)(x1y2 + x2y3 + x3y1 - x2y1 - x3y2 - x1y3)| *Area of triangle
|
//Base = v((x1-x2)²+(x1-x2)²) *Base of Triangle*
|
//Area = .5*Base*H *Solve for height
|
//Height = Area/.5/Base
|
|
var area = Math.abs(0.5 * (point1.x * point2.y + point2.x * point.y + point.x * point1.y - point2.x * point1.y - point.x * point2.y - point1.x * point.y));
|
var bottom = Math.sqrt(Math.pow(point1.x - point2.x, 2) + Math.pow(point1.y - point2.y, 2));
|
var height = area / bottom * 2;
|
|
return height;
|
};
|
|
var firstPoint = 0;
|
var lastPoint = points.length - 1;
|
var pointIndexsToKeep = [];
|
|
//Add the first and last index to the keepers
|
pointIndexsToKeep.push(firstPoint);
|
pointIndexsToKeep.push(lastPoint);
|
|
//The first and the last point cannot be the same
|
while (points[firstPoint].equals(points[lastPoint])) {
|
lastPoint--;
|
//Addition: the first point not equal to first point in the LineString is kept as well
|
pointIndexsToKeep.push(lastPoint);
|
}
|
|
douglasPeuckerReduction(points, firstPoint, lastPoint, tolerance);
|
var returnPoints = [];
|
pointIndexsToKeep.sort(compareNumbers);
|
for (var index = 0; index < pointIndexsToKeep.length; index++) {
|
returnPoints.push(points[pointIndexsToKeep[index]]);
|
}
|
return new OpenLayers.Geometry.LineString(returnPoints);
|
|
}
|
else {
|
return this;
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Geometry.LineString"
|
});
|
/* ======================================================================
|
OpenLayers/Geometry/MultiLineString.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Geometry/Collection.js
|
* @requires OpenLayers/Geometry/LineString.js
|
*/
|
|
/**
|
* Class: OpenLayers.Geometry.MultiLineString
|
* A MultiLineString is a geometry with multiple <OpenLayers.Geometry.LineString>
|
* components.
|
*
|
* Inherits from:
|
* - <OpenLayers.Geometry.Collection>
|
* - <OpenLayers.Geometry>
|
*/
|
OpenLayers.Geometry.MultiLineString = OpenLayers.Class(
|
OpenLayers.Geometry.Collection, {
|
|
/**
|
* Property: componentTypes
|
* {Array(String)} An array of class names representing the types of
|
* components that the collection can include. A null value means the
|
* component types are not restricted.
|
*/
|
componentTypes: ["OpenLayers.Geometry.LineString"],
|
|
/**
|
* Constructor: OpenLayers.Geometry.MultiLineString
|
* Constructor for a MultiLineString Geometry.
|
*
|
* Parameters:
|
* components - {Array(<OpenLayers.Geometry.LineString>)}
|
*
|
*/
|
|
/**
|
* Method: split
|
* Use this geometry (the source) to attempt to split a target geometry.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>} The target geometry.
|
* options - {Object} Properties of this object will be used to determine
|
* how the split is conducted.
|
*
|
* Valid options:
|
* mutual - {Boolean} Split the source geometry in addition to the target
|
* geometry. Default is false.
|
* edge - {Boolean} Allow splitting when only edges intersect. Default is
|
* true. If false, a vertex on the source must be within the tolerance
|
* distance of the intersection to be considered a split.
|
* tolerance - {Number} If a non-null value is provided, intersections
|
* within the tolerance distance of an existing vertex on the source
|
* will be assumed to occur at the vertex.
|
*
|
* Returns:
|
* {Array} A list of geometries (of this same type as the target) that
|
* result from splitting the target with the source geometry. The
|
* source and target geometry will remain unmodified. If no split
|
* results, null will be returned. If mutual is true and a split
|
* results, return will be an array of two arrays - the first will be
|
* all geometries that result from splitting the source geometry and
|
* the second will be all geometries that result from splitting the
|
* target geometry.
|
*/
|
split: function(geometry, options) {
|
var results = null;
|
var mutual = options && options.mutual;
|
var splits, sourceLine, sourceLines, sourceSplit, targetSplit;
|
var sourceParts = [];
|
var targetParts = [geometry];
|
for(var i=0, len=this.components.length; i<len; ++i) {
|
sourceLine = this.components[i];
|
sourceSplit = false;
|
for(var j=0; j < targetParts.length; ++j) {
|
splits = sourceLine.split(targetParts[j], options);
|
if(splits) {
|
if(mutual) {
|
sourceLines = splits[0];
|
for(var k=0, klen=sourceLines.length; k<klen; ++k) {
|
if(k===0 && sourceParts.length) {
|
sourceParts[sourceParts.length-1].addComponent(
|
sourceLines[k]
|
);
|
} else {
|
sourceParts.push(
|
new OpenLayers.Geometry.MultiLineString([
|
sourceLines[k]
|
])
|
);
|
}
|
}
|
sourceSplit = true;
|
splits = splits[1];
|
}
|
if(splits.length) {
|
// splice in new target parts
|
splits.unshift(j, 1);
|
Array.prototype.splice.apply(targetParts, splits);
|
break;
|
}
|
}
|
}
|
if(!sourceSplit) {
|
// source line was not hit
|
if(sourceParts.length) {
|
// add line to existing multi
|
sourceParts[sourceParts.length-1].addComponent(
|
sourceLine.clone()
|
);
|
} else {
|
// create a fresh multi
|
sourceParts = [
|
new OpenLayers.Geometry.MultiLineString(
|
sourceLine.clone()
|
)
|
];
|
}
|
}
|
}
|
if(sourceParts && sourceParts.length > 1) {
|
sourceSplit = true;
|
} else {
|
sourceParts = [];
|
}
|
if(targetParts && targetParts.length > 1) {
|
targetSplit = true;
|
} else {
|
targetParts = [];
|
}
|
if(sourceSplit || targetSplit) {
|
if(mutual) {
|
results = [sourceParts, targetParts];
|
} else {
|
results = targetParts;
|
}
|
}
|
return results;
|
},
|
|
/**
|
* Method: splitWith
|
* Split this geometry (the target) with the given geometry (the source).
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>} A geometry used to split this
|
* geometry (the source).
|
* options - {Object} Properties of this object will be used to determine
|
* how the split is conducted.
|
*
|
* Valid options:
|
* mutual - {Boolean} Split the source geometry in addition to the target
|
* geometry. Default is false.
|
* edge - {Boolean} Allow splitting when only edges intersect. Default is
|
* true. If false, a vertex on the source must be within the tolerance
|
* distance of the intersection to be considered a split.
|
* tolerance - {Number} If a non-null value is provided, intersections
|
* within the tolerance distance of an existing vertex on the source
|
* will be assumed to occur at the vertex.
|
*
|
* Returns:
|
* {Array} A list of geometries (of this same type as the target) that
|
* result from splitting the target with the source geometry. The
|
* source and target geometry will remain unmodified. If no split
|
* results, null will be returned. If mutual is true and a split
|
* results, return will be an array of two arrays - the first will be
|
* all geometries that result from splitting the source geometry and
|
* the second will be all geometries that result from splitting the
|
* target geometry.
|
*/
|
splitWith: function(geometry, options) {
|
var results = null;
|
var mutual = options && options.mutual;
|
var splits, targetLine, sourceLines, sourceSplit, targetSplit, sourceParts, targetParts;
|
if(geometry instanceof OpenLayers.Geometry.LineString) {
|
targetParts = [];
|
sourceParts = [geometry];
|
for(var i=0, len=this.components.length; i<len; ++i) {
|
targetSplit = false;
|
targetLine = this.components[i];
|
for(var j=0; j<sourceParts.length; ++j) {
|
splits = sourceParts[j].split(targetLine, options);
|
if(splits) {
|
if(mutual) {
|
sourceLines = splits[0];
|
if(sourceLines.length) {
|
// splice in new source parts
|
sourceLines.unshift(j, 1);
|
Array.prototype.splice.apply(sourceParts, sourceLines);
|
j += sourceLines.length - 2;
|
}
|
splits = splits[1];
|
if(splits.length === 0) {
|
splits = [targetLine.clone()];
|
}
|
}
|
for(var k=0, klen=splits.length; k<klen; ++k) {
|
if(k===0 && targetParts.length) {
|
targetParts[targetParts.length-1].addComponent(
|
splits[k]
|
);
|
} else {
|
targetParts.push(
|
new OpenLayers.Geometry.MultiLineString([
|
splits[k]
|
])
|
);
|
}
|
}
|
targetSplit = true;
|
}
|
}
|
if(!targetSplit) {
|
// target component was not hit
|
if(targetParts.length) {
|
// add it to any existing multi-line
|
targetParts[targetParts.length-1].addComponent(
|
targetLine.clone()
|
);
|
} else {
|
// or start with a fresh multi-line
|
targetParts = [
|
new OpenLayers.Geometry.MultiLineString([
|
targetLine.clone()
|
])
|
];
|
}
|
|
}
|
}
|
} else {
|
results = geometry.split(this);
|
}
|
if(sourceParts && sourceParts.length > 1) {
|
sourceSplit = true;
|
} else {
|
sourceParts = [];
|
}
|
if(targetParts && targetParts.length > 1) {
|
targetSplit = true;
|
} else {
|
targetParts = [];
|
}
|
if(sourceSplit || targetSplit) {
|
if(mutual) {
|
results = [sourceParts, targetParts];
|
} else {
|
results = targetParts;
|
}
|
}
|
return results;
|
},
|
|
CLASS_NAME: "OpenLayers.Geometry.MultiLineString"
|
});
|
/* ======================================================================
|
OpenLayers/Geometry/LinearRing.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Geometry/LineString.js
|
*/
|
|
/**
|
* Class: OpenLayers.Geometry.LinearRing
|
*
|
* A Linear Ring is a special LineString which is closed. It closes itself
|
* automatically on every addPoint/removePoint by adding a copy of the first
|
* point as the last point.
|
*
|
* Also, as it is the first in the line family to close itself, a getArea()
|
* function is defined to calculate the enclosed area of the linearRing
|
*
|
* Inherits:
|
* - <OpenLayers.Geometry.LineString>
|
*/
|
OpenLayers.Geometry.LinearRing = OpenLayers.Class(
|
OpenLayers.Geometry.LineString, {
|
|
/**
|
* Property: componentTypes
|
* {Array(String)} An array of class names representing the types of
|
* components that the collection can include. A null
|
* value means the component types are not restricted.
|
*/
|
componentTypes: ["OpenLayers.Geometry.Point"],
|
|
/**
|
* Constructor: OpenLayers.Geometry.LinearRing
|
* Linear rings are constructed with an array of points. This array
|
* can represent a closed or open ring. If the ring is open (the last
|
* point does not equal the first point), the constructor will close
|
* the ring. If the ring is already closed (the last point does equal
|
* the first point), it will be left closed.
|
*
|
* Parameters:
|
* points - {Array(<OpenLayers.Geometry.Point>)} points
|
*/
|
|
/**
|
* APIMethod: addComponent
|
* Adds a point to geometry components. If the point is to be added to
|
* the end of the components array and it is the same as the last point
|
* already in that array, the duplicate point is not added. This has
|
* the effect of closing the ring if it is not already closed, and
|
* doing the right thing if it is already closed. This behavior can
|
* be overridden by calling the method with a non-null index as the
|
* second argument.
|
*
|
* Parameters:
|
* point - {<OpenLayers.Geometry.Point>}
|
* index - {Integer} Index into the array to insert the component
|
*
|
* Returns:
|
* {Boolean} Was the Point successfully added?
|
*/
|
addComponent: function(point, index) {
|
var added = false;
|
|
//remove last point
|
var lastPoint = this.components.pop();
|
|
// given an index, add the point
|
// without an index only add non-duplicate points
|
if(index != null || !point.equals(lastPoint)) {
|
added = OpenLayers.Geometry.Collection.prototype.addComponent.apply(this,
|
arguments);
|
}
|
|
//append copy of first point
|
var firstPoint = this.components[0];
|
OpenLayers.Geometry.Collection.prototype.addComponent.apply(this,
|
[firstPoint]);
|
|
return added;
|
},
|
|
/**
|
* APIMethod: removeComponent
|
* Removes a point from geometry components.
|
*
|
* Parameters:
|
* point - {<OpenLayers.Geometry.Point>}
|
*
|
* Returns:
|
* {Boolean} The component was removed.
|
*/
|
removeComponent: function(point) {
|
var removed = this.components && (this.components.length > 3);
|
if (removed) {
|
//remove last point
|
this.components.pop();
|
|
//remove our point
|
OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this,
|
arguments);
|
//append copy of first point
|
var firstPoint = this.components[0];
|
OpenLayers.Geometry.Collection.prototype.addComponent.apply(this,
|
[firstPoint]);
|
}
|
return removed;
|
},
|
|
/**
|
* APIMethod: move
|
* Moves a geometry by the given displacement along positive x and y axes.
|
* This modifies the position of the geometry and clears the cached
|
* bounds.
|
*
|
* Parameters:
|
* x - {Float} Distance to move geometry in positive x direction.
|
* y - {Float} Distance to move geometry in positive y direction.
|
*/
|
move: function(x, y) {
|
for(var i = 0, len=this.components.length; i<len - 1; i++) {
|
this.components[i].move(x, y);
|
}
|
},
|
|
/**
|
* APIMethod: rotate
|
* Rotate a geometry around some origin
|
*
|
* Parameters:
|
* angle - {Float} Rotation angle in degrees (measured counterclockwise
|
* from the positive x-axis)
|
* origin - {<OpenLayers.Geometry.Point>} Center point for the rotation
|
*/
|
rotate: function(angle, origin) {
|
for(var i=0, len=this.components.length; i<len - 1; ++i) {
|
this.components[i].rotate(angle, origin);
|
}
|
},
|
|
/**
|
* APIMethod: resize
|
* Resize a geometry relative to some origin. Use this method to apply
|
* a uniform scaling to a geometry.
|
*
|
* Parameters:
|
* scale - {Float} Factor by which to scale the geometry. A scale of 2
|
* doubles the size of the geometry in each dimension
|
* (lines, for example, will be twice as long, and polygons
|
* will have four times the area).
|
* origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing
|
* ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry>} - The current geometry.
|
*/
|
resize: function(scale, origin, ratio) {
|
for(var i=0, len=this.components.length; i<len - 1; ++i) {
|
this.components[i].resize(scale, origin, ratio);
|
}
|
return this;
|
},
|
|
/**
|
* APIMethod: transform
|
* Reproject the components geometry from source to dest.
|
*
|
* Parameters:
|
* source - {<OpenLayers.Projection>}
|
* dest - {<OpenLayers.Projection>}
|
*
|
* Returns:
|
* {<OpenLayers.Geometry>}
|
*/
|
transform: function(source, dest) {
|
if (source && dest) {
|
for (var i=0, len=this.components.length; i<len - 1; i++) {
|
var component = this.components[i];
|
component.transform(source, dest);
|
}
|
this.bounds = null;
|
}
|
return this;
|
},
|
|
/**
|
* APIMethod: getCentroid
|
*
|
* Returns:
|
* {<OpenLayers.Geometry.Point>} The centroid of the collection
|
*/
|
getCentroid: function() {
|
if (this.components) {
|
var len = this.components.length;
|
if (len > 0 && len <= 2) {
|
return this.components[0].clone();
|
} else if (len > 2) {
|
var sumX = 0.0;
|
var sumY = 0.0;
|
var x0 = this.components[0].x;
|
var y0 = this.components[0].y;
|
var area = -1 * this.getArea();
|
if (area != 0) {
|
for (var i = 0; i < len - 1; i++) {
|
var b = this.components[i];
|
var c = this.components[i+1];
|
sumX += (b.x + c.x - 2 * x0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0));
|
sumY += (b.y + c.y - 2 * y0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0));
|
}
|
var x = x0 + sumX / (6 * area);
|
var y = y0 + sumY / (6 * area);
|
} else {
|
for (var i = 0; i < len - 1; i++) {
|
sumX += this.components[i].x;
|
sumY += this.components[i].y;
|
}
|
var x = sumX / (len - 1);
|
var y = sumY / (len - 1);
|
}
|
return new OpenLayers.Geometry.Point(x, y);
|
} else {
|
return null;
|
}
|
}
|
},
|
|
/**
|
* APIMethod: getArea
|
* Note - The area is positive if the ring is oriented CW, otherwise
|
* it will be negative.
|
*
|
* Returns:
|
* {Float} The signed area for a ring.
|
*/
|
getArea: function() {
|
var area = 0.0;
|
if ( this.components && (this.components.length > 2)) {
|
var sum = 0.0;
|
for (var i=0, len=this.components.length; i<len - 1; i++) {
|
var b = this.components[i];
|
var c = this.components[i+1];
|
sum += (b.x + c.x) * (c.y - b.y);
|
}
|
area = - sum / 2.0;
|
}
|
return area;
|
},
|
|
/**
|
* APIMethod: getGeodesicArea
|
* Calculate the approximate area of the polygon were it projected onto
|
* the earth. Note that this area will be positive if ring is oriented
|
* clockwise, otherwise it will be negative.
|
*
|
* Parameters:
|
* projection - {<OpenLayers.Projection>} The spatial reference system
|
* for the geometry coordinates. If not provided, Geographic/WGS84 is
|
* assumed.
|
*
|
* Reference:
|
* Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
|
* Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
|
* Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
|
*
|
* Returns:
|
* {float} The approximate signed geodesic area of the polygon in square
|
* meters.
|
*/
|
getGeodesicArea: function(projection) {
|
var ring = this; // so we can work with a clone if needed
|
if(projection) {
|
var gg = new OpenLayers.Projection("EPSG:4326");
|
if(!gg.equals(projection)) {
|
ring = this.clone().transform(projection, gg);
|
}
|
}
|
var area = 0.0;
|
var len = ring.components && ring.components.length;
|
if(len > 2) {
|
var p1, p2;
|
for(var i=0; i<len-1; i++) {
|
p1 = ring.components[i];
|
p2 = ring.components[i+1];
|
area += OpenLayers.Util.rad(p2.x - p1.x) *
|
(2 + Math.sin(OpenLayers.Util.rad(p1.y)) +
|
Math.sin(OpenLayers.Util.rad(p2.y)));
|
}
|
area = area * 6378137.0 * 6378137.0 / 2.0;
|
}
|
return area;
|
},
|
|
/**
|
* Method: containsPoint
|
* Test if a point is inside a linear ring. For the case where a point
|
* is coincident with a linear ring edge, returns 1. Otherwise,
|
* returns boolean.
|
*
|
* Parameters:
|
* point - {<OpenLayers.Geometry.Point>}
|
*
|
* Returns:
|
* {Boolean | Number} The point is inside the linear ring. Returns 1 if
|
* the point is coincident with an edge. Returns boolean otherwise.
|
*/
|
containsPoint: function(point) {
|
var approx = OpenLayers.Number.limitSigDigs;
|
var digs = 14;
|
var px = approx(point.x, digs);
|
var py = approx(point.y, digs);
|
function getX(y, x1, y1, x2, y2) {
|
return (y - y2) * ((x2 - x1) / (y2 - y1)) + x2;
|
}
|
var numSeg = this.components.length - 1;
|
var start, end, x1, y1, x2, y2, cx, cy;
|
var crosses = 0;
|
for(var i=0; i<numSeg; ++i) {
|
start = this.components[i];
|
x1 = approx(start.x, digs);
|
y1 = approx(start.y, digs);
|
end = this.components[i + 1];
|
x2 = approx(end.x, digs);
|
y2 = approx(end.y, digs);
|
|
/**
|
* The following conditions enforce five edge-crossing rules:
|
* 1. points coincident with edges are considered contained;
|
* 2. an upward edge includes its starting endpoint, and
|
* excludes its final endpoint;
|
* 3. a downward edge excludes its starting endpoint, and
|
* includes its final endpoint;
|
* 4. horizontal edges are excluded; and
|
* 5. the edge-ray intersection point must be strictly right
|
* of the point P.
|
*/
|
if(y1 == y2) {
|
// horizontal edge
|
if(py == y1) {
|
// point on horizontal line
|
if(x1 <= x2 && (px >= x1 && px <= x2) || // right or vert
|
x1 >= x2 && (px <= x1 && px >= x2)) { // left or vert
|
// point on edge
|
crosses = -1;
|
break;
|
}
|
}
|
// ignore other horizontal edges
|
continue;
|
}
|
cx = approx(getX(py, x1, y1, x2, y2), digs);
|
if(cx == px) {
|
// point on line
|
if(y1 < y2 && (py >= y1 && py <= y2) || // upward
|
y1 > y2 && (py <= y1 && py >= y2)) { // downward
|
// point on edge
|
crosses = -1;
|
break;
|
}
|
}
|
if(cx <= px) {
|
// no crossing to the right
|
continue;
|
}
|
if(x1 != x2 && (cx < Math.min(x1, x2) || cx > Math.max(x1, x2))) {
|
// no crossing
|
continue;
|
}
|
if(y1 < y2 && (py >= y1 && py < y2) || // upward
|
y1 > y2 && (py < y1 && py >= y2)) { // downward
|
++crosses;
|
}
|
}
|
var contained = (crosses == -1) ?
|
// on edge
|
1 :
|
// even (out) or odd (in)
|
!!(crosses & 1);
|
|
return contained;
|
},
|
|
/**
|
* APIMethod: intersects
|
* Determine if the input geometry intersects this one.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>} Any type of geometry.
|
*
|
* Returns:
|
* {Boolean} The input geometry intersects this one.
|
*/
|
intersects: function(geometry) {
|
var intersect = false;
|
if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
|
intersect = this.containsPoint(geometry);
|
} else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LineString") {
|
intersect = geometry.intersects(this);
|
} else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
|
intersect = OpenLayers.Geometry.LineString.prototype.intersects.apply(
|
this, [geometry]
|
);
|
} else {
|
// check for component intersections
|
for(var i=0, len=geometry.components.length; i<len; ++ i) {
|
intersect = geometry.components[i].intersects(this);
|
if(intersect) {
|
break;
|
}
|
}
|
}
|
return intersect;
|
},
|
|
/**
|
* APIMethod: getVertices
|
* Return a list of all points in this geometry.
|
*
|
* Parameters:
|
* nodes - {Boolean} For lines, only return vertices that are
|
* endpoints. If false, for lines, only vertices that are not
|
* endpoints will be returned. If not provided, all vertices will
|
* be returned.
|
*
|
* Returns:
|
* {Array} A list of all vertices in the geometry.
|
*/
|
getVertices: function(nodes) {
|
return (nodes === true) ? [] : this.components.slice(0, this.components.length-1);
|
},
|
|
CLASS_NAME: "OpenLayers.Geometry.LinearRing"
|
});
|
/* ======================================================================
|
OpenLayers/Geometry/Polygon.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Geometry/Collection.js
|
* @requires OpenLayers/Geometry/LinearRing.js
|
*/
|
|
/**
|
* Class: OpenLayers.Geometry.Polygon
|
* Polygon is a collection of Geometry.LinearRings.
|
*
|
* Inherits from:
|
* - <OpenLayers.Geometry.Collection>
|
* - <OpenLayers.Geometry>
|
*/
|
OpenLayers.Geometry.Polygon = OpenLayers.Class(
|
OpenLayers.Geometry.Collection, {
|
|
/**
|
* Property: componentTypes
|
* {Array(String)} An array of class names representing the types of
|
* components that the collection can include. A null value means the
|
* component types are not restricted.
|
*/
|
componentTypes: ["OpenLayers.Geometry.LinearRing"],
|
|
/**
|
* Constructor: OpenLayers.Geometry.Polygon
|
* Constructor for a Polygon geometry.
|
* The first ring (this.component[0])is the outer bounds of the polygon and
|
* all subsequent rings (this.component[1-n]) are internal holes.
|
*
|
*
|
* Parameters:
|
* components - {Array(<OpenLayers.Geometry.LinearRing>)}
|
*/
|
|
/**
|
* APIMethod: getArea
|
* Calculated by subtracting the areas of the internal holes from the
|
* area of the outer hole.
|
*
|
* Returns:
|
* {float} The area of the geometry
|
*/
|
getArea: function() {
|
var area = 0.0;
|
if ( this.components && (this.components.length > 0)) {
|
area += Math.abs(this.components[0].getArea());
|
for (var i=1, len=this.components.length; i<len; i++) {
|
area -= Math.abs(this.components[i].getArea());
|
}
|
}
|
return area;
|
},
|
|
/**
|
* APIMethod: getGeodesicArea
|
* Calculate the approximate area of the polygon were it projected onto
|
* the earth.
|
*
|
* Parameters:
|
* projection - {<OpenLayers.Projection>} The spatial reference system
|
* for the geometry coordinates. If not provided, Geographic/WGS84 is
|
* assumed.
|
*
|
* Reference:
|
* Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
|
* Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
|
* Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
|
*
|
* Returns:
|
* {float} The approximate geodesic area of the polygon in square meters.
|
*/
|
getGeodesicArea: function(projection) {
|
var area = 0.0;
|
if(this.components && (this.components.length > 0)) {
|
area += Math.abs(this.components[0].getGeodesicArea(projection));
|
for(var i=1, len=this.components.length; i<len; i++) {
|
area -= Math.abs(this.components[i].getGeodesicArea(projection));
|
}
|
}
|
return area;
|
},
|
|
/**
|
* Method: containsPoint
|
* Test if a point is inside a polygon. Points on a polygon edge are
|
* considered inside.
|
*
|
* Parameters:
|
* point - {<OpenLayers.Geometry.Point>}
|
*
|
* Returns:
|
* {Boolean | Number} The point is inside the polygon. Returns 1 if the
|
* point is on an edge. Returns boolean otherwise.
|
*/
|
containsPoint: function(point) {
|
var numRings = this.components.length;
|
var contained = false;
|
if(numRings > 0) {
|
// check exterior ring - 1 means on edge, boolean otherwise
|
contained = this.components[0].containsPoint(point);
|
if(contained !== 1) {
|
if(contained && numRings > 1) {
|
// check interior rings
|
var hole;
|
for(var i=1; i<numRings; ++i) {
|
hole = this.components[i].containsPoint(point);
|
if(hole) {
|
if(hole === 1) {
|
// on edge
|
contained = 1;
|
} else {
|
// in hole
|
contained = false;
|
}
|
break;
|
}
|
}
|
}
|
}
|
}
|
return contained;
|
},
|
|
/**
|
* APIMethod: intersects
|
* Determine if the input geometry intersects this one.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>} Any type of geometry.
|
*
|
* Returns:
|
* {Boolean} The input geometry intersects this one.
|
*/
|
intersects: function(geometry) {
|
var intersect = false;
|
var i, len;
|
if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
|
intersect = this.containsPoint(geometry);
|
} else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LineString" ||
|
geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
|
// check if rings/linestrings intersect
|
for(i=0, len=this.components.length; i<len; ++i) {
|
intersect = geometry.intersects(this.components[i]);
|
if(intersect) {
|
break;
|
}
|
}
|
if(!intersect) {
|
// check if this poly contains points of the ring/linestring
|
for(i=0, len=geometry.components.length; i<len; ++i) {
|
intersect = this.containsPoint(geometry.components[i]);
|
if(intersect) {
|
break;
|
}
|
}
|
}
|
} else {
|
for(i=0, len=geometry.components.length; i<len; ++ i) {
|
intersect = this.intersects(geometry.components[i]);
|
if(intersect) {
|
break;
|
}
|
}
|
}
|
// check case where this poly is wholly contained by another
|
if(!intersect && geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") {
|
// exterior ring points will be contained in the other geometry
|
var ring = this.components[0];
|
for(i=0, len=ring.components.length; i<len; ++i) {
|
intersect = geometry.containsPoint(ring.components[i]);
|
if(intersect) {
|
break;
|
}
|
}
|
}
|
return intersect;
|
},
|
|
/**
|
* APIMethod: distanceTo
|
* Calculate the closest distance between two geometries (on the x-y plane).
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>} The target geometry.
|
* options - {Object} Optional properties for configuring the distance
|
* calculation.
|
*
|
* Valid options:
|
* details - {Boolean} Return details from the distance calculation.
|
* Default is false.
|
* edge - {Boolean} Calculate the distance from this geometry to the
|
* nearest edge of the target geometry. Default is true. If true,
|
* calling distanceTo from a geometry that is wholly contained within
|
* the target will result in a non-zero distance. If false, whenever
|
* geometries intersect, calling distanceTo will return 0. If false,
|
* details cannot be returned.
|
*
|
* Returns:
|
* {Number | Object} The distance between this geometry and the target.
|
* If details is true, the return will be an object with distance,
|
* x0, y0, x1, and y1 properties. The x0 and y0 properties represent
|
* the coordinates of the closest point on this geometry. The x1 and y1
|
* properties represent the coordinates of the closest point on the
|
* target geometry.
|
*/
|
distanceTo: function(geometry, options) {
|
var edge = !(options && options.edge === false);
|
var result;
|
// this is the case where we might not be looking for distance to edge
|
if(!edge && this.intersects(geometry)) {
|
result = 0;
|
} else {
|
result = OpenLayers.Geometry.Collection.prototype.distanceTo.apply(
|
this, [geometry, options]
|
);
|
}
|
return result;
|
},
|
|
CLASS_NAME: "OpenLayers.Geometry.Polygon"
|
});
|
|
/**
|
* APIMethod: createRegularPolygon
|
* Create a regular polygon around a radius. Useful for creating circles
|
* and the like.
|
*
|
* Parameters:
|
* origin - {<OpenLayers.Geometry.Point>} center of polygon.
|
* radius - {Float} distance to vertex, in map units.
|
* sides - {Integer} Number of sides. 20 approximates a circle.
|
* rotation - {Float} original angle of rotation, in degrees.
|
*/
|
OpenLayers.Geometry.Polygon.createRegularPolygon = function(origin, radius, sides, rotation) {
|
var angle = Math.PI * ((1/sides) - (1/2));
|
if(rotation) {
|
angle += (rotation / 180) * Math.PI;
|
}
|
var rotatedAngle, x, y;
|
var points = [];
|
for(var i=0; i<sides; ++i) {
|
rotatedAngle = angle + (i * 2 * Math.PI / sides);
|
x = origin.x + (radius * Math.cos(rotatedAngle));
|
y = origin.y + (radius * Math.sin(rotatedAngle));
|
points.push(new OpenLayers.Geometry.Point(x, y));
|
}
|
var ring = new OpenLayers.Geometry.LinearRing(points);
|
return new OpenLayers.Geometry.Polygon([ring]);
|
};
|
/* ======================================================================
|
OpenLayers/Geometry/MultiPolygon.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Geometry/Collection.js
|
* @requires OpenLayers/Geometry/Polygon.js
|
*/
|
|
/**
|
* Class: OpenLayers.Geometry.MultiPolygon
|
* MultiPolygon is a geometry with multiple <OpenLayers.Geometry.Polygon>
|
* components. Create a new instance with the <OpenLayers.Geometry.MultiPolygon>
|
* constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Geometry.Collection>
|
*/
|
OpenLayers.Geometry.MultiPolygon = OpenLayers.Class(
|
OpenLayers.Geometry.Collection, {
|
|
/**
|
* Property: componentTypes
|
* {Array(String)} An array of class names representing the types of
|
* components that the collection can include. A null value means the
|
* component types are not restricted.
|
*/
|
componentTypes: ["OpenLayers.Geometry.Polygon"],
|
|
/**
|
* Constructor: OpenLayers.Geometry.MultiPolygon
|
* Create a new MultiPolygon geometry
|
*
|
* Parameters:
|
* components - {Array(<OpenLayers.Geometry.Polygon>)} An array of polygons
|
* used to generate the MultiPolygon
|
*
|
*/
|
|
CLASS_NAME: "OpenLayers.Geometry.MultiPolygon"
|
});
|
/* ======================================================================
|
OpenLayers/Format/GML.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML.js
|
* @requires OpenLayers/Feature/Vector.js
|
* @requires OpenLayers/Geometry/Point.js
|
* @requires OpenLayers/Geometry/MultiPoint.js
|
* @requires OpenLayers/Geometry/LineString.js
|
* @requires OpenLayers/Geometry/MultiLineString.js
|
* @requires OpenLayers/Geometry/Polygon.js
|
* @requires OpenLayers/Geometry/MultiPolygon.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.GML
|
* Read/Write GML. Create a new instance with the <OpenLayers.Format.GML>
|
* constructor. Supports the GML simple features profile.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.GML = OpenLayers.Class(OpenLayers.Format.XML, {
|
|
/**
|
* APIProperty: featureNS
|
* {String} Namespace used for feature attributes. Default is
|
* "http://mapserver.gis.umn.edu/mapserver".
|
*/
|
featureNS: "http://mapserver.gis.umn.edu/mapserver",
|
|
/**
|
* APIProperty: featurePrefix
|
* {String} Namespace alias (or prefix) for feature nodes. Default is
|
* "feature".
|
*/
|
featurePrefix: "feature",
|
|
/**
|
* APIProperty: featureName
|
* {String} Element name for features. Default is "featureMember".
|
*/
|
featureName: "featureMember",
|
|
/**
|
* APIProperty: layerName
|
* {String} Name of data layer. Default is "features".
|
*/
|
layerName: "features",
|
|
/**
|
* APIProperty: geometryName
|
* {String} Name of geometry element. Defaults to "geometry".
|
*/
|
geometryName: "geometry",
|
|
/**
|
* APIProperty: collectionName
|
* {String} Name of featureCollection element.
|
*/
|
collectionName: "FeatureCollection",
|
|
/**
|
* APIProperty: gmlns
|
* {String} GML Namespace.
|
*/
|
gmlns: "http://www.opengis.net/gml",
|
|
/**
|
* APIProperty: extractAttributes
|
* {Boolean} Extract attributes from GML.
|
*/
|
extractAttributes: true,
|
|
/**
|
* APIProperty: xy
|
* {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
|
* Changing is not recommended, a new Format should be instantiated.
|
*/
|
xy: true,
|
|
/**
|
* Constructor: OpenLayers.Format.GML
|
* Create a new parser for GML.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
initialize: function(options) {
|
// compile regular expressions once instead of every time they are used
|
this.regExes = {
|
trimSpace: (/^\s*|\s*$/g),
|
removeSpace: (/\s*/g),
|
splitSpace: (/\s+/),
|
trimComma: (/\s*,\s*/g)
|
};
|
OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
|
},
|
|
/**
|
* APIMethod: read
|
* Read data from a string, and return a list of features.
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
*
|
* Returns:
|
* {Array(<OpenLayers.Feature.Vector>)} An array of features.
|
*/
|
read: function(data) {
|
if(typeof data == "string") {
|
data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
|
}
|
var featureNodes = this.getElementsByTagNameNS(data.documentElement,
|
this.gmlns,
|
this.featureName);
|
var features = [];
|
for(var i=0; i<featureNodes.length; i++) {
|
var feature = this.parseFeature(featureNodes[i]);
|
if(feature) {
|
features.push(feature);
|
}
|
}
|
return features;
|
},
|
|
/**
|
* Method: parseFeature
|
* This function is the core of the GML parsing code in OpenLayers.
|
* It creates the geometries that are then attached to the returned
|
* feature, and calls parseAttributes() to get attribute data out.
|
*
|
* Parameters:
|
* node - {DOMElement} A GML feature node.
|
*/
|
parseFeature: function(node) {
|
// only accept one geometry per feature - look for highest "order"
|
var order = ["MultiPolygon", "Polygon",
|
"MultiLineString", "LineString",
|
"MultiPoint", "Point", "Envelope"];
|
// FIXME: In case we parse a feature with no geometry, but boundedBy an Envelope,
|
// this code creates a geometry derived from the Envelope. This is not correct.
|
var type, nodeList, geometry, parser;
|
for(var i=0; i<order.length; ++i) {
|
type = order[i];
|
nodeList = this.getElementsByTagNameNS(node, this.gmlns, type);
|
if(nodeList.length > 0) {
|
// only deal with first geometry of this type
|
parser = this.parseGeometry[type.toLowerCase()];
|
if(parser) {
|
geometry = parser.apply(this, [nodeList[0]]);
|
if (this.internalProjection && this.externalProjection) {
|
geometry.transform(this.externalProjection,
|
this.internalProjection);
|
}
|
} else {
|
throw new TypeError("Unsupported geometry type: " + type);
|
}
|
// stop looking for different geometry types
|
break;
|
}
|
}
|
|
var bounds;
|
var boxNodes = this.getElementsByTagNameNS(node, this.gmlns, "Box");
|
for(i=0; i<boxNodes.length; ++i) {
|
var boxNode = boxNodes[i];
|
var box = this.parseGeometry["box"].apply(this, [boxNode]);
|
var parentNode = boxNode.parentNode;
|
var parentName = parentNode.localName ||
|
parentNode.nodeName.split(":").pop();
|
if(parentName === "boundedBy") {
|
bounds = box;
|
} else {
|
geometry = box.toGeometry();
|
}
|
}
|
|
// construct feature (optionally with attributes)
|
var attributes;
|
if(this.extractAttributes) {
|
attributes = this.parseAttributes(node);
|
}
|
var feature = new OpenLayers.Feature.Vector(geometry, attributes);
|
feature.bounds = bounds;
|
|
feature.gml = {
|
featureType: node.firstChild.nodeName.split(":")[1],
|
featureNS: node.firstChild.namespaceURI,
|
featureNSPrefix: node.firstChild.prefix
|
};
|
|
// assign fid - this can come from a "fid" or "id" attribute
|
var childNode = node.firstChild;
|
var fid;
|
while(childNode) {
|
if(childNode.nodeType == 1) {
|
fid = childNode.getAttribute("fid") ||
|
childNode.getAttribute("id");
|
if(fid) {
|
break;
|
}
|
}
|
childNode = childNode.nextSibling;
|
}
|
feature.fid = fid;
|
return feature;
|
},
|
|
/**
|
* Property: parseGeometry
|
* Properties of this object are the functions that parse geometries based
|
* on their type.
|
*/
|
parseGeometry: {
|
|
/**
|
* Method: parseGeometry.point
|
* Given a GML node representing a point geometry, create an OpenLayers
|
* point geometry.
|
*
|
* Parameters:
|
* node - {DOMElement} A GML node.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry.Point>} A point geometry.
|
*/
|
point: function(node) {
|
/**
|
* Three coordinate variations to consider:
|
* 1) <gml:pos>x y z</gml:pos>
|
* 2) <gml:coordinates>x, y, z</gml:coordinates>
|
* 3) <gml:coord><gml:X>x</gml:X><gml:Y>y</gml:Y></gml:coord>
|
*/
|
var nodeList, coordString;
|
var coords = [];
|
|
// look for <gml:pos>
|
var nodeList = this.getElementsByTagNameNS(node, this.gmlns, "pos");
|
if(nodeList.length > 0) {
|
coordString = nodeList[0].firstChild.nodeValue;
|
coordString = coordString.replace(this.regExes.trimSpace, "");
|
coords = coordString.split(this.regExes.splitSpace);
|
}
|
|
// look for <gml:coordinates>
|
if(coords.length == 0) {
|
nodeList = this.getElementsByTagNameNS(node, this.gmlns,
|
"coordinates");
|
if(nodeList.length > 0) {
|
coordString = nodeList[0].firstChild.nodeValue;
|
coordString = coordString.replace(this.regExes.removeSpace,
|
"");
|
coords = coordString.split(",");
|
}
|
}
|
|
// look for <gml:coord>
|
if(coords.length == 0) {
|
nodeList = this.getElementsByTagNameNS(node, this.gmlns,
|
"coord");
|
if(nodeList.length > 0) {
|
var xList = this.getElementsByTagNameNS(nodeList[0],
|
this.gmlns, "X");
|
var yList = this.getElementsByTagNameNS(nodeList[0],
|
this.gmlns, "Y");
|
if(xList.length > 0 && yList.length > 0) {
|
coords = [xList[0].firstChild.nodeValue,
|
yList[0].firstChild.nodeValue];
|
}
|
}
|
}
|
|
// preserve third dimension
|
if(coords.length == 2) {
|
coords[2] = null;
|
}
|
|
if (this.xy) {
|
return new OpenLayers.Geometry.Point(coords[0], coords[1],
|
coords[2]);
|
}
|
else{
|
return new OpenLayers.Geometry.Point(coords[1], coords[0],
|
coords[2]);
|
}
|
},
|
|
/**
|
* Method: parseGeometry.multipoint
|
* Given a GML node representing a multipoint geometry, create an
|
* OpenLayers multipoint geometry.
|
*
|
* Parameters:
|
* node - {DOMElement} A GML node.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry.MultiPoint>} A multipoint geometry.
|
*/
|
multipoint: function(node) {
|
var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
|
"Point");
|
var components = [];
|
if(nodeList.length > 0) {
|
var point;
|
for(var i=0; i<nodeList.length; ++i) {
|
point = this.parseGeometry.point.apply(this, [nodeList[i]]);
|
if(point) {
|
components.push(point);
|
}
|
}
|
}
|
return new OpenLayers.Geometry.MultiPoint(components);
|
},
|
|
/**
|
* Method: parseGeometry.linestring
|
* Given a GML node representing a linestring geometry, create an
|
* OpenLayers linestring geometry.
|
*
|
* Parameters:
|
* node - {DOMElement} A GML node.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry.LineString>} A linestring geometry.
|
*/
|
linestring: function(node, ring) {
|
/**
|
* Two coordinate variations to consider:
|
* 1) <gml:posList dimension="d">x0 y0 z0 x1 y1 z1</gml:posList>
|
* 2) <gml:coordinates>x0, y0, z0 x1, y1, z1</gml:coordinates>
|
*/
|
var nodeList, coordString;
|
var coords = [];
|
var points = [];
|
|
// look for <gml:posList>
|
nodeList = this.getElementsByTagNameNS(node, this.gmlns, "posList");
|
if(nodeList.length > 0) {
|
coordString = this.getChildValue(nodeList[0]);
|
coordString = coordString.replace(this.regExes.trimSpace, "");
|
coords = coordString.split(this.regExes.splitSpace);
|
var dim = parseInt(nodeList[0].getAttribute("dimension"));
|
var j, x, y, z;
|
for(var i=0; i<coords.length/dim; ++i) {
|
j = i * dim;
|
x = coords[j];
|
y = coords[j+1];
|
z = (dim == 2) ? null : coords[j+2];
|
if (this.xy) {
|
points.push(new OpenLayers.Geometry.Point(x, y, z));
|
} else {
|
points.push(new OpenLayers.Geometry.Point(y, x, z));
|
}
|
}
|
}
|
|
// look for <gml:coordinates>
|
if(coords.length == 0) {
|
nodeList = this.getElementsByTagNameNS(node, this.gmlns,
|
"coordinates");
|
if(nodeList.length > 0) {
|
coordString = this.getChildValue(nodeList[0]);
|
coordString = coordString.replace(this.regExes.trimSpace,
|
"");
|
coordString = coordString.replace(this.regExes.trimComma,
|
",");
|
var pointList = coordString.split(this.regExes.splitSpace);
|
for(var i=0; i<pointList.length; ++i) {
|
coords = pointList[i].split(",");
|
if(coords.length == 2) {
|
coords[2] = null;
|
}
|
if (this.xy) {
|
points.push(new OpenLayers.Geometry.Point(coords[0],
|
coords[1],
|
coords[2]));
|
} else {
|
points.push(new OpenLayers.Geometry.Point(coords[1],
|
coords[0],
|
coords[2]));
|
}
|
}
|
}
|
}
|
|
var line = null;
|
if(points.length != 0) {
|
if(ring) {
|
line = new OpenLayers.Geometry.LinearRing(points);
|
} else {
|
line = new OpenLayers.Geometry.LineString(points);
|
}
|
}
|
return line;
|
},
|
|
/**
|
* Method: parseGeometry.multilinestring
|
* Given a GML node representing a multilinestring geometry, create an
|
* OpenLayers multilinestring geometry.
|
*
|
* Parameters:
|
* node - {DOMElement} A GML node.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry.MultiLineString>} A multilinestring geometry.
|
*/
|
multilinestring: function(node) {
|
var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
|
"LineString");
|
var components = [];
|
if(nodeList.length > 0) {
|
var line;
|
for(var i=0; i<nodeList.length; ++i) {
|
line = this.parseGeometry.linestring.apply(this,
|
[nodeList[i]]);
|
if(line) {
|
components.push(line);
|
}
|
}
|
}
|
return new OpenLayers.Geometry.MultiLineString(components);
|
},
|
|
/**
|
* Method: parseGeometry.polygon
|
* Given a GML node representing a polygon geometry, create an
|
* OpenLayers polygon geometry.
|
*
|
* Parameters:
|
* node - {DOMElement} A GML node.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry.Polygon>} A polygon geometry.
|
*/
|
polygon: function(node) {
|
var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
|
"LinearRing");
|
var components = [];
|
if(nodeList.length > 0) {
|
// this assumes exterior ring first, inner rings after
|
var ring;
|
for(var i=0; i<nodeList.length; ++i) {
|
ring = this.parseGeometry.linestring.apply(this,
|
[nodeList[i], true]);
|
if(ring) {
|
components.push(ring);
|
}
|
}
|
}
|
return new OpenLayers.Geometry.Polygon(components);
|
},
|
|
/**
|
* Method: parseGeometry.multipolygon
|
* Given a GML node representing a multipolygon geometry, create an
|
* OpenLayers multipolygon geometry.
|
*
|
* Parameters:
|
* node - {DOMElement} A GML node.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry.MultiPolygon>} A multipolygon geometry.
|
*/
|
multipolygon: function(node) {
|
var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
|
"Polygon");
|
var components = [];
|
if(nodeList.length > 0) {
|
var polygon;
|
for(var i=0; i<nodeList.length; ++i) {
|
polygon = this.parseGeometry.polygon.apply(this,
|
[nodeList[i]]);
|
if(polygon) {
|
components.push(polygon);
|
}
|
}
|
}
|
return new OpenLayers.Geometry.MultiPolygon(components);
|
},
|
|
envelope: function(node) {
|
var components = [];
|
var coordString;
|
var envelope;
|
|
var lpoint = this.getElementsByTagNameNS(node, this.gmlns, "lowerCorner");
|
if (lpoint.length > 0) {
|
var coords = [];
|
|
if(lpoint.length > 0) {
|
coordString = lpoint[0].firstChild.nodeValue;
|
coordString = coordString.replace(this.regExes.trimSpace, "");
|
coords = coordString.split(this.regExes.splitSpace);
|
}
|
|
if(coords.length == 2) {
|
coords[2] = null;
|
}
|
if (this.xy) {
|
var lowerPoint = new OpenLayers.Geometry.Point(coords[0], coords[1],coords[2]);
|
} else {
|
var lowerPoint = new OpenLayers.Geometry.Point(coords[1], coords[0],coords[2]);
|
}
|
}
|
|
var upoint = this.getElementsByTagNameNS(node, this.gmlns, "upperCorner");
|
if (upoint.length > 0) {
|
var coords = [];
|
|
if(upoint.length > 0) {
|
coordString = upoint[0].firstChild.nodeValue;
|
coordString = coordString.replace(this.regExes.trimSpace, "");
|
coords = coordString.split(this.regExes.splitSpace);
|
}
|
|
if(coords.length == 2) {
|
coords[2] = null;
|
}
|
if (this.xy) {
|
var upperPoint = new OpenLayers.Geometry.Point(coords[0], coords[1],coords[2]);
|
} else {
|
var upperPoint = new OpenLayers.Geometry.Point(coords[1], coords[0],coords[2]);
|
}
|
}
|
|
if (lowerPoint && upperPoint) {
|
components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y));
|
components.push(new OpenLayers.Geometry.Point(upperPoint.x, lowerPoint.y));
|
components.push(new OpenLayers.Geometry.Point(upperPoint.x, upperPoint.y));
|
components.push(new OpenLayers.Geometry.Point(lowerPoint.x, upperPoint.y));
|
components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y));
|
|
var ring = new OpenLayers.Geometry.LinearRing(components);
|
envelope = new OpenLayers.Geometry.Polygon([ring]);
|
}
|
return envelope;
|
},
|
|
/**
|
* Method: parseGeometry.box
|
* Given a GML node representing a box geometry, create an
|
* OpenLayers.Bounds.
|
*
|
* Parameters:
|
* node - {DOMElement} A GML node.
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} A bounds representing the box.
|
*/
|
box: function(node) {
|
var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
|
"coordinates");
|
var coordString;
|
var coords, beginPoint = null, endPoint = null;
|
if (nodeList.length > 0) {
|
coordString = nodeList[0].firstChild.nodeValue;
|
coords = coordString.split(" ");
|
if (coords.length == 2) {
|
beginPoint = coords[0].split(",");
|
endPoint = coords[1].split(",");
|
}
|
}
|
if (beginPoint !== null && endPoint !== null) {
|
return new OpenLayers.Bounds(parseFloat(beginPoint[0]),
|
parseFloat(beginPoint[1]),
|
parseFloat(endPoint[0]),
|
parseFloat(endPoint[1]) );
|
}
|
}
|
|
},
|
|
/**
|
* Method: parseAttributes
|
*
|
* Parameters:
|
* node - {DOMElement}
|
*
|
* Returns:
|
* {Object} An attributes object.
|
*/
|
parseAttributes: function(node) {
|
var attributes = {};
|
// assume attributes are children of the first type 1 child
|
var childNode = node.firstChild;
|
var children, i, child, grandchildren, grandchild, name, value;
|
while(childNode) {
|
if(childNode.nodeType == 1) {
|
// attributes are type 1 children with one type 3 child
|
children = childNode.childNodes;
|
for(i=0; i<children.length; ++i) {
|
child = children[i];
|
if(child.nodeType == 1) {
|
grandchildren = child.childNodes;
|
if(grandchildren.length == 1) {
|
grandchild = grandchildren[0];
|
if(grandchild.nodeType == 3 ||
|
grandchild.nodeType == 4) {
|
name = (child.prefix) ?
|
child.nodeName.split(":")[1] :
|
child.nodeName;
|
value = grandchild.nodeValue.replace(
|
this.regExes.trimSpace, "");
|
attributes[name] = value;
|
}
|
} else {
|
// If child has no childNodes (grandchildren),
|
// set an attribute with null value.
|
// e.g. <prefix:fieldname/> becomes
|
// {fieldname: null}
|
attributes[child.nodeName.split(":").pop()] = null;
|
}
|
}
|
}
|
break;
|
}
|
childNode = childNode.nextSibling;
|
}
|
return attributes;
|
},
|
|
/**
|
* APIMethod: write
|
* Generate a GML document string given a list of features.
|
*
|
* Parameters:
|
* features - {Array(<OpenLayers.Feature.Vector>)} List of features to
|
* serialize into a string.
|
*
|
* Returns:
|
* {String} A string representing the GML document.
|
*/
|
write: function(features) {
|
if(!(OpenLayers.Util.isArray(features))) {
|
features = [features];
|
}
|
var gml = this.createElementNS("http://www.opengis.net/wfs",
|
"wfs:" + this.collectionName);
|
for(var i=0; i<features.length; i++) {
|
gml.appendChild(this.createFeatureXML(features[i]));
|
}
|
return OpenLayers.Format.XML.prototype.write.apply(this, [gml]);
|
},
|
|
/**
|
* Method: createFeatureXML
|
* Accept an OpenLayers.Feature.Vector, and build a GML node for it.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>} The feature to be built as GML.
|
*
|
* Returns:
|
* {DOMElement} A node reprensting the feature in GML.
|
*/
|
createFeatureXML: function(feature) {
|
var geometry = feature.geometry;
|
var geometryNode = this.buildGeometryNode(geometry);
|
var geomContainer = this.createElementNS(this.featureNS,
|
this.featurePrefix + ":" +
|
this.geometryName);
|
geomContainer.appendChild(geometryNode);
|
var featureNode = this.createElementNS(this.gmlns,
|
"gml:" + this.featureName);
|
var featureContainer = this.createElementNS(this.featureNS,
|
this.featurePrefix + ":" +
|
this.layerName);
|
var fid = feature.fid || feature.id;
|
featureContainer.setAttribute("fid", fid);
|
featureContainer.appendChild(geomContainer);
|
for(var attr in feature.attributes) {
|
var attrText = this.createTextNode(feature.attributes[attr]);
|
var nodename = attr.substring(attr.lastIndexOf(":") + 1);
|
var attrContainer = this.createElementNS(this.featureNS,
|
this.featurePrefix + ":" +
|
nodename);
|
attrContainer.appendChild(attrText);
|
featureContainer.appendChild(attrContainer);
|
}
|
featureNode.appendChild(featureContainer);
|
return featureNode;
|
},
|
|
/**
|
* APIMethod: buildGeometryNode
|
*/
|
buildGeometryNode: function(geometry) {
|
if (this.externalProjection && this.internalProjection) {
|
geometry = geometry.clone();
|
geometry.transform(this.internalProjection,
|
this.externalProjection);
|
}
|
var className = geometry.CLASS_NAME;
|
var type = className.substring(className.lastIndexOf(".") + 1);
|
var builder = this.buildGeometry[type.toLowerCase()];
|
return builder.apply(this, [geometry]);
|
},
|
|
/**
|
* Property: buildGeometry
|
* Object containing methods to do the actual geometry node building
|
* based on geometry type.
|
*/
|
buildGeometry: {
|
// TBD retrieve the srs from layer
|
// srsName is non-standard, so not including it until it's right.
|
// gml.setAttribute("srsName",
|
// "http://www.opengis.net/gml/srs/epsg.xml#4326");
|
|
/**
|
* Method: buildGeometry.point
|
* Given an OpenLayers point geometry, create a GML point.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry.Point>} A point geometry.
|
*
|
* Returns:
|
* {DOMElement} A GML point node.
|
*/
|
point: function(geometry) {
|
var gml = this.createElementNS(this.gmlns, "gml:Point");
|
gml.appendChild(this.buildCoordinatesNode(geometry));
|
return gml;
|
},
|
|
/**
|
* Method: buildGeometry.multipoint
|
* Given an OpenLayers multipoint geometry, create a GML multipoint.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry.MultiPoint>} A multipoint geometry.
|
*
|
* Returns:
|
* {DOMElement} A GML multipoint node.
|
*/
|
multipoint: function(geometry) {
|
var gml = this.createElementNS(this.gmlns, "gml:MultiPoint");
|
var points = geometry.components;
|
var pointMember, pointGeom;
|
for(var i=0; i<points.length; i++) {
|
pointMember = this.createElementNS(this.gmlns,
|
"gml:pointMember");
|
pointGeom = this.buildGeometry.point.apply(this,
|
[points[i]]);
|
pointMember.appendChild(pointGeom);
|
gml.appendChild(pointMember);
|
}
|
return gml;
|
},
|
|
/**
|
* Method: buildGeometry.linestring
|
* Given an OpenLayers linestring geometry, create a GML linestring.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry.LineString>} A linestring geometry.
|
*
|
* Returns:
|
* {DOMElement} A GML linestring node.
|
*/
|
linestring: function(geometry) {
|
var gml = this.createElementNS(this.gmlns, "gml:LineString");
|
gml.appendChild(this.buildCoordinatesNode(geometry));
|
return gml;
|
},
|
|
/**
|
* Method: buildGeometry.multilinestring
|
* Given an OpenLayers multilinestring geometry, create a GML
|
* multilinestring.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry.MultiLineString>} A multilinestring
|
* geometry.
|
*
|
* Returns:
|
* {DOMElement} A GML multilinestring node.
|
*/
|
multilinestring: function(geometry) {
|
var gml = this.createElementNS(this.gmlns, "gml:MultiLineString");
|
var lines = geometry.components;
|
var lineMember, lineGeom;
|
for(var i=0; i<lines.length; ++i) {
|
lineMember = this.createElementNS(this.gmlns,
|
"gml:lineStringMember");
|
lineGeom = this.buildGeometry.linestring.apply(this,
|
[lines[i]]);
|
lineMember.appendChild(lineGeom);
|
gml.appendChild(lineMember);
|
}
|
return gml;
|
},
|
|
/**
|
* Method: buildGeometry.linearring
|
* Given an OpenLayers linearring geometry, create a GML linearring.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry.LinearRing>} A linearring geometry.
|
*
|
* Returns:
|
* {DOMElement} A GML linearring node.
|
*/
|
linearring: function(geometry) {
|
var gml = this.createElementNS(this.gmlns, "gml:LinearRing");
|
gml.appendChild(this.buildCoordinatesNode(geometry));
|
return gml;
|
},
|
|
/**
|
* Method: buildGeometry.polygon
|
* Given an OpenLayers polygon geometry, create a GML polygon.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry.Polygon>} A polygon geometry.
|
*
|
* Returns:
|
* {DOMElement} A GML polygon node.
|
*/
|
polygon: function(geometry) {
|
var gml = this.createElementNS(this.gmlns, "gml:Polygon");
|
var rings = geometry.components;
|
var ringMember, ringGeom, type;
|
for(var i=0; i<rings.length; ++i) {
|
type = (i==0) ? "outerBoundaryIs" : "innerBoundaryIs";
|
ringMember = this.createElementNS(this.gmlns,
|
"gml:" + type);
|
ringGeom = this.buildGeometry.linearring.apply(this,
|
[rings[i]]);
|
ringMember.appendChild(ringGeom);
|
gml.appendChild(ringMember);
|
}
|
return gml;
|
},
|
|
/**
|
* Method: buildGeometry.multipolygon
|
* Given an OpenLayers multipolygon geometry, create a GML multipolygon.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry.MultiPolygon>} A multipolygon
|
* geometry.
|
*
|
* Returns:
|
* {DOMElement} A GML multipolygon node.
|
*/
|
multipolygon: function(geometry) {
|
var gml = this.createElementNS(this.gmlns, "gml:MultiPolygon");
|
var polys = geometry.components;
|
var polyMember, polyGeom;
|
for(var i=0; i<polys.length; ++i) {
|
polyMember = this.createElementNS(this.gmlns,
|
"gml:polygonMember");
|
polyGeom = this.buildGeometry.polygon.apply(this,
|
[polys[i]]);
|
polyMember.appendChild(polyGeom);
|
gml.appendChild(polyMember);
|
}
|
return gml;
|
|
},
|
|
/**
|
* Method: buildGeometry.bounds
|
* Given an OpenLayers bounds, create a GML box.
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Geometry.Bounds>} A bounds object.
|
*
|
* Returns:
|
* {DOMElement} A GML box node.
|
*/
|
bounds: function(bounds) {
|
var gml = this.createElementNS(this.gmlns, "gml:Box");
|
gml.appendChild(this.buildCoordinatesNode(bounds));
|
return gml;
|
}
|
},
|
|
/**
|
* Method: buildCoordinates
|
* builds the coordinates XmlNode
|
* (code)
|
* <gml:coordinates decimal="." cs="," ts=" ">...</gml:coordinates>
|
* (end)
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {XmlNode} created xmlNode
|
*/
|
buildCoordinatesNode: function(geometry) {
|
var coordinatesNode = this.createElementNS(this.gmlns,
|
"gml:coordinates");
|
coordinatesNode.setAttribute("decimal", ".");
|
coordinatesNode.setAttribute("cs", ",");
|
coordinatesNode.setAttribute("ts", " ");
|
|
var parts = [];
|
|
if(geometry instanceof OpenLayers.Bounds){
|
parts.push(geometry.left + "," + geometry.bottom);
|
parts.push(geometry.right + "," + geometry.top);
|
} else {
|
var points = (geometry.components) ? geometry.components : [geometry];
|
for(var i=0; i<points.length; i++) {
|
parts.push(points[i].x + "," + points[i].y);
|
}
|
}
|
|
var txtNode = this.createTextNode(parts.join(" "));
|
coordinatesNode.appendChild(txtNode);
|
|
return coordinatesNode;
|
},
|
|
CLASS_NAME: "OpenLayers.Format.GML"
|
});
|
/* ======================================================================
|
OpenLayers/Format/GML/Base.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML.js
|
* @requires OpenLayers/Format/GML.js
|
*/
|
|
/**
|
* Though required in the full build, if the GML format is excluded, we set
|
* the namespace here.
|
*/
|
if(!OpenLayers.Format.GML) {
|
OpenLayers.Format.GML = {};
|
}
|
|
/**
|
* Class: OpenLayers.Format.GML.Base
|
* Superclass for GML parsers.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.GML.Base = OpenLayers.Class(OpenLayers.Format.XML, {
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
gml: "http://www.opengis.net/gml",
|
xlink: "http://www.w3.org/1999/xlink",
|
xsi: "http://www.w3.org/2001/XMLSchema-instance",
|
wfs: "http://www.opengis.net/wfs" // this is a convenience for reading wfs:FeatureCollection
|
},
|
|
/**
|
* Property: defaultPrefix
|
*/
|
defaultPrefix: "gml",
|
|
/**
|
* Property: schemaLocation
|
* {String} Schema location for a particular minor version.
|
*/
|
schemaLocation: null,
|
|
/**
|
* APIProperty: featureType
|
* {Array(String) or String} The local (without prefix) feature typeName(s).
|
*/
|
featureType: null,
|
|
/**
|
* APIProperty: featureNS
|
* {String} The feature namespace. Must be set in the options at
|
* construction.
|
*/
|
featureNS: null,
|
|
/**
|
* APIProperty: geometry
|
* {String} Name of geometry element. Defaults to "geometry". If null, it
|
* will be set on <read> when the first geometry is parsed.
|
*/
|
geometryName: "geometry",
|
|
/**
|
* APIProperty: extractAttributes
|
* {Boolean} Extract attributes from GML. Default is true.
|
*/
|
extractAttributes: true,
|
|
/**
|
* APIProperty: srsName
|
* {String} URI for spatial reference system. This is optional for
|
* single part geometries and mandatory for collections and multis.
|
* If set, the srsName attribute will be written for all geometries.
|
* Default is null.
|
*/
|
srsName: null,
|
|
/**
|
* APIProperty: xy
|
* {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
|
* Changing is not recommended, a new Format should be instantiated.
|
*/
|
xy: true,
|
|
/**
|
* Property: geometryTypes
|
* {Object} Maps OpenLayers geometry class names to GML element names.
|
* Use <setGeometryTypes> before accessing this property.
|
*/
|
geometryTypes: null,
|
|
/**
|
* Property: singleFeatureType
|
* {Boolean} True if there is only 1 featureType, and not an array
|
* of featuretypes.
|
*/
|
singleFeatureType: null,
|
|
/**
|
* Property: autoConfig
|
* {Boolean} Indicates if the format was configured without a <featureNS>,
|
* but auto-configured <featureNS> and <featureType> during read.
|
* Subclasses making use of <featureType> auto-configuration should make
|
* the first call to the <readNode> method (usually in the read method)
|
* with true as 3rd argument, so the auto-configured featureType can be
|
* reset and the format can be reused for subsequent reads with data from
|
* different featureTypes. Set to false after read if you want to keep the
|
* auto-configured values.
|
*/
|
|
/**
|
* Property: regExes
|
* Compiled regular expressions for manipulating strings.
|
*/
|
regExes: {
|
trimSpace: (/^\s*|\s*$/g),
|
removeSpace: (/\s*/g),
|
splitSpace: (/\s+/),
|
trimComma: (/\s*,\s*/g),
|
featureMember: (/^(.*:)?featureMembers?$/)
|
},
|
|
/**
|
* Constructor: OpenLayers.Format.GML.Base
|
* Instances of this class are not created directly. Use the
|
* <OpenLayers.Format.GML.v2> or <OpenLayers.Format.GML.v3> constructor
|
* instead.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*
|
* Valid options properties:
|
* featureType - {Array(String) or String} Local (without prefix) feature
|
* typeName(s) (required for write).
|
* featureNS - {String} Feature namespace (required for write).
|
* geometryName - {String} Geometry element name (required for write).
|
*/
|
initialize: function(options) {
|
OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
|
this.setGeometryTypes();
|
if(options && options.featureNS) {
|
this.setNamespace("feature", options.featureNS);
|
}
|
this.singleFeatureType = !options || (typeof options.featureType === "string");
|
},
|
|
/**
|
* Method: read
|
*
|
* Parameters:
|
* data - {DOMElement} A gml:featureMember element, a gml:featureMembers
|
* element, or an element containing either of the above at any level.
|
*
|
* Returns:
|
* {Array(<OpenLayers.Feature.Vector>)} An array of features.
|
*/
|
read: function(data) {
|
if(typeof data == "string") {
|
data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
|
}
|
if(data && data.nodeType == 9) {
|
data = data.documentElement;
|
}
|
var features = [];
|
this.readNode(data, {features: features}, true);
|
if(features.length == 0) {
|
// look for gml:featureMember elements
|
var elements = this.getElementsByTagNameNS(
|
data, this.namespaces.gml, "featureMember"
|
);
|
if(elements.length) {
|
for(var i=0, len=elements.length; i<len; ++i) {
|
this.readNode(elements[i], {features: features}, true);
|
}
|
} else {
|
// look for gml:featureMembers elements (this is v3, but does no harm here)
|
var elements = this.getElementsByTagNameNS(
|
data, this.namespaces.gml, "featureMembers"
|
);
|
if(elements.length) {
|
// there can be only one
|
this.readNode(elements[0], {features: features}, true);
|
}
|
}
|
}
|
return features;
|
},
|
|
/**
|
* Method: readNode
|
* Shorthand for applying one of the named readers given the node
|
* namespace and local name. Readers take two args (node, obj) and
|
* generally extend or modify the second.
|
*
|
* Parameters:
|
* node - {DOMElement} The node to be read (required).
|
* obj - {Object} The object to be modified (optional).
|
* first - {Boolean} Should be set to true for the first node read. This
|
* is usually the readNode call in the read method. Without this being
|
* set, auto-configured properties will stick on subsequent reads.
|
*
|
* Returns:
|
* {Object} The input object, modified (or a new one if none was provided).
|
*/
|
readNode: function(node, obj, first) {
|
// on subsequent calls of format.read(), we want to reset auto-
|
// configured properties and auto-configure again.
|
if (first === true && this.autoConfig === true) {
|
this.featureType = null;
|
delete this.namespaceAlias[this.featureNS];
|
delete this.namespaces["feature"];
|
this.featureNS = null;
|
}
|
// featureType auto-configuration
|
if (!this.featureNS && (!(node.prefix in this.namespaces) &&
|
node.parentNode.namespaceURI == this.namespaces["gml"] &&
|
this.regExes.featureMember.test(node.parentNode.nodeName))) {
|
this.featureType = node.nodeName.split(":").pop();
|
this.setNamespace("feature", node.namespaceURI);
|
this.featureNS = node.namespaceURI;
|
this.autoConfig = true;
|
}
|
return OpenLayers.Format.XML.prototype.readNode.apply(this, [node, obj]);
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"gml": {
|
"_inherit": function(node, obj, container) {
|
// To be implemented by version specific parsers
|
},
|
"featureMember": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"featureMembers": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"name": function(node, obj) {
|
obj.name = this.getChildValue(node);
|
},
|
"boundedBy": function(node, obj) {
|
var container = {};
|
this.readChildNodes(node, container);
|
if(container.components && container.components.length > 0) {
|
obj.bounds = container.components[0];
|
}
|
},
|
"Point": function(node, container) {
|
var obj = {points: []};
|
this.readChildNodes(node, obj);
|
if(!container.components) {
|
container.components = [];
|
}
|
container.components.push(obj.points[0]);
|
},
|
"coordinates": function(node, obj) {
|
var str = this.getChildValue(node).replace(
|
this.regExes.trimSpace, ""
|
);
|
str = str.replace(this.regExes.trimComma, ",");
|
var pointList = str.split(this.regExes.splitSpace);
|
var coords;
|
var numPoints = pointList.length;
|
var points = new Array(numPoints);
|
for(var i=0; i<numPoints; ++i) {
|
coords = pointList[i].split(",");
|
if (this.xy) {
|
points[i] = new OpenLayers.Geometry.Point(
|
coords[0], coords[1], coords[2]
|
);
|
} else {
|
points[i] = new OpenLayers.Geometry.Point(
|
coords[1], coords[0], coords[2]
|
);
|
}
|
}
|
obj.points = points;
|
},
|
"coord": function(node, obj) {
|
var coord = {};
|
this.readChildNodes(node, coord);
|
if(!obj.points) {
|
obj.points = [];
|
}
|
obj.points.push(new OpenLayers.Geometry.Point(
|
coord.x, coord.y, coord.z
|
));
|
},
|
"X": function(node, coord) {
|
coord.x = this.getChildValue(node);
|
},
|
"Y": function(node, coord) {
|
coord.y = this.getChildValue(node);
|
},
|
"Z": function(node, coord) {
|
coord.z = this.getChildValue(node);
|
},
|
"MultiPoint": function(node, container) {
|
var obj = {components: []};
|
this.readers.gml._inherit.apply(this, [node, obj, container]);
|
this.readChildNodes(node, obj);
|
container.components = [
|
new OpenLayers.Geometry.MultiPoint(obj.components)
|
];
|
},
|
"pointMember": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"LineString": function(node, container) {
|
var obj = {};
|
this.readers.gml._inherit.apply(this, [node, obj, container]);
|
this.readChildNodes(node, obj);
|
if(!container.components) {
|
container.components = [];
|
}
|
container.components.push(
|
new OpenLayers.Geometry.LineString(obj.points)
|
);
|
},
|
"MultiLineString": function(node, container) {
|
var obj = {components: []};
|
this.readers.gml._inherit.apply(this, [node, obj, container]);
|
this.readChildNodes(node, obj);
|
container.components = [
|
new OpenLayers.Geometry.MultiLineString(obj.components)
|
];
|
},
|
"lineStringMember": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"Polygon": function(node, container) {
|
var obj = {outer: null, inner: []};
|
this.readers.gml._inherit.apply(this, [node, obj, container]);
|
this.readChildNodes(node, obj);
|
obj.inner.unshift(obj.outer);
|
if(!container.components) {
|
container.components = [];
|
}
|
container.components.push(
|
new OpenLayers.Geometry.Polygon(obj.inner)
|
);
|
},
|
"LinearRing": function(node, obj) {
|
var container = {};
|
this.readers.gml._inherit.apply(this, [node, container]);
|
this.readChildNodes(node, container);
|
obj.components = [new OpenLayers.Geometry.LinearRing(
|
container.points
|
)];
|
},
|
"MultiPolygon": function(node, container) {
|
var obj = {components: []};
|
this.readers.gml._inherit.apply(this, [node, obj, container]);
|
this.readChildNodes(node, obj);
|
container.components = [
|
new OpenLayers.Geometry.MultiPolygon(obj.components)
|
];
|
},
|
"polygonMember": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"GeometryCollection": function(node, container) {
|
var obj = {components: []};
|
this.readers.gml._inherit.apply(this, [node, obj, container]);
|
this.readChildNodes(node, obj);
|
container.components = [
|
new OpenLayers.Geometry.Collection(obj.components)
|
];
|
},
|
"geometryMember": function(node, obj) {
|
this.readChildNodes(node, obj);
|
}
|
},
|
"feature": {
|
"*": function(node, obj) {
|
// The node can either be named like the featureType, or it
|
// can be a child of the feature:featureType. Children can be
|
// geometry or attributes.
|
var name;
|
var local = node.localName || node.nodeName.split(":").pop();
|
// Since an attribute can have the same name as the feature type
|
// we only want to read the node as a feature if the parent
|
// node can have feature nodes as children. In this case, the
|
// obj.features property is set.
|
if (obj.features) {
|
if (!this.singleFeatureType &&
|
(OpenLayers.Util.indexOf(this.featureType, local) !== -1)) {
|
name = "_typeName";
|
} else if(local === this.featureType) {
|
name = "_typeName";
|
}
|
} else {
|
// Assume attribute elements have one child node and that the child
|
// is a text node. Otherwise assume it is a geometry node.
|
if(node.childNodes.length == 0 ||
|
(node.childNodes.length == 1 && node.firstChild.nodeType == 3)) {
|
if(this.extractAttributes) {
|
name = "_attribute";
|
}
|
} else {
|
name = "_geometry";
|
}
|
}
|
if(name) {
|
this.readers.feature[name].apply(this, [node, obj]);
|
}
|
},
|
"_typeName": function(node, obj) {
|
var container = {components: [], attributes: {}};
|
this.readChildNodes(node, container);
|
// look for common gml namespaced elements
|
if(container.name) {
|
container.attributes.name = container.name;
|
}
|
var feature = new OpenLayers.Feature.Vector(
|
container.components[0], container.attributes
|
);
|
if (!this.singleFeatureType) {
|
feature.type = node.nodeName.split(":").pop();
|
feature.namespace = node.namespaceURI;
|
}
|
var fid = node.getAttribute("fid") ||
|
this.getAttributeNS(node, this.namespaces["gml"], "id");
|
if(fid) {
|
feature.fid = fid;
|
}
|
if(this.internalProjection && this.externalProjection &&
|
feature.geometry) {
|
feature.geometry.transform(
|
this.externalProjection, this.internalProjection
|
);
|
}
|
if(container.bounds) {
|
feature.bounds = container.bounds;
|
}
|
obj.features.push(feature);
|
},
|
"_geometry": function(node, obj) {
|
if (!this.geometryName) {
|
this.geometryName = node.nodeName.split(":").pop();
|
}
|
this.readChildNodes(node, obj);
|
},
|
"_attribute": function(node, obj) {
|
var local = node.localName || node.nodeName.split(":").pop();
|
var value = this.getChildValue(node);
|
obj.attributes[local] = value;
|
}
|
},
|
"wfs": {
|
"FeatureCollection": function(node, obj) {
|
this.readChildNodes(node, obj);
|
}
|
}
|
},
|
|
/**
|
* Method: write
|
*
|
* Parameters:
|
* features - {Array(<OpenLayers.Feature.Vector>) | OpenLayers.Feature.Vector}
|
* An array of features or a single feature.
|
*
|
* Returns:
|
* {String} Given an array of features, a doc with a gml:featureMembers
|
* element will be returned. Given a single feature, a doc with a
|
* gml:featureMember element will be returned.
|
*/
|
write: function(features) {
|
var name;
|
if(OpenLayers.Util.isArray(features)) {
|
name = "featureMembers";
|
} else {
|
name = "featureMember";
|
}
|
var root = this.writeNode("gml:" + name, features);
|
this.setAttributeNS(
|
root, this.namespaces["xsi"],
|
"xsi:schemaLocation", this.schemaLocation
|
);
|
|
return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
|
},
|
|
/**
|
* Property: writers
|
* As a compliment to the readers property, this structure contains public
|
* writing functions grouped by namespace alias and named like the
|
* node names they produce.
|
*/
|
writers: {
|
"gml": {
|
"featureMember": function(feature) {
|
var node = this.createElementNSPlus("gml:featureMember");
|
this.writeNode("feature:_typeName", feature, node);
|
return node;
|
},
|
"MultiPoint": function(geometry) {
|
var node = this.createElementNSPlus("gml:MultiPoint");
|
var components = geometry.components || [geometry];
|
for(var i=0, ii=components.length; i<ii; ++i) {
|
this.writeNode("pointMember", components[i], node);
|
}
|
return node;
|
},
|
"pointMember": function(geometry) {
|
var node = this.createElementNSPlus("gml:pointMember");
|
this.writeNode("Point", geometry, node);
|
return node;
|
},
|
"MultiLineString": function(geometry) {
|
var node = this.createElementNSPlus("gml:MultiLineString");
|
var components = geometry.components || [geometry];
|
for(var i=0, ii=components.length; i<ii; ++i) {
|
this.writeNode("lineStringMember", components[i], node);
|
}
|
return node;
|
},
|
"lineStringMember": function(geometry) {
|
var node = this.createElementNSPlus("gml:lineStringMember");
|
this.writeNode("LineString", geometry, node);
|
return node;
|
},
|
"MultiPolygon": function(geometry) {
|
var node = this.createElementNSPlus("gml:MultiPolygon");
|
var components = geometry.components || [geometry];
|
for(var i=0, ii=components.length; i<ii; ++i) {
|
this.writeNode(
|
"polygonMember", components[i], node
|
);
|
}
|
return node;
|
},
|
"polygonMember": function(geometry) {
|
var node = this.createElementNSPlus("gml:polygonMember");
|
this.writeNode("Polygon", geometry, node);
|
return node;
|
},
|
"GeometryCollection": function(geometry) {
|
var node = this.createElementNSPlus("gml:GeometryCollection");
|
for(var i=0, len=geometry.components.length; i<len; ++i) {
|
this.writeNode("geometryMember", geometry.components[i], node);
|
}
|
return node;
|
},
|
"geometryMember": function(geometry) {
|
var node = this.createElementNSPlus("gml:geometryMember");
|
var child = this.writeNode("feature:_geometry", geometry);
|
node.appendChild(child.firstChild);
|
return node;
|
}
|
},
|
"feature": {
|
"_typeName": function(feature) {
|
var node = this.createElementNSPlus("feature:" + this.featureType, {
|
attributes: {fid: feature.fid}
|
});
|
if(feature.geometry) {
|
this.writeNode("feature:_geometry", feature.geometry, node);
|
}
|
for(var name in feature.attributes) {
|
var value = feature.attributes[name];
|
if(value != null) {
|
this.writeNode(
|
"feature:_attribute",
|
{name: name, value: value}, node
|
);
|
}
|
}
|
return node;
|
},
|
"_geometry": function(geometry) {
|
if(this.externalProjection && this.internalProjection) {
|
geometry = geometry.clone().transform(
|
this.internalProjection, this.externalProjection
|
);
|
}
|
var node = this.createElementNSPlus(
|
"feature:" + this.geometryName
|
);
|
var type = this.geometryTypes[geometry.CLASS_NAME];
|
var child = this.writeNode("gml:" + type, geometry, node);
|
if(this.srsName) {
|
child.setAttribute("srsName", this.srsName);
|
}
|
return node;
|
},
|
"_attribute": function(obj) {
|
return this.createElementNSPlus("feature:" + obj.name, {
|
value: obj.value
|
});
|
}
|
},
|
"wfs": {
|
"FeatureCollection": function(features) {
|
/**
|
* This is only here because GML2 only describes abstract
|
* feature collections. Typically, you would not be using
|
* the GML format to write wfs elements. This just provides
|
* some way to write out lists of features. GML3 defines the
|
* featureMembers element, so that is used by default instead.
|
*/
|
var node = this.createElementNSPlus("wfs:FeatureCollection");
|
for(var i=0, len=features.length; i<len; ++i) {
|
this.writeNode("gml:featureMember", features[i], node);
|
}
|
return node;
|
}
|
}
|
},
|
|
/**
|
* Method: setGeometryTypes
|
* Sets the <geometryTypes> mapping.
|
*/
|
setGeometryTypes: function() {
|
this.geometryTypes = {
|
"OpenLayers.Geometry.Point": "Point",
|
"OpenLayers.Geometry.MultiPoint": "MultiPoint",
|
"OpenLayers.Geometry.LineString": "LineString",
|
"OpenLayers.Geometry.MultiLineString": "MultiLineString",
|
"OpenLayers.Geometry.Polygon": "Polygon",
|
"OpenLayers.Geometry.MultiPolygon": "MultiPolygon",
|
"OpenLayers.Geometry.Collection": "GeometryCollection"
|
};
|
},
|
|
CLASS_NAME: "OpenLayers.Format.GML.Base"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/GML/v3.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/GML/Base.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.GML.v3
|
* Parses GML version 3.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.GML.Base>
|
*/
|
OpenLayers.Format.GML.v3 = OpenLayers.Class(OpenLayers.Format.GML.Base, {
|
|
/**
|
* Property: schemaLocation
|
* {String} Schema location for a particular minor version. The writers
|
* conform with the Simple Features Profile for GML.
|
*/
|
schemaLocation: "http://www.opengis.net/gml http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsf.xsd",
|
|
/**
|
* Property: curve
|
* {Boolean} Write gml:Curve instead of gml:LineString elements. This also
|
* affects the elements in multi-part geometries. Default is false.
|
* To write gml:Curve elements instead of gml:LineString, set curve
|
* to true in the options to the contstructor (cannot be changed after
|
* instantiation).
|
*/
|
curve: false,
|
|
/**
|
* Property: multiCurve
|
* {Boolean} Write gml:MultiCurve instead of gml:MultiLineString. Since
|
* the latter is deprecated in GML 3, the default is true. To write
|
* gml:MultiLineString instead of gml:MultiCurve, set multiCurve to
|
* false in the options to the constructor (cannot be changed after
|
* instantiation).
|
*/
|
multiCurve: true,
|
|
/**
|
* Property: surface
|
* {Boolean} Write gml:Surface instead of gml:Polygon elements. This also
|
* affects the elements in multi-part geometries. Default is false.
|
* To write gml:Surface elements instead of gml:Polygon, set surface
|
* to true in the options to the contstructor (cannot be changed after
|
* instantiation).
|
*/
|
surface: false,
|
|
/**
|
* Property: multiSurface
|
* {Boolean} Write gml:multiSurface instead of gml:MultiPolygon. Since
|
* the latter is deprecated in GML 3, the default is true. To write
|
* gml:MultiPolygon instead of gml:multiSurface, set multiSurface to
|
* false in the options to the constructor (cannot be changed after
|
* instantiation).
|
*/
|
multiSurface: true,
|
|
/**
|
* Constructor: OpenLayers.Format.GML.v3
|
* Create a parser for GML v3.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*
|
* Valid options properties:
|
* featureType - {String} Local (without prefix) feature typeName (required).
|
* featureNS - {String} Feature namespace (required).
|
* geometryName - {String} Geometry element name.
|
*/
|
initialize: function(options) {
|
OpenLayers.Format.GML.Base.prototype.initialize.apply(this, [options]);
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"gml": OpenLayers.Util.applyDefaults({
|
"_inherit": function(node, obj, container) {
|
// SRSReferenceGroup attributes
|
var dim = parseInt(node.getAttribute("srsDimension"), 10) ||
|
(container && container.srsDimension);
|
if (dim) {
|
obj.srsDimension = dim;
|
}
|
},
|
"featureMembers": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"Curve": function(node, container) {
|
var obj = {points: []};
|
this.readers.gml._inherit.apply(this, [node, obj, container]);
|
this.readChildNodes(node, obj);
|
if(!container.components) {
|
container.components = [];
|
}
|
container.components.push(
|
new OpenLayers.Geometry.LineString(obj.points)
|
);
|
},
|
"segments": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"LineStringSegment": function(node, container) {
|
var obj = {};
|
this.readChildNodes(node, obj);
|
if(obj.points) {
|
Array.prototype.push.apply(container.points, obj.points);
|
}
|
},
|
"pos": function(node, obj) {
|
var str = this.getChildValue(node).replace(
|
this.regExes.trimSpace, ""
|
);
|
var coords = str.split(this.regExes.splitSpace);
|
var point;
|
if(this.xy) {
|
point = new OpenLayers.Geometry.Point(
|
coords[0], coords[1], coords[2]
|
);
|
} else {
|
point = new OpenLayers.Geometry.Point(
|
coords[1], coords[0], coords[2]
|
);
|
}
|
obj.points = [point];
|
},
|
"posList": function(node, obj) {
|
var str = this.getChildValue(node).replace(
|
this.regExes.trimSpace, ""
|
);
|
var coords = str.split(this.regExes.splitSpace);
|
// The "dimension" attribute is from the GML 3.0.1 spec.
|
var dim = obj.srsDimension ||
|
parseInt(node.getAttribute("srsDimension") || node.getAttribute("dimension"), 10) || 2;
|
var j, x, y, z;
|
var numPoints = coords.length / dim;
|
var points = new Array(numPoints);
|
for(var i=0, len=coords.length; i<len; i += dim) {
|
x = coords[i];
|
y = coords[i+1];
|
z = (dim == 2) ? undefined : coords[i+2];
|
if (this.xy) {
|
points[i/dim] = new OpenLayers.Geometry.Point(x, y, z);
|
} else {
|
points[i/dim] = new OpenLayers.Geometry.Point(y, x, z);
|
}
|
}
|
obj.points = points;
|
},
|
"Surface": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"patches": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"PolygonPatch": function(node, obj) {
|
this.readers.gml.Polygon.apply(this, [node, obj]);
|
},
|
"exterior": function(node, container) {
|
var obj = {};
|
this.readChildNodes(node, obj);
|
container.outer = obj.components[0];
|
},
|
"interior": function(node, container) {
|
var obj = {};
|
this.readChildNodes(node, obj);
|
container.inner.push(obj.components[0]);
|
},
|
"MultiCurve": function(node, container) {
|
var obj = {components: []};
|
this.readers.gml._inherit.apply(this, [node, obj, container]);
|
this.readChildNodes(node, obj);
|
if(obj.components.length > 0) {
|
container.components = [
|
new OpenLayers.Geometry.MultiLineString(obj.components)
|
];
|
}
|
},
|
"curveMember": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"MultiSurface": function(node, container) {
|
var obj = {components: []};
|
this.readers.gml._inherit.apply(this, [node, obj, container]);
|
this.readChildNodes(node, obj);
|
if(obj.components.length > 0) {
|
container.components = [
|
new OpenLayers.Geometry.MultiPolygon(obj.components)
|
];
|
}
|
},
|
"surfaceMember": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"surfaceMembers": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"pointMembers": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"lineStringMembers": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"polygonMembers": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"geometryMembers": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"Envelope": function(node, container) {
|
var obj = {points: new Array(2)};
|
this.readChildNodes(node, obj);
|
if(!container.components) {
|
container.components = [];
|
}
|
var min = obj.points[0];
|
var max = obj.points[1];
|
container.components.push(
|
new OpenLayers.Bounds(min.x, min.y, max.x, max.y)
|
);
|
},
|
"lowerCorner": function(node, container) {
|
var obj = {};
|
this.readers.gml.pos.apply(this, [node, obj]);
|
container.points[0] = obj.points[0];
|
},
|
"upperCorner": function(node, container) {
|
var obj = {};
|
this.readers.gml.pos.apply(this, [node, obj]);
|
container.points[1] = obj.points[0];
|
}
|
}, OpenLayers.Format.GML.Base.prototype.readers["gml"]),
|
"feature": OpenLayers.Format.GML.Base.prototype.readers["feature"],
|
"wfs": OpenLayers.Format.GML.Base.prototype.readers["wfs"]
|
},
|
|
/**
|
* Method: write
|
*
|
* Parameters:
|
* features - {Array(<OpenLayers.Feature.Vector>) | OpenLayers.Feature.Vector}
|
* An array of features or a single feature.
|
*
|
* Returns:
|
* {String} Given an array of features, a doc with a gml:featureMembers
|
* element will be returned. Given a single feature, a doc with a
|
* gml:featureMember element will be returned.
|
*/
|
write: function(features) {
|
var name;
|
if(OpenLayers.Util.isArray(features)) {
|
name = "featureMembers";
|
} else {
|
name = "featureMember";
|
}
|
var root = this.writeNode("gml:" + name, features);
|
this.setAttributeNS(
|
root, this.namespaces["xsi"],
|
"xsi:schemaLocation", this.schemaLocation
|
);
|
|
return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
|
},
|
|
/**
|
* Property: writers
|
* As a compliment to the readers property, this structure contains public
|
* writing functions grouped by namespace alias and named like the
|
* node names they produce.
|
*/
|
writers: {
|
"gml": OpenLayers.Util.applyDefaults({
|
"featureMembers": function(features) {
|
var node = this.createElementNSPlus("gml:featureMembers");
|
for(var i=0, len=features.length; i<len; ++i) {
|
this.writeNode("feature:_typeName", features[i], node);
|
}
|
return node;
|
},
|
"Point": function(geometry) {
|
var node = this.createElementNSPlus("gml:Point");
|
this.writeNode("pos", geometry, node);
|
return node;
|
},
|
"pos": function(point) {
|
// only 2d for simple features profile
|
var pos = (this.xy) ?
|
(point.x + " " + point.y) : (point.y + " " + point.x);
|
return this.createElementNSPlus("gml:pos", {
|
value: pos
|
});
|
},
|
"LineString": function(geometry) {
|
var node = this.createElementNSPlus("gml:LineString");
|
this.writeNode("posList", geometry.components, node);
|
return node;
|
},
|
"Curve": function(geometry) {
|
var node = this.createElementNSPlus("gml:Curve");
|
this.writeNode("segments", geometry, node);
|
return node;
|
},
|
"segments": function(geometry) {
|
var node = this.createElementNSPlus("gml:segments");
|
this.writeNode("LineStringSegment", geometry, node);
|
return node;
|
},
|
"LineStringSegment": function(geometry) {
|
var node = this.createElementNSPlus("gml:LineStringSegment");
|
this.writeNode("posList", geometry.components, node);
|
return node;
|
},
|
"posList": function(points) {
|
// only 2d for simple features profile
|
var len = points.length;
|
var parts = new Array(len);
|
var point;
|
for(var i=0; i<len; ++i) {
|
point = points[i];
|
if(this.xy) {
|
parts[i] = point.x + " " + point.y;
|
} else {
|
parts[i] = point.y + " " + point.x;
|
}
|
}
|
return this.createElementNSPlus("gml:posList", {
|
value: parts.join(" ")
|
});
|
},
|
"Surface": function(geometry) {
|
var node = this.createElementNSPlus("gml:Surface");
|
this.writeNode("patches", geometry, node);
|
return node;
|
},
|
"patches": function(geometry) {
|
var node = this.createElementNSPlus("gml:patches");
|
this.writeNode("PolygonPatch", geometry, node);
|
return node;
|
},
|
"PolygonPatch": function(geometry) {
|
var node = this.createElementNSPlus("gml:PolygonPatch", {
|
attributes: {interpolation: "planar"}
|
});
|
this.writeNode("exterior", geometry.components[0], node);
|
for(var i=1, len=geometry.components.length; i<len; ++i) {
|
this.writeNode(
|
"interior", geometry.components[i], node
|
);
|
}
|
return node;
|
},
|
"Polygon": function(geometry) {
|
var node = this.createElementNSPlus("gml:Polygon");
|
this.writeNode("exterior", geometry.components[0], node);
|
for(var i=1, len=geometry.components.length; i<len; ++i) {
|
this.writeNode(
|
"interior", geometry.components[i], node
|
);
|
}
|
return node;
|
},
|
"exterior": function(ring) {
|
var node = this.createElementNSPlus("gml:exterior");
|
this.writeNode("LinearRing", ring, node);
|
return node;
|
},
|
"interior": function(ring) {
|
var node = this.createElementNSPlus("gml:interior");
|
this.writeNode("LinearRing", ring, node);
|
return node;
|
},
|
"LinearRing": function(ring) {
|
var node = this.createElementNSPlus("gml:LinearRing");
|
this.writeNode("posList", ring.components, node);
|
return node;
|
},
|
"MultiCurve": function(geometry) {
|
var node = this.createElementNSPlus("gml:MultiCurve");
|
var components = geometry.components || [geometry];
|
for(var i=0, len=components.length; i<len; ++i) {
|
this.writeNode("curveMember", components[i], node);
|
}
|
return node;
|
},
|
"curveMember": function(geometry) {
|
var node = this.createElementNSPlus("gml:curveMember");
|
if(this.curve) {
|
this.writeNode("Curve", geometry, node);
|
} else {
|
this.writeNode("LineString", geometry, node);
|
}
|
return node;
|
},
|
"MultiSurface": function(geometry) {
|
var node = this.createElementNSPlus("gml:MultiSurface");
|
var components = geometry.components || [geometry];
|
for(var i=0, len=components.length; i<len; ++i) {
|
this.writeNode("surfaceMember", components[i], node);
|
}
|
return node;
|
},
|
"surfaceMember": function(polygon) {
|
var node = this.createElementNSPlus("gml:surfaceMember");
|
if(this.surface) {
|
this.writeNode("Surface", polygon, node);
|
} else {
|
this.writeNode("Polygon", polygon, node);
|
}
|
return node;
|
},
|
"Envelope": function(bounds) {
|
var node = this.createElementNSPlus("gml:Envelope");
|
this.writeNode("lowerCorner", bounds, node);
|
this.writeNode("upperCorner", bounds, node);
|
// srsName attribute is required for gml:Envelope
|
if(this.srsName) {
|
node.setAttribute("srsName", this.srsName);
|
}
|
return node;
|
},
|
"lowerCorner": function(bounds) {
|
// only 2d for simple features profile
|
var pos = (this.xy) ?
|
(bounds.left + " " + bounds.bottom) :
|
(bounds.bottom + " " + bounds.left);
|
return this.createElementNSPlus("gml:lowerCorner", {
|
value: pos
|
});
|
},
|
"upperCorner": function(bounds) {
|
// only 2d for simple features profile
|
var pos = (this.xy) ?
|
(bounds.right + " " + bounds.top) :
|
(bounds.top + " " + bounds.right);
|
return this.createElementNSPlus("gml:upperCorner", {
|
value: pos
|
});
|
}
|
}, OpenLayers.Format.GML.Base.prototype.writers["gml"]),
|
"feature": OpenLayers.Format.GML.Base.prototype.writers["feature"],
|
"wfs": OpenLayers.Format.GML.Base.prototype.writers["wfs"]
|
},
|
|
/**
|
* Method: setGeometryTypes
|
* Sets the <geometryTypes> mapping.
|
*/
|
setGeometryTypes: function() {
|
this.geometryTypes = {
|
"OpenLayers.Geometry.Point": "Point",
|
"OpenLayers.Geometry.MultiPoint": "MultiPoint",
|
"OpenLayers.Geometry.LineString": (this.curve === true) ? "Curve": "LineString",
|
"OpenLayers.Geometry.MultiLineString": (this.multiCurve === false) ? "MultiLineString" : "MultiCurve",
|
"OpenLayers.Geometry.Polygon": (this.surface === true) ? "Surface" : "Polygon",
|
"OpenLayers.Geometry.MultiPolygon": (this.multiSurface === false) ? "MultiPolygon" : "MultiSurface",
|
"OpenLayers.Geometry.Collection": "GeometryCollection"
|
};
|
},
|
|
CLASS_NAME: "OpenLayers.Format.GML.v3"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/Filter/v1_1_0.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/Filter/v1.js
|
* @requires OpenLayers/Format/GML/v3.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.Filter.v1_1_0
|
* Write ogc:Filter version 1.1.0.
|
*
|
* Differences from the v1.0.0 parser:
|
* - uses GML v3 instead of GML v2
|
* - reads matchCase attribute on ogc:PropertyIsEqual and
|
* ogc:PropertyIsNotEqual elements.
|
* - writes matchCase attribute from comparison filters of type EQUAL_TO,
|
* NOT_EQUAL_TO and LIKE.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.GML.v3>
|
* - <OpenLayers.Format.Filter.v1>
|
*/
|
OpenLayers.Format.Filter.v1_1_0 = OpenLayers.Class(
|
OpenLayers.Format.GML.v3, OpenLayers.Format.Filter.v1, {
|
|
/**
|
* Constant: VERSION
|
* {String} 1.1.0
|
*/
|
VERSION: "1.1.0",
|
|
/**
|
* Property: schemaLocation
|
* {String} http://www.opengis.net/ogc/filter/1.1.0/filter.xsd
|
*/
|
schemaLocation: "http://www.opengis.net/ogc/filter/1.1.0/filter.xsd",
|
|
/**
|
* Constructor: OpenLayers.Format.Filter.v1_1_0
|
* Instances of this class are not created directly. Use the
|
* <OpenLayers.Format.Filter> constructor instead.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
initialize: function(options) {
|
OpenLayers.Format.GML.v3.prototype.initialize.apply(
|
this, [options]
|
);
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"ogc": OpenLayers.Util.applyDefaults({
|
"PropertyIsEqualTo": function(node, obj) {
|
var matchCase = node.getAttribute("matchCase");
|
var filter = new OpenLayers.Filter.Comparison({
|
type: OpenLayers.Filter.Comparison.EQUAL_TO,
|
matchCase: !(matchCase === "false" || matchCase === "0")
|
});
|
this.readChildNodes(node, filter);
|
obj.filters.push(filter);
|
},
|
"PropertyIsNotEqualTo": function(node, obj) {
|
var matchCase = node.getAttribute("matchCase");
|
var filter = new OpenLayers.Filter.Comparison({
|
type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO,
|
matchCase: !(matchCase === "false" || matchCase === "0")
|
});
|
this.readChildNodes(node, filter);
|
obj.filters.push(filter);
|
},
|
"PropertyIsLike": function(node, obj) {
|
var filter = new OpenLayers.Filter.Comparison({
|
type: OpenLayers.Filter.Comparison.LIKE
|
});
|
this.readChildNodes(node, filter);
|
var wildCard = node.getAttribute("wildCard");
|
var singleChar = node.getAttribute("singleChar");
|
var esc = node.getAttribute("escapeChar");
|
filter.value2regex(wildCard, singleChar, esc);
|
obj.filters.push(filter);
|
}
|
}, OpenLayers.Format.Filter.v1.prototype.readers["ogc"]),
|
"gml": OpenLayers.Format.GML.v3.prototype.readers["gml"],
|
"feature": OpenLayers.Format.GML.v3.prototype.readers["feature"]
|
},
|
|
/**
|
* Property: writers
|
* As a compliment to the readers property, this structure contains public
|
* writing functions grouped by namespace alias and named like the
|
* node names they produce.
|
*/
|
writers: {
|
"ogc": OpenLayers.Util.applyDefaults({
|
"PropertyIsEqualTo": function(filter) {
|
var node = this.createElementNSPlus("ogc:PropertyIsEqualTo", {
|
attributes: {matchCase: filter.matchCase}
|
});
|
// no ogc:expression handling for PropertyName for now
|
this.writeNode("PropertyName", filter, node);
|
// handle Literals or Functions for now
|
this.writeOgcExpression(filter.value, node);
|
return node;
|
},
|
"PropertyIsNotEqualTo": function(filter) {
|
var node = this.createElementNSPlus("ogc:PropertyIsNotEqualTo", {
|
attributes: {matchCase: filter.matchCase}
|
});
|
// no ogc:expression handling for PropertyName for now
|
this.writeNode("PropertyName", filter, node);
|
// handle Literals or Functions for now
|
this.writeOgcExpression(filter.value, node);
|
return node;
|
},
|
"PropertyIsLike": function(filter) {
|
var node = this.createElementNSPlus("ogc:PropertyIsLike", {
|
attributes: {
|
matchCase: filter.matchCase,
|
wildCard: "*", singleChar: ".", escapeChar: "!"
|
}
|
});
|
// no ogc:expression handling for now
|
this.writeNode("PropertyName", filter, node);
|
// convert regex string to ogc string
|
this.writeNode("Literal", filter.regex2value(), node);
|
return node;
|
},
|
"BBOX": function(filter) {
|
var node = this.createElementNSPlus("ogc:BBOX");
|
// PropertyName is optional in 1.1.0
|
filter.property && this.writeNode("PropertyName", filter, node);
|
var box = this.writeNode("gml:Envelope", filter.value);
|
if(filter.projection) {
|
box.setAttribute("srsName", filter.projection);
|
}
|
node.appendChild(box);
|
return node;
|
},
|
"SortBy": function(sortProperties) {
|
var node = this.createElementNSPlus("ogc:SortBy");
|
for (var i=0,l=sortProperties.length;i<l;i++) {
|
this.writeNode(
|
"ogc:SortProperty",
|
sortProperties[i],
|
node
|
);
|
}
|
return node;
|
},
|
"SortProperty": function(sortProperty) {
|
var node = this.createElementNSPlus("ogc:SortProperty");
|
this.writeNode(
|
"ogc:PropertyName",
|
sortProperty,
|
node
|
);
|
this.writeNode(
|
"ogc:SortOrder",
|
(sortProperty.order == 'DESC') ? 'DESC' : 'ASC',
|
node
|
);
|
return node;
|
},
|
"SortOrder": function(value) {
|
var node = this.createElementNSPlus("ogc:SortOrder", {
|
value: value
|
});
|
return node;
|
}
|
}, OpenLayers.Format.Filter.v1.prototype.writers["ogc"]),
|
"gml": OpenLayers.Format.GML.v3.prototype.writers["gml"],
|
"feature": OpenLayers.Format.GML.v3.prototype.writers["feature"]
|
},
|
|
/**
|
* Method: writeSpatial
|
*
|
* Read a {<OpenLayers.Filter.Spatial>} filter and converts it into XML.
|
*
|
* Parameters:
|
* filter - {<OpenLayers.Filter.Spatial>} The filter.
|
* name - {String} Name of the generated XML element.
|
*
|
* Returns:
|
* {DOMElement} The created XML element.
|
*/
|
writeSpatial: function(filter, name) {
|
var node = this.createElementNSPlus("ogc:"+name);
|
this.writeNode("PropertyName", filter, node);
|
if(filter.value instanceof OpenLayers.Filter.Function) {
|
this.writeNode("Function", filter.value, node);
|
} else {
|
var child;
|
if(filter.value instanceof OpenLayers.Geometry) {
|
child = this.writeNode("feature:_geometry", filter.value).firstChild;
|
} else {
|
child = this.writeNode("gml:Envelope", filter.value);
|
}
|
if(filter.projection) {
|
child.setAttribute("srsName", filter.projection);
|
}
|
node.appendChild(child);
|
}
|
return node;
|
},
|
|
CLASS_NAME: "OpenLayers.Format.Filter.v1_1_0"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/OWSCommon.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML/VersionedOGC.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.OWSCommon
|
* Read OWSCommon. Create a new instance with the <OpenLayers.Format.OWSCommon>
|
* constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML.VersionedOGC>
|
*/
|
OpenLayers.Format.OWSCommon = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
|
|
/**
|
* APIProperty: defaultVersion
|
* {String} Version number to assume if none found. Default is "1.0.0".
|
*/
|
defaultVersion: "1.0.0",
|
|
/**
|
* Constructor: OpenLayers.Format.OWSCommon
|
* Create a new parser for OWSCommon.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* Method: getVersion
|
* Returns the version to use. Subclasses can override this function
|
* if a different version detection is needed.
|
*
|
* Parameters:
|
* root - {DOMElement}
|
* options - {Object} Optional configuration object.
|
*
|
* Returns:
|
* {String} The version to use.
|
*/
|
getVersion: function(root, options) {
|
var version = this.version;
|
if(!version) {
|
// remember version does not correspond to the OWS version
|
// it corresponds to the WMS/WFS/WCS etc. request version
|
var uri = root.getAttribute("xmlns:ows");
|
// the above will fail if the namespace prefix is different than
|
// ows and if the namespace is declared on a different element
|
if (uri && uri.substring(uri.lastIndexOf("/")+1) === "1.1") {
|
version ="1.1.0";
|
}
|
if(!version) {
|
version = this.defaultVersion;
|
}
|
}
|
return version;
|
},
|
|
/**
|
* APIMethod: read
|
* Read an OWSCommon document and return an object.
|
*
|
* Parameters:
|
* data - {String | DOMElement} Data to read.
|
* options - {Object} Options for the reader.
|
*
|
* Returns:
|
* {Object} An object representing the structure of the document.
|
*/
|
|
CLASS_NAME: "OpenLayers.Format.OWSCommon"
|
});
|
/* ======================================================================
|
OpenLayers/Format/OWSCommon/v1.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/OWSCommon.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.OWSCommon.v1
|
* Common readers and writers for OWSCommon v1.X formats
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.OWSCommon.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
|
|
/**
|
* Property: regExes
|
* Compiled regular expressions for manipulating strings.
|
*/
|
regExes: {
|
trimSpace: (/^\s*|\s*$/g),
|
removeSpace: (/\s*/g),
|
splitSpace: (/\s+/),
|
trimComma: (/\s*,\s*/g)
|
},
|
|
/**
|
* Method: read
|
*
|
* Parameters:
|
* data - {DOMElement} An OWSCommon document element.
|
* options - {Object} Options for the reader.
|
*
|
* Returns:
|
* {Object} An object representing the OWSCommon document.
|
*/
|
read: function(data, options) {
|
options = OpenLayers.Util.applyDefaults(options, this.options);
|
var ows = {};
|
this.readChildNodes(data, ows);
|
return ows;
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"ows": {
|
"Exception": function(node, exceptionReport) {
|
var exception = {
|
code: node.getAttribute('exceptionCode'),
|
locator: node.getAttribute('locator'),
|
texts: []
|
};
|
exceptionReport.exceptions.push(exception);
|
this.readChildNodes(node, exception);
|
},
|
"ExceptionText": function(node, exception) {
|
var text = this.getChildValue(node);
|
exception.texts.push(text);
|
},
|
"ServiceIdentification": function(node, obj) {
|
obj.serviceIdentification = {};
|
this.readChildNodes(node, obj.serviceIdentification);
|
},
|
"Title": function(node, obj) {
|
obj.title = this.getChildValue(node);
|
},
|
"Abstract": function(node, serviceIdentification) {
|
serviceIdentification["abstract"] = this.getChildValue(node);
|
},
|
"Keywords": function(node, serviceIdentification) {
|
serviceIdentification.keywords = {};
|
this.readChildNodes(node, serviceIdentification.keywords);
|
},
|
"Keyword": function(node, keywords) {
|
keywords[this.getChildValue(node)] = true;
|
},
|
"ServiceType": function(node, serviceIdentification) {
|
serviceIdentification.serviceType = {
|
codeSpace: node.getAttribute('codeSpace'),
|
value: this.getChildValue(node)};
|
},
|
"ServiceTypeVersion": function(node, serviceIdentification) {
|
serviceIdentification.serviceTypeVersion = this.getChildValue(node);
|
},
|
"Fees": function(node, serviceIdentification) {
|
serviceIdentification.fees = this.getChildValue(node);
|
},
|
"AccessConstraints": function(node, serviceIdentification) {
|
serviceIdentification.accessConstraints =
|
this.getChildValue(node);
|
},
|
"ServiceProvider": function(node, obj) {
|
obj.serviceProvider = {};
|
this.readChildNodes(node, obj.serviceProvider);
|
},
|
"ProviderName": function(node, serviceProvider) {
|
serviceProvider.providerName = this.getChildValue(node);
|
},
|
"ProviderSite": function(node, serviceProvider) {
|
serviceProvider.providerSite = this.getAttributeNS(node,
|
this.namespaces.xlink, "href");
|
},
|
"ServiceContact": function(node, serviceProvider) {
|
serviceProvider.serviceContact = {};
|
this.readChildNodes(node, serviceProvider.serviceContact);
|
},
|
"IndividualName": function(node, serviceContact) {
|
serviceContact.individualName = this.getChildValue(node);
|
},
|
"PositionName": function(node, serviceContact) {
|
serviceContact.positionName = this.getChildValue(node);
|
},
|
"ContactInfo": function(node, serviceContact) {
|
serviceContact.contactInfo = {};
|
this.readChildNodes(node, serviceContact.contactInfo);
|
},
|
"Phone": function(node, contactInfo) {
|
contactInfo.phone = {};
|
this.readChildNodes(node, contactInfo.phone);
|
},
|
"Voice": function(node, phone) {
|
phone.voice = this.getChildValue(node);
|
},
|
"Address": function(node, contactInfo) {
|
contactInfo.address = {};
|
this.readChildNodes(node, contactInfo.address);
|
},
|
"DeliveryPoint": function(node, address) {
|
address.deliveryPoint = this.getChildValue(node);
|
},
|
"City": function(node, address) {
|
address.city = this.getChildValue(node);
|
},
|
"AdministrativeArea": function(node, address) {
|
address.administrativeArea = this.getChildValue(node);
|
},
|
"PostalCode": function(node, address) {
|
address.postalCode = this.getChildValue(node);
|
},
|
"Country": function(node, address) {
|
address.country = this.getChildValue(node);
|
},
|
"ElectronicMailAddress": function(node, address) {
|
address.electronicMailAddress = this.getChildValue(node);
|
},
|
"Role": function(node, serviceContact) {
|
serviceContact.role = this.getChildValue(node);
|
},
|
"OperationsMetadata": function(node, obj) {
|
obj.operationsMetadata = {};
|
this.readChildNodes(node, obj.operationsMetadata);
|
},
|
"Operation": function(node, operationsMetadata) {
|
var name = node.getAttribute("name");
|
operationsMetadata[name] = {};
|
this.readChildNodes(node, operationsMetadata[name]);
|
},
|
"DCP": function(node, operation) {
|
operation.dcp = {};
|
this.readChildNodes(node, operation.dcp);
|
},
|
"HTTP": function(node, dcp) {
|
dcp.http = {};
|
this.readChildNodes(node, dcp.http);
|
},
|
"Get": function(node, http) {
|
if (!http.get) {
|
http.get = [];
|
}
|
var obj = {
|
url: this.getAttributeNS(node, this.namespaces.xlink, "href")
|
};
|
this.readChildNodes(node, obj);
|
http.get.push(obj);
|
},
|
"Post": function(node, http) {
|
if (!http.post) {
|
http.post = [];
|
}
|
var obj = {
|
url: this.getAttributeNS(node, this.namespaces.xlink, "href")
|
};
|
this.readChildNodes(node, obj);
|
http.post.push(obj);
|
},
|
"Parameter": function(node, operation) {
|
if (!operation.parameters) {
|
operation.parameters = {};
|
}
|
var name = node.getAttribute("name");
|
operation.parameters[name] = {};
|
this.readChildNodes(node, operation.parameters[name]);
|
},
|
"Constraint": function(node, obj) {
|
if (!obj.constraints) {
|
obj.constraints = {};
|
}
|
var name = node.getAttribute("name");
|
obj.constraints[name] = {};
|
this.readChildNodes(node, obj.constraints[name]);
|
},
|
"Value": function(node, allowedValues) {
|
allowedValues[this.getChildValue(node)] = true;
|
},
|
"OutputFormat": function(node, obj) {
|
obj.formats.push({value: this.getChildValue(node)});
|
this.readChildNodes(node, obj);
|
},
|
"WGS84BoundingBox": function(node, obj) {
|
var boundingBox = {};
|
boundingBox.crs = node.getAttribute("crs");
|
if (obj.BoundingBox) {
|
obj.BoundingBox.push(boundingBox);
|
} else {
|
obj.projection = boundingBox.crs;
|
boundingBox = obj;
|
}
|
this.readChildNodes(node, boundingBox);
|
},
|
"BoundingBox": function(node, obj) {
|
// FIXME: We consider that BoundingBox is the same as WGS84BoundingBox
|
// LowerCorner = "min_x min_y"
|
// UpperCorner = "max_x max_y"
|
// It should normally depend on the projection
|
this.readers['ows']['WGS84BoundingBox'].apply(this, [node, obj]);
|
},
|
"LowerCorner": function(node, obj) {
|
var str = this.getChildValue(node).replace(
|
this.regExes.trimSpace, "");
|
str = str.replace(this.regExes.trimComma, ",");
|
var pointList = str.split(this.regExes.splitSpace);
|
obj.left = pointList[0];
|
obj.bottom = pointList[1];
|
},
|
"UpperCorner": function(node, obj) {
|
var str = this.getChildValue(node).replace(
|
this.regExes.trimSpace, "");
|
str = str.replace(this.regExes.trimComma, ",");
|
var pointList = str.split(this.regExes.splitSpace);
|
obj.right = pointList[0];
|
obj.top = pointList[1];
|
obj.bounds = new OpenLayers.Bounds(obj.left, obj.bottom,
|
obj.right, obj.top);
|
delete obj.left;
|
delete obj.bottom;
|
delete obj.right;
|
delete obj.top;
|
},
|
"Language": function(node, obj) {
|
obj.language = this.getChildValue(node);
|
}
|
}
|
},
|
|
/**
|
* Property: writers
|
* As a compliment to the readers property, this structure contains public
|
* writing functions grouped by namespace alias and named like the
|
* node names they produce.
|
*/
|
writers: {
|
"ows": {
|
"BoundingBox": function(options, nodeName) {
|
var node = this.createElementNSPlus(nodeName || "ows:BoundingBox", {
|
attributes: {
|
crs: options.projection
|
}
|
});
|
this.writeNode("ows:LowerCorner", options, node);
|
this.writeNode("ows:UpperCorner", options, node);
|
return node;
|
},
|
"LowerCorner": function(options) {
|
var node = this.createElementNSPlus("ows:LowerCorner", {
|
value: options.bounds.left + " " + options.bounds.bottom });
|
return node;
|
},
|
"UpperCorner": function(options) {
|
var node = this.createElementNSPlus("ows:UpperCorner", {
|
value: options.bounds.right + " " + options.bounds.top });
|
return node;
|
},
|
"Identifier": function(identifier) {
|
var node = this.createElementNSPlus("ows:Identifier", {
|
value: identifier });
|
return node;
|
},
|
"Title": function(title) {
|
var node = this.createElementNSPlus("ows:Title", {
|
value: title });
|
return node;
|
},
|
"Abstract": function(abstractValue) {
|
var node = this.createElementNSPlus("ows:Abstract", {
|
value: abstractValue });
|
return node;
|
},
|
"OutputFormat": function(format) {
|
var node = this.createElementNSPlus("ows:OutputFormat", {
|
value: format });
|
return node;
|
}
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Format.OWSCommon.v1"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/OWSCommon/v1_0_0.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/OWSCommon/v1.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.OWSCommon.v1_0_0
|
* Parser for OWS Common version 1.0.0.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.OWSCommon.v1>
|
*/
|
OpenLayers.Format.OWSCommon.v1_0_0 = OpenLayers.Class(OpenLayers.Format.OWSCommon.v1, {
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
ows: "http://www.opengis.net/ows",
|
xlink: "http://www.w3.org/1999/xlink"
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"ows": OpenLayers.Util.applyDefaults({
|
"ExceptionReport": function(node, obj) {
|
obj.success = false;
|
obj.exceptionReport = {
|
version: node.getAttribute('version'),
|
language: node.getAttribute('language'),
|
exceptions: []
|
};
|
this.readChildNodes(node, obj.exceptionReport);
|
}
|
}, OpenLayers.Format.OWSCommon.v1.prototype.readers.ows)
|
},
|
|
/**
|
* Property: writers
|
* As a compliment to the readers property, this structure contains public
|
* writing functions grouped by namespace alias and named like the
|
* node names they produce.
|
*/
|
writers: {
|
"ows": OpenLayers.Format.OWSCommon.v1.prototype.writers.ows
|
},
|
|
CLASS_NAME: "OpenLayers.Format.OWSCommon.v1_0_0"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/WFST/v1_1_0.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/WFST/v1.js
|
* @requires OpenLayers/Format/Filter/v1_1_0.js
|
* @requires OpenLayers/Format/OWSCommon/v1_0_0.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WFST.v1_1_0
|
* A format for creating WFS v1.1.0 transactions. Create a new instance with the
|
* <OpenLayers.Format.WFST.v1_1_0> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.Filter.v1_1_0>
|
* - <OpenLayers.Format.WFST.v1>
|
*/
|
OpenLayers.Format.WFST.v1_1_0 = OpenLayers.Class(
|
OpenLayers.Format.Filter.v1_1_0, OpenLayers.Format.WFST.v1, {
|
|
/**
|
* Property: version
|
* {String} WFS version number.
|
*/
|
version: "1.1.0",
|
|
/**
|
* Property: schemaLocations
|
* {Object} Properties are namespace aliases, values are schema locations.
|
*/
|
schemaLocations: {
|
"wfs": "http://schemas.opengis.net/wfs/1.1.0/wfs.xsd"
|
},
|
|
/**
|
* Constructor: OpenLayers.Format.WFST.v1_1_0
|
* A class for parsing and generating WFS v1.1.0 transactions.
|
*
|
* To read additional information like hit count (numberOfFeatures) from
|
* the FeatureCollection, call the <OpenLayers.Format.WFST.v1.read> method
|
* with {output: "object"} as 2nd argument. Note that it is possible to
|
* just request the hit count from a WFS 1.1.0 server with the
|
* resultType="hits" request parameter.
|
*
|
* Parameters:
|
* options - {Object} Optional object whose properties will be set on the
|
* instance.
|
*
|
* Valid options properties:
|
* featureType - {String} Local (without prefix) feature typeName (required).
|
* featureNS - {String} Feature namespace (optional).
|
* featurePrefix - {String} Feature namespace alias (optional - only used
|
* if featureNS is provided). Default is 'feature'.
|
* geometryName - {String} Name of geometry attribute. Default is 'the_geom'.
|
*/
|
initialize: function(options) {
|
OpenLayers.Format.Filter.v1_1_0.prototype.initialize.apply(this, [options]);
|
OpenLayers.Format.WFST.v1.prototype.initialize.apply(this, [options]);
|
},
|
|
/**
|
* Method: readNode
|
* Shorthand for applying one of the named readers given the node
|
* namespace and local name. Readers take two args (node, obj) and
|
* generally extend or modify the second.
|
*
|
* Parameters:
|
* node - {DOMElement} The node to be read (required).
|
* obj - {Object} The object to be modified (optional).
|
* first - {Boolean} Should be set to true for the first node read. This
|
* is usually the readNode call in the read method. Without this being
|
* set, auto-configured properties will stick on subsequent reads.
|
*
|
* Returns:
|
* {Object} The input object, modified (or a new one if none was provided).
|
*/
|
readNode: function(node, obj, first) {
|
// Not the superclass, only the mixin classes inherit from
|
// Format.GML.v3. We need this because we don't want to get readNode
|
// from the superclass's superclass, which is OpenLayers.Format.XML.
|
return OpenLayers.Format.GML.v3.prototype.readNode.apply(this, arguments);
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"wfs": OpenLayers.Util.applyDefaults({
|
"FeatureCollection": function(node, obj) {
|
obj.numberOfFeatures = parseInt(node.getAttribute(
|
"numberOfFeatures"));
|
OpenLayers.Format.WFST.v1.prototype.readers["wfs"]["FeatureCollection"].apply(
|
this, arguments);
|
},
|
"TransactionResponse": function(node, obj) {
|
obj.insertIds = [];
|
obj.success = false;
|
this.readChildNodes(node, obj);
|
},
|
"TransactionSummary": function(node, obj) {
|
// this is a limited test of success
|
obj.success = true;
|
},
|
"InsertResults": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"Feature": function(node, container) {
|
var obj = {fids: []};
|
this.readChildNodes(node, obj);
|
container.insertIds.push(obj.fids[0]);
|
}
|
}, OpenLayers.Format.WFST.v1.prototype.readers["wfs"]),
|
"gml": OpenLayers.Format.GML.v3.prototype.readers["gml"],
|
"feature": OpenLayers.Format.GML.v3.prototype.readers["feature"],
|
"ogc": OpenLayers.Format.Filter.v1_1_0.prototype.readers["ogc"],
|
"ows": OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers["ows"]
|
},
|
|
/**
|
* Property: writers
|
* As a compliment to the readers property, this structure contains public
|
* writing functions grouped by namespace alias and named like the
|
* node names they produce.
|
*/
|
writers: {
|
"wfs": OpenLayers.Util.applyDefaults({
|
"GetFeature": function(options) {
|
var node = OpenLayers.Format.WFST.v1.prototype.writers["wfs"]["GetFeature"].apply(this, arguments);
|
options && this.setAttributes(node, {
|
resultType: options.resultType,
|
startIndex: options.startIndex,
|
count: options.count
|
});
|
return node;
|
},
|
"Query": function(options) {
|
options = OpenLayers.Util.extend({
|
featureNS: this.featureNS,
|
featurePrefix: this.featurePrefix,
|
featureType: this.featureType,
|
srsName: this.srsName
|
}, options);
|
var prefix = options.featurePrefix;
|
var node = this.createElementNSPlus("wfs:Query", {
|
attributes: {
|
typeName: (prefix ? prefix + ":" : "") +
|
options.featureType,
|
srsName: options.srsName
|
}
|
});
|
if(options.featureNS) {
|
node.setAttribute("xmlns:" + prefix, options.featureNS);
|
}
|
if(options.propertyNames) {
|
for(var i=0,len = options.propertyNames.length; i<len; i++) {
|
this.writeNode(
|
"wfs:PropertyName",
|
{property: options.propertyNames[i]},
|
node
|
);
|
}
|
}
|
if(options.filter) {
|
OpenLayers.Format.WFST.v1_1_0.prototype.setFilterProperty.call(this, options.filter);
|
this.writeNode("ogc:Filter", options.filter, node);
|
}
|
return node;
|
},
|
"PropertyName": function(obj) {
|
return this.createElementNSPlus("wfs:PropertyName", {
|
value: obj.property
|
});
|
}
|
}, OpenLayers.Format.WFST.v1.prototype.writers["wfs"]),
|
"gml": OpenLayers.Format.GML.v3.prototype.writers["gml"],
|
"feature": OpenLayers.Format.GML.v3.prototype.writers["feature"],
|
"ogc": OpenLayers.Format.Filter.v1_1_0.prototype.writers["ogc"]
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WFST.v1_1_0"
|
});
|
/* ======================================================================
|
OpenLayers/Protocol.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
*/
|
|
/**
|
* Class: OpenLayers.Protocol
|
* Abstract vector layer protocol class. Not to be instantiated directly. Use
|
* one of the protocol subclasses instead.
|
*/
|
OpenLayers.Protocol = OpenLayers.Class({
|
|
/**
|
* Property: format
|
* {<OpenLayers.Format>} The format used by this protocol.
|
*/
|
format: null,
|
|
/**
|
* Property: options
|
* {Object} Any options sent to the constructor.
|
*/
|
options: null,
|
|
/**
|
* Property: autoDestroy
|
* {Boolean} The creator of the protocol can set autoDestroy to false
|
* to fully control when the protocol is destroyed. Defaults to
|
* true.
|
*/
|
autoDestroy: true,
|
|
/**
|
* Property: defaultFilter
|
* {<OpenLayers.Filter>} Optional default filter to read requests
|
*/
|
defaultFilter: null,
|
|
/**
|
* Constructor: OpenLayers.Protocol
|
* Abstract class for vector protocols. Create instances of a subclass.
|
*
|
* Parameters:
|
* options - {Object} Optional object whose properties will be set on the
|
* instance.
|
*/
|
initialize: function(options) {
|
options = options || {};
|
OpenLayers.Util.extend(this, options);
|
this.options = options;
|
},
|
|
/**
|
* Method: mergeWithDefaultFilter
|
* Merge filter passed to the read method with the default one
|
*
|
* Parameters:
|
* filter - {<OpenLayers.Filter>}
|
*/
|
mergeWithDefaultFilter: function(filter) {
|
var merged;
|
if (filter && this.defaultFilter) {
|
merged = new OpenLayers.Filter.Logical({
|
type: OpenLayers.Filter.Logical.AND,
|
filters: [this.defaultFilter, filter]
|
});
|
} else {
|
merged = filter || this.defaultFilter || undefined;
|
}
|
return merged;
|
},
|
|
/**
|
* APIMethod: destroy
|
* Clean up the protocol.
|
*/
|
destroy: function() {
|
this.options = null;
|
this.format = null;
|
},
|
|
/**
|
* APIMethod: read
|
* Construct a request for reading new features.
|
*
|
* Parameters:
|
* options - {Object} Optional object for configuring the request.
|
*
|
* Returns:
|
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
|
* object, the same object will be passed to the callback function passed
|
* if one exists in the options object.
|
*/
|
read: function(options) {
|
options = options || {};
|
options.filter = this.mergeWithDefaultFilter(options.filter);
|
},
|
|
|
/**
|
* APIMethod: create
|
* Construct a request for writing newly created features.
|
*
|
* Parameters:
|
* features - {Array({<OpenLayers.Feature.Vector>})} or
|
* {<OpenLayers.Feature.Vector>}
|
* options - {Object} Optional object for configuring the request.
|
*
|
* Returns:
|
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
|
* object, the same object will be passed to the callback function passed
|
* if one exists in the options object.
|
*/
|
create: function() {
|
},
|
|
/**
|
* APIMethod: update
|
* Construct a request updating modified features.
|
*
|
* Parameters:
|
* features - {Array({<OpenLayers.Feature.Vector>})} or
|
* {<OpenLayers.Feature.Vector>}
|
* options - {Object} Optional object for configuring the request.
|
*
|
* Returns:
|
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
|
* object, the same object will be passed to the callback function passed
|
* if one exists in the options object.
|
*/
|
update: function() {
|
},
|
|
/**
|
* APIMethod: delete
|
* Construct a request deleting a removed feature.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
* options - {Object} Optional object for configuring the request.
|
*
|
* Returns:
|
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
|
* object, the same object will be passed to the callback function passed
|
* if one exists in the options object.
|
*/
|
"delete": function() {
|
},
|
|
/**
|
* APIMethod: commit
|
* Go over the features and for each take action
|
* based on the feature state. Possible actions are create,
|
* update and delete.
|
*
|
* Parameters:
|
* features - {Array({<OpenLayers.Feature.Vector>})}
|
* options - {Object} Object whose possible keys are "create", "update",
|
* "delete", "callback" and "scope", the values referenced by the
|
* first three are objects as passed to the "create", "update", and
|
* "delete" methods, the value referenced by the "callback" key is
|
* a function which is called when the commit operation is complete
|
* using the scope referenced by the "scope" key.
|
*
|
* Returns:
|
* {Array({<OpenLayers.Protocol.Response>})} An array of
|
* <OpenLayers.Protocol.Response> objects.
|
*/
|
commit: function() {
|
},
|
|
/**
|
* Method: abort
|
* Abort an ongoing request.
|
*
|
* Parameters:
|
* response - {<OpenLayers.Protocol.Response>}
|
*/
|
abort: function(response) {
|
},
|
|
/**
|
* Method: createCallback
|
* Returns a function that applies the given public method with resp and
|
* options arguments.
|
*
|
* Parameters:
|
* method - {Function} The method to be applied by the callback.
|
* response - {<OpenLayers.Protocol.Response>} The protocol response object.
|
* options - {Object} Options sent to the protocol method
|
*/
|
createCallback: function(method, response, options) {
|
return OpenLayers.Function.bind(function() {
|
method.apply(this, [response, options]);
|
}, this);
|
},
|
|
CLASS_NAME: "OpenLayers.Protocol"
|
});
|
|
/**
|
* Class: OpenLayers.Protocol.Response
|
* Protocols return Response objects to their users.
|
*/
|
OpenLayers.Protocol.Response = OpenLayers.Class({
|
/**
|
* Property: code
|
* {Number} - OpenLayers.Protocol.Response.SUCCESS or
|
* OpenLayers.Protocol.Response.FAILURE
|
*/
|
code: null,
|
|
/**
|
* Property: requestType
|
* {String} The type of request this response corresponds to. Either
|
* "create", "read", "update" or "delete".
|
*/
|
requestType: null,
|
|
/**
|
* Property: last
|
* {Boolean} - true if this is the last response expected in a commit,
|
* false otherwise, defaults to true.
|
*/
|
last: true,
|
|
/**
|
* Property: features
|
* {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}
|
* The features returned in the response by the server. Depending on the
|
* protocol's read payload, either features or data will be populated.
|
*/
|
features: null,
|
|
/**
|
* Property: data
|
* {Object}
|
* The data returned in the response by the server. Depending on the
|
* protocol's read payload, either features or data will be populated.
|
*/
|
data: null,
|
|
/**
|
* Property: reqFeatures
|
* {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}
|
* The features provided by the user and placed in the request by the
|
* protocol.
|
*/
|
reqFeatures: null,
|
|
/**
|
* Property: priv
|
*/
|
priv: null,
|
|
/**
|
* Property: error
|
* {Object} The error object in case a service exception was encountered.
|
*/
|
error: null,
|
|
/**
|
* Constructor: OpenLayers.Protocol.Response
|
*
|
* Parameters:
|
* options - {Object} Optional object whose properties will be set on the
|
* instance.
|
*/
|
initialize: function(options) {
|
OpenLayers.Util.extend(this, options);
|
},
|
|
/**
|
* Method: success
|
*
|
* Returns:
|
* {Boolean} - true on success, false otherwise
|
*/
|
success: function() {
|
return this.code > 0;
|
},
|
|
CLASS_NAME: "OpenLayers.Protocol.Response"
|
});
|
|
OpenLayers.Protocol.Response.SUCCESS = 1;
|
OpenLayers.Protocol.Response.FAILURE = 0;
|
/* ======================================================================
|
OpenLayers/Format/JSON.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* Note:
|
* This work draws heavily from the public domain JSON serializer/deserializer
|
* at http://www.json.org/json.js. Rewritten so that it doesn't modify
|
* basic data prototypes.
|
*/
|
|
/**
|
* @requires OpenLayers/Format.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.JSON
|
* A parser to read/write JSON safely. Create a new instance with the
|
* <OpenLayers.Format.JSON> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format>
|
*/
|
OpenLayers.Format.JSON = OpenLayers.Class(OpenLayers.Format, {
|
|
/**
|
* APIProperty: indent
|
* {String} For "pretty" printing, the indent string will be used once for
|
* each indentation level.
|
*/
|
indent: " ",
|
|
/**
|
* APIProperty: space
|
* {String} For "pretty" printing, the space string will be used after
|
* the ":" separating a name/value pair.
|
*/
|
space: " ",
|
|
/**
|
* APIProperty: newline
|
* {String} For "pretty" printing, the newline string will be used at the
|
* end of each name/value pair or array item.
|
*/
|
newline: "\n",
|
|
/**
|
* Property: level
|
* {Integer} For "pretty" printing, this is incremented/decremented during
|
* serialization.
|
*/
|
level: 0,
|
|
/**
|
* Property: pretty
|
* {Boolean} Serialize with extra whitespace for structure. This is set
|
* by the <write> method.
|
*/
|
pretty: false,
|
|
/**
|
* Property: nativeJSON
|
* {Boolean} Does the browser support native json?
|
*/
|
nativeJSON: (function() {
|
return !!(window.JSON && typeof JSON.parse == "function" && typeof JSON.stringify == "function");
|
})(),
|
|
/**
|
* Constructor: OpenLayers.Format.JSON
|
* Create a new parser for JSON.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* APIMethod: read
|
* Deserialize a json string.
|
*
|
* Parameters:
|
* json - {String} A JSON string
|
* filter - {Function} A function which will be called for every key and
|
* value at every level of the final result. Each value will be
|
* replaced by the result of the filter function. This can be used to
|
* reform generic objects into instances of classes, or to transform
|
* date strings into Date objects.
|
*
|
* Returns:
|
* {Object} An object, array, string, or number .
|
*/
|
read: function(json, filter) {
|
var object;
|
if (this.nativeJSON) {
|
object = JSON.parse(json, filter);
|
} else try {
|
/**
|
* Parsing happens in three stages. In the first stage, we run the
|
* text against a regular expression which looks for non-JSON
|
* characters. We are especially concerned with '()' and 'new'
|
* because they can cause invocation, and '=' because it can
|
* cause mutation. But just to be safe, we will reject all
|
* unexpected characters.
|
*/
|
if (/^[\],:{}\s]*$/.test(json.replace(/\\["\\\/bfnrtu]/g, '@').
|
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
|
replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
|
|
/**
|
* In the second stage we use the eval function to compile the
|
* text into a JavaScript structure. The '{' operator is
|
* subject to a syntactic ambiguity in JavaScript - it can
|
* begin a block or an object literal. We wrap the text in
|
* parens to eliminate the ambiguity.
|
*/
|
object = eval('(' + json + ')');
|
|
/**
|
* In the optional third stage, we recursively walk the new
|
* structure, passing each name/value pair to a filter
|
* function for possible transformation.
|
*/
|
if(typeof filter === 'function') {
|
function walk(k, v) {
|
if(v && typeof v === 'object') {
|
for(var i in v) {
|
if(v.hasOwnProperty(i)) {
|
v[i] = walk(i, v[i]);
|
}
|
}
|
}
|
return filter(k, v);
|
}
|
object = walk('', object);
|
}
|
}
|
} catch(e) {
|
// Fall through if the regexp test fails.
|
}
|
|
if(this.keepData) {
|
this.data = object;
|
}
|
|
return object;
|
},
|
|
/**
|
* APIMethod: write
|
* Serialize an object into a JSON string.
|
*
|
* Parameters:
|
* value - {String} The object, array, string, number, boolean or date
|
* to be serialized.
|
* pretty - {Boolean} Structure the output with newlines and indentation.
|
* Default is false.
|
*
|
* Returns:
|
* {String} The JSON string representation of the input value.
|
*/
|
write: function(value, pretty) {
|
this.pretty = !!pretty;
|
var json = null;
|
var type = typeof value;
|
if(this.serialize[type]) {
|
try {
|
json = (!this.pretty && this.nativeJSON) ?
|
JSON.stringify(value) :
|
this.serialize[type].apply(this, [value]);
|
} catch(err) {
|
OpenLayers.Console.error("Trouble serializing: " + err);
|
}
|
}
|
return json;
|
},
|
|
/**
|
* Method: writeIndent
|
* Output an indentation string depending on the indentation level.
|
*
|
* Returns:
|
* {String} An appropriate indentation string.
|
*/
|
writeIndent: function() {
|
var pieces = [];
|
if(this.pretty) {
|
for(var i=0; i<this.level; ++i) {
|
pieces.push(this.indent);
|
}
|
}
|
return pieces.join('');
|
},
|
|
/**
|
* Method: writeNewline
|
* Output a string representing a newline if in pretty printing mode.
|
*
|
* Returns:
|
* {String} A string representing a new line.
|
*/
|
writeNewline: function() {
|
return (this.pretty) ? this.newline : '';
|
},
|
|
/**
|
* Method: writeSpace
|
* Output a string representing a space if in pretty printing mode.
|
*
|
* Returns:
|
* {String} A space.
|
*/
|
writeSpace: function() {
|
return (this.pretty) ? this.space : '';
|
},
|
|
/**
|
* Property: serialize
|
* Object with properties corresponding to the serializable data types.
|
* Property values are functions that do the actual serializing.
|
*/
|
serialize: {
|
/**
|
* Method: serialize.object
|
* Transform an object into a JSON string.
|
*
|
* Parameters:
|
* object - {Object} The object to be serialized.
|
*
|
* Returns:
|
* {String} A JSON string representing the object.
|
*/
|
'object': function(object) {
|
// three special objects that we want to treat differently
|
if(object == null) {
|
return "null";
|
}
|
if(object.constructor == Date) {
|
return this.serialize.date.apply(this, [object]);
|
}
|
if(object.constructor == Array) {
|
return this.serialize.array.apply(this, [object]);
|
}
|
var pieces = ['{'];
|
this.level += 1;
|
var key, keyJSON, valueJSON;
|
|
var addComma = false;
|
for(key in object) {
|
if(object.hasOwnProperty(key)) {
|
// recursive calls need to allow for sub-classing
|
keyJSON = OpenLayers.Format.JSON.prototype.write.apply(this,
|
[key, this.pretty]);
|
valueJSON = OpenLayers.Format.JSON.prototype.write.apply(this,
|
[object[key], this.pretty]);
|
if(keyJSON != null && valueJSON != null) {
|
if(addComma) {
|
pieces.push(',');
|
}
|
pieces.push(this.writeNewline(), this.writeIndent(),
|
keyJSON, ':', this.writeSpace(), valueJSON);
|
addComma = true;
|
}
|
}
|
}
|
|
this.level -= 1;
|
pieces.push(this.writeNewline(), this.writeIndent(), '}');
|
return pieces.join('');
|
},
|
|
/**
|
* Method: serialize.array
|
* Transform an array into a JSON string.
|
*
|
* Parameters:
|
* array - {Array} The array to be serialized
|
*
|
* Returns:
|
* {String} A JSON string representing the array.
|
*/
|
'array': function(array) {
|
var json;
|
var pieces = ['['];
|
this.level += 1;
|
|
for(var i=0, len=array.length; i<len; ++i) {
|
// recursive calls need to allow for sub-classing
|
json = OpenLayers.Format.JSON.prototype.write.apply(this,
|
[array[i], this.pretty]);
|
if(json != null) {
|
if(i > 0) {
|
pieces.push(',');
|
}
|
pieces.push(this.writeNewline(), this.writeIndent(), json);
|
}
|
}
|
|
this.level -= 1;
|
pieces.push(this.writeNewline(), this.writeIndent(), ']');
|
return pieces.join('');
|
},
|
|
/**
|
* Method: serialize.string
|
* Transform a string into a JSON string.
|
*
|
* Parameters:
|
* string - {String} The string to be serialized
|
*
|
* Returns:
|
* {String} A JSON string representing the string.
|
*/
|
'string': function(string) {
|
// If the string contains no control characters, no quote characters, and no
|
// backslash characters, then we can simply slap some quotes around it.
|
// Otherwise we must also replace the offending characters with safe
|
// sequences.
|
var m = {
|
'\b': '\\b',
|
'\t': '\\t',
|
'\n': '\\n',
|
'\f': '\\f',
|
'\r': '\\r',
|
'"' : '\\"',
|
'\\': '\\\\'
|
};
|
if(/["\\\x00-\x1f]/.test(string)) {
|
return '"' + string.replace(/([\x00-\x1f\\"])/g, function(a, b) {
|
var c = m[b];
|
if(c) {
|
return c;
|
}
|
c = b.charCodeAt();
|
return '\\u00' +
|
Math.floor(c / 16).toString(16) +
|
(c % 16).toString(16);
|
}) + '"';
|
}
|
return '"' + string + '"';
|
},
|
|
/**
|
* Method: serialize.number
|
* Transform a number into a JSON string.
|
*
|
* Parameters:
|
* number - {Number} The number to be serialized.
|
*
|
* Returns:
|
* {String} A JSON string representing the number.
|
*/
|
'number': function(number) {
|
return isFinite(number) ? String(number) : "null";
|
},
|
|
/**
|
* Method: serialize.boolean
|
* Transform a boolean into a JSON string.
|
*
|
* Parameters:
|
* bool - {Boolean} The boolean to be serialized.
|
*
|
* Returns:
|
* {String} A JSON string representing the boolean.
|
*/
|
'boolean': function(bool) {
|
return String(bool);
|
},
|
|
/**
|
* Method: serialize.object
|
* Transform a date into a JSON string.
|
*
|
* Parameters:
|
* date - {Date} The date to be serialized.
|
*
|
* Returns:
|
* {String} A JSON string representing the date.
|
*/
|
'date': function(date) {
|
function format(number) {
|
// Format integers to have at least two digits.
|
return (number < 10) ? '0' + number : number;
|
}
|
return '"' + date.getFullYear() + '-' +
|
format(date.getMonth() + 1) + '-' +
|
format(date.getDate()) + 'T' +
|
format(date.getHours()) + ':' +
|
format(date.getMinutes()) + ':' +
|
format(date.getSeconds()) + '"';
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Format.JSON"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/GeoJSON.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/JSON.js
|
* @requires OpenLayers/Feature/Vector.js
|
* @requires OpenLayers/Geometry/Point.js
|
* @requires OpenLayers/Geometry/MultiPoint.js
|
* @requires OpenLayers/Geometry/LineString.js
|
* @requires OpenLayers/Geometry/MultiLineString.js
|
* @requires OpenLayers/Geometry/Polygon.js
|
* @requires OpenLayers/Geometry/MultiPolygon.js
|
* @requires OpenLayers/Console.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.GeoJSON
|
* Read and write GeoJSON. Create a new parser with the
|
* <OpenLayers.Format.GeoJSON> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.JSON>
|
*/
|
OpenLayers.Format.GeoJSON = OpenLayers.Class(OpenLayers.Format.JSON, {
|
|
/**
|
* APIProperty: ignoreExtraDims
|
* {Boolean} Ignore dimensions higher than 2 when reading geometry
|
* coordinates.
|
*/
|
ignoreExtraDims: false,
|
|
/**
|
* Constructor: OpenLayers.Format.GeoJSON
|
* Create a new parser for GeoJSON.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* APIMethod: read
|
* Deserialize a GeoJSON string.
|
*
|
* Parameters:
|
* json - {String} A GeoJSON string
|
* type - {String} Optional string that determines the structure of
|
* the output. Supported values are "Geometry", "Feature", and
|
* "FeatureCollection". If absent or null, a default of
|
* "FeatureCollection" is assumed.
|
* filter - {Function} A function which will be called for every key and
|
* value at every level of the final result. Each value will be
|
* replaced by the result of the filter function. This can be used to
|
* reform generic objects into instances of classes, or to transform
|
* date strings into Date objects.
|
*
|
* Returns:
|
* {Object} The return depends on the value of the type argument. If type
|
* is "FeatureCollection" (the default), the return will be an array
|
* of <OpenLayers.Feature.Vector>. If type is "Geometry", the input json
|
* must represent a single geometry, and the return will be an
|
* <OpenLayers.Geometry>. If type is "Feature", the input json must
|
* represent a single feature, and the return will be an
|
* <OpenLayers.Feature.Vector>.
|
*/
|
read: function(json, type, filter) {
|
type = (type) ? type : "FeatureCollection";
|
var results = null;
|
var obj = null;
|
if (typeof json == "string") {
|
obj = OpenLayers.Format.JSON.prototype.read.apply(this,
|
[json, filter]);
|
} else {
|
obj = json;
|
}
|
if(!obj) {
|
OpenLayers.Console.error("Bad JSON: " + json);
|
} else if(typeof(obj.type) != "string") {
|
OpenLayers.Console.error("Bad GeoJSON - no type: " + json);
|
} else if(this.isValidType(obj, type)) {
|
switch(type) {
|
case "Geometry":
|
try {
|
results = this.parseGeometry(obj);
|
} catch(err) {
|
OpenLayers.Console.error(err);
|
}
|
break;
|
case "Feature":
|
try {
|
results = this.parseFeature(obj);
|
results.type = "Feature";
|
} catch(err) {
|
OpenLayers.Console.error(err);
|
}
|
break;
|
case "FeatureCollection":
|
// for type FeatureCollection, we allow input to be any type
|
results = [];
|
switch(obj.type) {
|
case "Feature":
|
try {
|
results.push(this.parseFeature(obj));
|
} catch(err) {
|
results = null;
|
OpenLayers.Console.error(err);
|
}
|
break;
|
case "FeatureCollection":
|
for(var i=0, len=obj.features.length; i<len; ++i) {
|
try {
|
results.push(this.parseFeature(obj.features[i]));
|
} catch(err) {
|
results = null;
|
OpenLayers.Console.error(err);
|
}
|
}
|
break;
|
default:
|
try {
|
var geom = this.parseGeometry(obj);
|
results.push(new OpenLayers.Feature.Vector(geom));
|
} catch(err) {
|
results = null;
|
OpenLayers.Console.error(err);
|
}
|
}
|
break;
|
}
|
}
|
return results;
|
},
|
|
/**
|
* Method: isValidType
|
* Check if a GeoJSON object is a valid representative of the given type.
|
*
|
* Returns:
|
* {Boolean} The object is valid GeoJSON object of the given type.
|
*/
|
isValidType: function(obj, type) {
|
var valid = false;
|
switch(type) {
|
case "Geometry":
|
if(OpenLayers.Util.indexOf(
|
["Point", "MultiPoint", "LineString", "MultiLineString",
|
"Polygon", "MultiPolygon", "Box", "GeometryCollection"],
|
obj.type) == -1) {
|
// unsupported geometry type
|
OpenLayers.Console.error("Unsupported geometry type: " +
|
obj.type);
|
} else {
|
valid = true;
|
}
|
break;
|
case "FeatureCollection":
|
// allow for any type to be converted to a feature collection
|
valid = true;
|
break;
|
default:
|
// for Feature types must match
|
if(obj.type == type) {
|
valid = true;
|
} else {
|
OpenLayers.Console.error("Cannot convert types from " +
|
obj.type + " to " + type);
|
}
|
}
|
return valid;
|
},
|
|
/**
|
* Method: parseFeature
|
* Convert a feature object from GeoJSON into an
|
* <OpenLayers.Feature.Vector>.
|
*
|
* Parameters:
|
* obj - {Object} An object created from a GeoJSON object
|
*
|
* Returns:
|
* {<OpenLayers.Feature.Vector>} A feature.
|
*/
|
parseFeature: function(obj) {
|
var feature, geometry, attributes, bbox;
|
attributes = (obj.properties) ? obj.properties : {};
|
bbox = (obj.geometry && obj.geometry.bbox) || obj.bbox;
|
try {
|
geometry = this.parseGeometry(obj.geometry);
|
} catch(err) {
|
// deal with bad geometries
|
throw err;
|
}
|
feature = new OpenLayers.Feature.Vector(geometry, attributes);
|
if(bbox) {
|
feature.bounds = OpenLayers.Bounds.fromArray(bbox);
|
}
|
if(obj.id) {
|
feature.fid = obj.id;
|
}
|
return feature;
|
},
|
|
/**
|
* Method: parseGeometry
|
* Convert a geometry object from GeoJSON into an <OpenLayers.Geometry>.
|
*
|
* Parameters:
|
* obj - {Object} An object created from a GeoJSON object
|
*
|
* Returns:
|
* {<OpenLayers.Geometry>} A geometry.
|
*/
|
parseGeometry: function(obj) {
|
if (obj == null) {
|
return null;
|
}
|
var geometry, collection = false;
|
if(obj.type == "GeometryCollection") {
|
if(!(OpenLayers.Util.isArray(obj.geometries))) {
|
throw "GeometryCollection must have geometries array: " + obj;
|
}
|
var numGeom = obj.geometries.length;
|
var components = new Array(numGeom);
|
for(var i=0; i<numGeom; ++i) {
|
components[i] = this.parseGeometry.apply(
|
this, [obj.geometries[i]]
|
);
|
}
|
geometry = new OpenLayers.Geometry.Collection(components);
|
collection = true;
|
} else {
|
if(!(OpenLayers.Util.isArray(obj.coordinates))) {
|
throw "Geometry must have coordinates array: " + obj;
|
}
|
if(!this.parseCoords[obj.type.toLowerCase()]) {
|
throw "Unsupported geometry type: " + obj.type;
|
}
|
try {
|
geometry = this.parseCoords[obj.type.toLowerCase()].apply(
|
this, [obj.coordinates]
|
);
|
} catch(err) {
|
// deal with bad coordinates
|
throw err;
|
}
|
}
|
// We don't reproject collections because the children are reprojected
|
// for us when they are created.
|
if (this.internalProjection && this.externalProjection && !collection) {
|
geometry.transform(this.externalProjection,
|
this.internalProjection);
|
}
|
return geometry;
|
},
|
|
/**
|
* Property: parseCoords
|
* Object with properties corresponding to the GeoJSON geometry types.
|
* Property values are functions that do the actual parsing.
|
*/
|
parseCoords: {
|
/**
|
* Method: parseCoords.point
|
* Convert a coordinate array from GeoJSON into an
|
* <OpenLayers.Geometry>.
|
*
|
* Parameters:
|
* array - {Object} The coordinates array from the GeoJSON fragment.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry>} A geometry.
|
*/
|
"point": function(array) {
|
if (this.ignoreExtraDims == false &&
|
array.length != 2) {
|
throw "Only 2D points are supported: " + array;
|
}
|
return new OpenLayers.Geometry.Point(array[0], array[1]);
|
},
|
|
/**
|
* Method: parseCoords.multipoint
|
* Convert a coordinate array from GeoJSON into an
|
* <OpenLayers.Geometry>.
|
*
|
* Parameters:
|
* array - {Object} The coordinates array from the GeoJSON fragment.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry>} A geometry.
|
*/
|
"multipoint": function(array) {
|
var points = [];
|
var p = null;
|
for(var i=0, len=array.length; i<len; ++i) {
|
try {
|
p = this.parseCoords["point"].apply(this, [array[i]]);
|
} catch(err) {
|
throw err;
|
}
|
points.push(p);
|
}
|
return new OpenLayers.Geometry.MultiPoint(points);
|
},
|
|
/**
|
* Method: parseCoords.linestring
|
* Convert a coordinate array from GeoJSON into an
|
* <OpenLayers.Geometry>.
|
*
|
* Parameters:
|
* array - {Object} The coordinates array from the GeoJSON fragment.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry>} A geometry.
|
*/
|
"linestring": function(array) {
|
var points = [];
|
var p = null;
|
for(var i=0, len=array.length; i<len; ++i) {
|
try {
|
p = this.parseCoords["point"].apply(this, [array[i]]);
|
} catch(err) {
|
throw err;
|
}
|
points.push(p);
|
}
|
return new OpenLayers.Geometry.LineString(points);
|
},
|
|
/**
|
* Method: parseCoords.multilinestring
|
* Convert a coordinate array from GeoJSON into an
|
* <OpenLayers.Geometry>.
|
*
|
* Parameters:
|
* array - {Object} The coordinates array from the GeoJSON fragment.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry>} A geometry.
|
*/
|
"multilinestring": function(array) {
|
var lines = [];
|
var l = null;
|
for(var i=0, len=array.length; i<len; ++i) {
|
try {
|
l = this.parseCoords["linestring"].apply(this, [array[i]]);
|
} catch(err) {
|
throw err;
|
}
|
lines.push(l);
|
}
|
return new OpenLayers.Geometry.MultiLineString(lines);
|
},
|
|
/**
|
* Method: parseCoords.polygon
|
* Convert a coordinate array from GeoJSON into an
|
* <OpenLayers.Geometry>.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry>} A geometry.
|
*/
|
"polygon": function(array) {
|
var rings = [];
|
var r, l;
|
for(var i=0, len=array.length; i<len; ++i) {
|
try {
|
l = this.parseCoords["linestring"].apply(this, [array[i]]);
|
} catch(err) {
|
throw err;
|
}
|
r = new OpenLayers.Geometry.LinearRing(l.components);
|
rings.push(r);
|
}
|
return new OpenLayers.Geometry.Polygon(rings);
|
},
|
|
/**
|
* Method: parseCoords.multipolygon
|
* Convert a coordinate array from GeoJSON into an
|
* <OpenLayers.Geometry>.
|
*
|
* Parameters:
|
* array - {Object} The coordinates array from the GeoJSON fragment.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry>} A geometry.
|
*/
|
"multipolygon": function(array) {
|
var polys = [];
|
var p = null;
|
for(var i=0, len=array.length; i<len; ++i) {
|
try {
|
p = this.parseCoords["polygon"].apply(this, [array[i]]);
|
} catch(err) {
|
throw err;
|
}
|
polys.push(p);
|
}
|
return new OpenLayers.Geometry.MultiPolygon(polys);
|
},
|
|
/**
|
* Method: parseCoords.box
|
* Convert a coordinate array from GeoJSON into an
|
* <OpenLayers.Geometry>.
|
*
|
* Parameters:
|
* array - {Object} The coordinates array from the GeoJSON fragment.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry>} A geometry.
|
*/
|
"box": function(array) {
|
if(array.length != 2) {
|
throw "GeoJSON box coordinates must have 2 elements";
|
}
|
return new OpenLayers.Geometry.Polygon([
|
new OpenLayers.Geometry.LinearRing([
|
new OpenLayers.Geometry.Point(array[0][0], array[0][1]),
|
new OpenLayers.Geometry.Point(array[1][0], array[0][1]),
|
new OpenLayers.Geometry.Point(array[1][0], array[1][1]),
|
new OpenLayers.Geometry.Point(array[0][0], array[1][1]),
|
new OpenLayers.Geometry.Point(array[0][0], array[0][1])
|
])
|
]);
|
}
|
|
},
|
|
/**
|
* APIMethod: write
|
* Serialize a feature, geometry, array of features into a GeoJSON string.
|
*
|
* Parameters:
|
* obj - {Object} An <OpenLayers.Feature.Vector>, <OpenLayers.Geometry>,
|
* or an array of features.
|
* pretty - {Boolean} Structure the output with newlines and indentation.
|
* Default is false.
|
*
|
* Returns:
|
* {String} The GeoJSON string representation of the input geometry,
|
* features, or array of features.
|
*/
|
write: function(obj, pretty) {
|
var geojson = {
|
"type": null
|
};
|
if(OpenLayers.Util.isArray(obj)) {
|
geojson.type = "FeatureCollection";
|
var numFeatures = obj.length;
|
geojson.features = new Array(numFeatures);
|
for(var i=0; i<numFeatures; ++i) {
|
var element = obj[i];
|
if(!element instanceof OpenLayers.Feature.Vector) {
|
var msg = "FeatureCollection only supports collections " +
|
"of features: " + element;
|
throw msg;
|
}
|
geojson.features[i] = this.extract.feature.apply(
|
this, [element]
|
);
|
}
|
} else if (obj.CLASS_NAME.indexOf("OpenLayers.Geometry") == 0) {
|
geojson = this.extract.geometry.apply(this, [obj]);
|
} else if (obj instanceof OpenLayers.Feature.Vector) {
|
geojson = this.extract.feature.apply(this, [obj]);
|
if(obj.layer && obj.layer.projection) {
|
geojson.crs = this.createCRSObject(obj);
|
}
|
}
|
return OpenLayers.Format.JSON.prototype.write.apply(this,
|
[geojson, pretty]);
|
},
|
|
/**
|
* Method: createCRSObject
|
* Create the CRS object for an object.
|
*
|
* Parameters:
|
* object - {<OpenLayers.Feature.Vector>}
|
*
|
* Returns:
|
* {Object} An object which can be assigned to the crs property
|
* of a GeoJSON object.
|
*/
|
createCRSObject: function(object) {
|
var proj = object.layer.projection.toString();
|
var crs = {};
|
if (proj.match(/epsg:/i)) {
|
var code = parseInt(proj.substring(proj.indexOf(":") + 1));
|
if (code == 4326) {
|
crs = {
|
"type": "name",
|
"properties": {
|
"name": "urn:ogc:def:crs:OGC:1.3:CRS84"
|
}
|
};
|
} else {
|
crs = {
|
"type": "name",
|
"properties": {
|
"name": "EPSG:" + code
|
}
|
};
|
}
|
}
|
return crs;
|
},
|
|
/**
|
* Property: extract
|
* Object with properties corresponding to the GeoJSON types.
|
* Property values are functions that do the actual value extraction.
|
*/
|
extract: {
|
/**
|
* Method: extract.feature
|
* Return a partial GeoJSON object representing a single feature.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
*
|
* Returns:
|
* {Object} An object representing the point.
|
*/
|
'feature': function(feature) {
|
var geom = this.extract.geometry.apply(this, [feature.geometry]);
|
var json = {
|
"type": "Feature",
|
"properties": feature.attributes,
|
"geometry": geom
|
};
|
if (feature.fid != null) {
|
json.id = feature.fid;
|
}
|
return json;
|
},
|
|
/**
|
* Method: extract.geometry
|
* Return a GeoJSON object representing a single geometry.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {Object} An object representing the geometry.
|
*/
|
'geometry': function(geometry) {
|
if (geometry == null) {
|
return null;
|
}
|
if (this.internalProjection && this.externalProjection) {
|
geometry = geometry.clone();
|
geometry.transform(this.internalProjection,
|
this.externalProjection);
|
}
|
var geometryType = geometry.CLASS_NAME.split('.')[2];
|
var data = this.extract[geometryType.toLowerCase()].apply(this, [geometry]);
|
var json;
|
if(geometryType == "Collection") {
|
json = {
|
"type": "GeometryCollection",
|
"geometries": data
|
};
|
} else {
|
json = {
|
"type": geometryType,
|
"coordinates": data
|
};
|
}
|
|
return json;
|
},
|
|
/**
|
* Method: extract.point
|
* Return an array of coordinates from a point.
|
*
|
* Parameters:
|
* point - {<OpenLayers.Geometry.Point>}
|
*
|
* Returns:
|
* {Array} An array of coordinates representing the point.
|
*/
|
'point': function(point) {
|
return [point.x, point.y];
|
},
|
|
/**
|
* Method: extract.multipoint
|
* Return an array of point coordinates from a multipoint.
|
*
|
* Parameters:
|
* multipoint - {<OpenLayers.Geometry.MultiPoint>}
|
*
|
* Returns:
|
* {Array} An array of point coordinate arrays representing
|
* the multipoint.
|
*/
|
'multipoint': function(multipoint) {
|
var array = [];
|
for(var i=0, len=multipoint.components.length; i<len; ++i) {
|
array.push(this.extract.point.apply(this, [multipoint.components[i]]));
|
}
|
return array;
|
},
|
|
/**
|
* Method: extract.linestring
|
* Return an array of coordinate arrays from a linestring.
|
*
|
* Parameters:
|
* linestring - {<OpenLayers.Geometry.LineString>}
|
*
|
* Returns:
|
* {Array} An array of coordinate arrays representing
|
* the linestring.
|
*/
|
'linestring': function(linestring) {
|
var array = [];
|
for(var i=0, len=linestring.components.length; i<len; ++i) {
|
array.push(this.extract.point.apply(this, [linestring.components[i]]));
|
}
|
return array;
|
},
|
|
/**
|
* Method: extract.multilinestring
|
* Return an array of linestring arrays from a linestring.
|
*
|
* Parameters:
|
* multilinestring - {<OpenLayers.Geometry.MultiLineString>}
|
*
|
* Returns:
|
* {Array} An array of linestring arrays representing
|
* the multilinestring.
|
*/
|
'multilinestring': function(multilinestring) {
|
var array = [];
|
for(var i=0, len=multilinestring.components.length; i<len; ++i) {
|
array.push(this.extract.linestring.apply(this, [multilinestring.components[i]]));
|
}
|
return array;
|
},
|
|
/**
|
* Method: extract.polygon
|
* Return an array of linear ring arrays from a polygon.
|
*
|
* Parameters:
|
* polygon - {<OpenLayers.Geometry.Polygon>}
|
*
|
* Returns:
|
* {Array} An array of linear ring arrays representing the polygon.
|
*/
|
'polygon': function(polygon) {
|
var array = [];
|
for(var i=0, len=polygon.components.length; i<len; ++i) {
|
array.push(this.extract.linestring.apply(this, [polygon.components[i]]));
|
}
|
return array;
|
},
|
|
/**
|
* Method: extract.multipolygon
|
* Return an array of polygon arrays from a multipolygon.
|
*
|
* Parameters:
|
* multipolygon - {<OpenLayers.Geometry.MultiPolygon>}
|
*
|
* Returns:
|
* {Array} An array of polygon arrays representing
|
* the multipolygon
|
*/
|
'multipolygon': function(multipolygon) {
|
var array = [];
|
for(var i=0, len=multipolygon.components.length; i<len; ++i) {
|
array.push(this.extract.polygon.apply(this, [multipolygon.components[i]]));
|
}
|
return array;
|
},
|
|
/**
|
* Method: extract.collection
|
* Return an array of geometries from a geometry collection.
|
*
|
* Parameters:
|
* collection - {<OpenLayers.Geometry.Collection>}
|
*
|
* Returns:
|
* {Array} An array of geometry objects representing the geometry
|
* collection.
|
*/
|
'collection': function(collection) {
|
var len = collection.components.length;
|
var array = new Array(len);
|
for(var i=0; i<len; ++i) {
|
array[i] = this.extract.geometry.apply(
|
this, [collection.components[i]]
|
);
|
}
|
return array;
|
}
|
|
|
},
|
|
CLASS_NAME: "OpenLayers.Format.GeoJSON"
|
|
});
|
/* ======================================================================
|
OpenLayers/Protocol/Script.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Protocol.js
|
* @requires OpenLayers/Feature/Vector.js
|
* @requires OpenLayers/Format/GeoJSON.js
|
*/
|
|
/**
|
* if application uses the query string, for example, for BBOX parameters,
|
* OpenLayers/Format/QueryStringFilter.js should be included in the build config file
|
*/
|
|
/**
|
* Class: OpenLayers.Protocol.Script
|
* A basic Script protocol for vector layers. Create a new instance with the
|
* <OpenLayers.Protocol.Script> constructor. A script protocol is used to
|
* get around the same origin policy. It works with services that return
|
* JSONP - that is, JSON wrapped in a client-specified callback. The
|
* protocol handles fetching and parsing of feature data and sends parsed
|
* features to the <callback> configured with the protocol. The protocol
|
* expects features serialized as GeoJSON by default, but can be configured
|
* to work with other formats by setting the <format> property.
|
*
|
* Inherits from:
|
* - <OpenLayers.Protocol>
|
*/
|
OpenLayers.Protocol.Script = OpenLayers.Class(OpenLayers.Protocol, {
|
|
/**
|
* APIProperty: url
|
* {String} Service URL. The service is expected to return serialized
|
* features wrapped in a named callback (where the callback name is
|
* generated by this protocol).
|
* Read-only, set through the options passed to the constructor.
|
*/
|
url: null,
|
|
/**
|
* APIProperty: params
|
* {Object} Query string parameters to be appended to the URL.
|
* Read-only, set through the options passed to the constructor.
|
* Example: {maxFeatures: 50}
|
*/
|
params: null,
|
|
/**
|
* APIProperty: callback
|
* {Object} Function to be called when the <read> operation completes.
|
*/
|
callback: null,
|
|
/**
|
* APIProperty: callbackTemplate
|
* {String} Template for creating a unique callback function name
|
* for the registry. Should include ${id}. The ${id} variable will be
|
* replaced with a string identifier prefixed with a "c" (e.g. c1, c2).
|
* Default is "OpenLayers.Protocol.Script.registry.${id}".
|
*/
|
callbackTemplate: "OpenLayers.Protocol.Script.registry.${id}",
|
|
/**
|
* APIProperty: callbackKey
|
* {String} The name of the query string parameter that the service
|
* recognizes as the callback identifier. Default is "callback".
|
* This key is used to generate the URL for the script. For example
|
* setting <callbackKey> to "myCallback" would result in a URL like
|
* http://example.com/?myCallback=...
|
*/
|
callbackKey: "callback",
|
|
/**
|
* APIProperty: callbackPrefix
|
* {String} Where a service requires that the callback query string
|
* parameter value is prefixed by some string, this value may be set.
|
* For example, setting <callbackPrefix> to "foo:" would result in a
|
* URL like http://example.com/?callback=foo:... Default is "".
|
*/
|
callbackPrefix: "",
|
|
/**
|
* APIProperty: scope
|
* {Object} Optional ``this`` object for the callback. Read-only, set
|
* through the options passed to the constructor.
|
*/
|
scope: null,
|
|
/**
|
* APIProperty: format
|
* {<OpenLayers.Format>} Format for parsing features. Default is an
|
* <OpenLayers.Format.GeoJSON> format. If an alternative is provided,
|
* the format's read method must take an object and return an array
|
* of features.
|
*/
|
format: null,
|
|
/**
|
* Property: pendingRequests
|
* {Object} References all pending requests. Property names are script
|
* identifiers and property values are script elements.
|
*/
|
pendingRequests: null,
|
|
/**
|
* APIProperty: srsInBBOX
|
* {Boolean} Include the SRS identifier in BBOX query string parameter.
|
* Setting this property has no effect if a custom filterToParams method
|
* is provided. Default is false. If true and the layer has a
|
* projection object set, any BBOX filter will be serialized with a
|
* fifth item identifying the projection.
|
* E.g. bbox=-1000,-1000,1000,1000,EPSG:900913
|
*/
|
srsInBBOX: false,
|
|
/**
|
* Constructor: OpenLayers.Protocol.Script
|
* A class for giving layers generic Script protocol.
|
*
|
* Parameters:
|
* options - {Object} Optional object whose properties will be set on the
|
* instance.
|
*
|
* Valid options include:
|
* url - {String}
|
* params - {Object}
|
* callback - {Function}
|
* scope - {Object}
|
*/
|
initialize: function(options) {
|
options = options || {};
|
this.params = {};
|
this.pendingRequests = {};
|
OpenLayers.Protocol.prototype.initialize.apply(this, arguments);
|
if (!this.format) {
|
this.format = new OpenLayers.Format.GeoJSON();
|
}
|
|
if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) {
|
var format = new OpenLayers.Format.QueryStringFilter({
|
srsInBBOX: this.srsInBBOX
|
});
|
this.filterToParams = function(filter, params) {
|
return format.write(filter, params);
|
};
|
}
|
},
|
|
/**
|
* APIMethod: read
|
* Construct a request for reading new features.
|
*
|
* Parameters:
|
* options - {Object} Optional object for configuring the request.
|
* This object is modified and should not be reused.
|
*
|
* Valid options:
|
* url - {String} Url for the request.
|
* params - {Object} Parameters to get serialized as a query string.
|
* filter - {<OpenLayers.Filter>} Filter to get serialized as a
|
* query string.
|
*
|
* Returns:
|
* {<OpenLayers.Protocol.Response>} A response object, whose "priv" property
|
* references the injected script. This object is also passed to the
|
* callback function when the request completes, its "features" property
|
* is then populated with the features received from the server.
|
*/
|
read: function(options) {
|
OpenLayers.Protocol.prototype.read.apply(this, arguments);
|
options = OpenLayers.Util.applyDefaults(options, this.options);
|
options.params = OpenLayers.Util.applyDefaults(
|
options.params, this.options.params
|
);
|
if (options.filter && this.filterToParams) {
|
options.params = this.filterToParams(
|
options.filter, options.params
|
);
|
}
|
var response = new OpenLayers.Protocol.Response({requestType: "read"});
|
var request = this.createRequest(
|
options.url,
|
options.params,
|
OpenLayers.Function.bind(function(data) {
|
response.data = data;
|
this.handleRead(response, options);
|
}, this)
|
);
|
response.priv = request;
|
return response;
|
},
|
|
/**
|
* APIMethod: filterToParams
|
* Optional method to translate an <OpenLayers.Filter> object into an object
|
* that can be serialized as request query string provided. If a custom
|
* method is not provided, any filter will not be serialized.
|
*
|
* Parameters:
|
* filter - {<OpenLayers.Filter>} filter to convert.
|
* params - {Object} The parameters object.
|
*
|
* Returns:
|
* {Object} The resulting parameters object.
|
*/
|
|
/**
|
* Method: createRequest
|
* Issues a request for features by creating injecting a script in the
|
* document head.
|
*
|
* Parameters:
|
* url - {String} Service URL.
|
* params - {Object} Query string parameters.
|
* callback - {Function} Callback to be called with resulting data.
|
*
|
* Returns:
|
* {HTMLScriptElement} The script pending execution.
|
*/
|
createRequest: function(url, params, callback) {
|
var id = OpenLayers.Protocol.Script.register(callback);
|
var name = OpenLayers.String.format(this.callbackTemplate, {id: id});
|
params = OpenLayers.Util.extend({}, params);
|
params[this.callbackKey] = this.callbackPrefix + name;
|
url = OpenLayers.Util.urlAppend(
|
url, OpenLayers.Util.getParameterString(params)
|
);
|
var script = document.createElement("script");
|
script.type = "text/javascript";
|
script.src = url;
|
script.id = "OpenLayers_Protocol_Script_" + id;
|
this.pendingRequests[script.id] = script;
|
var head = document.getElementsByTagName("head")[0];
|
head.appendChild(script);
|
return script;
|
},
|
|
/**
|
* Method: destroyRequest
|
* Remove a script node associated with a response from the document. Also
|
* unregisters the callback and removes the script from the
|
* <pendingRequests> object.
|
*
|
* Parameters:
|
* script - {HTMLScriptElement}
|
*/
|
destroyRequest: function(script) {
|
OpenLayers.Protocol.Script.unregister(script.id.split("_").pop());
|
delete this.pendingRequests[script.id];
|
if (script.parentNode) {
|
script.parentNode.removeChild(script);
|
}
|
},
|
|
/**
|
* Method: handleRead
|
* Individual callbacks are created for read, create and update, should
|
* a subclass need to override each one separately.
|
*
|
* Parameters:
|
* response - {<OpenLayers.Protocol.Response>} The response object to pass to
|
* the user callback.
|
* options - {Object} The user options passed to the read call.
|
*/
|
handleRead: function(response, options) {
|
this.handleResponse(response, options);
|
},
|
|
/**
|
* Method: handleResponse
|
* Called by CRUD specific handlers.
|
*
|
* Parameters:
|
* response - {<OpenLayers.Protocol.Response>} The response object to pass to
|
* any user callback.
|
* options - {Object} The user options passed to the create, read, update,
|
* or delete call.
|
*/
|
handleResponse: function(response, options) {
|
if (options.callback) {
|
if (response.data) {
|
response.features = this.parseFeatures(response.data);
|
response.code = OpenLayers.Protocol.Response.SUCCESS;
|
} else {
|
response.code = OpenLayers.Protocol.Response.FAILURE;
|
}
|
this.destroyRequest(response.priv);
|
options.callback.call(options.scope, response);
|
}
|
},
|
|
/**
|
* Method: parseFeatures
|
* Read Script response body and return features.
|
*
|
* Parameters:
|
* data - {Object} The data sent to the callback function by the server.
|
*
|
* Returns:
|
* {Array({<OpenLayers.Feature.Vector>})} or
|
* {<OpenLayers.Feature.Vector>} Array of features or a single feature.
|
*/
|
parseFeatures: function(data) {
|
return this.format.read(data);
|
},
|
|
/**
|
* APIMethod: abort
|
* Abort an ongoing request. If no response is provided, all pending
|
* requests will be aborted.
|
*
|
* Parameters:
|
* response - {<OpenLayers.Protocol.Response>} The response object returned
|
* from a <read> request.
|
*/
|
abort: function(response) {
|
if (response) {
|
this.destroyRequest(response.priv);
|
} else {
|
for (var key in this.pendingRequests) {
|
this.destroyRequest(this.pendingRequests[key]);
|
}
|
}
|
},
|
|
/**
|
* APIMethod: destroy
|
* Clean up the protocol.
|
*/
|
destroy: function() {
|
this.abort();
|
delete this.params;
|
delete this.format;
|
OpenLayers.Protocol.prototype.destroy.apply(this);
|
},
|
|
CLASS_NAME: "OpenLayers.Protocol.Script"
|
});
|
|
(function() {
|
var o = OpenLayers.Protocol.Script;
|
var counter = 0;
|
o.registry = {};
|
|
/**
|
* Function: OpenLayers.Protocol.Script.register
|
* Register a callback for a newly created script.
|
*
|
* Parameters:
|
* callback - {Function} The callback to be executed when the newly added
|
* script loads. This callback will be called with a single argument
|
* that is the JSON returned by the service.
|
*
|
* Returns:
|
* {Number} An identifier for retrieving the registered callback.
|
*/
|
o.register = function(callback) {
|
var id = "c"+(++counter);
|
o.registry[id] = function() {
|
callback.apply(this, arguments);
|
};
|
return id;
|
};
|
|
/**
|
* Function: OpenLayers.Protocol.Script.unregister
|
* Unregister a callback previously registered with the register function.
|
*
|
* Parameters:
|
* id - {Number} The identifer returned by the register function.
|
*/
|
o.unregister = function(id) {
|
delete o.registry[id];
|
};
|
})();
|
/* ======================================================================
|
OpenLayers/Format/EncodedPolyline.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format.js
|
* @requires OpenLayers/Feature/Vector.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.EncodedPolyline
|
* Class for reading and writing encoded polylines. Create a new instance
|
* with the <OpenLayers.Format.EncodedPolyline> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format>
|
*/
|
OpenLayers.Format.EncodedPolyline = OpenLayers.Class(OpenLayers.Format, {
|
|
/**
|
* APIProperty: geometryType
|
* {String} Geometry type to output. One of: linestring (default),
|
* linearring, point, multipoint or polygon. If the geometryType is
|
* point, only the first point of the string is returned.
|
*/
|
geometryType: "linestring",
|
|
/**
|
* Constructor: OpenLayers.Format.EncodedPolyline
|
* Create a new parser for encoded polylines
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance
|
*
|
* Returns:
|
* {<OpenLayers.Format.EncodedPolyline>} A new encoded polylines parser.
|
*/
|
initialize: function(options) {
|
OpenLayers.Format.prototype.initialize.apply(this, [options]);
|
},
|
|
/**
|
* APIMethod: read
|
* Deserialize an encoded polyline string and return a vector feature.
|
*
|
* Parameters:
|
* encoded - {String} An encoded polyline string
|
*
|
* Returns:
|
* {<OpenLayers.Feature.Vector>} A vector feature with a linestring.
|
*/
|
read: function(encoded) {
|
var geomType;
|
if (this.geometryType == "linestring")
|
geomType = OpenLayers.Geometry.LineString;
|
else if (this.geometryType == "linearring")
|
geomType = OpenLayers.Geometry.LinearRing;
|
else if (this.geometryType == "multipoint")
|
geomType = OpenLayers.Geometry.MultiPoint;
|
else if (this.geometryType != "point" && this.geometryType != "polygon")
|
return null;
|
|
var flatPoints = this.decodeDeltas(encoded, 2);
|
var flatPointsLength = flatPoints.length;
|
|
var pointGeometries = [];
|
for (var i = 0; i + 1 < flatPointsLength;) {
|
var y = flatPoints[i++], x = flatPoints[i++];
|
pointGeometries.push(new OpenLayers.Geometry.Point(x, y));
|
}
|
|
|
if (this.geometryType == "point")
|
return new OpenLayers.Feature.Vector(
|
pointGeometries[0]
|
);
|
|
if (this.geometryType == "polygon")
|
return new OpenLayers.Feature.Vector(
|
new OpenLayers.Geometry.Polygon([
|
new OpenLayers.Geometry.LinearRing(pointGeometries)
|
])
|
);
|
|
return new OpenLayers.Feature.Vector(
|
new geomType(pointGeometries)
|
);
|
},
|
|
/**
|
* APIMethod: decode
|
* Deserialize an encoded string and return an array of n-dimensional
|
* points.
|
*
|
* Parameters:
|
* encoded - {String} An encoded string
|
* dims - {int} The dimension of the points that are returned
|
*
|
* Returns:
|
* {Array(Array(int))} An array containing n-dimensional arrays of
|
* coordinates.
|
*/
|
decode: function(encoded, dims, opt_factor) {
|
var factor = opt_factor || 1e5;
|
var flatPoints = this.decodeDeltas(encoded, dims, factor);
|
var flatPointsLength = flatPoints.length;
|
|
var points = [];
|
for (var i = 0; i + (dims - 1) < flatPointsLength;) {
|
var point = [];
|
|
for (var dim = 0; dim < dims; ++dim) {
|
point.push(flatPoints[i++])
|
}
|
|
points.push(point);
|
}
|
|
return points;
|
},
|
|
/**
|
* APIMethod: write
|
* Serialize a feature or array of features into a WKT string.
|
*
|
* Parameters:
|
* features - {<OpenLayers.Feature.Vector>|Array} A feature or array of
|
* features
|
*
|
* Returns:
|
* {String} The WKT string representation of the input geometries
|
*/
|
write: function(features) {
|
var feature;
|
if (features.constructor == Array)
|
feature = features[0];
|
else
|
feature = features;
|
|
var geometry = feature.geometry;
|
var type = geometry.CLASS_NAME.split('.')[2].toLowerCase();
|
|
var pointGeometries;
|
if (type == "point")
|
pointGeometries = new Array(geometry);
|
else if (type == "linestring" ||
|
type == "linearring" ||
|
type == "multipoint")
|
pointGeometries = geometry.components;
|
else if (type == "polygon")
|
pointGeometries = geometry.components[0].components;
|
else
|
return null;
|
|
var flatPoints = [];
|
|
var pointGeometriesLength = pointGeometries.length;
|
for (var i = 0; i < pointGeometriesLength; ++i) {
|
var pointGeometry = pointGeometries[i];
|
flatPoints.push(pointGeometry.y);
|
flatPoints.push(pointGeometry.x);
|
}
|
|
return this.encodeDeltas(flatPoints, 2);
|
},
|
|
/**
|
* APIMethod: encode
|
* Serialize an array of n-dimensional points and return an encoded string
|
*
|
* Parameters:
|
* points - {Array(Array(int))} An array containing n-dimensional
|
* arrays of coordinates
|
* dims - {int} The dimension of the points that should be read
|
*
|
* Returns:
|
* {String} An encoded string
|
*/
|
encode: function (points, dims, opt_factor) {
|
var factor = opt_factor || 1e5;
|
var flatPoints = [];
|
|
var pointsLength = points.length;
|
for (var i = 0; i < pointsLength; ++i) {
|
var point = points[i];
|
|
for (var dim = 0; dim < dims; ++dim) {
|
flatPoints.push(point[dim]);
|
}
|
}
|
|
return this.encodeDeltas(flatPoints, dims, factor);
|
},
|
|
/**
|
* APIMethod: encodeDeltas
|
* Encode a list of n-dimensional points and return an encoded string
|
*
|
* Attention: This function will modify the passed array!
|
*
|
* Parameters:
|
* numbers - {Array.<number>} A list of n-dimensional points.
|
* dimension - {number} The dimension of the points in the list.
|
* opt_factor - {number=} The factor by which the numbers will be
|
* multiplied. The remaining decimal places will get rounded away.
|
*
|
* Returns:
|
* {string} The encoded string.
|
*/
|
encodeDeltas: function(numbers, dimension, opt_factor) {
|
var factor = opt_factor || 1e5;
|
var d;
|
|
var lastNumbers = new Array(dimension);
|
for (d = 0; d < dimension; ++d) {
|
lastNumbers[d] = 0;
|
}
|
|
var numbersLength = numbers.length;
|
for (var i = 0; i < numbersLength;) {
|
for (d = 0; d < dimension; ++d, ++i) {
|
var num = numbers[i];
|
var delta = num - lastNumbers[d];
|
lastNumbers[d] = num;
|
|
numbers[i] = delta;
|
}
|
}
|
|
return this.encodeFloats(numbers, factor);
|
},
|
|
|
/**
|
* APIMethod: decodeDeltas
|
* Decode a list of n-dimensional points from an encoded string
|
*
|
* Parameters:
|
* encoded - {string} An encoded string.
|
* dimension - {number} The dimension of the points in the encoded string.
|
* opt_factor - {number=} The factor by which the resulting numbers will
|
* be divided.
|
*
|
* Returns:
|
* {Array.<number>} A list of n-dimensional points.
|
*/
|
decodeDeltas: function(encoded, dimension, opt_factor) {
|
var factor = opt_factor || 1e5;
|
var d;
|
|
var lastNumbers = new Array(dimension);
|
for (d = 0; d < dimension; ++d) {
|
lastNumbers[d] = 0;
|
}
|
|
var numbers = this.decodeFloats(encoded, factor);
|
|
var numbersLength = numbers.length;
|
for (var i = 0; i < numbersLength;) {
|
for (d = 0; d < dimension; ++d, ++i) {
|
lastNumbers[d] += numbers[i];
|
|
numbers[i] = lastNumbers[d];
|
}
|
}
|
|
return numbers;
|
},
|
|
|
/**
|
* APIMethod: encodeFloats
|
* Encode a list of floating point numbers and return an encoded string
|
*
|
* Attention: This function will modify the passed array!
|
*
|
* Parameters:
|
* numbers - {Array.<number>} A list of floating point numbers.
|
* opt_factor - {number=} The factor by which the numbers will be
|
* multiplied. The remaining decimal places will get rounded away.
|
*
|
* Returns:
|
* {string} The encoded string.
|
*/
|
encodeFloats: function(numbers, opt_factor) {
|
var factor = opt_factor || 1e5;
|
|
var numbersLength = numbers.length;
|
for (var i = 0; i < numbersLength; ++i) {
|
numbers[i] = Math.round(numbers[i] * factor);
|
}
|
|
return this.encodeSignedIntegers(numbers);
|
},
|
|
|
/**
|
* APIMethod: decodeFloats
|
* Decode a list of floating point numbers from an encoded string
|
*
|
* Parameters:
|
* encoded - {string} An encoded string.
|
* opt_factor - {number=} The factor by which the result will be divided.
|
*
|
* Returns:
|
* {Array.<number>} A list of floating point numbers.
|
*/
|
decodeFloats: function(encoded, opt_factor) {
|
var factor = opt_factor || 1e5;
|
|
var numbers = this.decodeSignedIntegers(encoded);
|
|
var numbersLength = numbers.length;
|
for (var i = 0; i < numbersLength; ++i) {
|
numbers[i] /= factor;
|
}
|
|
return numbers;
|
},
|
|
|
/**
|
* APIMethod: encodeSignedIntegers
|
* Encode a list of signed integers and return an encoded string
|
*
|
* Attention: This function will modify the passed array!
|
*
|
* Parameters:
|
* numbers - {Array.<number>} A list of signed integers.
|
*
|
* Returns:
|
* {string} The encoded string.
|
*/
|
encodeSignedIntegers: function(numbers) {
|
var numbersLength = numbers.length;
|
for (var i = 0; i < numbersLength; ++i) {
|
var num = numbers[i];
|
|
var signedNum = num << 1;
|
if (num < 0) {
|
signedNum = ~(signedNum);
|
}
|
|
numbers[i] = signedNum;
|
}
|
|
return this.encodeUnsignedIntegers(numbers);
|
},
|
|
|
/**
|
* APIMethod: decodeSignedIntegers
|
* Decode a list of signed integers from an encoded string
|
*
|
* Parameters:
|
* encoded - {string} An encoded string.
|
*
|
* Returns:
|
* {Array.<number>} A list of signed integers.
|
*/
|
decodeSignedIntegers: function(encoded) {
|
var numbers = this.decodeUnsignedIntegers(encoded);
|
|
var numbersLength = numbers.length;
|
for (var i = 0; i < numbersLength; ++i) {
|
var num = numbers[i];
|
numbers[i] = (num & 1) ? ~(num >> 1) : (num >> 1);
|
}
|
|
return numbers;
|
},
|
|
|
/**
|
* APIMethod: encodeUnsignedIntegers
|
* Encode a list of unsigned integers and return an encoded string
|
*
|
* Parameters:
|
* numbers - {Array.<number>} A list of unsigned integers.
|
*
|
* Returns:
|
* {string} The encoded string.
|
*/
|
encodeUnsignedIntegers: function(numbers) {
|
var encoded = '';
|
|
var numbersLength = numbers.length;
|
for (var i = 0; i < numbersLength; ++i) {
|
encoded += this.encodeUnsignedInteger(numbers[i]);
|
}
|
|
return encoded;
|
},
|
|
|
/**
|
* APIMethod: decodeUnsignedIntegers
|
* Decode a list of unsigned integers from an encoded string
|
*
|
* Parameters:
|
* encoded - {string} An encoded string.
|
*
|
* Returns:
|
* {Array.<number>} A list of unsigned integers.
|
*/
|
decodeUnsignedIntegers: function(encoded) {
|
var numbers = [];
|
|
var current = 0;
|
var shift = 0;
|
|
var encodedLength = encoded.length;
|
for (var i = 0; i < encodedLength; ++i) {
|
var b = encoded.charCodeAt(i) - 63;
|
|
current |= (b & 0x1f) << shift;
|
|
if (b < 0x20) {
|
numbers.push(current);
|
current = 0;
|
shift = 0;
|
} else {
|
shift += 5;
|
}
|
}
|
|
return numbers;
|
},
|
|
|
/**
|
* Method: encodeFloat
|
* Encode one single floating point number and return an encoded string
|
*
|
* Parameters:
|
* num - {number} Floating point number that should be encoded.
|
* opt_factor - {number=} The factor by which num will be multiplied.
|
* The remaining decimal places will get rounded away.
|
*
|
* Returns:
|
* {string} The encoded string.
|
*/
|
encodeFloat: function(num, opt_factor) {
|
num = Math.round(num * (opt_factor || 1e5));
|
return this.encodeSignedInteger(num);
|
},
|
|
|
/**
|
* Method: decodeFloat
|
* Decode one single floating point number from an encoded string
|
*
|
* Parameters:
|
* encoded - {string} An encoded string.
|
* opt_factor - {number=} The factor by which the result will be divided.
|
*
|
* Returns:
|
* {number} The decoded floating point number.
|
*/
|
decodeFloat: function(encoded, opt_factor) {
|
var result = this.decodeSignedInteger(encoded);
|
return result / (opt_factor || 1e5);
|
},
|
|
|
/**
|
* Method: encodeSignedInteger
|
* Encode one single signed integer and return an encoded string
|
*
|
* Parameters:
|
* num - {number} Signed integer that should be encoded.
|
*
|
* Returns:
|
* {string} The encoded string.
|
*/
|
encodeSignedInteger: function(num) {
|
var signedNum = num << 1;
|
if (num < 0) {
|
signedNum = ~(signedNum);
|
}
|
|
return this.encodeUnsignedInteger(signedNum);
|
},
|
|
|
/**
|
* Method: decodeSignedInteger
|
* Decode one single signed integer from an encoded string
|
*
|
* Parameters:
|
* encoded - {string} An encoded string.
|
*
|
* Returns:
|
* {number} The decoded signed integer.
|
*/
|
decodeSignedInteger: function(encoded) {
|
var result = this.decodeUnsignedInteger(encoded);
|
return ((result & 1) ? ~(result >> 1) : (result >> 1));
|
},
|
|
|
/**
|
* Method: encodeUnsignedInteger
|
* Encode one single unsigned integer and return an encoded string
|
*
|
* Parameters:
|
* num - {number} Unsigned integer that should be encoded.
|
*
|
* Returns:
|
* {string} The encoded string.
|
*/
|
encodeUnsignedInteger: function(num) {
|
var value, encoded = '';
|
while (num >= 0x20) {
|
value = (0x20 | (num & 0x1f)) + 63;
|
encoded += (String.fromCharCode(value));
|
num >>= 5;
|
}
|
value = num + 63;
|
encoded += (String.fromCharCode(value));
|
return encoded;
|
},
|
|
|
/**
|
* Method: decodeUnsignedInteger
|
* Decode one single unsigned integer from an encoded string
|
*
|
* Parameters:
|
* encoded - {string} An encoded string.
|
*
|
* Returns:
|
* {number} The decoded unsigned integer.
|
*/
|
decodeUnsignedInteger: function(encoded) {
|
var result = 0;
|
var shift = 0;
|
|
var encodedLength = encoded.length;
|
for (var i = 0; i < encodedLength; ++i) {
|
var b = encoded.charCodeAt(i) - 63;
|
|
result |= (b & 0x1f) << shift;
|
|
if (b < 0x20)
|
break;
|
|
shift += 5;
|
}
|
|
return result;
|
},
|
|
CLASS_NAME: "OpenLayers.Format.EncodedPolyline"
|
});
|
/* ======================================================================
|
OpenLayers/Control/Panel.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/Events/buttonclick.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.Panel
|
* The Panel control is a container for other controls. With it toolbars
|
* may be composed.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.Panel = OpenLayers.Class(OpenLayers.Control, {
|
/**
|
* Property: controls
|
* {Array(<OpenLayers.Control>)}
|
*/
|
controls: null,
|
|
/**
|
* APIProperty: autoActivate
|
* {Boolean} Activate the control when it is added to a map. Default is
|
* true.
|
*/
|
autoActivate: true,
|
|
/**
|
* APIProperty: defaultControl
|
* {<OpenLayers.Control>} The control which is activated when the control is
|
* activated (turned on), which also happens at instantiation.
|
* If <saveState> is true, <defaultControl> will be nullified after the
|
* first activation of the panel.
|
*/
|
defaultControl: null,
|
|
/**
|
* APIProperty: saveState
|
* {Boolean} If set to true, the active state of this panel's controls will
|
* be stored on panel deactivation, and restored on reactivation. Default
|
* is false.
|
*/
|
saveState: false,
|
|
/**
|
* APIProperty: allowDepress
|
* {Boolean} If is true the <OpenLayers.Control.TYPE_TOOL> controls can
|
* be deactivated by clicking the icon that represents them. Default
|
* is false.
|
*/
|
allowDepress: false,
|
|
/**
|
* Property: activeState
|
* {Object} stores the active state of this panel's controls.
|
*/
|
activeState: null,
|
|
/**
|
* Constructor: OpenLayers.Control.Panel
|
* Create a new control panel.
|
*
|
* Each control in the panel is represented by an icon. When clicking
|
* on an icon, the <activateControl> method is called.
|
*
|
* Specific properties for controls on a panel:
|
* type - {Number} One of <OpenLayers.Control.TYPE_TOOL>,
|
* <OpenLayers.Control.TYPE_TOGGLE>, <OpenLayers.Control.TYPE_BUTTON>.
|
* If not provided, <OpenLayers.Control.TYPE_TOOL> is assumed.
|
* title - {string} Text displayed when mouse is over the icon that
|
* represents the control.
|
*
|
* The <OpenLayers.Control.type> of a control determines the behavior when
|
* clicking its icon:
|
* <OpenLayers.Control.TYPE_TOOL> - The control is activated and other
|
* controls of this type in the same panel are deactivated. This is
|
* the default type.
|
* <OpenLayers.Control.TYPE_TOGGLE> - The active state of the control is
|
* toggled.
|
* <OpenLayers.Control.TYPE_BUTTON> - The
|
* <OpenLayers.Control.Button.trigger> method of the control is called,
|
* but its active state is not changed.
|
*
|
* If a control is <OpenLayers.Control.active>, it will be drawn with the
|
* olControl[Name]ItemActive class, otherwise with the
|
* olControl[Name]ItemInactive class.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be used
|
* to extend the control.
|
*/
|
initialize: function(options) {
|
OpenLayers.Control.prototype.initialize.apply(this, [options]);
|
this.controls = [];
|
this.activeState = {};
|
},
|
|
/**
|
* APIMethod: destroy
|
*/
|
destroy: function() {
|
if (this.map) {
|
this.map.events.unregister("buttonclick", this, this.onButtonClick);
|
}
|
OpenLayers.Control.prototype.destroy.apply(this, arguments);
|
for (var ctl, i = this.controls.length - 1; i >= 0; i--) {
|
ctl = this.controls[i];
|
if (ctl.events) {
|
ctl.events.un({
|
activate: this.iconOn,
|
deactivate: this.iconOff
|
});
|
}
|
ctl.panel_div = null;
|
}
|
this.activeState = null;
|
},
|
|
/**
|
* APIMethod: activate
|
*/
|
activate: function() {
|
if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {
|
var control;
|
for (var i=0, len=this.controls.length; i<len; i++) {
|
control = this.controls[i];
|
if (control === this.defaultControl ||
|
(this.saveState && this.activeState[control.id])) {
|
control.activate();
|
}
|
}
|
if (this.saveState === true) {
|
this.defaultControl = null;
|
}
|
this.redraw();
|
return true;
|
} else {
|
return false;
|
}
|
},
|
|
/**
|
* APIMethod: deactivate
|
*/
|
deactivate: function() {
|
if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {
|
var control;
|
for (var i=0, len=this.controls.length; i<len; i++) {
|
control = this.controls[i];
|
this.activeState[control.id] = control.deactivate();
|
}
|
this.redraw();
|
return true;
|
} else {
|
return false;
|
}
|
},
|
|
/**
|
* Method: draw
|
*
|
* Returns:
|
* {DOMElement}
|
*/
|
draw: function() {
|
OpenLayers.Control.prototype.draw.apply(this, arguments);
|
if (this.outsideViewport) {
|
this.events.attachToElement(this.div);
|
this.events.register("buttonclick", this, this.onButtonClick);
|
} else {
|
this.map.events.register("buttonclick", this, this.onButtonClick);
|
}
|
this.addControlsToMap(this.controls);
|
return this.div;
|
},
|
|
/**
|
* Method: redraw
|
*/
|
redraw: function() {
|
for (var l=this.div.childNodes.length, i=l-1; i>=0; i--) {
|
this.div.removeChild(this.div.childNodes[i]);
|
}
|
this.div.innerHTML = "";
|
if (this.active) {
|
for (var i=0, len=this.controls.length; i<len; i++) {
|
this.div.appendChild(this.controls[i].panel_div);
|
}
|
}
|
},
|
|
/**
|
* APIMethod: activateControl
|
* This method is called when the user click on the icon representing a
|
* control in the panel.
|
*
|
* Parameters:
|
* control - {<OpenLayers.Control>}
|
*/
|
activateControl: function (control) {
|
if (!this.active) { return false; }
|
if (control.type == OpenLayers.Control.TYPE_BUTTON) {
|
control.trigger();
|
return;
|
}
|
if (control.type == OpenLayers.Control.TYPE_TOGGLE) {
|
if (control.active) {
|
control.deactivate();
|
} else {
|
control.activate();
|
}
|
return;
|
}
|
if (this.allowDepress && control.active) {
|
control.deactivate();
|
} else {
|
var c;
|
for (var i=0, len=this.controls.length; i<len; i++) {
|
c = this.controls[i];
|
if (c != control &&
|
(c.type === OpenLayers.Control.TYPE_TOOL || c.type == null)) {
|
c.deactivate();
|
}
|
}
|
control.activate();
|
}
|
},
|
|
/**
|
* APIMethod: addControls
|
* To build a toolbar, you add a set of controls to it. addControls
|
* lets you add a single control or a list of controls to the
|
* Control Panel.
|
*
|
* Parameters:
|
* controls - {<OpenLayers.Control>} Controls to add in the panel.
|
*/
|
addControls: function(controls) {
|
if (!(OpenLayers.Util.isArray(controls))) {
|
controls = [controls];
|
}
|
this.controls = this.controls.concat(controls);
|
|
for (var i=0, len=controls.length; i<len; i++) {
|
var control = controls[i],
|
element = this.createControlMarkup(control);
|
OpenLayers.Element.addClass(element,
|
control.displayClass + "ItemInactive");
|
OpenLayers.Element.addClass(element, "olButton");
|
if (control.title != "" && !element.title) {
|
element.title = control.title;
|
}
|
control.panel_div = element;
|
}
|
|
if (this.map) { // map.addControl() has already been called on the panel
|
this.addControlsToMap(controls);
|
this.redraw();
|
}
|
},
|
|
/**
|
* APIMethod: createControlMarkup
|
* This function just creates a div for the control. If specific HTML
|
* markup is needed this function can be overridden in specific classes,
|
* or at panel instantiation time:
|
*
|
* Example:
|
* (code)
|
* var panel = new OpenLayers.Control.Panel({
|
* defaultControl: control,
|
* // ovverride createControlMarkup to create actual buttons
|
* // including texts wrapped into span elements.
|
* createControlMarkup: function(control) {
|
* var button = document.createElement('button'),
|
* span = document.createElement('span');
|
* if (control.text) {
|
* span.innerHTML = control.text;
|
* }
|
* return button;
|
* }
|
* });
|
* (end)
|
*
|
* Parameters:
|
* control - {<OpenLayers.Control>} The control to create the HTML
|
* markup for.
|
*
|
* Returns:
|
* {DOMElement} The markup.
|
*/
|
createControlMarkup: function(control) {
|
return document.createElement("div");
|
},
|
|
/**
|
* Method: addControlsToMap
|
* Only for internal use in draw() and addControls() methods.
|
*
|
* Parameters:
|
* controls - {Array(<OpenLayers.Control>)} Controls to add into map.
|
*/
|
addControlsToMap: function (controls) {
|
var control;
|
for (var i=0, len=controls.length; i<len; i++) {
|
control = controls[i];
|
if (control.autoActivate === true) {
|
control.autoActivate = false;
|
this.map.addControl(control);
|
control.autoActivate = true;
|
} else {
|
this.map.addControl(control);
|
control.deactivate();
|
}
|
control.events.on({
|
activate: this.iconOn,
|
deactivate: this.iconOff
|
});
|
}
|
},
|
|
/**
|
* Method: iconOn
|
* Internal use, for use only with "controls[i].events.on/un".
|
*/
|
iconOn: function() {
|
var d = this.panel_div; // "this" refers to a control on panel!
|
var re = new RegExp("\\b(" + this.displayClass + "Item)Inactive\\b");
|
d.className = d.className.replace(re, "$1Active");
|
},
|
|
/**
|
* Method: iconOff
|
* Internal use, for use only with "controls[i].events.on/un".
|
*/
|
iconOff: function() {
|
var d = this.panel_div; // "this" refers to a control on panel!
|
var re = new RegExp("\\b(" + this.displayClass + "Item)Active\\b");
|
d.className = d.className.replace(re, "$1Inactive");
|
},
|
|
/**
|
* Method: onButtonClick
|
*
|
* Parameters:
|
* evt - {Event}
|
*/
|
onButtonClick: function (evt) {
|
var controls = this.controls,
|
button = evt.buttonElement;
|
for (var i=controls.length-1; i>=0; --i) {
|
if (controls[i].panel_div === button) {
|
this.activateControl(controls[i]);
|
break;
|
}
|
}
|
},
|
|
/**
|
* APIMethod: getControlsBy
|
* Get a list of controls with properties matching the given criteria.
|
*
|
* Parameters:
|
* property - {String} A control property to be matched.
|
* match - {String | Object} A string to match. Can also be a regular
|
* expression literal or object. In addition, it can be any object
|
* with a method named test. For reqular expressions or other, if
|
* match.test(control[property]) evaluates to true, the control will be
|
* included in the array returned. If no controls are found, an empty
|
* array is returned.
|
*
|
* Returns:
|
* {Array(<OpenLayers.Control>)} A list of controls matching the given criteria.
|
* An empty array is returned if no matches are found.
|
*/
|
getControlsBy: function(property, match) {
|
var test = (typeof match.test == "function");
|
var found = OpenLayers.Array.filter(this.controls, function(item) {
|
return item[property] == match || (test && match.test(item[property]));
|
});
|
return found;
|
},
|
|
/**
|
* APIMethod: getControlsByName
|
* Get a list of contorls with names matching the given name.
|
*
|
* Parameters:
|
* match - {String | Object} A control name. The name can also be a regular
|
* expression literal or object. In addition, it can be any object
|
* with a method named test. For reqular expressions or other, if
|
* name.test(control.name) evaluates to true, the control will be included
|
* in the list of controls returned. If no controls are found, an empty
|
* array is returned.
|
*
|
* Returns:
|
* {Array(<OpenLayers.Control>)} A list of controls matching the given name.
|
* An empty array is returned if no matches are found.
|
*/
|
getControlsByName: function(match) {
|
return this.getControlsBy("name", match);
|
},
|
|
/**
|
* APIMethod: getControlsByClass
|
* Get a list of controls of a given type (CLASS_NAME).
|
*
|
* Parameters:
|
* match - {String | Object} A control class name. The type can also be a
|
* regular expression literal or object. In addition, it can be any
|
* object with a method named test. For reqular expressions or other,
|
* if type.test(control.CLASS_NAME) evaluates to true, the control will
|
* be included in the list of controls returned. If no controls are
|
* found, an empty array is returned.
|
*
|
* Returns:
|
* {Array(<OpenLayers.Control>)} A list of controls matching the given type.
|
* An empty array is returned if no matches are found.
|
*/
|
getControlsByClass: function(match) {
|
return this.getControlsBy("CLASS_NAME", match);
|
},
|
|
CLASS_NAME: "OpenLayers.Control.Panel"
|
});
|
|
/* ======================================================================
|
OpenLayers/Control/Button.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.Button
|
* The Button control is a very simple push-button, for use with
|
* <OpenLayers.Control.Panel>.
|
* When clicked, the function trigger() is executed.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*
|
* Use:
|
* (code)
|
* var button = new OpenLayers.Control.Button({
|
* displayClass: "MyButton", trigger: myFunction
|
* });
|
* panel.addControls([button]);
|
* (end)
|
*
|
* Will create a button with CSS class MyButtonItemInactive, that
|
* will call the function MyFunction() when clicked.
|
*/
|
OpenLayers.Control.Button = OpenLayers.Class(OpenLayers.Control, {
|
/**
|
* Property: type
|
* {Integer} OpenLayers.Control.TYPE_BUTTON.
|
*/
|
type: OpenLayers.Control.TYPE_BUTTON,
|
|
/**
|
* Method: trigger
|
* Called by a control panel when the button is clicked.
|
*/
|
trigger: function() {},
|
|
CLASS_NAME: "OpenLayers.Control.Button"
|
});
|
/* ======================================================================
|
OpenLayers/Control/ZoomIn.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control/Button.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.ZoomIn
|
* The ZoomIn control is a button to increase the zoom level of a map.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.ZoomIn = OpenLayers.Class(OpenLayers.Control.Button, {
|
|
/**
|
* Method: trigger
|
*/
|
trigger: function(){
|
if (this.map) {
|
this.map.zoomIn();
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Control.ZoomIn"
|
});
|
/* ======================================================================
|
OpenLayers/Control/ZoomOut.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control/Button.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.ZoomOut
|
* The ZoomOut control is a button to decrease the zoom level of a map.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.ZoomOut = OpenLayers.Class(OpenLayers.Control.Button, {
|
|
/**
|
* Method: trigger
|
*/
|
trigger: function(){
|
if (this.map) {
|
this.map.zoomOut();
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Control.ZoomOut"
|
});
|
/* ======================================================================
|
OpenLayers/Control/ZoomToMaxExtent.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control/Button.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.ZoomToMaxExtent
|
* The ZoomToMaxExtent control is a button that zooms out to the maximum
|
* extent of the map. It is designed to be used with a
|
* <OpenLayers.Control.Panel>.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.ZoomToMaxExtent = OpenLayers.Class(OpenLayers.Control.Button, {
|
|
/**
|
* Method: trigger
|
*
|
* Called whenever this control is being rendered inside of a panel and a
|
* click occurs on this controls element. Actually zooms to the maximum
|
* extent of this controls map.
|
*/
|
trigger: function() {
|
if (this.map) {
|
this.map.zoomToMaxExtent();
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Control.ZoomToMaxExtent"
|
});
|
/* ======================================================================
|
OpenLayers/Control/ZoomPanel.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control/Panel.js
|
* @requires OpenLayers/Control/ZoomIn.js
|
* @requires OpenLayers/Control/ZoomOut.js
|
* @requires OpenLayers/Control/ZoomToMaxExtent.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.ZoomPanel
|
* The ZoomPanel control is a compact collecton of 3 zoom controls: a
|
* <OpenLayers.Control.ZoomIn>, a <OpenLayers.Control.ZoomToMaxExtent>, and a
|
* <OpenLayers.Control.ZoomOut>. By default it is drawn in the upper left
|
* corner of the map.
|
*
|
* Note:
|
* If you wish to use this class with the default images and you want
|
* it to look nice in ie6, you should add the following, conditionally
|
* added css stylesheet to your HTML file:
|
*
|
* (code)
|
* <!--[if lte IE 6]>
|
* <link rel="stylesheet" href="../theme/default/ie6-style.css" type="text/css" />
|
* <![endif]-->
|
* (end)
|
*
|
* Inherits from:
|
* - <OpenLayers.Control.Panel>
|
*/
|
OpenLayers.Control.ZoomPanel = OpenLayers.Class(OpenLayers.Control.Panel, {
|
|
/**
|
* Constructor: OpenLayers.Control.ZoomPanel
|
* Add the three zooming controls.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be used
|
* to extend the control.
|
*/
|
initialize: function(options) {
|
OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);
|
this.addControls([
|
new OpenLayers.Control.ZoomIn(),
|
new OpenLayers.Control.ZoomToMaxExtent(),
|
new OpenLayers.Control.ZoomOut()
|
]);
|
},
|
|
CLASS_NAME: "OpenLayers.Control.ZoomPanel"
|
});
|
/* ======================================================================
|
OpenLayers/Layer/HTTPRequest.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Layer.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.HTTPRequest
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer>
|
*/
|
OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, {
|
|
/**
|
* Constant: URL_HASH_FACTOR
|
* {Float} Used to hash URL param strings for multi-WMS server selection.
|
* Set to the Golden Ratio per Knuth's recommendation.
|
*/
|
URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2,
|
|
/**
|
* Property: url
|
* {Array(String) or String} This is either an array of url strings or
|
* a single url string.
|
*/
|
url: null,
|
|
/**
|
* Property: params
|
* {Object} Hashtable of key/value parameters
|
*/
|
params: null,
|
|
/**
|
* APIProperty: reproject
|
* *Deprecated*. See http://docs.openlayers.org/library/spherical_mercator.html
|
* for information on the replacement for this functionality.
|
* {Boolean} Whether layer should reproject itself based on base layer
|
* locations. This allows reprojection onto commercial layers.
|
* Default is false: Most layers can't reproject, but layers
|
* which can create non-square geographic pixels can, like WMS.
|
*
|
*/
|
reproject: false,
|
|
/**
|
* Constructor: OpenLayers.Layer.HTTPRequest
|
*
|
* Parameters:
|
* name - {String}
|
* url - {Array(String) or String}
|
* params - {Object}
|
* options - {Object} Hashtable of extra options to tag onto the layer
|
*/
|
initialize: function(name, url, params, options) {
|
OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);
|
this.url = url;
|
if (!this.params) {
|
this.params = OpenLayers.Util.extend({}, params);
|
}
|
},
|
|
/**
|
* APIMethod: destroy
|
*/
|
destroy: function() {
|
this.url = null;
|
this.params = null;
|
OpenLayers.Layer.prototype.destroy.apply(this, arguments);
|
},
|
|
/**
|
* APIMethod: clone
|
*
|
* Parameters:
|
* obj - {Object}
|
*
|
* Returns:
|
* {<OpenLayers.Layer.HTTPRequest>} An exact clone of this
|
* <OpenLayers.Layer.HTTPRequest>
|
*/
|
clone: function (obj) {
|
|
if (obj == null) {
|
obj = new OpenLayers.Layer.HTTPRequest(this.name,
|
this.url,
|
this.params,
|
this.getOptions());
|
}
|
|
//get all additions from superclasses
|
obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
|
|
// copy/set any non-init, non-simple values here
|
|
return obj;
|
},
|
|
/**
|
* APIMethod: setUrl
|
*
|
* Parameters:
|
* newUrl - {String}
|
*/
|
setUrl: function(newUrl) {
|
this.url = newUrl;
|
},
|
|
/**
|
* APIMethod: mergeNewParams
|
*
|
* Parameters:
|
* newParams - {Object}
|
*
|
* Returns:
|
* redrawn: {Boolean} whether the layer was actually redrawn.
|
*/
|
mergeNewParams:function(newParams) {
|
this.params = OpenLayers.Util.extend(this.params, newParams);
|
var ret = this.redraw();
|
if(this.map != null) {
|
this.map.events.triggerEvent("changelayer", {
|
layer: this,
|
property: "params"
|
});
|
}
|
return ret;
|
},
|
|
/**
|
* APIMethod: redraw
|
* Redraws the layer. Returns true if the layer was redrawn, false if not.
|
*
|
* Parameters:
|
* force - {Boolean} Force redraw by adding random parameter.
|
*
|
* Returns:
|
* {Boolean} The layer was redrawn.
|
*/
|
redraw: function(force) {
|
if (force) {
|
return this.mergeNewParams({"_olSalt": Math.random()});
|
} else {
|
return OpenLayers.Layer.prototype.redraw.apply(this, []);
|
}
|
},
|
|
/**
|
* Method: selectUrl
|
* selectUrl() implements the standard floating-point multiplicative
|
* hash function described by Knuth, and hashes the contents of the
|
* given param string into a float between 0 and 1. This float is then
|
* scaled to the size of the provided urls array, and used to select
|
* a URL.
|
*
|
* Parameters:
|
* paramString - {String}
|
* urls - {Array(String)}
|
*
|
* Returns:
|
* {String} An entry from the urls array, deterministically selected based
|
* on the paramString.
|
*/
|
selectUrl: function(paramString, urls) {
|
var product = 1;
|
for (var i=0, len=paramString.length; i<len; i++) {
|
product *= paramString.charCodeAt(i) * this.URL_HASH_FACTOR;
|
product -= Math.floor(product);
|
}
|
return urls[Math.floor(product * urls.length)];
|
},
|
|
/**
|
* Method: getFullRequestString
|
* Combine url with layer's params and these newParams.
|
*
|
* does checking on the serverPath variable, allowing for cases when it
|
* is supplied with trailing ? or &, as well as cases where not.
|
*
|
* return in formatted string like this:
|
* "server?key1=value1&key2=value2&key3=value3"
|
*
|
* WARNING: The altUrl parameter is deprecated and will be removed in 3.0.
|
*
|
* Parameters:
|
* newParams - {Object}
|
* altUrl - {String} Use this as the url instead of the layer's url
|
*
|
* Returns:
|
* {String}
|
*/
|
getFullRequestString:function(newParams, altUrl) {
|
|
// if not altUrl passed in, use layer's url
|
var url = altUrl || this.url;
|
|
// create a new params hashtable with all the layer params and the
|
// new params together. then convert to string
|
var allParams = OpenLayers.Util.extend({}, this.params);
|
allParams = OpenLayers.Util.extend(allParams, newParams);
|
var paramsString = OpenLayers.Util.getParameterString(allParams);
|
|
// if url is not a string, it should be an array of strings,
|
// in which case we will deterministically select one of them in
|
// order to evenly distribute requests to different urls.
|
//
|
if (OpenLayers.Util.isArray(url)) {
|
url = this.selectUrl(paramsString, url);
|
}
|
|
// ignore parameters that are already in the url search string
|
var urlParams =
|
OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));
|
for(var key in allParams) {
|
if(key.toUpperCase() in urlParams) {
|
delete allParams[key];
|
}
|
}
|
paramsString = OpenLayers.Util.getParameterString(allParams);
|
|
return OpenLayers.Util.urlAppend(url, paramsString);
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.HTTPRequest"
|
});
|
/* ======================================================================
|
OpenLayers/Tile.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
* @requires OpenLayers/Util.js
|
*/
|
|
/**
|
* Class: OpenLayers.Tile
|
* This is a class designed to designate a single tile, however
|
* it is explicitly designed to do relatively little. Tiles store
|
* information about themselves -- such as the URL that they are related
|
* to, and their size - but do not add themselves to the layer div
|
* automatically, for example. Create a new tile with the
|
* <OpenLayers.Tile> constructor, or a subclass.
|
*
|
* TBD 3.0 - remove reference to url in above paragraph
|
*
|
*/
|
OpenLayers.Tile = OpenLayers.Class({
|
|
/**
|
* APIProperty: events
|
* {<OpenLayers.Events>} An events object that handles all
|
* events on the tile.
|
*
|
* Register a listener for a particular event with the following syntax:
|
* (code)
|
* tile.events.register(type, obj, listener);
|
* (end)
|
*
|
* Supported event types:
|
* beforedraw - Triggered before the tile is drawn. Used to defer
|
* drawing to an animation queue. To defer drawing, listeners need
|
* to return false, which will abort drawing. The queue handler needs
|
* to call <draw>(true) to actually draw the tile.
|
* loadstart - Triggered when tile loading starts.
|
* loadend - Triggered when tile loading ends.
|
* loaderror - Triggered before the loadend event (i.e. when the tile is
|
* still hidden) if the tile could not be loaded.
|
* reload - Triggered when an already loading tile is reloaded.
|
* unload - Triggered before a tile is unloaded.
|
*/
|
events: null,
|
|
/**
|
* APIProperty: eventListeners
|
* {Object} If set as an option at construction, the eventListeners
|
* object will be registered with <OpenLayers.Events.on>. Object
|
* structure must be a listeners object as shown in the example for
|
* the events.on method.
|
*
|
* This options can be set in the ``tileOptions`` option from
|
* <OpenLayers.Layer.Grid>. For example, to be notified of the
|
* ``loadend`` event of each tiles:
|
* (code)
|
* new OpenLayers.Layer.OSM('osm', 'http://tile.openstreetmap.org/${z}/${x}/${y}.png', {
|
* tileOptions: {
|
* eventListeners: {
|
* 'loadend': function(evt) {
|
* // do something on loadend
|
* }
|
* }
|
* }
|
* });
|
* (end)
|
*/
|
eventListeners: null,
|
|
/**
|
* Property: id
|
* {String} null
|
*/
|
id: null,
|
|
/**
|
* Property: layer
|
* {<OpenLayers.Layer>} layer the tile is attached to
|
*/
|
layer: null,
|
|
/**
|
* Property: url
|
* {String} url of the request.
|
*
|
* TBD 3.0
|
* Deprecated. The base tile class does not need an url. This should be
|
* handled in subclasses. Does not belong here.
|
*/
|
url: null,
|
|
/**
|
* APIProperty: bounds
|
* {<OpenLayers.Bounds>} null
|
*/
|
bounds: null,
|
|
/**
|
* Property: size
|
* {<OpenLayers.Size>} null
|
*/
|
size: null,
|
|
/**
|
* Property: position
|
* {<OpenLayers.Pixel>} Top Left pixel of the tile
|
*/
|
position: null,
|
|
/**
|
* Property: isLoading
|
* {Boolean} Is the tile loading?
|
*/
|
isLoading: false,
|
|
/** TBD 3.0 -- remove 'url' from the list of parameters to the constructor.
|
* there is no need for the base tile class to have a url.
|
*/
|
|
/**
|
* Constructor: OpenLayers.Tile
|
* Constructor for a new <OpenLayers.Tile> instance.
|
*
|
* Parameters:
|
* layer - {<OpenLayers.Layer>} layer that the tile will go in.
|
* position - {<OpenLayers.Pixel>}
|
* bounds - {<OpenLayers.Bounds>}
|
* url - {<String>}
|
* size - {<OpenLayers.Size>}
|
* options - {Object}
|
*/
|
initialize: function(layer, position, bounds, url, size, options) {
|
this.layer = layer;
|
this.position = position.clone();
|
this.setBounds(bounds);
|
this.url = url;
|
if (size) {
|
this.size = size.clone();
|
}
|
|
//give the tile a unique id based on its BBOX.
|
this.id = OpenLayers.Util.createUniqueID("Tile_");
|
|
OpenLayers.Util.extend(this, options);
|
|
this.events = new OpenLayers.Events(this);
|
if (this.eventListeners instanceof Object) {
|
this.events.on(this.eventListeners);
|
}
|
},
|
|
/**
|
* Method: unload
|
* Call immediately before destroying if you are listening to tile
|
* events, so that counters are properly handled if tile is still
|
* loading at destroy-time. Will only fire an event if the tile is
|
* still loading.
|
*/
|
unload: function() {
|
if (this.isLoading) {
|
this.isLoading = false;
|
this.events.triggerEvent("unload");
|
}
|
},
|
|
/**
|
* APIMethod: destroy
|
* Nullify references to prevent circular references and memory leaks.
|
*/
|
destroy:function() {
|
this.layer = null;
|
this.bounds = null;
|
this.size = null;
|
this.position = null;
|
|
if (this.eventListeners) {
|
this.events.un(this.eventListeners);
|
}
|
this.events.destroy();
|
this.eventListeners = null;
|
this.events = null;
|
},
|
|
/**
|
* Method: draw
|
* Clear whatever is currently in the tile, then return whether or not
|
* it should actually be re-drawn. This is an example implementation
|
* that can be overridden by subclasses. The minimum thing to do here
|
* is to call <clear> and return the result from <shouldDraw>.
|
*
|
* Parameters:
|
* force - {Boolean} If true, the tile will not be cleared and no beforedraw
|
* event will be fired. This is used for drawing tiles asynchronously
|
* after drawing has been cancelled by returning false from a beforedraw
|
* listener.
|
*
|
* Returns:
|
* {Boolean} Whether or not the tile should actually be drawn. Returns null
|
* if a beforedraw listener returned false.
|
*/
|
draw: function(force) {
|
if (!force) {
|
//clear tile's contents and mark as not drawn
|
this.clear();
|
}
|
var draw = this.shouldDraw();
|
if (draw && !force && this.events.triggerEvent("beforedraw") === false) {
|
draw = null;
|
}
|
return draw;
|
},
|
|
/**
|
* Method: shouldDraw
|
* Return whether or not the tile should actually be (re-)drawn. The only
|
* case where we *wouldn't* want to draw the tile is if the tile is outside
|
* its layer's maxExtent
|
*
|
* Returns:
|
* {Boolean} Whether or not the tile should actually be drawn.
|
*/
|
shouldDraw: function() {
|
var withinMaxExtent = false,
|
maxExtent = this.layer.maxExtent;
|
if (maxExtent) {
|
var map = this.layer.map;
|
var worldBounds = map.baseLayer.wrapDateLine && map.getMaxExtent();
|
if (this.bounds.intersectsBounds(maxExtent, {inclusive: false, worldBounds: worldBounds})) {
|
withinMaxExtent = true;
|
}
|
}
|
|
return withinMaxExtent || this.layer.displayOutsideMaxExtent;
|
},
|
|
/**
|
* Method: setBounds
|
* Sets the bounds on this instance
|
*
|
* Parameters:
|
* bounds {<OpenLayers.Bounds>}
|
*/
|
setBounds: function(bounds) {
|
bounds = bounds.clone();
|
if (this.layer.map.baseLayer.wrapDateLine) {
|
var worldExtent = this.layer.map.getMaxExtent(),
|
tolerance = this.layer.map.getResolution();
|
bounds = bounds.wrapDateLine(worldExtent, {
|
leftTolerance: tolerance,
|
rightTolerance: tolerance
|
});
|
}
|
this.bounds = bounds;
|
},
|
|
/**
|
* Method: moveTo
|
* Reposition the tile.
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
* position - {<OpenLayers.Pixel>}
|
* redraw - {Boolean} Call draw method on tile after moving.
|
* Default is true
|
*/
|
moveTo: function (bounds, position, redraw) {
|
if (redraw == null) {
|
redraw = true;
|
}
|
|
this.setBounds(bounds);
|
this.position = position.clone();
|
if (redraw) {
|
this.draw();
|
}
|
},
|
|
/**
|
* Method: clear
|
* Clear the tile of any bounds/position-related data so that it can
|
* be reused in a new location.
|
*/
|
clear: function(draw) {
|
// to be extended by subclasses
|
},
|
|
CLASS_NAME: "OpenLayers.Tile"
|
});
|
/* ======================================================================
|
OpenLayers/Tile/Image.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Tile.js
|
* @requires OpenLayers/Animation.js
|
* @requires OpenLayers/Util.js
|
*/
|
|
/**
|
* Class: OpenLayers.Tile.Image
|
* Instances of OpenLayers.Tile.Image are used to manage the image tiles
|
* used by various layers. Create a new image tile with the
|
* <OpenLayers.Tile.Image> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Tile>
|
*/
|
OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, {
|
|
/**
|
* APIProperty: events
|
* {<OpenLayers.Events>} An events object that handles all
|
* events on the tile.
|
*
|
* Register a listener for a particular event with the following syntax:
|
* (code)
|
* tile.events.register(type, obj, listener);
|
* (end)
|
*
|
* Supported event types (in addition to the <OpenLayers.Tile> events):
|
* beforeload - Triggered before an image is prepared for loading, when the
|
* url for the image is known already. Listeners may call <setImage> on
|
* the tile instance. If they do so, that image will be used and no new
|
* one will be created.
|
*/
|
|
/**
|
* APIProperty: url
|
* {String} The URL of the image being requested. No default. Filled in by
|
* layer.getURL() function. May be modified by loadstart listeners.
|
*/
|
url: null,
|
|
/**
|
* Property: imgDiv
|
* {HTMLImageElement} The image for this tile.
|
*/
|
imgDiv: null,
|
|
/**
|
* Property: frame
|
* {DOMElement} The image element is appended to the frame. Any gutter on
|
* the image will be hidden behind the frame. If no gutter is set,
|
* this will be null.
|
*/
|
frame: null,
|
|
/**
|
* Property: imageReloadAttempts
|
* {Integer} Attempts to load the image.
|
*/
|
imageReloadAttempts: null,
|
|
/**
|
* Property: layerAlphaHack
|
* {Boolean} True if the png alpha hack needs to be applied on the layer's div.
|
*/
|
layerAlphaHack: null,
|
|
/**
|
* Property: asyncRequestId
|
* {Integer} ID of an request to see if request is still valid. This is a
|
* number which increments by 1 for each asynchronous request.
|
*/
|
asyncRequestId: null,
|
|
/**
|
* APIProperty: maxGetUrlLength
|
* {Number} If set, requests that would result in GET urls with more
|
* characters than the number provided will be made using form-encoded
|
* HTTP POST. It is good practice to avoid urls that are longer than 2048
|
* characters.
|
*
|
* Caution:
|
* Older versions of Gecko based browsers (e.g. Firefox < 3.5) and most
|
* Opera versions do not fully support this option. On all browsers,
|
* transition effects are not supported if POST requests are used.
|
*/
|
maxGetUrlLength: null,
|
|
/**
|
* Property: canvasContext
|
* {CanvasRenderingContext2D} A canvas context associated with
|
* the tile image.
|
*/
|
canvasContext: null,
|
|
/**
|
* APIProperty: crossOriginKeyword
|
* The value of the crossorigin keyword to use when loading images. This is
|
* only relevant when using <getCanvasContext> for tiles from remote
|
* origins and should be set to either 'anonymous' or 'use-credentials'
|
* for servers that send Access-Control-Allow-Origin headers with their
|
* tiles.
|
*/
|
crossOriginKeyword: null,
|
|
/** TBD 3.0 - reorder the parameters to the init function to remove
|
* URL. the getUrl() function on the layer gets called on
|
* each draw(), so no need to specify it here.
|
*/
|
|
/**
|
* Constructor: OpenLayers.Tile.Image
|
* Constructor for a new <OpenLayers.Tile.Image> instance.
|
*
|
* Parameters:
|
* layer - {<OpenLayers.Layer>} layer that the tile will go in.
|
* position - {<OpenLayers.Pixel>}
|
* bounds - {<OpenLayers.Bounds>}
|
* url - {<String>} Deprecated. Remove me in 3.0.
|
* size - {<OpenLayers.Size>}
|
* options - {Object}
|
*/
|
initialize: function(layer, position, bounds, url, size, options) {
|
OpenLayers.Tile.prototype.initialize.apply(this, arguments);
|
|
this.url = url; //deprecated remove me
|
|
this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack();
|
|
if (this.maxGetUrlLength != null || this.layer.gutter || this.layerAlphaHack) {
|
// only create frame if it's needed
|
this.frame = document.createElement("div");
|
this.frame.style.position = "absolute";
|
this.frame.style.overflow = "hidden";
|
}
|
if (this.maxGetUrlLength != null) {
|
OpenLayers.Util.extend(this, OpenLayers.Tile.Image.IFrame);
|
}
|
},
|
|
/**
|
* APIMethod: destroy
|
* nullify references to prevent circular references and memory leaks
|
*/
|
destroy: function() {
|
if (this.imgDiv) {
|
this.clear();
|
this.imgDiv = null;
|
this.frame = null;
|
}
|
// don't handle async requests any more
|
this.asyncRequestId = null;
|
OpenLayers.Tile.prototype.destroy.apply(this, arguments);
|
},
|
|
/**
|
* Method: draw
|
* Check that a tile should be drawn, and draw it.
|
*
|
* Returns:
|
* {Boolean} Was a tile drawn? Or null if a beforedraw listener returned
|
* false.
|
*/
|
draw: function() {
|
var shouldDraw = OpenLayers.Tile.prototype.draw.apply(this, arguments);
|
if (shouldDraw) {
|
// The layer's reproject option is deprecated.
|
if (this.layer != this.layer.map.baseLayer && this.layer.reproject) {
|
// getBoundsFromBaseLayer is defined in deprecated.js.
|
this.bounds = this.getBoundsFromBaseLayer(this.position);
|
}
|
if (this.isLoading) {
|
//if we're already loading, send 'reload' instead of 'loadstart'.
|
this._loadEvent = "reload";
|
} else {
|
this.isLoading = true;
|
this._loadEvent = "loadstart";
|
}
|
this.renderTile();
|
this.positionTile();
|
} else if (shouldDraw === false) {
|
this.unload();
|
}
|
return shouldDraw;
|
},
|
|
/**
|
* Method: renderTile
|
* Internal function to actually initialize the image tile,
|
* position it correctly, and set its url.
|
*/
|
renderTile: function() {
|
if (this.layer.async) {
|
// Asynchronous image requests call the asynchronous getURL method
|
// on the layer to fetch an image that covers 'this.bounds'.
|
var id = this.asyncRequestId = (this.asyncRequestId || 0) + 1;
|
this.layer.getURLasync(this.bounds, function(url) {
|
if (id == this.asyncRequestId) {
|
this.url = url;
|
this.initImage();
|
}
|
}, this);
|
} else {
|
// synchronous image requests get the url immediately.
|
this.url = this.layer.getURL(this.bounds);
|
this.initImage();
|
}
|
},
|
|
/**
|
* Method: positionTile
|
* Using the properties currenty set on the layer, position the tile correctly.
|
* This method is used both by the async and non-async versions of the Tile.Image
|
* code.
|
*/
|
positionTile: function() {
|
var style = this.getTile().style,
|
size = this.frame ? this.size :
|
this.layer.getImageSize(this.bounds),
|
ratio = 1;
|
if (this.layer instanceof OpenLayers.Layer.Grid) {
|
ratio = this.layer.getServerResolution() / this.layer.map.getResolution();
|
}
|
style.left = this.position.x + "px";
|
style.top = this.position.y + "px";
|
style.width = Math.round(ratio * size.w) + "px";
|
style.height = Math.round(ratio * size.h) + "px";
|
},
|
|
/**
|
* Method: clear
|
* Remove the tile from the DOM, clear it of any image related data so that
|
* it can be reused in a new location.
|
*/
|
clear: function() {
|
OpenLayers.Tile.prototype.clear.apply(this, arguments);
|
var img = this.imgDiv;
|
if (img) {
|
var tile = this.getTile();
|
if (tile.parentNode === this.layer.div) {
|
this.layer.div.removeChild(tile);
|
}
|
this.setImgSrc();
|
if (this.layerAlphaHack === true) {
|
img.style.filter = "";
|
}
|
OpenLayers.Element.removeClass(img, "olImageLoadError");
|
}
|
this.canvasContext = null;
|
},
|
|
/**
|
* Method: getImage
|
* Returns or creates and returns the tile image.
|
*/
|
getImage: function() {
|
if (!this.imgDiv) {
|
this.imgDiv = OpenLayers.Tile.Image.IMAGE.cloneNode(false);
|
|
var style = this.imgDiv.style;
|
if (this.frame) {
|
var left = 0, top = 0;
|
if (this.layer.gutter) {
|
left = this.layer.gutter / this.layer.tileSize.w * 100;
|
top = this.layer.gutter / this.layer.tileSize.h * 100;
|
}
|
style.left = -left + "%";
|
style.top = -top + "%";
|
style.width = (2 * left + 100) + "%";
|
style.height = (2 * top + 100) + "%";
|
}
|
style.visibility = "hidden";
|
style.opacity = 0;
|
if (this.layer.opacity < 1) {
|
style.filter = 'alpha(opacity=' +
|
(this.layer.opacity * 100) +
|
')';
|
}
|
style.position = "absolute";
|
if (this.layerAlphaHack) {
|
// move the image out of sight
|
style.paddingTop = style.height;
|
style.height = "0";
|
style.width = "100%";
|
}
|
if (this.frame) {
|
this.frame.appendChild(this.imgDiv);
|
}
|
}
|
|
return this.imgDiv;
|
},
|
|
/**
|
* APIMethod: setImage
|
* Sets the image element for this tile. This method should only be called
|
* from beforeload listeners.
|
*
|
* Parameters
|
* img - {HTMLImageElement} The image to use for this tile.
|
*/
|
setImage: function(img) {
|
this.imgDiv = img;
|
},
|
|
/**
|
* Method: initImage
|
* Creates the content for the frame on the tile.
|
*/
|
initImage: function() {
|
if (!this.url && !this.imgDiv) {
|
// fast path out - if there is no tile url and no previous image
|
this.isLoading = false;
|
return;
|
}
|
this.events.triggerEvent('beforeload');
|
this.layer.div.appendChild(this.getTile());
|
this.events.triggerEvent(this._loadEvent);
|
var img = this.getImage();
|
var src = img.getAttribute('src') || '';
|
if (this.url && OpenLayers.Util.isEquivalentUrl(src, this.url)) {
|
this._loadTimeout = window.setTimeout(
|
OpenLayers.Function.bind(this.onImageLoad, this), 0
|
);
|
} else {
|
this.stopLoading();
|
if (this.crossOriginKeyword) {
|
img.removeAttribute("crossorigin");
|
}
|
OpenLayers.Event.observe(img, "load",
|
OpenLayers.Function.bind(this.onImageLoad, this)
|
);
|
OpenLayers.Event.observe(img, "error",
|
OpenLayers.Function.bind(this.onImageError, this)
|
);
|
this.imageReloadAttempts = 0;
|
this.setImgSrc(this.url);
|
}
|
},
|
|
/**
|
* Method: setImgSrc
|
* Sets the source for the tile image
|
*
|
* Parameters:
|
* url - {String} or undefined to hide the image
|
*/
|
setImgSrc: function(url) {
|
var img = this.imgDiv;
|
if (url) {
|
img.style.visibility = 'hidden';
|
img.style.opacity = 0;
|
// don't set crossOrigin if the url is a data URL
|
if (this.crossOriginKeyword) {
|
if (url.substr(0, 5) !== 'data:') {
|
img.setAttribute("crossorigin", this.crossOriginKeyword);
|
} else {
|
img.removeAttribute("crossorigin");
|
}
|
}
|
img.src = url;
|
} else {
|
// Remove reference to the image, and leave it to the browser's
|
// caching and garbage collection.
|
this.stopLoading();
|
this.imgDiv = null;
|
if (img.parentNode) {
|
img.parentNode.removeChild(img);
|
}
|
}
|
},
|
|
/**
|
* Method: getTile
|
* Get the tile's markup.
|
*
|
* Returns:
|
* {DOMElement} The tile's markup
|
*/
|
getTile: function() {
|
return this.frame ? this.frame : this.getImage();
|
},
|
|
/**
|
* Method: createBackBuffer
|
* Create a backbuffer for this tile. A backbuffer isn't exactly a clone
|
* of the tile's markup, because we want to avoid the reloading of the
|
* image. So we clone the frame, and steal the image from the tile.
|
*
|
* Returns:
|
* {DOMElement} The markup, or undefined if the tile has no image
|
* or if it's currently loading.
|
*/
|
createBackBuffer: function() {
|
if (!this.imgDiv || this.isLoading) {
|
return;
|
}
|
var backBuffer;
|
if (this.frame) {
|
backBuffer = this.frame.cloneNode(false);
|
backBuffer.appendChild(this.imgDiv);
|
} else {
|
backBuffer = this.imgDiv;
|
}
|
this.imgDiv = null;
|
return backBuffer;
|
},
|
|
/**
|
* Method: onImageLoad
|
* Handler for the image onload event
|
*/
|
onImageLoad: function() {
|
var img = this.imgDiv;
|
this.stopLoading();
|
img.style.visibility = 'inherit';
|
img.style.opacity = this.layer.opacity;
|
this.isLoading = false;
|
this.canvasContext = null;
|
this.events.triggerEvent("loadend");
|
|
if (this.layerAlphaHack === true) {
|
img.style.filter =
|
"progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" +
|
img.src + "', sizingMethod='scale')";
|
}
|
},
|
|
/**
|
* Method: onImageError
|
* Handler for the image onerror event
|
*/
|
onImageError: function() {
|
var img = this.imgDiv;
|
if (img.src != null) {
|
this.imageReloadAttempts++;
|
if (this.imageReloadAttempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) {
|
this.setImgSrc(this.layer.getURL(this.bounds));
|
} else {
|
OpenLayers.Element.addClass(img, "olImageLoadError");
|
this.events.triggerEvent("loaderror");
|
this.onImageLoad();
|
}
|
}
|
},
|
|
/**
|
* Method: stopLoading
|
* Stops a loading sequence so <onImageLoad> won't be executed.
|
*/
|
stopLoading: function() {
|
OpenLayers.Event.stopObservingElement(this.imgDiv);
|
window.clearTimeout(this._loadTimeout);
|
delete this._loadTimeout;
|
},
|
|
/**
|
* APIMethod: getCanvasContext
|
* Returns a canvas context associated with the tile image (with
|
* the image drawn on it).
|
* Returns undefined if the browser does not support canvas, if
|
* the tile has no image or if it's currently loading.
|
*
|
* The function returns a canvas context instance but the
|
* underlying canvas is still available in the 'canvas' property:
|
* (code)
|
* var context = tile.getCanvasContext();
|
* if (context) {
|
* var data = context.canvas.toDataURL('image/jpeg');
|
* }
|
* (end)
|
*
|
* Returns:
|
* {Boolean}
|
*/
|
getCanvasContext: function() {
|
if (OpenLayers.CANVAS_SUPPORTED && this.imgDiv && !this.isLoading) {
|
if (!this.canvasContext) {
|
var canvas = document.createElement("canvas");
|
canvas.width = this.size.w;
|
canvas.height = this.size.h;
|
this.canvasContext = canvas.getContext("2d");
|
this.canvasContext.drawImage(this.imgDiv, 0, 0);
|
}
|
return this.canvasContext;
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Tile.Image"
|
|
});
|
|
/**
|
* Constant: OpenLayers.Tile.Image.IMAGE
|
* {HTMLImageElement} The image for a tile.
|
*/
|
OpenLayers.Tile.Image.IMAGE = (function() {
|
var img = new Image();
|
img.className = "olTileImage";
|
// avoid image gallery menu in IE6
|
img.galleryImg = "no";
|
return img;
|
}());
|
|
/* ======================================================================
|
OpenLayers/Layer/Grid.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Layer/HTTPRequest.js
|
* @requires OpenLayers/Tile/Image.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.Grid
|
* Base class for layers that use a lattice of tiles. Create a new grid
|
* layer with the <OpenLayers.Layer.Grid> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer.HTTPRequest>
|
*/
|
OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
|
|
/**
|
* APIProperty: tileSize
|
* {<OpenLayers.Size>}
|
*/
|
tileSize: null,
|
|
/**
|
* Property: tileOriginCorner
|
* {String} If the <tileOrigin> property is not provided, the tile origin
|
* will be derived from the layer's <maxExtent>. The corner of the
|
* <maxExtent> used is determined by this property. Acceptable values
|
* are "tl" (top left), "tr" (top right), "bl" (bottom left), and "br"
|
* (bottom right). Default is "bl".
|
*/
|
tileOriginCorner: "bl",
|
|
/**
|
* APIProperty: tileOrigin
|
* {<OpenLayers.LonLat>} Optional origin for aligning the grid of tiles.
|
* If provided, requests for tiles at all resolutions will be aligned
|
* with this location (no tiles shall overlap this location). If
|
* not provided, the grid of tiles will be aligned with the layer's
|
* <maxExtent>. Default is ``null``.
|
*/
|
tileOrigin: null,
|
|
/** APIProperty: tileOptions
|
* {Object} optional configuration options for <OpenLayers.Tile> instances
|
* created by this Layer, if supported by the tile class.
|
*/
|
tileOptions: null,
|
|
/**
|
* APIProperty: tileClass
|
* {<OpenLayers.Tile>} The tile class to use for this layer.
|
* Defaults is OpenLayers.Tile.Image.
|
*/
|
tileClass: OpenLayers.Tile.Image,
|
|
/**
|
* Property: grid
|
* {Array(Array(<OpenLayers.Tile>))} This is an array of rows, each row is
|
* an array of tiles.
|
*/
|
grid: null,
|
|
/**
|
* APIProperty: singleTile
|
* {Boolean} Moves the layer into single-tile mode, meaning that one tile
|
* will be loaded. The tile's size will be determined by the 'ratio'
|
* property. When the tile is dragged such that it does not cover the
|
* entire viewport, it is reloaded.
|
*/
|
singleTile: false,
|
|
/** APIProperty: ratio
|
* {Float} Used only when in single-tile mode, this specifies the
|
* ratio of the size of the single tile to the size of the map.
|
* Default value is 1.5.
|
*/
|
ratio: 1.5,
|
|
/**
|
* APIProperty: buffer
|
* {Integer} Used only when in gridded mode, this specifies the number of
|
* extra rows and colums of tiles on each side which will
|
* surround the minimum grid tiles to cover the map.
|
* For very slow loading layers, a larger value may increase
|
* performance somewhat when dragging, but will increase bandwidth
|
* use significantly.
|
*/
|
buffer: 0,
|
|
/**
|
* APIProperty: transitionEffect
|
* {String} The transition effect to use when the map is zoomed.
|
* Two posible values:
|
*
|
* "resize" - Existing tiles are resized on zoom to provide a visual
|
* effect of the zoom having taken place immediately. As the
|
* new tiles become available, they are drawn on top of the
|
* resized tiles (this is the default setting).
|
* "map-resize" - Existing tiles are resized on zoom and placed below the
|
* base layer. New tiles for the base layer will cover existing tiles.
|
* This setting is recommended when having an overlay duplicated during
|
* the transition is undesirable (e.g. street labels or big transparent
|
* fills).
|
* null - No transition effect.
|
*
|
* Using "resize" on non-opaque layers can cause undesired visual
|
* effects. Set transitionEffect to null in this case.
|
*/
|
transitionEffect: "resize",
|
|
/**
|
* APIProperty: numLoadingTiles
|
* {Integer} How many tiles are still loading?
|
*/
|
numLoadingTiles: 0,
|
|
/**
|
* Property: serverResolutions
|
* {Array(Number}} This property is documented in subclasses as
|
* an API property.
|
*/
|
serverResolutions: null,
|
|
/**
|
* Property: loading
|
* {Boolean} Indicates if tiles are being loaded.
|
*/
|
loading: false,
|
|
/**
|
* Property: backBuffer
|
* {DOMElement} The back buffer.
|
*/
|
backBuffer: null,
|
|
/**
|
* Property: gridResolution
|
* {Number} The resolution of the current grid. Used for backbuffer and
|
* client zoom. This property is updated every time the grid is
|
* initialized.
|
*/
|
gridResolution: null,
|
|
/**
|
* Property: backBufferResolution
|
* {Number} The resolution of the current back buffer. This property is
|
* updated each time a back buffer is created.
|
*/
|
backBufferResolution: null,
|
|
/**
|
* Property: backBufferLonLat
|
* {Object} The top-left corner of the current back buffer. Includes lon
|
* and lat properties. This object is updated each time a back buffer
|
* is created.
|
*/
|
backBufferLonLat: null,
|
|
/**
|
* Property: backBufferTimerId
|
* {Number} The id of the back buffer timer. This timer is used to
|
* delay the removal of the back buffer, thereby preventing
|
* flash effects caused by tile animation.
|
*/
|
backBufferTimerId: null,
|
|
/**
|
* APIProperty: removeBackBufferDelay
|
* {Number} Delay for removing the backbuffer when all tiles have finished
|
* loading. Can be set to 0 when no css opacity transitions for the
|
* olTileImage class are used. Default is 0 for <singleTile> layers,
|
* 2500 for tiled layers. See <className> for more information on
|
* tile animation.
|
*/
|
removeBackBufferDelay: null,
|
|
/**
|
* APIProperty: className
|
* {String} Name of the class added to the layer div. If not set in the
|
* options passed to the constructor then className defaults to
|
* "olLayerGridSingleTile" for single tile layers (see <singleTile>),
|
* and "olLayerGrid" for non single tile layers.
|
*
|
* Note:
|
*
|
* The displaying of tiles is not animated by default for single tile
|
* layers - OpenLayers' default theme (style.css) includes this:
|
* (code)
|
* .olLayerGrid .olTileImage {
|
* -webkit-transition: opacity 0.2s linear;
|
* -moz-transition: opacity 0.2s linear;
|
* -o-transition: opacity 0.2s linear;
|
* transition: opacity 0.2s linear;
|
* }
|
* (end)
|
* To animate tile displaying for any grid layer the following
|
* CSS rule can be used:
|
* (code)
|
* .olTileImage {
|
* -webkit-transition: opacity 0.2s linear;
|
* -moz-transition: opacity 0.2s linear;
|
* -o-transition: opacity 0.2s linear;
|
* transition: opacity 0.2s linear;
|
* }
|
* (end)
|
* In that case, to avoid flash effects, <removeBackBufferDelay>
|
* should not be zero.
|
*/
|
className: null,
|
|
/**
|
* Register a listener for a particular event with the following syntax:
|
* (code)
|
* layer.events.register(type, obj, listener);
|
* (end)
|
*
|
* Listeners will be called with a reference to an event object. The
|
* properties of this event depends on exactly what happened.
|
*
|
* All event objects have at least the following properties:
|
* object - {Object} A reference to layer.events.object.
|
* element - {DOMElement} A reference to layer.events.element.
|
*
|
* Supported event types:
|
* addtile - Triggered when a tile is added to this layer. Listeners receive
|
* an object as first argument, which has a tile property that
|
* references the tile that has been added.
|
* tileloadstart - Triggered when a tile starts loading. Listeners receive
|
* an object as first argument, which has a tile property that
|
* references the tile that starts loading.
|
* tileloaded - Triggered when each new tile is
|
* loaded, as a means of progress update to listeners.
|
* listeners can access 'numLoadingTiles' if they wish to keep
|
* track of the loading progress. Listeners are called with an object
|
* with a 'tile' property as first argument, making the loaded tile
|
* available to the listener, and an 'aborted' property, which will be
|
* true when loading was aborted and no tile data is available.
|
* tileerror - Triggered before the tileloaded event (i.e. when the tile is
|
* still hidden) if a tile failed to load. Listeners receive an object
|
* as first argument, which has a tile property that references the
|
* tile that could not be loaded.
|
* retile - Triggered when the layer recreates its tile grid.
|
*/
|
|
/**
|
* Property: gridLayout
|
* {Object} Object containing properties tilelon, tilelat, startcol,
|
* startrow
|
*/
|
gridLayout: null,
|
|
/**
|
* Property: rowSign
|
* {Number} 1 for grids starting at the top, -1 for grids starting at the
|
* bottom. This is used for several grid index and offset calculations.
|
*/
|
rowSign: null,
|
|
/**
|
* Property: transitionendEvents
|
* {Array} Event names for transitionend
|
*/
|
transitionendEvents: [
|
'transitionend', 'webkitTransitionEnd', 'otransitionend',
|
'oTransitionEnd'
|
],
|
|
/**
|
* Constructor: OpenLayers.Layer.Grid
|
* Create a new grid layer
|
*
|
* Parameters:
|
* name - {String}
|
* url - {String}
|
* params - {Object}
|
* options - {Object} Hashtable of extra options to tag onto the layer
|
*/
|
initialize: function(name, url, params, options) {
|
OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this,
|
arguments);
|
this.grid = [];
|
this._removeBackBuffer = OpenLayers.Function.bind(this.removeBackBuffer, this);
|
|
this.initProperties();
|
|
this.rowSign = this.tileOriginCorner.substr(0, 1) === "t" ? 1 : -1;
|
},
|
|
/**
|
* Method: initProperties
|
* Set any properties that depend on the value of singleTile.
|
* Currently sets removeBackBufferDelay and className
|
*/
|
initProperties: function() {
|
if (this.options.removeBackBufferDelay === undefined) {
|
this.removeBackBufferDelay = this.singleTile ? 0 : 2500;
|
}
|
|
if (this.options.className === undefined) {
|
this.className = this.singleTile ? 'olLayerGridSingleTile' :
|
'olLayerGrid';
|
}
|
},
|
|
/**
|
* Method: setMap
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>} The map.
|
*/
|
setMap: function(map) {
|
OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this, map);
|
OpenLayers.Element.addClass(this.div, this.className);
|
},
|
|
/**
|
* Method: removeMap
|
* Called when the layer is removed from the map.
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>} The map.
|
*/
|
removeMap: function(map) {
|
this.removeBackBuffer();
|
},
|
|
/**
|
* APIMethod: destroy
|
* Deconstruct the layer and clear the grid.
|
*/
|
destroy: function() {
|
this.removeBackBuffer();
|
this.clearGrid();
|
|
this.grid = null;
|
this.tileSize = null;
|
OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments);
|
},
|
|
/**
|
* APIMethod: mergeNewParams
|
* Refetches tiles with new params merged, keeping a backbuffer. Each
|
* loading new tile will have a css class of '.olTileReplacing'. If a
|
* stylesheet applies a 'display: none' style to that class, any fade-in
|
* transition will not apply, and backbuffers for each tile will be removed
|
* as soon as the tile is loaded.
|
*
|
* Parameters:
|
* newParams - {Object}
|
*
|
* Returns:
|
* redrawn: {Boolean} whether the layer was actually redrawn.
|
*/
|
|
/**
|
* Method: clearGrid
|
* Go through and remove all tiles from the grid, calling
|
* destroy() on each of them to kill circular references
|
*/
|
clearGrid:function() {
|
if (this.grid) {
|
for(var iRow=0, len=this.grid.length; iRow<len; iRow++) {
|
var row = this.grid[iRow];
|
for(var iCol=0, clen=row.length; iCol<clen; iCol++) {
|
var tile = row[iCol];
|
this.destroyTile(tile);
|
}
|
}
|
this.grid = [];
|
this.gridResolution = null;
|
this.gridLayout = null;
|
}
|
},
|
|
/**
|
* APIMethod: addOptions
|
*
|
* Parameters:
|
* newOptions - {Object}
|
* reinitialize - {Boolean} If set to true, and if resolution options of the
|
* current baseLayer were changed, the map will be recentered to make
|
* sure that it is displayed with a valid resolution, and a
|
* changebaselayer event will be triggered.
|
*/
|
addOptions: function (newOptions, reinitialize) {
|
var singleTileChanged = newOptions.singleTile !== undefined &&
|
newOptions.singleTile !== this.singleTile;
|
OpenLayers.Layer.HTTPRequest.prototype.addOptions.apply(this, arguments);
|
if (this.map && singleTileChanged) {
|
this.initProperties();
|
this.clearGrid();
|
this.tileSize = this.options.tileSize;
|
this.setTileSize();
|
this.moveTo(null, true);
|
}
|
},
|
|
/**
|
* APIMethod: clone
|
* Create a clone of this layer
|
*
|
* Parameters:
|
* obj - {Object} Is this ever used?
|
*
|
* Returns:
|
* {<OpenLayers.Layer.Grid>} An exact clone of this OpenLayers.Layer.Grid
|
*/
|
clone: function (obj) {
|
|
if (obj == null) {
|
obj = new OpenLayers.Layer.Grid(this.name,
|
this.url,
|
this.params,
|
this.getOptions());
|
}
|
|
//get all additions from superclasses
|
obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]);
|
|
// copy/set any non-init, non-simple values here
|
if (this.tileSize != null) {
|
obj.tileSize = this.tileSize.clone();
|
}
|
|
// we do not want to copy reference to grid, so we make a new array
|
obj.grid = [];
|
obj.gridResolution = null;
|
// same for backbuffer
|
obj.backBuffer = null;
|
obj.backBufferTimerId = null;
|
obj.loading = false;
|
obj.numLoadingTiles = 0;
|
|
return obj;
|
},
|
|
/**
|
* Method: moveTo
|
* This function is called whenever the map is moved. All the moving
|
* of actual 'tiles' is done by the map, but moveTo's role is to accept
|
* a bounds and make sure the data that that bounds requires is pre-loaded.
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
* zoomChanged - {Boolean}
|
* dragging - {Boolean}
|
*/
|
moveTo:function(bounds, zoomChanged, dragging) {
|
|
OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments);
|
|
bounds = bounds || this.map.getExtent();
|
|
if (bounds != null) {
|
|
// if grid is empty or zoom has changed, we *must* re-tile
|
var forceReTile = !this.grid.length || zoomChanged;
|
|
// total bounds of the tiles
|
var tilesBounds = this.getTilesBounds();
|
|
// the new map resolution
|
var resolution = this.map.getResolution();
|
|
// the server-supported resolution for the new map resolution
|
var serverResolution = this.getServerResolution(resolution);
|
|
if (this.singleTile) {
|
|
// We want to redraw whenever even the slightest part of the
|
// current bounds is not contained by our tile.
|
// (thus, we do not specify partial -- its default is false)
|
|
if ( forceReTile ||
|
(!dragging && !tilesBounds.containsBounds(bounds))) {
|
|
// In single tile mode with no transition effect, we insert
|
// a non-scaled backbuffer when the layer is moved. But if
|
// a zoom occurs right after a move, i.e. before the new
|
// image is received, we need to remove the backbuffer, or
|
// an ill-positioned image will be visible during the zoom
|
// transition.
|
|
if(zoomChanged && this.transitionEffect !== 'resize') {
|
this.removeBackBuffer();
|
}
|
|
if(!zoomChanged || this.transitionEffect === 'resize') {
|
this.applyBackBuffer(resolution);
|
}
|
|
this.initSingleTile(bounds);
|
}
|
} else {
|
|
// if the bounds have changed such that they are not even
|
// *partially* contained by our tiles (e.g. when user has
|
// programmatically panned to the other side of the earth on
|
// zoom level 18), then moveGriddedTiles could potentially have
|
// to run through thousands of cycles, so we want to reTile
|
// instead (thus, partial true).
|
forceReTile = forceReTile ||
|
!tilesBounds.intersectsBounds(bounds, {
|
worldBounds: this.map.baseLayer.wrapDateLine &&
|
this.map.getMaxExtent()
|
});
|
|
if(forceReTile) {
|
if(zoomChanged && (this.transitionEffect === 'resize' ||
|
this.gridResolution === resolution)) {
|
this.applyBackBuffer(resolution);
|
}
|
this.initGriddedTiles(bounds);
|
} else {
|
this.moveGriddedTiles();
|
}
|
}
|
}
|
},
|
|
/**
|
* Method: getTileData
|
* Given a map location, retrieve a tile and the pixel offset within that
|
* tile corresponding to the location. If there is not an existing
|
* tile in the grid that covers the given location, null will be
|
* returned.
|
*
|
* Parameters:
|
* loc - {<OpenLayers.LonLat>} map location
|
*
|
* Returns:
|
* {Object} Object with the following properties: tile ({<OpenLayers.Tile>}),
|
* i ({Number} x-pixel offset from top left), and j ({Integer} y-pixel
|
* offset from top left).
|
*/
|
getTileData: function(loc) {
|
var data = null,
|
x = loc.lon,
|
y = loc.lat,
|
numRows = this.grid.length;
|
|
if (this.map && numRows) {
|
var res = this.map.getResolution(),
|
tileWidth = this.tileSize.w,
|
tileHeight = this.tileSize.h,
|
bounds = this.grid[0][0].bounds,
|
left = bounds.left,
|
top = bounds.top;
|
|
if (x < left) {
|
// deal with multiple worlds
|
if (this.map.baseLayer.wrapDateLine) {
|
var worldWidth = this.map.getMaxExtent().getWidth();
|
var worldsAway = Math.ceil((left - x) / worldWidth);
|
x += worldWidth * worldsAway;
|
}
|
}
|
// tile distance to location (fractional number of tiles);
|
var dtx = (x - left) / (res * tileWidth);
|
var dty = (top - y) / (res * tileHeight);
|
// index of tile in grid
|
var col = Math.floor(dtx);
|
var row = Math.floor(dty);
|
if (row >= 0 && row < numRows) {
|
var tile = this.grid[row][col];
|
if (tile) {
|
data = {
|
tile: tile,
|
// pixel index within tile
|
i: Math.floor((dtx - col) * tileWidth),
|
j: Math.floor((dty - row) * tileHeight)
|
};
|
}
|
}
|
}
|
return data;
|
},
|
|
/**
|
* Method: destroyTile
|
*
|
* Parameters:
|
* tile - {<OpenLayers.Tile>}
|
*/
|
destroyTile: function(tile) {
|
this.removeTileMonitoringHooks(tile);
|
tile.destroy();
|
},
|
|
/**
|
* Method: getServerResolution
|
* Return the closest server-supported resolution.
|
*
|
* Parameters:
|
* resolution - {Number} The base resolution. If undefined the
|
* map resolution is used.
|
*
|
* Returns:
|
* {Number} The closest server resolution value.
|
*/
|
getServerResolution: function(resolution) {
|
var distance = Number.POSITIVE_INFINITY;
|
resolution = resolution || this.map.getResolution();
|
if(this.serverResolutions &&
|
OpenLayers.Util.indexOf(this.serverResolutions, resolution) === -1) {
|
var i, newDistance, newResolution, serverResolution;
|
for(i=this.serverResolutions.length-1; i>= 0; i--) {
|
newResolution = this.serverResolutions[i];
|
newDistance = Math.abs(newResolution - resolution);
|
if (newDistance > distance) {
|
break;
|
}
|
distance = newDistance;
|
serverResolution = newResolution;
|
}
|
resolution = serverResolution;
|
}
|
return resolution;
|
},
|
|
/**
|
* Method: getServerZoom
|
* Return the zoom value corresponding to the best matching server
|
* resolution, taking into account <serverResolutions> and <zoomOffset>.
|
*
|
* Returns:
|
* {Number} The closest server supported zoom. This is not the map zoom
|
* level, but an index of the server's resolutions array.
|
*/
|
getServerZoom: function() {
|
var resolution = this.getServerResolution();
|
return this.serverResolutions ?
|
OpenLayers.Util.indexOf(this.serverResolutions, resolution) :
|
this.map.getZoomForResolution(resolution) + (this.zoomOffset || 0);
|
},
|
|
/**
|
* Method: applyBackBuffer
|
* Create, insert, scale and position a back buffer for the layer.
|
*
|
* Parameters:
|
* resolution - {Number} The resolution to transition to.
|
*/
|
applyBackBuffer: function(resolution) {
|
if(this.backBufferTimerId !== null) {
|
this.removeBackBuffer();
|
}
|
var backBuffer = this.backBuffer;
|
if(!backBuffer) {
|
backBuffer = this.createBackBuffer();
|
if(!backBuffer) {
|
return;
|
}
|
if (resolution === this.gridResolution) {
|
this.div.insertBefore(backBuffer, this.div.firstChild);
|
} else {
|
this.map.baseLayer.div.parentNode.insertBefore(backBuffer, this.map.baseLayer.div);
|
}
|
this.backBuffer = backBuffer;
|
|
// set some information in the instance for subsequent
|
// calls to applyBackBuffer where the same back buffer
|
// is reused
|
var topLeftTileBounds = this.grid[0][0].bounds;
|
this.backBufferLonLat = {
|
lon: topLeftTileBounds.left,
|
lat: topLeftTileBounds.top
|
};
|
this.backBufferResolution = this.gridResolution;
|
}
|
|
var ratio = this.backBufferResolution / resolution;
|
|
// scale the tiles inside the back buffer
|
var tiles = backBuffer.childNodes, tile;
|
for (var i=tiles.length-1; i>=0; --i) {
|
tile = tiles[i];
|
tile.style.top = ((ratio * tile._i * tile._h) | 0) + 'px';
|
tile.style.left = ((ratio * tile._j * tile._w) | 0) + 'px';
|
tile.style.width = Math.round(ratio * tile._w) + 'px';
|
tile.style.height = Math.round(ratio * tile._h) + 'px';
|
}
|
|
// and position it (based on the grid's top-left corner)
|
var position = this.getViewPortPxFromLonLat(
|
this.backBufferLonLat, resolution);
|
var leftOffset = this.map.layerContainerOriginPx.x;
|
var topOffset = this.map.layerContainerOriginPx.y;
|
backBuffer.style.left = Math.round(position.x - leftOffset) + 'px';
|
backBuffer.style.top = Math.round(position.y - topOffset) + 'px';
|
},
|
|
/**
|
* Method: createBackBuffer
|
* Create a back buffer.
|
*
|
* Returns:
|
* {DOMElement} The DOM element for the back buffer, undefined if the
|
* grid isn't initialized yet.
|
*/
|
createBackBuffer: function() {
|
var backBuffer;
|
if(this.grid.length > 0) {
|
backBuffer = document.createElement('div');
|
backBuffer.id = this.div.id + '_bb';
|
backBuffer.className = 'olBackBuffer';
|
backBuffer.style.position = 'absolute';
|
var map = this.map;
|
backBuffer.style.zIndex = this.transitionEffect === 'resize' ?
|
this.getZIndex() - 1 :
|
// 'map-resize':
|
map.Z_INDEX_BASE.BaseLayer -
|
(map.getNumLayers() - map.getLayerIndex(this));
|
for(var i=0, lenI=this.grid.length; i<lenI; i++) {
|
for(var j=0, lenJ=this.grid[i].length; j<lenJ; j++) {
|
var tile = this.grid[i][j],
|
markup = this.grid[i][j].createBackBuffer();
|
if (markup) {
|
markup._i = i;
|
markup._j = j;
|
markup._w = tile.size.w;
|
markup._h = tile.size.h;
|
markup.id = tile.id + '_bb';
|
backBuffer.appendChild(markup);
|
}
|
}
|
}
|
}
|
return backBuffer;
|
},
|
|
/**
|
* Method: removeBackBuffer
|
* Remove back buffer from DOM.
|
*/
|
removeBackBuffer: function() {
|
if (this._transitionElement) {
|
for (var i=this.transitionendEvents.length-1; i>=0; --i) {
|
OpenLayers.Event.stopObserving(this._transitionElement,
|
this.transitionendEvents[i], this._removeBackBuffer);
|
}
|
delete this._transitionElement;
|
}
|
if(this.backBuffer) {
|
if (this.backBuffer.parentNode) {
|
this.backBuffer.parentNode.removeChild(this.backBuffer);
|
}
|
this.backBuffer = null;
|
this.backBufferResolution = null;
|
if(this.backBufferTimerId !== null) {
|
window.clearTimeout(this.backBufferTimerId);
|
this.backBufferTimerId = null;
|
}
|
}
|
},
|
|
/**
|
* Method: moveByPx
|
* Move the layer based on pixel vector.
|
*
|
* Parameters:
|
* dx - {Number}
|
* dy - {Number}
|
*/
|
moveByPx: function(dx, dy) {
|
if (!this.singleTile) {
|
this.moveGriddedTiles();
|
}
|
},
|
|
/**
|
* APIMethod: setTileSize
|
* Check if we are in singleTile mode and if so, set the size as a ratio
|
* of the map size (as specified by the layer's 'ratio' property).
|
*
|
* Parameters:
|
* size - {<OpenLayers.Size>}
|
*/
|
setTileSize: function(size) {
|
if (this.singleTile) {
|
size = this.map.getSize();
|
size.h = parseInt(size.h * this.ratio, 10);
|
size.w = parseInt(size.w * this.ratio, 10);
|
}
|
OpenLayers.Layer.HTTPRequest.prototype.setTileSize.apply(this, [size]);
|
},
|
|
/**
|
* APIMethod: getTilesBounds
|
* Return the bounds of the tile grid.
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} A Bounds object representing the bounds of all the
|
* currently loaded tiles (including those partially or not at all seen
|
* onscreen).
|
*/
|
getTilesBounds: function() {
|
var bounds = null;
|
|
var length = this.grid.length;
|
if (length) {
|
var bottomLeftTileBounds = this.grid[length - 1][0].bounds,
|
width = this.grid[0].length * bottomLeftTileBounds.getWidth(),
|
height = this.grid.length * bottomLeftTileBounds.getHeight();
|
|
bounds = new OpenLayers.Bounds(bottomLeftTileBounds.left,
|
bottomLeftTileBounds.bottom,
|
bottomLeftTileBounds.left + width,
|
bottomLeftTileBounds.bottom + height);
|
}
|
return bounds;
|
},
|
|
/**
|
* Method: initSingleTile
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
*/
|
initSingleTile: function(bounds) {
|
this.events.triggerEvent("retile");
|
|
//determine new tile bounds
|
var center = bounds.getCenterLonLat();
|
var tileWidth = bounds.getWidth() * this.ratio;
|
var tileHeight = bounds.getHeight() * this.ratio;
|
|
var tileBounds =
|
new OpenLayers.Bounds(center.lon - (tileWidth/2),
|
center.lat - (tileHeight/2),
|
center.lon + (tileWidth/2),
|
center.lat + (tileHeight/2));
|
|
var px = this.map.getLayerPxFromLonLat({
|
lon: tileBounds.left,
|
lat: tileBounds.top
|
});
|
|
if (!this.grid.length) {
|
this.grid[0] = [];
|
}
|
|
var tile = this.grid[0][0];
|
if (!tile) {
|
tile = this.addTile(tileBounds, px);
|
|
this.addTileMonitoringHooks(tile);
|
tile.draw();
|
this.grid[0][0] = tile;
|
} else {
|
tile.moveTo(tileBounds, px);
|
}
|
|
//remove all but our single tile
|
this.removeExcessTiles(1,1);
|
|
// store the resolution of the grid
|
this.gridResolution = this.getServerResolution();
|
},
|
|
/**
|
* Method: calculateGridLayout
|
* Generate parameters for the grid layout.
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bound>|Object} OpenLayers.Bounds or an
|
* object with a 'left' and 'top' properties.
|
* origin - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an
|
* object with a 'lon' and 'lat' properties.
|
* resolution - {Number}
|
*
|
* Returns:
|
* {Object} Object containing properties tilelon, tilelat, startcol,
|
* startrow
|
*/
|
calculateGridLayout: function(bounds, origin, resolution) {
|
var tilelon = resolution * this.tileSize.w;
|
var tilelat = resolution * this.tileSize.h;
|
|
var offsetlon = bounds.left - origin.lon;
|
var tilecol = Math.floor(offsetlon/tilelon) - this.buffer;
|
|
var rowSign = this.rowSign;
|
|
var offsetlat = rowSign * (origin.lat - bounds.top + tilelat);
|
var tilerow = Math[~rowSign ? 'floor' : 'ceil'](offsetlat/tilelat) - this.buffer * rowSign;
|
|
return {
|
tilelon: tilelon, tilelat: tilelat,
|
startcol: tilecol, startrow: tilerow
|
};
|
|
},
|
|
/**
|
* Method: getTileOrigin
|
* Determine the origin for aligning the grid of tiles. If a <tileOrigin>
|
* property is supplied, that will be returned. Otherwise, the origin
|
* will be derived from the layer's <maxExtent> property. In this case,
|
* the tile origin will be the corner of the <maxExtent> given by the
|
* <tileOriginCorner> property.
|
*
|
* Returns:
|
* {<OpenLayers.LonLat>} The tile origin.
|
*/
|
getTileOrigin: function() {
|
var origin = this.tileOrigin;
|
if (!origin) {
|
var extent = this.getMaxExtent();
|
var edges = ({
|
"tl": ["left", "top"],
|
"tr": ["right", "top"],
|
"bl": ["left", "bottom"],
|
"br": ["right", "bottom"]
|
})[this.tileOriginCorner];
|
origin = new OpenLayers.LonLat(extent[edges[0]], extent[edges[1]]);
|
}
|
return origin;
|
},
|
|
/**
|
* Method: getTileBoundsForGridIndex
|
*
|
* Parameters:
|
* row - {Number} The row of the grid
|
* col - {Number} The column of the grid
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} The bounds for the tile at (row, col)
|
*/
|
getTileBoundsForGridIndex: function(row, col) {
|
var origin = this.getTileOrigin();
|
var tileLayout = this.gridLayout;
|
var tilelon = tileLayout.tilelon;
|
var tilelat = tileLayout.tilelat;
|
var startcol = tileLayout.startcol;
|
var startrow = tileLayout.startrow;
|
var rowSign = this.rowSign;
|
return new OpenLayers.Bounds(
|
origin.lon + (startcol + col) * tilelon,
|
origin.lat - (startrow + row * rowSign) * tilelat * rowSign,
|
origin.lon + (startcol + col + 1) * tilelon,
|
origin.lat - (startrow + (row - 1) * rowSign) * tilelat * rowSign
|
);
|
},
|
|
/**
|
* Method: initGriddedTiles
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
*/
|
initGriddedTiles:function(bounds) {
|
this.events.triggerEvent("retile");
|
|
// work out mininum number of rows and columns; this is the number of
|
// tiles required to cover the viewport plus at least one for panning
|
|
var viewSize = this.map.getSize();
|
|
var origin = this.getTileOrigin();
|
var resolution = this.map.getResolution(),
|
serverResolution = this.getServerResolution(),
|
ratio = resolution / serverResolution,
|
tileSize = {
|
w: this.tileSize.w / ratio,
|
h: this.tileSize.h / ratio
|
};
|
|
var minRows = Math.ceil(viewSize.h/tileSize.h) +
|
2 * this.buffer + 1;
|
var minCols = Math.ceil(viewSize.w/tileSize.w) +
|
2 * this.buffer + 1;
|
|
var tileLayout = this.calculateGridLayout(bounds, origin, serverResolution);
|
this.gridLayout = tileLayout;
|
|
var tilelon = tileLayout.tilelon;
|
var tilelat = tileLayout.tilelat;
|
|
var layerContainerDivLeft = this.map.layerContainerOriginPx.x;
|
var layerContainerDivTop = this.map.layerContainerOriginPx.y;
|
|
var tileBounds = this.getTileBoundsForGridIndex(0, 0);
|
var startPx = this.map.getViewPortPxFromLonLat(
|
new OpenLayers.LonLat(tileBounds.left, tileBounds.top)
|
);
|
startPx.x = Math.round(startPx.x) - layerContainerDivLeft;
|
startPx.y = Math.round(startPx.y) - layerContainerDivTop;
|
|
var tileData = [], center = this.map.getCenter();
|
|
var rowidx = 0;
|
do {
|
var row = this.grid[rowidx];
|
if (!row) {
|
row = [];
|
this.grid.push(row);
|
}
|
|
var colidx = 0;
|
do {
|
tileBounds = this.getTileBoundsForGridIndex(rowidx, colidx);
|
var px = startPx.clone();
|
px.x = px.x + colidx * Math.round(tileSize.w);
|
px.y = px.y + rowidx * Math.round(tileSize.h);
|
var tile = row[colidx];
|
if (!tile) {
|
tile = this.addTile(tileBounds, px);
|
this.addTileMonitoringHooks(tile);
|
row.push(tile);
|
} else {
|
tile.moveTo(tileBounds, px, false);
|
}
|
var tileCenter = tileBounds.getCenterLonLat();
|
tileData.push({
|
tile: tile,
|
distance: Math.pow(tileCenter.lon - center.lon, 2) +
|
Math.pow(tileCenter.lat - center.lat, 2)
|
});
|
|
colidx += 1;
|
} while ((tileBounds.right <= bounds.right + tilelon * this.buffer)
|
|| colidx < minCols);
|
|
rowidx += 1;
|
} while((tileBounds.bottom >= bounds.bottom - tilelat * this.buffer)
|
|| rowidx < minRows);
|
|
//shave off exceess rows and colums
|
this.removeExcessTiles(rowidx, colidx);
|
|
var resolution = this.getServerResolution();
|
// store the resolution of the grid
|
this.gridResolution = resolution;
|
|
//now actually draw the tiles
|
tileData.sort(function(a, b) {
|
return a.distance - b.distance;
|
});
|
for (var i=0, ii=tileData.length; i<ii; ++i) {
|
tileData[i].tile.draw();
|
}
|
},
|
|
/**
|
* Method: getMaxExtent
|
* Get this layer's maximum extent. (Implemented as a getter for
|
* potential specific implementations in sub-classes.)
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>}
|
*/
|
getMaxExtent: function() {
|
return this.maxExtent;
|
},
|
|
/**
|
* APIMethod: addTile
|
* Create a tile, initialize it, and add it to the layer div.
|
*
|
* Parameters
|
* bounds - {<OpenLayers.Bounds>}
|
* position - {<OpenLayers.Pixel>}
|
*
|
* Returns:
|
* {<OpenLayers.Tile>} The added OpenLayers.Tile
|
*/
|
addTile: function(bounds, position) {
|
var tile = new this.tileClass(
|
this, position, bounds, null, this.tileSize, this.tileOptions
|
);
|
this.events.triggerEvent("addtile", {tile: tile});
|
return tile;
|
},
|
|
/**
|
* Method: addTileMonitoringHooks
|
* This function takes a tile as input and adds the appropriate hooks to
|
* the tile so that the layer can keep track of the loading tiles.
|
*
|
* Parameters:
|
* tile - {<OpenLayers.Tile>}
|
*/
|
addTileMonitoringHooks: function(tile) {
|
|
var replacingCls = 'olTileReplacing';
|
|
tile.onLoadStart = function() {
|
//if that was first tile then trigger a 'loadstart' on the layer
|
if (this.loading === false) {
|
this.loading = true;
|
this.events.triggerEvent("loadstart");
|
}
|
this.events.triggerEvent("tileloadstart", {tile: tile});
|
this.numLoadingTiles++;
|
if (!this.singleTile && this.backBuffer && this.gridResolution === this.backBufferResolution) {
|
OpenLayers.Element.addClass(tile.getTile(), replacingCls);
|
}
|
};
|
|
tile.onLoadEnd = function(evt) {
|
this.numLoadingTiles--;
|
var aborted = evt.type === 'unload';
|
this.events.triggerEvent("tileloaded", {
|
tile: tile,
|
aborted: aborted
|
});
|
if (!this.singleTile && !aborted && this.backBuffer && this.gridResolution === this.backBufferResolution) {
|
var tileDiv = tile.getTile();
|
if (OpenLayers.Element.getStyle(tileDiv, 'display') === 'none') {
|
var bufferTile = document.getElementById(tile.id + '_bb');
|
if (bufferTile) {
|
bufferTile.parentNode.removeChild(bufferTile);
|
}
|
}
|
OpenLayers.Element.removeClass(tileDiv, replacingCls);
|
}
|
//if that was the last tile, then trigger a 'loadend' on the layer
|
if (this.numLoadingTiles === 0) {
|
if (this.backBuffer) {
|
if (this.backBuffer.childNodes.length === 0) {
|
// no tiles transitioning, remove immediately
|
this.removeBackBuffer();
|
} else {
|
// wait until transition has ended or delay has passed
|
this._transitionElement = aborted ?
|
this.div.lastChild : tile.imgDiv;
|
var transitionendEvents = this.transitionendEvents;
|
for (var i=transitionendEvents.length-1; i>=0; --i) {
|
OpenLayers.Event.observe(this._transitionElement,
|
transitionendEvents[i],
|
this._removeBackBuffer);
|
}
|
// the removal of the back buffer is delayed to prevent
|
// flash effects due to the animation of tile displaying
|
this.backBufferTimerId = window.setTimeout(
|
this._removeBackBuffer, this.removeBackBufferDelay
|
);
|
}
|
}
|
this.loading = false;
|
this.events.triggerEvent("loadend");
|
}
|
};
|
|
tile.onLoadError = function() {
|
this.events.triggerEvent("tileerror", {tile: tile});
|
};
|
|
tile.events.on({
|
"loadstart": tile.onLoadStart,
|
"loadend": tile.onLoadEnd,
|
"unload": tile.onLoadEnd,
|
"loaderror": tile.onLoadError,
|
scope: this
|
});
|
},
|
|
/**
|
* Method: removeTileMonitoringHooks
|
* This function takes a tile as input and removes the tile hooks
|
* that were added in addTileMonitoringHooks()
|
*
|
* Parameters:
|
* tile - {<OpenLayers.Tile>}
|
*/
|
removeTileMonitoringHooks: function(tile) {
|
tile.unload();
|
tile.events.un({
|
"loadstart": tile.onLoadStart,
|
"loadend": tile.onLoadEnd,
|
"unload": tile.onLoadEnd,
|
"loaderror": tile.onLoadError,
|
scope: this
|
});
|
},
|
|
/**
|
* Method: moveGriddedTiles
|
*/
|
moveGriddedTiles: function() {
|
var buffer = this.buffer + 1;
|
while(true) {
|
var tlTile = this.grid[0][0];
|
var tlViewPort = {
|
x: tlTile.position.x +
|
this.map.layerContainerOriginPx.x,
|
y: tlTile.position.y +
|
this.map.layerContainerOriginPx.y
|
};
|
var ratio = this.getServerResolution() / this.map.getResolution();
|
var tileSize = {
|
w: Math.round(this.tileSize.w * ratio),
|
h: Math.round(this.tileSize.h * ratio)
|
};
|
if (tlViewPort.x > -tileSize.w * (buffer - 1)) {
|
this.shiftColumn(true, tileSize);
|
} else if (tlViewPort.x < -tileSize.w * buffer) {
|
this.shiftColumn(false, tileSize);
|
} else if (tlViewPort.y > -tileSize.h * (buffer - 1)) {
|
this.shiftRow(true, tileSize);
|
} else if (tlViewPort.y < -tileSize.h * buffer) {
|
this.shiftRow(false, tileSize);
|
} else {
|
break;
|
}
|
}
|
},
|
|
/**
|
* Method: shiftRow
|
* Shifty grid work
|
*
|
* Parameters:
|
* prepend - {Boolean} if true, prepend to beginning.
|
* if false, then append to end
|
* tileSize - {Object} rendered tile size; object with w and h properties
|
*/
|
shiftRow: function(prepend, tileSize) {
|
var grid = this.grid;
|
var rowIndex = prepend ? 0 : (grid.length - 1);
|
var sign = prepend ? -1 : 1;
|
var rowSign = this.rowSign;
|
var tileLayout = this.gridLayout;
|
tileLayout.startrow += sign * rowSign;
|
|
var modelRow = grid[rowIndex];
|
var row = grid[prepend ? 'pop' : 'shift']();
|
for (var i=0, len=row.length; i<len; i++) {
|
var tile = row[i];
|
var position = modelRow[i].position.clone();
|
position.y += tileSize.h * sign;
|
tile.moveTo(this.getTileBoundsForGridIndex(rowIndex, i), position);
|
}
|
grid[prepend ? 'unshift' : 'push'](row);
|
},
|
|
/**
|
* Method: shiftColumn
|
* Shift grid work in the other dimension
|
*
|
* Parameters:
|
* prepend - {Boolean} if true, prepend to beginning.
|
* if false, then append to end
|
* tileSize - {Object} rendered tile size; object with w and h properties
|
*/
|
shiftColumn: function(prepend, tileSize) {
|
var grid = this.grid;
|
var colIndex = prepend ? 0 : (grid[0].length - 1);
|
var sign = prepend ? -1 : 1;
|
var tileLayout = this.gridLayout;
|
tileLayout.startcol += sign;
|
|
for (var i=0, len=grid.length; i<len; i++) {
|
var row = grid[i];
|
var position = row[colIndex].position.clone();
|
var tile = row[prepend ? 'pop' : 'shift']();
|
position.x += tileSize.w * sign;
|
tile.moveTo(this.getTileBoundsForGridIndex(i, colIndex), position);
|
row[prepend ? 'unshift' : 'push'](tile);
|
}
|
},
|
|
/**
|
* Method: removeExcessTiles
|
* When the size of the map or the buffer changes, we may need to
|
* remove some excess rows and columns.
|
*
|
* Parameters:
|
* rows - {Integer} Maximum number of rows we want our grid to have.
|
* columns - {Integer} Maximum number of columns we want our grid to have.
|
*/
|
removeExcessTiles: function(rows, columns) {
|
var i, l;
|
|
// remove extra rows
|
while (this.grid.length > rows) {
|
var row = this.grid.pop();
|
for (i=0, l=row.length; i<l; i++) {
|
var tile = row[i];
|
this.destroyTile(tile);
|
}
|
}
|
|
// remove extra columns
|
for (i=0, l=this.grid.length; i<l; i++) {
|
while (this.grid[i].length > columns) {
|
var row = this.grid[i];
|
var tile = row.pop();
|
this.destroyTile(tile);
|
}
|
}
|
},
|
|
/**
|
* Method: onMapResize
|
* For singleTile layers, this will set a new tile size according to the
|
* dimensions of the map pane.
|
*/
|
onMapResize: function() {
|
if (this.singleTile) {
|
this.clearGrid();
|
this.setTileSize();
|
}
|
},
|
|
/**
|
* APIMethod: getTileBounds
|
* Returns The tile bounds for a layer given a pixel location.
|
*
|
* Parameters:
|
* viewPortPx - {<OpenLayers.Pixel>} The location in the viewport.
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} Bounds of the tile at the given pixel location.
|
*/
|
getTileBounds: function(viewPortPx) {
|
var maxExtent = this.maxExtent;
|
var resolution = this.getResolution();
|
var tileMapWidth = resolution * this.tileSize.w;
|
var tileMapHeight = resolution * this.tileSize.h;
|
var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);
|
var tileLeft = maxExtent.left + (tileMapWidth *
|
Math.floor((mapPoint.lon -
|
maxExtent.left) /
|
tileMapWidth));
|
var tileBottom = maxExtent.bottom + (tileMapHeight *
|
Math.floor((mapPoint.lat -
|
maxExtent.bottom) /
|
tileMapHeight));
|
return new OpenLayers.Bounds(tileLeft, tileBottom,
|
tileLeft + tileMapWidth,
|
tileBottom + tileMapHeight);
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.Grid"
|
});
|
/* ======================================================================
|
OpenLayers/Format/ArcXML.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML.js
|
* @requires OpenLayers/Geometry/Polygon.js
|
* @requires OpenLayers/Geometry/Point.js
|
* @requires OpenLayers/Geometry/MultiPolygon.js
|
* @requires OpenLayers/Geometry/LinearRing.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.ArcXML
|
* Read/Write ArcXML. Create a new instance with the <OpenLayers.Format.ArcXML>
|
* constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.ArcXML = OpenLayers.Class(OpenLayers.Format.XML, {
|
|
/**
|
* Property: fontStyleKeys
|
* {Array} List of keys used in font styling.
|
*/
|
fontStyleKeys: [
|
'antialiasing', 'blockout', 'font', 'fontcolor','fontsize', 'fontstyle',
|
'glowing', 'interval', 'outline', 'printmode', 'shadow', 'transparency'
|
],
|
|
/**
|
* Property: request
|
* A get_image request destined for an ArcIMS server.
|
*/
|
request: null,
|
|
/**
|
* Property: response
|
* A parsed response from an ArcIMS server.
|
*/
|
response: null,
|
|
/**
|
* Constructor: OpenLayers.Format.ArcXML
|
* Create a new parser/writer for ArcXML. Create an instance of this class
|
* to begin authoring a request to an ArcIMS service. This is used
|
* primarily by the ArcIMS layer, but could be used to do other wild
|
* stuff, like geocoding.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
initialize: function(options) {
|
this.request = new OpenLayers.Format.ArcXML.Request();
|
this.response = new OpenLayers.Format.ArcXML.Response();
|
|
if (options) {
|
if (options.requesttype == "feature") {
|
this.request.get_image = null;
|
|
var qry = this.request.get_feature.query;
|
this.addCoordSys(qry.featurecoordsys, options.featureCoordSys);
|
this.addCoordSys(qry.filtercoordsys, options.filterCoordSys);
|
|
if (options.polygon) {
|
qry.isspatial = true;
|
qry.spatialfilter.polygon = options.polygon;
|
} else if (options.envelope) {
|
qry.isspatial = true;
|
qry.spatialfilter.envelope = {minx:0, miny:0, maxx:0, maxy:0};
|
this.parseEnvelope(qry.spatialfilter.envelope, options.envelope);
|
}
|
} else if (options.requesttype == "image") {
|
this.request.get_feature = null;
|
|
var props = this.request.get_image.properties;
|
this.parseEnvelope(props.envelope, options.envelope);
|
|
this.addLayers(props.layerlist, options.layers);
|
this.addImageSize(props.imagesize, options.tileSize);
|
this.addCoordSys(props.featurecoordsys, options.featureCoordSys);
|
this.addCoordSys(props.filtercoordsys, options.filterCoordSys);
|
} else {
|
// if an arcxml object is being created with no request type, it is
|
// probably going to consume a response, so do not throw an error if
|
// the requesttype is not defined
|
this.request = null;
|
}
|
}
|
|
OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
|
},
|
|
/**
|
* Method: parseEnvelope
|
* Parse an array of coordinates into an ArcXML envelope structure.
|
*
|
* Parameters:
|
* env - {Object} An envelope object that will contain the parsed coordinates.
|
* arr - {Array(double)} An array of coordinates in the order: [ minx, miny, maxx, maxy ]
|
*/
|
parseEnvelope: function(env, arr) {
|
if (arr && arr.length == 4) {
|
env.minx = arr[0];
|
env.miny = arr[1];
|
env.maxx = arr[2];
|
env.maxy = arr[3];
|
}
|
},
|
|
/**
|
* Method: addLayers
|
* Add a collection of layers to another collection of layers. Each layer in the list is tuple of
|
* { id, visible }. These layer collections represent the
|
* /ARCXML/REQUEST/get_image/PROPERTIES/LAYERLIST/LAYERDEF items in ArcXML
|
*
|
* TODO: Add support for dynamic layer rendering.
|
*
|
* Parameters:
|
* ll - {Array({id,visible})} A list of layer definitions.
|
* lyrs - {Array({id,visible})} A list of layer definitions.
|
*/
|
addLayers: function(ll, lyrs) {
|
for(var lind = 0, len=lyrs.length; lind < len; lind++) {
|
ll.push(lyrs[lind]);
|
}
|
},
|
|
/**
|
* Method: addImageSize
|
* Set the size of the requested image.
|
*
|
* Parameters:
|
* imsize - {Object} An ArcXML imagesize object.
|
* olsize - {<OpenLayers.Size>} The image size to set.
|
*/
|
addImageSize: function(imsize, olsize) {
|
if (olsize !== null) {
|
imsize.width = olsize.w;
|
imsize.height = olsize.h;
|
imsize.printwidth = olsize.w;
|
imsize.printheight = olsize.h;
|
}
|
},
|
|
/**
|
* Method: addCoordSys
|
* Add the coordinate system information to an object. The object may be
|
*
|
* Parameters:
|
* featOrFilt - {Object} A featurecoordsys or filtercoordsys ArcXML structure.
|
* fsys - {String} or {<OpenLayers.Projection>} or {filtercoordsys} or
|
* {featurecoordsys} A projection representation. If it's a {String},
|
* the value is assumed to be the SRID. If it's a {OpenLayers.Projection}
|
* AND Proj4js is available, the projection number and name are extracted
|
* from there. If it's a filter or feature ArcXML structure, it is copied.
|
*/
|
addCoordSys: function(featOrFilt, fsys) {
|
if (typeof fsys == "string") {
|
featOrFilt.id = parseInt(fsys);
|
featOrFilt.string = fsys;
|
}
|
// is this a proj4js instance?
|
else if (typeof fsys == "object" && fsys.proj !== null){
|
featOrFilt.id = fsys.proj.srsProjNumber;
|
featOrFilt.string = fsys.proj.srsCode;
|
} else {
|
featOrFilt = fsys;
|
}
|
},
|
|
/**
|
* APIMethod: iserror
|
* Check to see if the response from the server was an error.
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse. If nothing is supplied,
|
* the current response is examined.
|
*
|
* Returns:
|
* {Boolean} true if the response was an error.
|
*/
|
iserror: function(data) {
|
var ret = null;
|
|
if (!data) {
|
ret = (this.response.error !== '');
|
} else {
|
data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
|
var errorNodes = data.documentElement.getElementsByTagName("ERROR");
|
ret = (errorNodes !== null && errorNodes.length > 0);
|
}
|
|
return ret;
|
},
|
|
/**
|
* APIMethod: read
|
* Read data from a string, and return an response.
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
*
|
* Returns:
|
* {<OpenLayers.Format.ArcXML.Response>} An ArcXML response. Note that this response
|
* data may change in the future.
|
*/
|
read: function(data) {
|
if(typeof data == "string") {
|
data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
|
}
|
|
var arcNode = null;
|
if (data && data.documentElement) {
|
if(data.documentElement.nodeName == "ARCXML") {
|
arcNode = data.documentElement;
|
} else {
|
arcNode = data.documentElement.getElementsByTagName("ARCXML")[0];
|
}
|
}
|
|
// in Safari, arcNode will be there but will have a child named
|
// parsererror
|
if (!arcNode || arcNode.firstChild.nodeName === 'parsererror') {
|
var error, source;
|
try {
|
error = data.firstChild.nodeValue;
|
source = data.firstChild.childNodes[1].firstChild.nodeValue;
|
} catch (err) {
|
// pass
|
}
|
throw {
|
message: "Error parsing the ArcXML request",
|
error: error,
|
source: source
|
};
|
}
|
|
var response = this.parseResponse(arcNode);
|
return response;
|
},
|
|
/**
|
* APIMethod: write
|
* Generate an ArcXml document string for sending to an ArcIMS server.
|
*
|
* Returns:
|
* {String} A string representing the ArcXML document request.
|
*/
|
write: function(request) {
|
if (!request) {
|
request = this.request;
|
}
|
var root = this.createElementNS("", "ARCXML");
|
root.setAttribute("version","1.1");
|
|
var reqElem = this.createElementNS("", "REQUEST");
|
|
if (request.get_image != null) {
|
var getElem = this.createElementNS("", "GET_IMAGE");
|
reqElem.appendChild(getElem);
|
|
var propElem = this.createElementNS("", "PROPERTIES");
|
getElem.appendChild(propElem);
|
|
var props = request.get_image.properties;
|
if (props.featurecoordsys != null) {
|
var feat = this.createElementNS("", "FEATURECOORDSYS");
|
propElem.appendChild(feat);
|
|
if (props.featurecoordsys.id === 0) {
|
feat.setAttribute("string", props.featurecoordsys['string']);
|
}
|
else {
|
feat.setAttribute("id", props.featurecoordsys.id);
|
}
|
}
|
|
if (props.filtercoordsys != null) {
|
var filt = this.createElementNS("", "FILTERCOORDSYS");
|
propElem.appendChild(filt);
|
|
if (props.filtercoordsys.id === 0) {
|
filt.setAttribute("string", props.filtercoordsys.string);
|
}
|
else {
|
filt.setAttribute("id", props.filtercoordsys.id);
|
}
|
}
|
|
if (props.envelope != null) {
|
var env = this.createElementNS("", "ENVELOPE");
|
propElem.appendChild(env);
|
|
env.setAttribute("minx", props.envelope.minx);
|
env.setAttribute("miny", props.envelope.miny);
|
env.setAttribute("maxx", props.envelope.maxx);
|
env.setAttribute("maxy", props.envelope.maxy);
|
}
|
|
var imagesz = this.createElementNS("", "IMAGESIZE");
|
propElem.appendChild(imagesz);
|
|
imagesz.setAttribute("height", props.imagesize.height);
|
imagesz.setAttribute("width", props.imagesize.width);
|
|
if (props.imagesize.height != props.imagesize.printheight ||
|
props.imagesize.width != props.imagesize.printwidth) {
|
imagesz.setAttribute("printheight", props.imagesize.printheight);
|
imagesz.setArrtibute("printwidth", props.imagesize.printwidth);
|
}
|
|
if (props.background != null) {
|
var backgrnd = this.createElementNS("", "BACKGROUND");
|
propElem.appendChild(backgrnd);
|
|
backgrnd.setAttribute("color",
|
props.background.color.r + "," +
|
props.background.color.g + "," +
|
props.background.color.b);
|
|
if (props.background.transcolor !== null) {
|
backgrnd.setAttribute("transcolor",
|
props.background.transcolor.r + "," +
|
props.background.transcolor.g + "," +
|
props.background.transcolor.b);
|
}
|
}
|
|
if (props.layerlist != null && props.layerlist.length > 0) {
|
var layerlst = this.createElementNS("", "LAYERLIST");
|
propElem.appendChild(layerlst);
|
|
for (var ld = 0; ld < props.layerlist.length; ld++) {
|
var ldef = this.createElementNS("", "LAYERDEF");
|
layerlst.appendChild(ldef);
|
|
ldef.setAttribute("id", props.layerlist[ld].id);
|
ldef.setAttribute("visible", props.layerlist[ld].visible);
|
|
if (typeof props.layerlist[ld].query == "object") {
|
var query = props.layerlist[ld].query;
|
|
if (query.where.length < 0) {
|
continue;
|
}
|
|
var queryElem = null;
|
if (typeof query.spatialfilter == "boolean" && query.spatialfilter) {
|
// handle spatial filter madness
|
queryElem = this.createElementNS("", "SPATIALQUERY");
|
}
|
else {
|
queryElem = this.createElementNS("", "QUERY");
|
}
|
|
queryElem.setAttribute("where", query.where);
|
|
if (typeof query.accuracy == "number" && query.accuracy > 0) {
|
queryElem.setAttribute("accuracy", query.accuracy);
|
}
|
if (typeof query.featurelimit == "number" && query.featurelimit < 2000) {
|
queryElem.setAttribute("featurelimit", query.featurelimit);
|
}
|
if (typeof query.subfields == "string" && query.subfields != "#ALL#") {
|
queryElem.setAttribute("subfields", query.subfields);
|
}
|
if (typeof query.joinexpression == "string" && query.joinexpression.length > 0) {
|
queryElem.setAttribute("joinexpression", query.joinexpression);
|
}
|
if (typeof query.jointables == "string" && query.jointables.length > 0) {
|
queryElem.setAttribute("jointables", query.jointables);
|
}
|
|
ldef.appendChild(queryElem);
|
}
|
|
if (typeof props.layerlist[ld].renderer == "object") {
|
this.addRenderer(ldef, props.layerlist[ld].renderer);
|
}
|
}
|
}
|
} else if (request.get_feature != null) {
|
var getElem = this.createElementNS("", "GET_FEATURES");
|
getElem.setAttribute("outputmode", "newxml");
|
getElem.setAttribute("checkesc", "true");
|
|
if (request.get_feature.geometry) {
|
getElem.setAttribute("geometry", request.get_feature.geometry);
|
}
|
else {
|
getElem.setAttribute("geometry", "false");
|
}
|
|
if (request.get_feature.compact) {
|
getElem.setAttribute("compact", request.get_feature.compact);
|
}
|
|
if (request.get_feature.featurelimit == "number") {
|
getElem.setAttribute("featurelimit", request.get_feature.featurelimit);
|
}
|
|
getElem.setAttribute("globalenvelope", "true");
|
reqElem.appendChild(getElem);
|
|
if (request.get_feature.layer != null && request.get_feature.layer.length > 0) {
|
var lyrElem = this.createElementNS("", "LAYER");
|
lyrElem.setAttribute("id", request.get_feature.layer);
|
getElem.appendChild(lyrElem);
|
}
|
|
var fquery = request.get_feature.query;
|
if (fquery != null) {
|
var qElem = null;
|
if (fquery.isspatial) {
|
qElem = this.createElementNS("", "SPATIALQUERY");
|
} else {
|
qElem = this.createElementNS("", "QUERY");
|
}
|
getElem.appendChild(qElem);
|
|
if (typeof fquery.accuracy == "number") {
|
qElem.setAttribute("accuracy", fquery.accuracy);
|
}
|
//qElem.setAttribute("featurelimit", "5");
|
|
if (fquery.featurecoordsys != null) {
|
var fcsElem1 = this.createElementNS("", "FEATURECOORDSYS");
|
|
if (fquery.featurecoordsys.id == 0) {
|
fcsElem1.setAttribute("string", fquery.featurecoordsys.string);
|
} else {
|
fcsElem1.setAttribute("id", fquery.featurecoordsys.id);
|
}
|
qElem.appendChild(fcsElem1);
|
}
|
|
if (fquery.filtercoordsys != null) {
|
var fcsElem2 = this.createElementNS("", "FILTERCOORDSYS");
|
|
if (fquery.filtercoordsys.id === 0) {
|
fcsElem2.setAttribute("string", fquery.filtercoordsys.string);
|
} else {
|
fcsElem2.setAttribute("id", fquery.filtercoordsys.id);
|
}
|
qElem.appendChild(fcsElem2);
|
}
|
|
if (fquery.buffer > 0) {
|
var bufElem = this.createElementNS("", "BUFFER");
|
bufElem.setAttribute("distance", fquery.buffer);
|
qElem.appendChild(bufElem);
|
}
|
|
if (fquery.isspatial) {
|
var spfElem = this.createElementNS("", "SPATIALFILTER");
|
spfElem.setAttribute("relation", fquery.spatialfilter.relation);
|
qElem.appendChild(spfElem);
|
|
if (fquery.spatialfilter.envelope) {
|
var envElem = this.createElementNS("", "ENVELOPE");
|
envElem.setAttribute("minx", fquery.spatialfilter.envelope.minx);
|
envElem.setAttribute("miny", fquery.spatialfilter.envelope.miny);
|
envElem.setAttribute("maxx", fquery.spatialfilter.envelope.maxx);
|
envElem.setAttribute("maxy", fquery.spatialfilter.envelope.maxy);
|
spfElem.appendChild(envElem);
|
} else if(typeof fquery.spatialfilter.polygon == "object") {
|
spfElem.appendChild(this.writePolygonGeometry(fquery.spatialfilter.polygon));
|
}
|
}
|
|
if (fquery.where != null && fquery.where.length > 0) {
|
qElem.setAttribute("where", fquery.where);
|
}
|
}
|
}
|
|
root.appendChild(reqElem);
|
|
return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
|
},
|
|
|
addGroupRenderer: function(ldef, toprenderer) {
|
var topRelem = this.createElementNS("", "GROUPRENDERER");
|
ldef.appendChild(topRelem);
|
|
for (var rind = 0; rind < toprenderer.length; rind++) {
|
var renderer = toprenderer[rind];
|
this.addRenderer(topRelem, renderer);
|
}
|
},
|
|
|
addRenderer: function(topRelem, renderer) {
|
if (OpenLayers.Util.isArray(renderer)) {
|
this.addGroupRenderer(topRelem, renderer);
|
} else {
|
var renderElem = this.createElementNS("", renderer.type.toUpperCase() + "RENDERER");
|
topRelem.appendChild(renderElem);
|
|
if (renderElem.tagName == "VALUEMAPRENDERER") {
|
this.addValueMapRenderer(renderElem, renderer);
|
} else if (renderElem.tagName == "VALUEMAPLABELRENDERER") {
|
this.addValueMapLabelRenderer(renderElem, renderer);
|
} else if (renderElem.tagName == "SIMPLELABELRENDERER") {
|
this.addSimpleLabelRenderer(renderElem, renderer);
|
} else if (renderElem.tagName == "SCALEDEPENDENTRENDERER") {
|
this.addScaleDependentRenderer(renderElem, renderer);
|
}
|
}
|
},
|
|
|
addScaleDependentRenderer: function(renderElem, renderer) {
|
if (typeof renderer.lower == "string" || typeof renderer.lower == "number") {
|
renderElem.setAttribute("lower", renderer.lower);
|
}
|
if (typeof renderer.upper == "string" || typeof renderer.upper == "number") {
|
renderElem.setAttribute("upper", renderer.upper);
|
}
|
|
this.addRenderer(renderElem, renderer.renderer);
|
},
|
|
|
addValueMapLabelRenderer: function(renderElem, renderer) {
|
renderElem.setAttribute("lookupfield", renderer.lookupfield);
|
renderElem.setAttribute("labelfield", renderer.labelfield);
|
|
if (typeof renderer.exacts == "object") {
|
for (var ext=0, extlen=renderer.exacts.length; ext<extlen; ext++) {
|
var exact = renderer.exacts[ext];
|
|
var eelem = this.createElementNS("", "EXACT");
|
|
if (typeof exact.value == "string") {
|
eelem.setAttribute("value", exact.value);
|
}
|
if (typeof exact.label == "string") {
|
eelem.setAttribute("label", exact.label);
|
}
|
if (typeof exact.method == "string") {
|
eelem.setAttribute("method", exact.method);
|
}
|
|
renderElem.appendChild(eelem);
|
|
if (typeof exact.symbol == "object") {
|
var selem = null;
|
|
if (exact.symbol.type == "text") {
|
selem = this.createElementNS("", "TEXTSYMBOL");
|
}
|
|
if (selem != null) {
|
var keys = this.fontStyleKeys;
|
for (var i = 0, len = keys.length; i < len; i++) {
|
var key = keys[i];
|
if (exact.symbol[key]) {
|
selem.setAttribute(key, exact.symbol[key]);
|
}
|
}
|
eelem.appendChild(selem);
|
}
|
}
|
} // for each exact
|
}
|
},
|
|
addValueMapRenderer: function(renderElem, renderer) {
|
renderElem.setAttribute("lookupfield", renderer.lookupfield);
|
|
if (typeof renderer.ranges == "object") {
|
for(var rng=0, rnglen=renderer.ranges.length; rng<rnglen; rng++) {
|
var range = renderer.ranges[rng];
|
|
var relem = this.createElementNS("", "RANGE");
|
relem.setAttribute("lower", range.lower);
|
relem.setAttribute("upper", range.upper);
|
|
renderElem.appendChild(relem);
|
|
if (typeof range.symbol == "object") {
|
var selem = null;
|
|
if (range.symbol.type == "simplepolygon") {
|
selem = this.createElementNS("", "SIMPLEPOLYGONSYMBOL");
|
}
|
|
if (selem != null) {
|
if (typeof range.symbol.boundarycolor == "string") {
|
selem.setAttribute("boundarycolor", range.symbol.boundarycolor);
|
}
|
if (typeof range.symbol.fillcolor == "string") {
|
selem.setAttribute("fillcolor", range.symbol.fillcolor);
|
}
|
if (typeof range.symbol.filltransparency == "number") {
|
selem.setAttribute("filltransparency", range.symbol.filltransparency);
|
}
|
relem.appendChild(selem);
|
}
|
}
|
} // for each range
|
} else if (typeof renderer.exacts == "object") {
|
for (var ext=0, extlen=renderer.exacts.length; ext<extlen; ext++) {
|
var exact = renderer.exacts[ext];
|
|
var eelem = this.createElementNS("", "EXACT");
|
if (typeof exact.value == "string") {
|
eelem.setAttribute("value", exact.value);
|
}
|
if (typeof exact.label == "string") {
|
eelem.setAttribute("label", exact.label);
|
}
|
if (typeof exact.method == "string") {
|
eelem.setAttribute("method", exact.method);
|
}
|
|
renderElem.appendChild(eelem);
|
|
if (typeof exact.symbol == "object") {
|
var selem = null;
|
|
if (exact.symbol.type == "simplemarker") {
|
selem = this.createElementNS("", "SIMPLEMARKERSYMBOL");
|
}
|
|
if (selem != null) {
|
if (typeof exact.symbol.antialiasing == "string") {
|
selem.setAttribute("antialiasing", exact.symbol.antialiasing);
|
}
|
if (typeof exact.symbol.color == "string") {
|
selem.setAttribute("color", exact.symbol.color);
|
}
|
if (typeof exact.symbol.outline == "string") {
|
selem.setAttribute("outline", exact.symbol.outline);
|
}
|
if (typeof exact.symbol.overlap == "string") {
|
selem.setAttribute("overlap", exact.symbol.overlap);
|
}
|
if (typeof exact.symbol.shadow == "string") {
|
selem.setAttribute("shadow", exact.symbol.shadow);
|
}
|
if (typeof exact.symbol.transparency == "number") {
|
selem.setAttribute("transparency", exact.symbol.transparency);
|
}
|
//if (typeof exact.symbol.type == "string")
|
// selem.setAttribute("type", exact.symbol.type);
|
if (typeof exact.symbol.usecentroid == "string") {
|
selem.setAttribute("usecentroid", exact.symbol.usecentroid);
|
}
|
if (typeof exact.symbol.width == "number") {
|
selem.setAttribute("width", exact.symbol.width);
|
}
|
|
eelem.appendChild(selem);
|
}
|
}
|
} // for each exact
|
}
|
},
|
|
|
addSimpleLabelRenderer: function(renderElem, renderer) {
|
renderElem.setAttribute("field", renderer.field);
|
var keys = ['featureweight', 'howmanylabels', 'labelbufferratio',
|
'labelpriorities', 'labelweight', 'linelabelposition',
|
'rotationalangles'];
|
for (var i=0, len=keys.length; i<len; i++) {
|
var key = keys[i];
|
if (renderer[key]) {
|
renderElem.setAttribute(key, renderer[key]);
|
}
|
}
|
|
if (renderer.symbol.type == "text") {
|
var symbol = renderer.symbol;
|
var selem = this.createElementNS("", "TEXTSYMBOL");
|
renderElem.appendChild(selem);
|
|
var keys = this.fontStyleKeys;
|
for (var i=0, len=keys.length; i<len; i++) {
|
var key = keys[i];
|
if (symbol[key]) {
|
selem.setAttribute(key, renderer[key]);
|
}
|
}
|
}
|
},
|
|
writePolygonGeometry: function(polygon) {
|
if (!(polygon instanceof OpenLayers.Geometry.Polygon)) {
|
throw {
|
message:'Cannot write polygon geometry to ArcXML with an ' +
|
polygon.CLASS_NAME + ' object.',
|
geometry: polygon
|
};
|
}
|
|
var polyElem = this.createElementNS("", "POLYGON");
|
|
for (var ln=0, lnlen=polygon.components.length; ln<lnlen; ln++) {
|
var ring = polygon.components[ln];
|
var ringElem = this.createElementNS("", "RING");
|
|
for (var rn=0, rnlen=ring.components.length; rn<rnlen; rn++) {
|
var point = ring.components[rn];
|
var pointElem = this.createElementNS("", "POINT");
|
|
pointElem.setAttribute("x", point.x);
|
pointElem.setAttribute("y", point.y);
|
|
ringElem.appendChild(pointElem);
|
}
|
|
polyElem.appendChild(ringElem);
|
}
|
|
return polyElem;
|
},
|
|
/**
|
* Method: parseResponse
|
* Take an ArcXML response, and parse in into this object's internal properties.
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} The ArcXML response, as either a string or the
|
* top level DOMElement of the response.
|
*/
|
parseResponse: function(data) {
|
if(typeof data == "string") {
|
var newData = new OpenLayers.Format.XML();
|
data = newData.read(data);
|
}
|
var response = new OpenLayers.Format.ArcXML.Response();
|
|
var errorNode = data.getElementsByTagName("ERROR");
|
|
if (errorNode != null && errorNode.length > 0) {
|
response.error = this.getChildValue(errorNode, "Unknown error.");
|
} else {
|
var responseNode = data.getElementsByTagName("RESPONSE");
|
|
if (responseNode == null || responseNode.length == 0) {
|
response.error = "No RESPONSE tag found in ArcXML response.";
|
return response;
|
}
|
|
var rtype = responseNode[0].firstChild.nodeName;
|
if (rtype == "#text") {
|
rtype = responseNode[0].firstChild.nextSibling.nodeName;
|
}
|
|
if (rtype == "IMAGE") {
|
var envelopeNode = data.getElementsByTagName("ENVELOPE");
|
var outputNode = data.getElementsByTagName("OUTPUT");
|
|
if (envelopeNode == null || envelopeNode.length == 0) {
|
response.error = "No ENVELOPE tag found in ArcXML response.";
|
} else if (outputNode == null || outputNode.length == 0) {
|
response.error = "No OUTPUT tag found in ArcXML response.";
|
} else {
|
var envAttr = this.parseAttributes(envelopeNode[0]);
|
var outputAttr = this.parseAttributes(outputNode[0]);
|
|
if (typeof outputAttr.type == "string") {
|
response.image = {
|
envelope: envAttr,
|
output: {
|
type: outputAttr.type,
|
data: this.getChildValue(outputNode[0])
|
}
|
};
|
} else {
|
response.image = { envelope: envAttr, output: outputAttr };
|
}
|
}
|
} else if (rtype == "FEATURES") {
|
var features = responseNode[0].getElementsByTagName("FEATURES");
|
|
// get the feature count
|
var featureCount = features[0].getElementsByTagName("FEATURECOUNT");
|
response.features.featurecount = featureCount[0].getAttribute("count");
|
|
if (response.features.featurecount > 0) {
|
// get the feature envelope
|
var envelope = features[0].getElementsByTagName("ENVELOPE");
|
response.features.envelope = this.parseAttributes(envelope[0], typeof(0));
|
|
// get the field values per feature
|
var featureList = features[0].getElementsByTagName("FEATURE");
|
for (var fn = 0; fn < featureList.length; fn++) {
|
var feature = new OpenLayers.Feature.Vector();
|
var fields = featureList[fn].getElementsByTagName("FIELD");
|
|
for (var fdn = 0; fdn < fields.length; fdn++) {
|
var fieldName = fields[fdn].getAttribute("name");
|
var fieldValue = fields[fdn].getAttribute("value");
|
feature.attributes[ fieldName ] = fieldValue;
|
}
|
|
var geom = featureList[fn].getElementsByTagName("POLYGON");
|
|
if (geom.length > 0) {
|
// if there is a polygon, create an openlayers polygon, and assign
|
// it to the .geometry property of the feature
|
var ring = geom[0].getElementsByTagName("RING");
|
|
var polys = [];
|
for (var rn = 0; rn < ring.length; rn++) {
|
var linearRings = [];
|
linearRings.push(this.parsePointGeometry(ring[rn]));
|
|
var holes = ring[rn].getElementsByTagName("HOLE");
|
for (var hn = 0; hn < holes.length; hn++) {
|
linearRings.push(this.parsePointGeometry(holes[hn]));
|
}
|
holes = null;
|
polys.push(new OpenLayers.Geometry.Polygon(linearRings));
|
linearRings = null;
|
}
|
ring = null;
|
|
if (polys.length == 1) {
|
feature.geometry = polys[0];
|
} else
|
{
|
feature.geometry = new OpenLayers.Geometry.MultiPolygon(polys);
|
}
|
}
|
|
response.features.feature.push(feature);
|
}
|
}
|
} else {
|
response.error = "Unidentified response type.";
|
}
|
}
|
return response;
|
},
|
|
|
/**
|
* Method: parseAttributes
|
*
|
* Parameters:
|
* node - {<DOMElement>} An element to parse attributes from.
|
*
|
* Returns:
|
* {Object} An attributes object, with properties set to attribute values.
|
*/
|
parseAttributes: function(node,type) {
|
var attributes = {};
|
for(var attr = 0; attr < node.attributes.length; attr++) {
|
if (type == "number") {
|
attributes[node.attributes[attr].nodeName] = parseFloat(node.attributes[attr].nodeValue);
|
} else {
|
attributes[node.attributes[attr].nodeName] = node.attributes[attr].nodeValue;
|
}
|
}
|
return attributes;
|
},
|
|
|
/**
|
* Method: parsePointGeometry
|
*
|
* Parameters:
|
* node - {<DOMElement>} An element to parse <COORDS> or <POINT> arcxml data from.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry.LinearRing>} A linear ring represented by the node's points.
|
*/
|
parsePointGeometry: function(node) {
|
var ringPoints = [];
|
var coords = node.getElementsByTagName("COORDS");
|
|
if (coords.length > 0) {
|
// if coords is present, it's the only coords item
|
var coordArr = this.getChildValue(coords[0]);
|
coordArr = coordArr.split(/;/);
|
for (var cn = 0; cn < coordArr.length; cn++) {
|
var coordItems = coordArr[cn].split(/ /);
|
ringPoints.push(new OpenLayers.Geometry.Point(coordItems[0], coordItems[1]));
|
}
|
coords = null;
|
} else {
|
var point = node.getElementsByTagName("POINT");
|
if (point.length > 0) {
|
for (var pn = 0; pn < point.length; pn++) {
|
ringPoints.push(
|
new OpenLayers.Geometry.Point(
|
parseFloat(point[pn].getAttribute("x")),
|
parseFloat(point[pn].getAttribute("y"))
|
)
|
);
|
}
|
}
|
point = null;
|
}
|
|
return new OpenLayers.Geometry.LinearRing(ringPoints);
|
},
|
|
CLASS_NAME: "OpenLayers.Format.ArcXML"
|
});
|
|
OpenLayers.Format.ArcXML.Request = OpenLayers.Class({
|
initialize: function(params) {
|
var defaults = {
|
get_image: {
|
properties: {
|
background: null,
|
/*{
|
color: { r:255, g:255, b:255 },
|
transcolor: null
|
},*/
|
draw: true,
|
envelope: {
|
minx: 0,
|
miny: 0,
|
maxx: 0,
|
maxy: 0
|
},
|
featurecoordsys: {
|
id:0,
|
string:"",
|
datumtransformid:0,
|
datumtransformstring:""
|
},
|
filtercoordsys:{
|
id:0,
|
string:"",
|
datumtransformid:0,
|
datumtransformstring:""
|
},
|
imagesize:{
|
height:0,
|
width:0,
|
dpi:96,
|
printheight:0,
|
printwidth:0,
|
scalesymbols:false
|
},
|
layerlist:[],
|
/* no support for legends */
|
output:{
|
baseurl:"",
|
legendbaseurl:"",
|
legendname:"",
|
legendpath:"",
|
legendurl:"",
|
name:"",
|
path:"",
|
type:"jpg",
|
url:""
|
}
|
}
|
},
|
|
get_feature: {
|
layer: "",
|
query: {
|
isspatial: false,
|
featurecoordsys: {
|
id:0,
|
string:"",
|
datumtransformid:0,
|
datumtransformstring:""
|
},
|
filtercoordsys: {
|
id:0,
|
string:"",
|
datumtransformid:0,
|
datumtransformstring:""
|
},
|
buffer:0,
|
where:"",
|
spatialfilter: {
|
relation: "envelope_intersection",
|
envelope: null
|
}
|
}
|
},
|
|
environment: {
|
separators: {
|
cs:" ",
|
ts:";"
|
}
|
},
|
|
layer: [],
|
workspaces: []
|
};
|
|
return OpenLayers.Util.extend(this, defaults);
|
},
|
|
CLASS_NAME: "OpenLayers.Format.ArcXML.Request"
|
});
|
|
OpenLayers.Format.ArcXML.Response = OpenLayers.Class({
|
initialize: function(params) {
|
var defaults = {
|
image: {
|
envelope:null,
|
output:''
|
},
|
|
features: {
|
featurecount: 0,
|
envelope: null,
|
feature: []
|
},
|
|
error:''
|
};
|
|
return OpenLayers.Util.extend(this, defaults);
|
},
|
|
CLASS_NAME: "OpenLayers.Format.ArcXML.Response"
|
});
|
/* ======================================================================
|
OpenLayers/Request/XMLHttpRequest.js
|
====================================================================== */
|
|
// XMLHttpRequest.js Copyright (C) 2010 Sergey Ilinsky (http://www.ilinsky.com)
|
//
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
// you may not use this file except in compliance with the License.
|
// You may obtain a copy of the License at
|
//
|
// http://www.apache.org/licenses/LICENSE-2.0
|
//
|
// Unless required by applicable law or agreed to in writing, software
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// See the License for the specific language governing permissions and
|
// limitations under the License.
|
|
/**
|
* @requires OpenLayers/Request.js
|
*/
|
|
(function () {
|
|
// Save reference to earlier defined object implementation (if any)
|
var oXMLHttpRequest = window.XMLHttpRequest;
|
|
// Define on browser type
|
var bGecko = !!window.controllers,
|
bIE = window.document.all && !window.opera,
|
bIE7 = bIE && window.navigator.userAgent.match(/MSIE 7.0/);
|
|
// Enables "XMLHttpRequest()" call next to "new XMLHttpReques()"
|
function fXMLHttpRequest() {
|
this._object = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject("Microsoft.XMLHTTP");
|
this._listeners = [];
|
};
|
|
// Constructor
|
function cXMLHttpRequest() {
|
return new fXMLHttpRequest;
|
};
|
cXMLHttpRequest.prototype = fXMLHttpRequest.prototype;
|
|
// BUGFIX: Firefox with Firebug installed would break pages if not executed
|
if (bGecko && oXMLHttpRequest.wrapped)
|
cXMLHttpRequest.wrapped = oXMLHttpRequest.wrapped;
|
|
// Constants
|
cXMLHttpRequest.UNSENT = 0;
|
cXMLHttpRequest.OPENED = 1;
|
cXMLHttpRequest.HEADERS_RECEIVED = 2;
|
cXMLHttpRequest.LOADING = 3;
|
cXMLHttpRequest.DONE = 4;
|
|
// Public Properties
|
cXMLHttpRequest.prototype.readyState = cXMLHttpRequest.UNSENT;
|
cXMLHttpRequest.prototype.responseText = '';
|
cXMLHttpRequest.prototype.responseXML = null;
|
cXMLHttpRequest.prototype.status = 0;
|
cXMLHttpRequest.prototype.statusText = '';
|
|
// Priority proposal
|
cXMLHttpRequest.prototype.priority = "NORMAL";
|
|
// Instance-level Events Handlers
|
cXMLHttpRequest.prototype.onreadystatechange = null;
|
|
// Class-level Events Handlers
|
cXMLHttpRequest.onreadystatechange = null;
|
cXMLHttpRequest.onopen = null;
|
cXMLHttpRequest.onsend = null;
|
cXMLHttpRequest.onabort = null;
|
|
// Public Methods
|
cXMLHttpRequest.prototype.open = function(sMethod, sUrl, bAsync, sUser, sPassword) {
|
// Delete headers, required when object is reused
|
delete this._headers;
|
|
// When bAsync parameter value is omitted, use true as default
|
if (arguments.length < 3)
|
bAsync = true;
|
|
// Save async parameter for fixing Gecko bug with missing readystatechange in synchronous requests
|
this._async = bAsync;
|
|
// Set the onreadystatechange handler
|
var oRequest = this,
|
nState = this.readyState,
|
fOnUnload;
|
|
// BUGFIX: IE - memory leak on page unload (inter-page leak)
|
if (bIE && bAsync) {
|
fOnUnload = function() {
|
if (nState != cXMLHttpRequest.DONE) {
|
fCleanTransport(oRequest);
|
// Safe to abort here since onreadystatechange handler removed
|
oRequest.abort();
|
}
|
};
|
window.attachEvent("onunload", fOnUnload);
|
}
|
|
// Add method sniffer
|
if (cXMLHttpRequest.onopen)
|
cXMLHttpRequest.onopen.apply(this, arguments);
|
|
if (arguments.length > 4)
|
this._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
|
else
|
if (arguments.length > 3)
|
this._object.open(sMethod, sUrl, bAsync, sUser);
|
else
|
this._object.open(sMethod, sUrl, bAsync);
|
|
this.readyState = cXMLHttpRequest.OPENED;
|
fReadyStateChange(this);
|
|
this._object.onreadystatechange = function() {
|
if (bGecko && !bAsync)
|
return;
|
|
// Synchronize state
|
oRequest.readyState = oRequest._object.readyState;
|
|
//
|
fSynchronizeValues(oRequest);
|
|
// BUGFIX: Firefox fires unnecessary DONE when aborting
|
if (oRequest._aborted) {
|
// Reset readyState to UNSENT
|
oRequest.readyState = cXMLHttpRequest.UNSENT;
|
|
// Return now
|
return;
|
}
|
|
if (oRequest.readyState == cXMLHttpRequest.DONE) {
|
// Free up queue
|
delete oRequest._data;
|
/* if (bAsync)
|
fQueue_remove(oRequest);*/
|
//
|
fCleanTransport(oRequest);
|
// Uncomment this block if you need a fix for IE cache
|
/*
|
// BUGFIX: IE - cache issue
|
if (!oRequest._object.getResponseHeader("Date")) {
|
// Save object to cache
|
oRequest._cached = oRequest._object;
|
|
// Instantiate a new transport object
|
cXMLHttpRequest.call(oRequest);
|
|
// Re-send request
|
if (sUser) {
|
if (sPassword)
|
oRequest._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
|
else
|
oRequest._object.open(sMethod, sUrl, bAsync, sUser);
|
}
|
else
|
oRequest._object.open(sMethod, sUrl, bAsync);
|
oRequest._object.setRequestHeader("If-Modified-Since", oRequest._cached.getResponseHeader("Last-Modified") || new window.Date(0));
|
// Copy headers set
|
if (oRequest._headers)
|
for (var sHeader in oRequest._headers)
|
if (typeof oRequest._headers[sHeader] == "string") // Some frameworks prototype objects with functions
|
oRequest._object.setRequestHeader(sHeader, oRequest._headers[sHeader]);
|
|
oRequest._object.onreadystatechange = function() {
|
// Synchronize state
|
oRequest.readyState = oRequest._object.readyState;
|
|
if (oRequest._aborted) {
|
//
|
oRequest.readyState = cXMLHttpRequest.UNSENT;
|
|
// Return
|
return;
|
}
|
|
if (oRequest.readyState == cXMLHttpRequest.DONE) {
|
// Clean Object
|
fCleanTransport(oRequest);
|
|
// get cached request
|
if (oRequest.status == 304)
|
oRequest._object = oRequest._cached;
|
|
//
|
delete oRequest._cached;
|
|
//
|
fSynchronizeValues(oRequest);
|
|
//
|
fReadyStateChange(oRequest);
|
|
// BUGFIX: IE - memory leak in interrupted
|
if (bIE && bAsync)
|
window.detachEvent("onunload", fOnUnload);
|
}
|
};
|
oRequest._object.send(null);
|
|
// Return now - wait until re-sent request is finished
|
return;
|
};
|
*/
|
// BUGFIX: IE - memory leak in interrupted
|
if (bIE && bAsync)
|
window.detachEvent("onunload", fOnUnload);
|
}
|
|
// BUGFIX: Some browsers (Internet Explorer, Gecko) fire OPEN readystate twice
|
if (nState != oRequest.readyState)
|
fReadyStateChange(oRequest);
|
|
nState = oRequest.readyState;
|
}
|
};
|
function fXMLHttpRequest_send(oRequest) {
|
oRequest._object.send(oRequest._data);
|
|
// BUGFIX: Gecko - missing readystatechange calls in synchronous requests
|
if (bGecko && !oRequest._async) {
|
oRequest.readyState = cXMLHttpRequest.OPENED;
|
|
// Synchronize state
|
fSynchronizeValues(oRequest);
|
|
// Simulate missing states
|
while (oRequest.readyState < cXMLHttpRequest.DONE) {
|
oRequest.readyState++;
|
fReadyStateChange(oRequest);
|
// Check if we are aborted
|
if (oRequest._aborted)
|
return;
|
}
|
}
|
};
|
cXMLHttpRequest.prototype.send = function(vData) {
|
// Add method sniffer
|
if (cXMLHttpRequest.onsend)
|
cXMLHttpRequest.onsend.apply(this, arguments);
|
|
if (!arguments.length)
|
vData = null;
|
|
// BUGFIX: Safari - fails sending documents created/modified dynamically, so an explicit serialization required
|
// BUGFIX: IE - rewrites any custom mime-type to "text/xml" in case an XMLNode is sent
|
// BUGFIX: Gecko - fails sending Element (this is up to the implementation either to standard)
|
if (vData && vData.nodeType) {
|
vData = window.XMLSerializer ? new window.XMLSerializer().serializeToString(vData) : vData.xml;
|
if (!this._headers["Content-Type"])
|
this._object.setRequestHeader("Content-Type", "application/xml");
|
}
|
|
this._data = vData;
|
/*
|
// Add to queue
|
if (this._async)
|
fQueue_add(this);
|
else*/
|
fXMLHttpRequest_send(this);
|
};
|
cXMLHttpRequest.prototype.abort = function() {
|
// Add method sniffer
|
if (cXMLHttpRequest.onabort)
|
cXMLHttpRequest.onabort.apply(this, arguments);
|
|
// BUGFIX: Gecko - unnecessary DONE when aborting
|
if (this.readyState > cXMLHttpRequest.UNSENT)
|
this._aborted = true;
|
|
this._object.abort();
|
|
// BUGFIX: IE - memory leak
|
fCleanTransport(this);
|
|
this.readyState = cXMLHttpRequest.UNSENT;
|
|
delete this._data;
|
/* if (this._async)
|
fQueue_remove(this);*/
|
};
|
cXMLHttpRequest.prototype.getAllResponseHeaders = function() {
|
return this._object.getAllResponseHeaders();
|
};
|
cXMLHttpRequest.prototype.getResponseHeader = function(sName) {
|
return this._object.getResponseHeader(sName);
|
};
|
cXMLHttpRequest.prototype.setRequestHeader = function(sName, sValue) {
|
// BUGFIX: IE - cache issue
|
if (!this._headers)
|
this._headers = {};
|
this._headers[sName] = sValue;
|
|
return this._object.setRequestHeader(sName, sValue);
|
};
|
|
// EventTarget interface implementation
|
cXMLHttpRequest.prototype.addEventListener = function(sName, fHandler, bUseCapture) {
|
for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
|
if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)
|
return;
|
// Add listener
|
this._listeners.push([sName, fHandler, bUseCapture]);
|
};
|
|
cXMLHttpRequest.prototype.removeEventListener = function(sName, fHandler, bUseCapture) {
|
for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
|
if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)
|
break;
|
// Remove listener
|
if (oListener)
|
this._listeners.splice(nIndex, 1);
|
};
|
|
cXMLHttpRequest.prototype.dispatchEvent = function(oEvent) {
|
var oEventPseudo = {
|
'type': oEvent.type,
|
'target': this,
|
'currentTarget':this,
|
'eventPhase': 2,
|
'bubbles': oEvent.bubbles,
|
'cancelable': oEvent.cancelable,
|
'timeStamp': oEvent.timeStamp,
|
'stopPropagation': function() {}, // There is no flow
|
'preventDefault': function() {}, // There is no default action
|
'initEvent': function() {} // Original event object should be initialized
|
};
|
|
// Execute onreadystatechange
|
if (oEventPseudo.type == "readystatechange" && this.onreadystatechange)
|
(this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEventPseudo]);
|
|
// Execute listeners
|
for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
|
if (oListener[0] == oEventPseudo.type && !oListener[2])
|
(oListener[1].handleEvent || oListener[1]).apply(this, [oEventPseudo]);
|
};
|
|
//
|
cXMLHttpRequest.prototype.toString = function() {
|
return '[' + "object" + ' ' + "XMLHttpRequest" + ']';
|
};
|
|
cXMLHttpRequest.toString = function() {
|
return '[' + "XMLHttpRequest" + ']';
|
};
|
|
// Helper function
|
function fReadyStateChange(oRequest) {
|
// Sniffing code
|
if (cXMLHttpRequest.onreadystatechange)
|
cXMLHttpRequest.onreadystatechange.apply(oRequest);
|
|
// Fake event
|
oRequest.dispatchEvent({
|
'type': "readystatechange",
|
'bubbles': false,
|
'cancelable': false,
|
'timeStamp': new Date + 0
|
});
|
};
|
|
function fGetDocument(oRequest) {
|
var oDocument = oRequest.responseXML,
|
sResponse = oRequest.responseText;
|
// Try parsing responseText
|
if (bIE && sResponse && oDocument && !oDocument.documentElement && oRequest.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)) {
|
oDocument = new window.ActiveXObject("Microsoft.XMLDOM");
|
oDocument.async = false;
|
oDocument.validateOnParse = false;
|
oDocument.loadXML(sResponse);
|
}
|
// Check if there is no error in document
|
if (oDocument)
|
if ((bIE && oDocument.parseError != 0) || !oDocument.documentElement || (oDocument.documentElement && oDocument.documentElement.tagName == "parsererror"))
|
return null;
|
return oDocument;
|
};
|
|
function fSynchronizeValues(oRequest) {
|
try { oRequest.responseText = oRequest._object.responseText; } catch (e) {}
|
try { oRequest.responseXML = fGetDocument(oRequest._object); } catch (e) {}
|
try { oRequest.status = oRequest._object.status; } catch (e) {}
|
try { oRequest.statusText = oRequest._object.statusText; } catch (e) {}
|
};
|
|
function fCleanTransport(oRequest) {
|
// BUGFIX: IE - memory leak (on-page leak)
|
oRequest._object.onreadystatechange = new window.Function;
|
};
|
/*
|
// Queue manager
|
var oQueuePending = {"CRITICAL":[],"HIGH":[],"NORMAL":[],"LOW":[],"LOWEST":[]},
|
aQueueRunning = [];
|
function fQueue_add(oRequest) {
|
oQueuePending[oRequest.priority in oQueuePending ? oRequest.priority : "NORMAL"].push(oRequest);
|
//
|
setTimeout(fQueue_process);
|
};
|
|
function fQueue_remove(oRequest) {
|
for (var nIndex = 0, bFound = false; nIndex < aQueueRunning.length; nIndex++)
|
if (bFound)
|
aQueueRunning[nIndex - 1] = aQueueRunning[nIndex];
|
else
|
if (aQueueRunning[nIndex] == oRequest)
|
bFound = true;
|
if (bFound)
|
aQueueRunning.length--;
|
//
|
setTimeout(fQueue_process);
|
};
|
|
function fQueue_process() {
|
if (aQueueRunning.length < 6) {
|
for (var sPriority in oQueuePending) {
|
if (oQueuePending[sPriority].length) {
|
var oRequest = oQueuePending[sPriority][0];
|
oQueuePending[sPriority] = oQueuePending[sPriority].slice(1);
|
//
|
aQueueRunning.push(oRequest);
|
// Send request
|
fXMLHttpRequest_send(oRequest);
|
break;
|
}
|
}
|
}
|
};
|
*/
|
// Internet Explorer 5.0 (missing apply)
|
if (!window.Function.prototype.apply) {
|
window.Function.prototype.apply = function(oRequest, oArguments) {
|
if (!oArguments)
|
oArguments = [];
|
oRequest.__func = this;
|
oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]);
|
delete oRequest.__func;
|
};
|
};
|
|
// Register new object with window
|
/**
|
* Class: OpenLayers.Request.XMLHttpRequest
|
* Standard-compliant (W3C) cross-browser implementation of the
|
* XMLHttpRequest object. From
|
* http://code.google.com/p/xmlhttprequest/.
|
*/
|
if (!OpenLayers.Request) {
|
/**
|
* This allows for OpenLayers/Request.js to be included
|
* before or after this script.
|
*/
|
OpenLayers.Request = {};
|
}
|
OpenLayers.Request.XMLHttpRequest = cXMLHttpRequest;
|
})();
|
/* ======================================================================
|
OpenLayers/Request.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Events.js
|
* @requires OpenLayers/Request/XMLHttpRequest.js
|
*/
|
|
/**
|
* TODO: deprecate me
|
* Use OpenLayers.Request.proxy instead.
|
*/
|
OpenLayers.ProxyHost = "";
|
|
/**
|
* Namespace: OpenLayers.Request
|
* The OpenLayers.Request namespace contains convenience methods for working
|
* with XMLHttpRequests. These methods work with a cross-browser
|
* W3C compliant <OpenLayers.Request.XMLHttpRequest> class.
|
*/
|
if (!OpenLayers.Request) {
|
/**
|
* This allows for OpenLayers/Request/XMLHttpRequest.js to be included
|
* before or after this script.
|
*/
|
OpenLayers.Request = {};
|
}
|
OpenLayers.Util.extend(OpenLayers.Request, {
|
|
/**
|
* Constant: DEFAULT_CONFIG
|
* {Object} Default configuration for all requests.
|
*/
|
DEFAULT_CONFIG: {
|
method: "GET",
|
url: window.location.href,
|
async: true,
|
user: undefined,
|
password: undefined,
|
params: null,
|
proxy: OpenLayers.ProxyHost,
|
headers: {},
|
data: null,
|
callback: function() {},
|
success: null,
|
failure: null,
|
scope: null
|
},
|
|
/**
|
* Constant: URL_SPLIT_REGEX
|
*/
|
URL_SPLIT_REGEX: /([^:]*:)\/\/([^:]*:?[^@]*@)?([^:\/\?]*):?([^\/\?]*)/,
|
|
/**
|
* APIProperty: events
|
* {<OpenLayers.Events>} An events object that handles all
|
* events on the {<OpenLayers.Request>} object.
|
*
|
* All event listeners will receive an event object with three properties:
|
* request - {<OpenLayers.Request.XMLHttpRequest>} The request object.
|
* config - {Object} The config object sent to the specific request method.
|
* requestUrl - {String} The request url.
|
*
|
* Supported event types:
|
* complete - Triggered when we have a response from the request, if a
|
* listener returns false, no further response processing will take
|
* place.
|
* success - Triggered when the HTTP response has a success code (200-299).
|
* failure - Triggered when the HTTP response does not have a success code.
|
*/
|
events: new OpenLayers.Events(this),
|
|
/**
|
* Method: makeSameOrigin
|
* Using the specified proxy, returns a same origin url of the provided url.
|
*
|
* Parameters:
|
* url - {String} An arbitrary url
|
* proxy {String|Function} The proxy to use to make the provided url a
|
* same origin url.
|
*
|
* Returns
|
* {String} the same origin url. If no proxy is provided, the returned url
|
* will be the same as the provided url.
|
*/
|
makeSameOrigin: function(url, proxy) {
|
var sameOrigin = url.indexOf("http") !== 0;
|
var urlParts = !sameOrigin && url.match(this.URL_SPLIT_REGEX);
|
if (urlParts) {
|
var location = window.location;
|
sameOrigin =
|
urlParts[1] == location.protocol &&
|
urlParts[3] == location.hostname;
|
var uPort = urlParts[4], lPort = location.port;
|
if (uPort != 80 && uPort != "" || lPort != "80" && lPort != "") {
|
sameOrigin = sameOrigin && uPort == lPort;
|
}
|
}
|
if (!sameOrigin) {
|
if (proxy) {
|
if (typeof proxy == "function") {
|
url = proxy(url);
|
} else {
|
url = proxy + encodeURIComponent(url);
|
}
|
}
|
}
|
return url;
|
},
|
|
/**
|
* APIMethod: issue
|
* Create a new XMLHttpRequest object, open it, set any headers, bind
|
* a callback to done state, and send any data. It is recommended that
|
* you use one <GET>, <POST>, <PUT>, <DELETE>, <OPTIONS>, or <HEAD>.
|
* This method is only documented to provide detail on the configuration
|
* options available to all request methods.
|
*
|
* Parameters:
|
* config - {Object} Object containing properties for configuring the
|
* request. Allowed configuration properties are described below.
|
* This object is modified and should not be reused.
|
*
|
* Allowed config properties:
|
* method - {String} One of GET, POST, PUT, DELETE, HEAD, or
|
* OPTIONS. Default is GET.
|
* url - {String} URL for the request.
|
* async - {Boolean} Open an asynchronous request. Default is true.
|
* user - {String} User for relevant authentication scheme. Set
|
* to null to clear current user.
|
* password - {String} Password for relevant authentication scheme.
|
* Set to null to clear current password.
|
* proxy - {String} Optional proxy. Defaults to
|
* <OpenLayers.ProxyHost>.
|
* params - {Object} Any key:value pairs to be appended to the
|
* url as a query string. Assumes url doesn't already include a query
|
* string or hash. Typically, this is only appropriate for <GET>
|
* requests where the query string will be appended to the url.
|
* Parameter values that are arrays will be
|
* concatenated with a comma (note that this goes against form-encoding)
|
* as is done with <OpenLayers.Util.getParameterString>.
|
* headers - {Object} Object with header:value pairs to be set on
|
* the request.
|
* data - {String | Document} Optional data to send with the request.
|
* Typically, this is only used with <POST> and <PUT> requests.
|
* Make sure to provide the appropriate "Content-Type" header for your
|
* data. For <POST> and <PUT> requests, the content type defaults to
|
* "application-xml". If your data is a different content type, or
|
* if you are using a different HTTP method, set the "Content-Type"
|
* header to match your data type.
|
* callback - {Function} Function to call when request is done.
|
* To determine if the request failed, check request.status (200
|
* indicates success).
|
* success - {Function} Optional function to call if request status is in
|
* the 200s. This will be called in addition to callback above and
|
* would typically only be used as an alternative.
|
* failure - {Function} Optional function to call if request status is not
|
* in the 200s. This will be called in addition to callback above and
|
* would typically only be used as an alternative.
|
* scope - {Object} If callback is a public method on some object,
|
* set the scope to that object.
|
*
|
* Returns:
|
* {XMLHttpRequest} Request object. To abort the request before a response
|
* is received, call abort() on the request object.
|
*/
|
issue: function(config) {
|
// apply default config - proxy host may have changed
|
var defaultConfig = OpenLayers.Util.extend(
|
this.DEFAULT_CONFIG,
|
{proxy: OpenLayers.ProxyHost}
|
);
|
config = config || {};
|
config.headers = config.headers || {};
|
config = OpenLayers.Util.applyDefaults(config, defaultConfig);
|
config.headers = OpenLayers.Util.applyDefaults(config.headers, defaultConfig.headers);
|
// Always set the "X-Requested-With" header to signal that this request
|
// was issued through the XHR-object. Since header keys are case
|
// insensitive and we want to allow overriding of the "X-Requested-With"
|
// header through the user we cannot use applyDefaults, but have to
|
// check manually whether we were called with a "X-Requested-With"
|
// header.
|
var customRequestedWithHeader = false,
|
headerKey;
|
for(headerKey in config.headers) {
|
if (config.headers.hasOwnProperty( headerKey )) {
|
if (headerKey.toLowerCase() === 'x-requested-with') {
|
customRequestedWithHeader = true;
|
}
|
}
|
}
|
if (customRequestedWithHeader === false) {
|
// we did not have a custom "X-Requested-With" header
|
config.headers['X-Requested-With'] = 'XMLHttpRequest';
|
}
|
|
// create request, open, and set headers
|
var request = new OpenLayers.Request.XMLHttpRequest();
|
var url = OpenLayers.Util.urlAppend(config.url,
|
OpenLayers.Util.getParameterString(config.params || {}));
|
url = OpenLayers.Request.makeSameOrigin(url, config.proxy);
|
request.open(
|
config.method, url, config.async, config.user, config.password
|
);
|
for(var header in config.headers) {
|
request.setRequestHeader(header, config.headers[header]);
|
}
|
|
var events = this.events;
|
|
// we want to execute runCallbacks with "this" as the
|
// execution scope
|
var self = this;
|
|
request.onreadystatechange = function() {
|
if(request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) {
|
var proceed = events.triggerEvent(
|
"complete",
|
{request: request, config: config, requestUrl: url}
|
);
|
if(proceed !== false) {
|
self.runCallbacks(
|
{request: request, config: config, requestUrl: url}
|
);
|
}
|
}
|
};
|
|
// send request (optionally with data) and return
|
// call in a timeout for asynchronous requests so the return is
|
// available before readyState == 4 for cached docs
|
if(config.async === false) {
|
request.send(config.data);
|
} else {
|
window.setTimeout(function(){
|
if (request.readyState !== 0) { // W3C: 0-UNSENT
|
request.send(config.data);
|
}
|
}, 0);
|
}
|
return request;
|
},
|
|
/**
|
* Method: runCallbacks
|
* Calls the complete, success and failure callbacks. Application
|
* can listen to the "complete" event, have the listener
|
* display a confirm window and always return false, and
|
* execute OpenLayers.Request.runCallbacks if the user
|
* hits "yes" in the confirm window.
|
*
|
* Parameters:
|
* options - {Object} Hash containing request, config and requestUrl keys
|
*/
|
runCallbacks: function(options) {
|
var request = options.request;
|
var config = options.config;
|
|
// bind callbacks to readyState 4 (done)
|
var complete = (config.scope) ?
|
OpenLayers.Function.bind(config.callback, config.scope) :
|
config.callback;
|
|
// optional success callback
|
var success;
|
if(config.success) {
|
success = (config.scope) ?
|
OpenLayers.Function.bind(config.success, config.scope) :
|
config.success;
|
}
|
|
// optional failure callback
|
var failure;
|
if(config.failure) {
|
failure = (config.scope) ?
|
OpenLayers.Function.bind(config.failure, config.scope) :
|
config.failure;
|
}
|
|
if (OpenLayers.Util.createUrlObject(config.url).protocol == "file:" &&
|
request.responseText) {
|
request.status = 200;
|
}
|
complete(request);
|
|
if (!request.status || (request.status >= 200 && request.status < 300)) {
|
this.events.triggerEvent("success", options);
|
if(success) {
|
success(request);
|
}
|
}
|
if(request.status && (request.status < 200 || request.status >= 300)) {
|
this.events.triggerEvent("failure", options);
|
if(failure) {
|
failure(request);
|
}
|
}
|
},
|
|
/**
|
* APIMethod: GET
|
* Send an HTTP GET request. Additional configuration properties are
|
* documented in the <issue> method, with the method property set
|
* to GET.
|
*
|
* Parameters:
|
* config - {Object} Object with properties for configuring the request.
|
* See the <issue> method for documentation of allowed properties.
|
* This object is modified and should not be reused.
|
*
|
* Returns:
|
* {XMLHttpRequest} Request object.
|
*/
|
GET: function(config) {
|
config = OpenLayers.Util.extend(config, {method: "GET"});
|
return OpenLayers.Request.issue(config);
|
},
|
|
/**
|
* APIMethod: POST
|
* Send a POST request. Additional configuration properties are
|
* documented in the <issue> method, with the method property set
|
* to POST and "Content-Type" header set to "application/xml".
|
*
|
* Parameters:
|
* config - {Object} Object with properties for configuring the request.
|
* See the <issue> method for documentation of allowed properties. The
|
* default "Content-Type" header will be set to "application-xml" if
|
* none is provided. This object is modified and should not be reused.
|
*
|
* Returns:
|
* {XMLHttpRequest} Request object.
|
*/
|
POST: function(config) {
|
config = OpenLayers.Util.extend(config, {method: "POST"});
|
// set content type to application/xml if it isn't already set
|
config.headers = config.headers ? config.headers : {};
|
if(!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) {
|
config.headers["Content-Type"] = "application/xml";
|
}
|
return OpenLayers.Request.issue(config);
|
},
|
|
/**
|
* APIMethod: PUT
|
* Send an HTTP PUT request. Additional configuration properties are
|
* documented in the <issue> method, with the method property set
|
* to PUT and "Content-Type" header set to "application/xml".
|
*
|
* Parameters:
|
* config - {Object} Object with properties for configuring the request.
|
* See the <issue> method for documentation of allowed properties. The
|
* default "Content-Type" header will be set to "application-xml" if
|
* none is provided. This object is modified and should not be reused.
|
*
|
* Returns:
|
* {XMLHttpRequest} Request object.
|
*/
|
PUT: function(config) {
|
config = OpenLayers.Util.extend(config, {method: "PUT"});
|
// set content type to application/xml if it isn't already set
|
config.headers = config.headers ? config.headers : {};
|
if(!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) {
|
config.headers["Content-Type"] = "application/xml";
|
}
|
return OpenLayers.Request.issue(config);
|
},
|
|
/**
|
* APIMethod: DELETE
|
* Send an HTTP DELETE request. Additional configuration properties are
|
* documented in the <issue> method, with the method property set
|
* to DELETE.
|
*
|
* Parameters:
|
* config - {Object} Object with properties for configuring the request.
|
* See the <issue> method for documentation of allowed properties.
|
* This object is modified and should not be reused.
|
*
|
* Returns:
|
* {XMLHttpRequest} Request object.
|
*/
|
DELETE: function(config) {
|
config = OpenLayers.Util.extend(config, {method: "DELETE"});
|
return OpenLayers.Request.issue(config);
|
},
|
|
/**
|
* APIMethod: HEAD
|
* Send an HTTP HEAD request. Additional configuration properties are
|
* documented in the <issue> method, with the method property set
|
* to HEAD.
|
*
|
* Parameters:
|
* config - {Object} Object with properties for configuring the request.
|
* See the <issue> method for documentation of allowed properties.
|
* This object is modified and should not be reused.
|
*
|
* Returns:
|
* {XMLHttpRequest} Request object.
|
*/
|
HEAD: function(config) {
|
config = OpenLayers.Util.extend(config, {method: "HEAD"});
|
return OpenLayers.Request.issue(config);
|
},
|
|
/**
|
* APIMethod: OPTIONS
|
* Send an HTTP OPTIONS request. Additional configuration properties are
|
* documented in the <issue> method, with the method property set
|
* to OPTIONS.
|
*
|
* Parameters:
|
* config - {Object} Object with properties for configuring the request.
|
* See the <issue> method for documentation of allowed properties.
|
* This object is modified and should not be reused.
|
*
|
* Returns:
|
* {XMLHttpRequest} Request object.
|
*/
|
OPTIONS: function(config) {
|
config = OpenLayers.Util.extend(config, {method: "OPTIONS"});
|
return OpenLayers.Request.issue(config);
|
}
|
|
});
|
/* ======================================================================
|
OpenLayers/Layer/ArcIMS.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Layer/Grid.js
|
* @requires OpenLayers/Format/ArcXML.js
|
* @requires OpenLayers/Request.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.ArcIMS
|
* Instances of OpenLayers.Layer.ArcIMS are used to display data from ESRI ArcIMS
|
* Mapping Services. Create a new ArcIMS layer with the <OpenLayers.Layer.ArcIMS>
|
* constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer.Grid>
|
*/
|
OpenLayers.Layer.ArcIMS = OpenLayers.Class(OpenLayers.Layer.Grid, {
|
|
/**
|
* Constant: DEFAULT_PARAMS
|
* {Object} Default query string parameters.
|
*/
|
DEFAULT_PARAMS: {
|
ClientVersion: "9.2",
|
ServiceName: ''
|
},
|
|
/**
|
* APIProperty: featureCoordSys
|
* {String} Code for feature coordinate system. Default is "4326".
|
*/
|
featureCoordSys: "4326",
|
|
/**
|
* APIProperty: filterCoordSys
|
* {String} Code for filter coordinate system. Default is "4326".
|
*/
|
filterCoordSys: "4326",
|
|
/**
|
* APIProperty: layers
|
* {Array} An array of objects with layer properties.
|
*/
|
layers: null,
|
|
/**
|
* APIProperty: async
|
* {Boolean} Request images asynchronously. Default is true.
|
*/
|
async: true,
|
|
/**
|
* APIProperty: name
|
* {String} Layer name. Default is "ArcIMS".
|
*/
|
name: "ArcIMS",
|
|
/**
|
* APIProperty: isBaseLayer
|
* {Boolean} The layer is a base layer. Default is true.
|
*/
|
isBaseLayer: true,
|
|
/**
|
* Constant: DEFAULT_OPTIONS
|
* {Object} Default layers properties.
|
*/
|
DEFAULT_OPTIONS: {
|
tileSize: new OpenLayers.Size(512, 512),
|
featureCoordSys: "4326",
|
filterCoordSys: "4326",
|
layers: null,
|
isBaseLayer: true,
|
async: true,
|
name: "ArcIMS"
|
},
|
|
/**
|
* Constructor: OpenLayers.Layer.ArcIMS
|
* Create a new ArcIMS layer object.
|
*
|
* Example:
|
* (code)
|
* var arcims = new OpenLayers.Layer.ArcIMS(
|
* "Global Sample",
|
* "http://sample.avencia.com/servlet/com.esri.esrimap.Esrimap",
|
* {
|
* service: "OpenLayers_Sample",
|
* layers: [
|
* // layers to manipulate
|
* {id: "1", visible: true}
|
* ]
|
* }
|
* );
|
* (end)
|
*
|
* Parameters:
|
* name - {String} A name for the layer
|
* url - {String} Base url for the ArcIMS server
|
* options - {Object} Optional object with properties to be set on the
|
* layer.
|
*/
|
initialize: function(name, url, options) {
|
|
this.tileSize = new OpenLayers.Size(512, 512);
|
|
// parameters
|
this.params = OpenLayers.Util.applyDefaults(
|
{ServiceName: options.serviceName},
|
this.DEFAULT_PARAMS
|
);
|
this.options = OpenLayers.Util.applyDefaults(
|
options, this.DEFAULT_OPTIONS
|
);
|
|
OpenLayers.Layer.Grid.prototype.initialize.apply(
|
this, [name, url, this.params, options]
|
);
|
|
//layer is transparent
|
if (this.transparent) {
|
|
// unless explicitly set in options, make layer an overlay
|
if (!this.isBaseLayer) {
|
this.isBaseLayer = false;
|
}
|
|
// jpegs can never be transparent, so intelligently switch the
|
// format, depending on the browser's capabilities
|
if (this.format == "image/jpeg") {
|
this.format = OpenLayers.Util.alphaHack() ? "image/gif" : "image/png";
|
}
|
}
|
|
// create an empty layer list if no layers specified in the options
|
if (this.options.layers === null) {
|
this.options.layers = [];
|
}
|
},
|
|
/**
|
* Method: getURL
|
* Return an image url this layer.
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the
|
* request.
|
*
|
* Returns:
|
* {String} A string with the map image's url.
|
*/
|
getURL: function(bounds) {
|
var url = "";
|
bounds = this.adjustBounds(bounds);
|
|
// create an arcxml request to generate the image
|
var axlReq = new OpenLayers.Format.ArcXML(
|
OpenLayers.Util.extend(this.options, {
|
requesttype: "image",
|
envelope: bounds.toArray(),
|
tileSize: this.tileSize
|
})
|
);
|
|
// create a synchronous ajax request to get an arcims image
|
var req = new OpenLayers.Request.POST({
|
url: this.getFullRequestString(),
|
data: axlReq.write(),
|
async: false
|
});
|
|
// if the response exists
|
if (req != null) {
|
var doc = req.responseXML;
|
|
if (!doc || !doc.documentElement) {
|
doc = req.responseText;
|
}
|
|
// create a new arcxml format to read the response
|
var axlResp = new OpenLayers.Format.ArcXML();
|
var arcxml = axlResp.read(doc);
|
url = this.getUrlOrImage(arcxml.image.output);
|
}
|
|
return url;
|
},
|
|
|
/**
|
* Method: getURLasync
|
* Get an image url this layer asynchronously, and execute a callback
|
* when the image url is generated.
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the
|
* request.
|
* callback - {Function} Function to call when image url is retrieved.
|
* scope - {Object} The scope of the callback method.
|
*/
|
getURLasync: function(bounds, callback, scope) {
|
bounds = this.adjustBounds(bounds);
|
|
// create an arcxml request to generate the image
|
var axlReq = new OpenLayers.Format.ArcXML(
|
OpenLayers.Util.extend(this.options, {
|
requesttype: "image",
|
envelope: bounds.toArray(),
|
tileSize: this.tileSize
|
})
|
);
|
|
// create an asynchronous ajax request to get an arcims image
|
OpenLayers.Request.POST({
|
url: this.getFullRequestString(),
|
async: true,
|
data: axlReq.write(),
|
callback: function(req) {
|
// process the response from ArcIMS, and call the callback function
|
// to set the image URL
|
var doc = req.responseXML;
|
if (!doc || !doc.documentElement) {
|
doc = req.responseText;
|
}
|
|
// create a new arcxml format to read the response
|
var axlResp = new OpenLayers.Format.ArcXML();
|
var arcxml = axlResp.read(doc);
|
|
callback.call(scope, this.getUrlOrImage(arcxml.image.output));
|
},
|
scope: this
|
});
|
},
|
|
/**
|
* Method: getUrlOrImage
|
* Extract a url or image from the ArcXML image output.
|
*
|
* Parameters:
|
* output - {Object} The image.output property of the object returned from
|
* the ArcXML format read method.
|
*
|
* Returns:
|
* {String} A URL for an image (potentially with the data protocol).
|
*/
|
getUrlOrImage: function(output) {
|
var ret = "";
|
if(output.url) {
|
// If the image response output url is a string, then the image
|
// data is not inline.
|
ret = output.url;
|
} else if(output.data) {
|
// The image data is inline and base64 encoded, create a data
|
// url for the image. This will only work for small images,
|
// due to browser url length limits.
|
ret = "data:image/" + output.type +
|
";base64," + output.data;
|
}
|
return ret;
|
},
|
|
/**
|
* Method: setLayerQuery
|
* Set the query definition on this layer. Query definitions are used to
|
* render parts of the spatial data in an image, and can be used to
|
* filter features or layers in the ArcIMS service.
|
*
|
* Parameters:
|
* id - {String} The ArcIMS layer ID.
|
* querydef - {Object} The query definition to apply to this layer.
|
*/
|
setLayerQuery: function(id, querydef) {
|
// find the matching layer, if it exists
|
for (var lyr = 0; lyr < this.options.layers.length; lyr++) {
|
if (id == this.options.layers[lyr].id) {
|
// replace this layer definition
|
this.options.layers[lyr].query = querydef;
|
return;
|
}
|
}
|
|
// no layer found, create a new definition
|
this.options.layers.push({id: id, visible: true, query: querydef});
|
},
|
|
/**
|
* Method: getFeatureInfo
|
* Get feature information from ArcIMS. Using the applied geometry, apply
|
* the options to the query (buffer, area/envelope intersection), and
|
* query the ArcIMS service.
|
*
|
* A note about accuracy:
|
* ArcIMS interprets the accuracy attribute in feature requests to be
|
* something like the 'modulus' operator on feature coordinates,
|
* applied to the database geometry of the feature. It doesn't round,
|
* so your feature coordinates may be up to (1 x accuracy) offset from
|
* the actual feature coordinates. If the accuracy of the layer is not
|
* specified, the accuracy will be computed to be approximately 1
|
* feature coordinate per screen pixel.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.LonLat>} or {<OpenLayers.Geometry.Polygon>} The
|
* geometry to use when making the query. This should be a closed
|
* polygon for behavior approximating a free selection.
|
* layer - {Object} The ArcIMS layer definition. This is an anonymous object
|
* that looks like:
|
* (code)
|
* {
|
* id: "ArcXML layer ID", // the ArcXML layer ID
|
* query: {
|
* where: "STATE = 'PA'", // the where clause of the query
|
* accuracy: 100 // the accuracy of the returned feature
|
* }
|
* }
|
* (end)
|
* options - {Object} Object with non-default properties to set on the layer.
|
* Supported properties are buffer, callback, scope, and any other
|
* properties applicable to the ArcXML format. Set the 'callback' and
|
* 'scope' for an object and function to recieve the parsed features
|
* from ArcIMS.
|
*/
|
getFeatureInfo: function(geometry, layer, options) {
|
// set the buffer to 1 unit (dd/m/ft?) by default
|
var buffer = options.buffer || 1;
|
// empty callback by default
|
var callback = options.callback || function() {};
|
// default scope is window (global)
|
var scope = options.scope || window;
|
|
// apply these option to the request options
|
var requestOptions = {};
|
OpenLayers.Util.extend(requestOptions, this.options);
|
|
// this is a feature request
|
requestOptions.requesttype = "feature";
|
|
if (geometry instanceof OpenLayers.LonLat) {
|
// create an envelope if the geometry is really a lon/lat
|
requestOptions.polygon = null;
|
requestOptions.envelope = [
|
geometry.lon - buffer,
|
geometry.lat - buffer,
|
geometry.lon + buffer,
|
geometry.lat + buffer
|
];
|
} else if (geometry instanceof OpenLayers.Geometry.Polygon) {
|
// use the polygon assigned, and empty the envelope
|
requestOptions.envelope = null;
|
requestOptions.polygon = geometry;
|
}
|
|
// create an arcxml request to get feature requests
|
var arcxml = new OpenLayers.Format.ArcXML(requestOptions);
|
|
// apply any get feature options to the arcxml request
|
OpenLayers.Util.extend(arcxml.request.get_feature, options);
|
|
arcxml.request.get_feature.layer = layer.id;
|
if (typeof layer.query.accuracy == "number") {
|
// set the accuracy if it was specified
|
arcxml.request.get_feature.query.accuracy = layer.query.accuracy;
|
} else {
|
// guess that the accuracy is 1 per screen pixel
|
var mapCenter = this.map.getCenter();
|
var viewPx = this.map.getViewPortPxFromLonLat(mapCenter);
|
viewPx.x++;
|
var mapOffCenter = this.map.getLonLatFromPixel(viewPx);
|
arcxml.request.get_feature.query.accuracy = mapOffCenter.lon - mapCenter.lon;
|
}
|
|
// set the get_feature query to be the same as the layer passed in
|
arcxml.request.get_feature.query.where = layer.query.where;
|
|
// use area_intersection
|
arcxml.request.get_feature.query.spatialfilter.relation = "area_intersection";
|
|
// create a new asynchronous request to get the feature info
|
OpenLayers.Request.POST({
|
url: this.getFullRequestString({'CustomService': 'Query'}),
|
data: arcxml.write(),
|
callback: function(request) {
|
// parse the arcxml response
|
var response = arcxml.parseResponse(request.responseText);
|
|
if (!arcxml.iserror()) {
|
// if the arcxml is not an error, call the callback with the features parsed
|
callback.call(scope, response.features);
|
} else {
|
// if the arcxml is an error, return null features selected
|
callback.call(scope, null);
|
}
|
}
|
});
|
},
|
|
/**
|
* Method: clone
|
* Create a clone of this layer
|
*
|
* Returns:
|
* {<OpenLayers.Layer.ArcIMS>} An exact clone of this layer
|
*/
|
clone: function (obj) {
|
|
if (obj == null) {
|
obj = new OpenLayers.Layer.ArcIMS(this.name,
|
this.url,
|
this.getOptions());
|
}
|
|
//get all additions from superclasses
|
obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
|
|
// copy/set any non-init, non-simple values here
|
|
return obj;
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.ArcIMS"
|
});
|
/* ======================================================================
|
OpenLayers/Control/PanZoom.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/Events/buttonclick.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.PanZoom
|
* The PanZoom is a visible control, composed of a
|
* <OpenLayers.Control.PanPanel> and a <OpenLayers.Control.ZoomPanel>. By
|
* default it is drawn in the upper left corner of the map.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.PanZoom = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* APIProperty: slideFactor
|
* {Integer} Number of pixels by which we'll pan the map in any direction
|
* on clicking the arrow buttons. If you want to pan by some ratio
|
* of the map dimensions, use <slideRatio> instead.
|
*/
|
slideFactor: 50,
|
|
/**
|
* APIProperty: slideRatio
|
* {Number} The fraction of map width/height by which we'll pan the map
|
* on clicking the arrow buttons. Default is null. If set, will
|
* override <slideFactor>. E.g. if slideRatio is .5, then the Pan Up
|
* button will pan up half the map height.
|
*/
|
slideRatio: null,
|
|
/**
|
* Property: buttons
|
* {Array(DOMElement)} Array of Button Divs
|
*/
|
buttons: null,
|
|
/**
|
* Property: position
|
* {<OpenLayers.Pixel>}
|
*/
|
position: null,
|
|
/**
|
* Constructor: OpenLayers.Control.PanZoom
|
*
|
* Parameters:
|
* options - {Object}
|
*/
|
initialize: function(options) {
|
this.position = new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X,
|
OpenLayers.Control.PanZoom.Y);
|
OpenLayers.Control.prototype.initialize.apply(this, arguments);
|
},
|
|
/**
|
* APIMethod: destroy
|
*/
|
destroy: function() {
|
if (this.map) {
|
this.map.events.unregister("buttonclick", this, this.onButtonClick);
|
}
|
this.removeButtons();
|
this.buttons = null;
|
this.position = null;
|
OpenLayers.Control.prototype.destroy.apply(this, arguments);
|
},
|
|
/**
|
* Method: setMap
|
*
|
* Properties:
|
* map - {<OpenLayers.Map>}
|
*/
|
setMap: function(map) {
|
OpenLayers.Control.prototype.setMap.apply(this, arguments);
|
this.map.events.register("buttonclick", this, this.onButtonClick);
|
},
|
|
/**
|
* Method: draw
|
*
|
* Parameters:
|
* px - {<OpenLayers.Pixel>}
|
*
|
* Returns:
|
* {DOMElement} A reference to the container div for the PanZoom control.
|
*/
|
draw: function(px) {
|
// initialize our internal div
|
OpenLayers.Control.prototype.draw.apply(this, arguments);
|
px = this.position;
|
|
// place the controls
|
this.buttons = [];
|
|
var sz = {w: 18, h: 18};
|
var centered = new OpenLayers.Pixel(px.x+sz.w/2, px.y);
|
|
this._addButton("panup", "north-mini.png", centered, sz);
|
px.y = centered.y+sz.h;
|
this._addButton("panleft", "west-mini.png", px, sz);
|
this._addButton("panright", "east-mini.png", px.add(sz.w, 0), sz);
|
this._addButton("pandown", "south-mini.png",
|
centered.add(0, sz.h*2), sz);
|
this._addButton("zoomin", "zoom-plus-mini.png",
|
centered.add(0, sz.h*3+5), sz);
|
this._addButton("zoomworld", "zoom-world-mini.png",
|
centered.add(0, sz.h*4+5), sz);
|
this._addButton("zoomout", "zoom-minus-mini.png",
|
centered.add(0, sz.h*5+5), sz);
|
return this.div;
|
},
|
|
/**
|
* Method: _addButton
|
*
|
* Parameters:
|
* id - {String}
|
* img - {String}
|
* xy - {<OpenLayers.Pixel>}
|
* sz - {<OpenLayers.Size>}
|
*
|
* Returns:
|
* {DOMElement} A Div (an alphaImageDiv, to be precise) that contains the
|
* image of the button, and has all the proper event handlers set.
|
*/
|
_addButton:function(id, img, xy, sz) {
|
var imgLocation = OpenLayers.Util.getImageLocation(img);
|
var btn = OpenLayers.Util.createAlphaImageDiv(
|
this.id + "_" + id,
|
xy, sz, imgLocation, "absolute");
|
btn.style.cursor = "pointer";
|
//we want to add the outer div
|
this.div.appendChild(btn);
|
btn.action = id;
|
btn.className = "olButton";
|
|
//we want to remember/reference the outer div
|
this.buttons.push(btn);
|
return btn;
|
},
|
|
/**
|
* Method: _removeButton
|
*
|
* Parameters:
|
* btn - {Object}
|
*/
|
_removeButton: function(btn) {
|
this.div.removeChild(btn);
|
OpenLayers.Util.removeItem(this.buttons, btn);
|
},
|
|
/**
|
* Method: removeButtons
|
*/
|
removeButtons: function() {
|
for(var i=this.buttons.length-1; i>=0; --i) {
|
this._removeButton(this.buttons[i]);
|
}
|
},
|
|
/**
|
* Method: onButtonClick
|
*
|
* Parameters:
|
* evt - {Event}
|
*/
|
onButtonClick: function(evt) {
|
var btn = evt.buttonElement;
|
switch (btn.action) {
|
case "panup":
|
this.map.pan(0, -this.getSlideFactor("h"));
|
break;
|
case "pandown":
|
this.map.pan(0, this.getSlideFactor("h"));
|
break;
|
case "panleft":
|
this.map.pan(-this.getSlideFactor("w"), 0);
|
break;
|
case "panright":
|
this.map.pan(this.getSlideFactor("w"), 0);
|
break;
|
case "zoomin":
|
this.map.zoomIn();
|
break;
|
case "zoomout":
|
this.map.zoomOut();
|
break;
|
case "zoomworld":
|
this.map.zoomToMaxExtent();
|
break;
|
}
|
},
|
|
/**
|
* Method: getSlideFactor
|
*
|
* Parameters:
|
* dim - {String} "w" or "h" (for width or height).
|
*
|
* Returns:
|
* {Number} The slide factor for panning in the requested direction.
|
*/
|
getSlideFactor: function(dim) {
|
return this.slideRatio ?
|
this.map.getSize()[dim] * this.slideRatio :
|
this.slideFactor;
|
},
|
|
CLASS_NAME: "OpenLayers.Control.PanZoom"
|
});
|
|
/**
|
* Constant: X
|
* {Integer}
|
*/
|
OpenLayers.Control.PanZoom.X = 4;
|
|
/**
|
* Constant: Y
|
* {Integer}
|
*/
|
OpenLayers.Control.PanZoom.Y = 4;
|
/* ======================================================================
|
OpenLayers/Control/PanZoomBar.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Control/PanZoom.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.PanZoomBar
|
* The PanZoomBar is a visible control composed of a
|
* <OpenLayers.Control.PanPanel> and a <OpenLayers.Control.ZoomBar>.
|
* By default it is displayed in the upper left corner of the map as 4
|
* directional arrows above a vertical slider.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control.PanZoom>
|
*/
|
OpenLayers.Control.PanZoomBar = OpenLayers.Class(OpenLayers.Control.PanZoom, {
|
|
/**
|
* APIProperty: zoomStopWidth
|
*/
|
zoomStopWidth: 18,
|
|
/**
|
* APIProperty: zoomStopHeight
|
*/
|
zoomStopHeight: 11,
|
|
/**
|
* Property: slider
|
*/
|
slider: null,
|
|
/**
|
* Property: sliderEvents
|
* {<OpenLayers.Events>}
|
*/
|
sliderEvents: null,
|
|
/**
|
* Property: zoombarDiv
|
* {DOMElement}
|
*/
|
zoombarDiv: null,
|
|
/**
|
* APIProperty: zoomWorldIcon
|
* {Boolean}
|
*/
|
zoomWorldIcon: false,
|
|
/**
|
* APIProperty: panIcons
|
* {Boolean} Set this property to false not to display the pan icons. If
|
* false the zoom world icon is placed under the zoom bar. Defaults to
|
* true.
|
*/
|
panIcons: true,
|
|
/**
|
* APIProperty: forceFixedZoomLevel
|
* {Boolean} Force a fixed zoom level even though the map has
|
* fractionalZoom
|
*/
|
forceFixedZoomLevel: false,
|
|
/**
|
* Property: mouseDragStart
|
* {<OpenLayers.Pixel>}
|
*/
|
mouseDragStart: null,
|
|
/**
|
* Property: deltaY
|
* {Number} The cumulative vertical pixel offset during a zoom bar drag.
|
*/
|
deltaY: null,
|
|
/**
|
* Property: zoomStart
|
* {<OpenLayers.Pixel>}
|
*/
|
zoomStart: null,
|
|
/**
|
* Constructor: OpenLayers.Control.PanZoomBar
|
*/
|
|
/**
|
* APIMethod: destroy
|
*/
|
destroy: function() {
|
|
this._removeZoomBar();
|
|
this.map.events.un({
|
"changebaselayer": this.redraw,
|
"updatesize": this.redraw,
|
scope: this
|
});
|
|
OpenLayers.Control.PanZoom.prototype.destroy.apply(this, arguments);
|
|
delete this.mouseDragStart;
|
delete this.zoomStart;
|
},
|
|
/**
|
* Method: setMap
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
setMap: function(map) {
|
OpenLayers.Control.PanZoom.prototype.setMap.apply(this, arguments);
|
this.map.events.on({
|
"changebaselayer": this.redraw,
|
"updatesize": this.redraw,
|
scope: this
|
});
|
},
|
|
/**
|
* Method: redraw
|
* clear the div and start over.
|
*/
|
redraw: function() {
|
if (this.div != null) {
|
this.removeButtons();
|
this._removeZoomBar();
|
}
|
this.draw();
|
},
|
|
/**
|
* Method: draw
|
*
|
* Parameters:
|
* px - {<OpenLayers.Pixel>}
|
*/
|
draw: function(px) {
|
// initialize our internal div
|
OpenLayers.Control.prototype.draw.apply(this, arguments);
|
px = this.position.clone();
|
|
// place the controls
|
this.buttons = [];
|
|
var sz = {w: 18, h: 18};
|
if (this.panIcons) {
|
var centered = new OpenLayers.Pixel(px.x+sz.w/2, px.y);
|
var wposition = sz.w;
|
|
if (this.zoomWorldIcon) {
|
centered = new OpenLayers.Pixel(px.x+sz.w, px.y);
|
}
|
|
this._addButton("panup", "north-mini.png", centered, sz);
|
px.y = centered.y+sz.h;
|
this._addButton("panleft", "west-mini.png", px, sz);
|
if (this.zoomWorldIcon) {
|
this._addButton("zoomworld", "zoom-world-mini.png", px.add(sz.w, 0), sz);
|
|
wposition *= 2;
|
}
|
this._addButton("panright", "east-mini.png", px.add(wposition, 0), sz);
|
this._addButton("pandown", "south-mini.png", centered.add(0, sz.h*2), sz);
|
this._addButton("zoomin", "zoom-plus-mini.png", centered.add(0, sz.h*3+5), sz);
|
centered = this._addZoomBar(centered.add(0, sz.h*4 + 5));
|
this._addButton("zoomout", "zoom-minus-mini.png", centered, sz);
|
}
|
else {
|
this._addButton("zoomin", "zoom-plus-mini.png", px, sz);
|
centered = this._addZoomBar(px.add(0, sz.h));
|
this._addButton("zoomout", "zoom-minus-mini.png", centered, sz);
|
if (this.zoomWorldIcon) {
|
centered = centered.add(0, sz.h+3);
|
this._addButton("zoomworld", "zoom-world-mini.png", centered, sz);
|
}
|
}
|
return this.div;
|
},
|
|
/**
|
* Method: _addZoomBar
|
*
|
* Parameters:
|
* centered - {<OpenLayers.Pixel>} where zoombar drawing is to start.
|
*/
|
_addZoomBar:function(centered) {
|
var imgLocation = OpenLayers.Util.getImageLocation("slider.png");
|
var id = this.id + "_" + this.map.id;
|
var minZoom = this.map.getMinZoom();
|
var zoomsToEnd = this.map.getNumZoomLevels() - 1 - this.map.getZoom();
|
var slider = OpenLayers.Util.createAlphaImageDiv(id,
|
centered.add(-1, zoomsToEnd * this.zoomStopHeight),
|
{w: 20, h: 9},
|
imgLocation,
|
"absolute");
|
slider.style.cursor = "move";
|
this.slider = slider;
|
|
this.sliderEvents = new OpenLayers.Events(this, slider, null, true,
|
{includeXY: true});
|
this.sliderEvents.on({
|
"touchstart": this.zoomBarDown,
|
"touchmove": this.zoomBarDrag,
|
"touchend": this.zoomBarUp,
|
"mousedown": this.zoomBarDown,
|
"mousemove": this.zoomBarDrag,
|
"mouseup": this.zoomBarUp
|
});
|
|
var sz = {
|
w: this.zoomStopWidth,
|
h: this.zoomStopHeight * (this.map.getNumZoomLevels() - minZoom)
|
};
|
var imgLocation = OpenLayers.Util.getImageLocation("zoombar.png");
|
var div = null;
|
|
if (OpenLayers.Util.alphaHack()) {
|
var id = this.id + "_" + this.map.id;
|
div = OpenLayers.Util.createAlphaImageDiv(id, centered,
|
{w: sz.w, h: this.zoomStopHeight},
|
imgLocation,
|
"absolute", null, "crop");
|
div.style.height = sz.h + "px";
|
} else {
|
div = OpenLayers.Util.createDiv(
|
'OpenLayers_Control_PanZoomBar_Zoombar' + this.map.id,
|
centered,
|
sz,
|
imgLocation);
|
}
|
div.style.cursor = "pointer";
|
div.className = "olButton";
|
this.zoombarDiv = div;
|
|
this.div.appendChild(div);
|
|
this.startTop = parseInt(div.style.top);
|
this.div.appendChild(slider);
|
|
this.map.events.register("zoomend", this, this.moveZoomBar);
|
|
centered = centered.add(0,
|
this.zoomStopHeight * (this.map.getNumZoomLevels() - minZoom));
|
return centered;
|
},
|
|
/**
|
* Method: _removeZoomBar
|
*/
|
_removeZoomBar: function() {
|
this.sliderEvents.un({
|
"touchstart": this.zoomBarDown,
|
"touchmove": this.zoomBarDrag,
|
"touchend": this.zoomBarUp,
|
"mousedown": this.zoomBarDown,
|
"mousemove": this.zoomBarDrag,
|
"mouseup": this.zoomBarUp
|
});
|
this.sliderEvents.destroy();
|
|
this.div.removeChild(this.zoombarDiv);
|
this.zoombarDiv = null;
|
this.div.removeChild(this.slider);
|
this.slider = null;
|
|
this.map.events.unregister("zoomend", this, this.moveZoomBar);
|
},
|
|
/**
|
* Method: onButtonClick
|
*
|
* Parameters:
|
* evt - {Event}
|
*/
|
onButtonClick: function(evt) {
|
OpenLayers.Control.PanZoom.prototype.onButtonClick.apply(this, arguments);
|
if (evt.buttonElement === this.zoombarDiv) {
|
var levels = evt.buttonXY.y / this.zoomStopHeight;
|
if(this.forceFixedZoomLevel || !this.map.fractionalZoom) {
|
levels = Math.floor(levels);
|
}
|
var zoom = (this.map.getNumZoomLevels() - 1) - levels;
|
zoom = Math.min(Math.max(zoom, 0), this.map.getNumZoomLevels() - 1);
|
this.map.zoomTo(zoom);
|
}
|
},
|
|
/**
|
* Method: passEventToSlider
|
* This function is used to pass events that happen on the div, or the map,
|
* through to the slider, which then does its moving thing.
|
*
|
* Parameters:
|
* evt - {<OpenLayers.Event>}
|
*/
|
passEventToSlider:function(evt) {
|
this.sliderEvents.handleBrowserEvent(evt);
|
},
|
|
/*
|
* Method: zoomBarDown
|
* event listener for clicks on the slider
|
*
|
* Parameters:
|
* evt - {<OpenLayers.Event>}
|
*/
|
zoomBarDown:function(evt) {
|
if (!OpenLayers.Event.isLeftClick(evt) && !OpenLayers.Event.isSingleTouch(evt)) {
|
return;
|
}
|
this.map.events.on({
|
"touchmove": this.passEventToSlider,
|
"mousemove": this.passEventToSlider,
|
"mouseup": this.passEventToSlider,
|
scope: this
|
});
|
this.mouseDragStart = evt.xy.clone();
|
this.zoomStart = evt.xy.clone();
|
this.div.style.cursor = "move";
|
// reset the div offsets just in case the div moved
|
this.zoombarDiv.offsets = null;
|
OpenLayers.Event.stop(evt);
|
},
|
|
/*
|
* Method: zoomBarDrag
|
* This is what happens when a click has occurred, and the client is
|
* dragging. Here we must ensure that the slider doesn't go beyond the
|
* bottom/top of the zoombar div, as well as moving the slider to its new
|
* visual location
|
*
|
* Parameters:
|
* evt - {<OpenLayers.Event>}
|
*/
|
zoomBarDrag:function(evt) {
|
if (this.mouseDragStart != null) {
|
var deltaY = this.mouseDragStart.y - evt.xy.y;
|
var offsets = OpenLayers.Util.pagePosition(this.zoombarDiv);
|
if ((evt.clientY - offsets[1]) > 0 &&
|
(evt.clientY - offsets[1]) < parseInt(this.zoombarDiv.style.height) - 2) {
|
var newTop = parseInt(this.slider.style.top) - deltaY;
|
this.slider.style.top = newTop+"px";
|
this.mouseDragStart = evt.xy.clone();
|
}
|
// set cumulative displacement
|
this.deltaY = this.zoomStart.y - evt.xy.y;
|
OpenLayers.Event.stop(evt);
|
}
|
},
|
|
/*
|
* Method: zoomBarUp
|
* Perform cleanup when a mouseup event is received -- discover new zoom
|
* level and switch to it.
|
*
|
* Parameters:
|
* evt - {<OpenLayers.Event>}
|
*/
|
zoomBarUp:function(evt) {
|
if (!OpenLayers.Event.isLeftClick(evt) && evt.type !== "touchend") {
|
return;
|
}
|
if (this.mouseDragStart) {
|
this.div.style.cursor="";
|
this.map.events.un({
|
"touchmove": this.passEventToSlider,
|
"mouseup": this.passEventToSlider,
|
"mousemove": this.passEventToSlider,
|
scope: this
|
});
|
var zoomLevel = this.map.zoom;
|
if (!this.forceFixedZoomLevel && this.map.fractionalZoom) {
|
zoomLevel += this.deltaY/this.zoomStopHeight;
|
zoomLevel = Math.min(Math.max(zoomLevel, 0),
|
this.map.getNumZoomLevels() - 1);
|
} else {
|
zoomLevel += this.deltaY/this.zoomStopHeight;
|
zoomLevel = Math.max(Math.round(zoomLevel), 0);
|
}
|
this.map.zoomTo(zoomLevel);
|
this.mouseDragStart = null;
|
this.zoomStart = null;
|
this.deltaY = 0;
|
OpenLayers.Event.stop(evt);
|
}
|
},
|
|
/*
|
* Method: moveZoomBar
|
* Change the location of the slider to match the current zoom level.
|
*/
|
moveZoomBar:function() {
|
var newTop =
|
((this.map.getNumZoomLevels()-1) - this.map.getZoom()) *
|
this.zoomStopHeight + this.startTop + 1;
|
this.slider.style.top = newTop + "px";
|
},
|
|
CLASS_NAME: "OpenLayers.Control.PanZoomBar"
|
});
|
/* ======================================================================
|
OpenLayers/Format/WFSCapabilities.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML/VersionedOGC.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WFSCapabilities
|
* Read WFS Capabilities.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML.VersionedOGC>
|
*/
|
OpenLayers.Format.WFSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
|
|
/**
|
* APIProperty: defaultVersion
|
* {String} Version number to assume if none found. Default is "1.1.0".
|
*/
|
defaultVersion: "1.1.0",
|
|
/**
|
* Constructor: OpenLayers.Format.WFSCapabilities
|
* Create a new parser for WFS capabilities.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* APIMethod: read
|
* Read capabilities data from a string, and return a list of layers.
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
*
|
* Returns:
|
* {Array} List of named layers.
|
*/
|
|
CLASS_NAME: "OpenLayers.Format.WFSCapabilities"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/WFSCapabilities/v1.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/WFSCapabilities.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WFSCapabilities.v1
|
* Abstract class not to be instantiated directly.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.WFSCapabilities.v1 = OpenLayers.Class(
|
OpenLayers.Format.XML, {
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
wfs: "http://www.opengis.net/wfs",
|
xlink: "http://www.w3.org/1999/xlink",
|
xsi: "http://www.w3.org/2001/XMLSchema-instance",
|
ows: "http://www.opengis.net/ows"
|
},
|
|
|
/**
|
* APIProperty: errorProperty
|
* {String} Which property of the returned object to check for in order to
|
* determine whether or not parsing has failed. In the case that the
|
* errorProperty is undefined on the returned object, the document will be
|
* run through an OGCExceptionReport parser.
|
*/
|
errorProperty: "featureTypeList",
|
|
/**
|
* Property: defaultPrefix
|
*/
|
defaultPrefix: "wfs",
|
|
/**
|
* Constructor: OpenLayers.Format.WFSCapabilities.v1_1
|
* Create an instance of one of the subclasses.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* APIMethod: read
|
* Read capabilities data from a string, and return a list of layers.
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
*
|
* Returns:
|
* {Array} List of named layers.
|
*/
|
read: function(data) {
|
if(typeof data == "string") {
|
data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
|
}
|
var raw = data;
|
if(data && data.nodeType == 9) {
|
data = data.documentElement;
|
}
|
var capabilities = {};
|
this.readNode(data, capabilities);
|
return capabilities;
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"wfs": {
|
"WFS_Capabilities": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"FeatureTypeList": function(node, request) {
|
request.featureTypeList = {
|
featureTypes: []
|
};
|
this.readChildNodes(node, request.featureTypeList);
|
},
|
"FeatureType": function(node, featureTypeList) {
|
var featureType = {};
|
this.readChildNodes(node, featureType);
|
featureTypeList.featureTypes.push(featureType);
|
},
|
"Name": function(node, obj) {
|
var name = this.getChildValue(node);
|
if(name) {
|
var parts = name.split(":");
|
obj.name = parts.pop();
|
if(parts.length > 0) {
|
obj.featureNS = this.lookupNamespaceURI(node, parts[0]);
|
}
|
}
|
},
|
"Title": function(node, obj) {
|
var title = this.getChildValue(node);
|
if(title) {
|
obj.title = title;
|
}
|
},
|
"Abstract": function(node, obj) {
|
var abst = this.getChildValue(node);
|
if(abst) {
|
obj["abstract"] = abst;
|
}
|
}
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WFSCapabilities.v1"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/WFSCapabilities/v1_1_0.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/WFSCapabilities/v1.js
|
* @requires OpenLayers/Format/OWSCommon/v1.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WFSCapabilities/v1_1_0
|
* Read WFS Capabilities version 1.1.0.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.WFSCapabilities>
|
*/
|
OpenLayers.Format.WFSCapabilities.v1_1_0 = OpenLayers.Class(
|
OpenLayers.Format.WFSCapabilities.v1, {
|
|
/**
|
* Property: regExes
|
* Compiled regular expressions for manipulating strings.
|
*/
|
regExes: {
|
trimSpace: (/^\s*|\s*$/g),
|
removeSpace: (/\s*/g),
|
splitSpace: (/\s+/),
|
trimComma: (/\s*,\s*/g)
|
},
|
|
/**
|
* Constructor: OpenLayers.Format.WFSCapabilities.v1_1_0
|
* Create a new parser for WFS capabilities version 1.1.0.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"wfs": OpenLayers.Util.applyDefaults({
|
"DefaultSRS": function(node, obj) {
|
var defaultSRS = this.getChildValue(node);
|
if (defaultSRS) {
|
obj.srs = defaultSRS;
|
}
|
}
|
}, OpenLayers.Format.WFSCapabilities.v1.prototype.readers["wfs"]),
|
"ows": OpenLayers.Format.OWSCommon.v1.prototype.readers.ows
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WFSCapabilities.v1_1_0"
|
|
});
|
/* ======================================================================
|
OpenLayers/Layer/Image.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Layer.js
|
* @requires OpenLayers/Tile/Image.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.Image
|
* Instances of OpenLayers.Layer.Image are used to display data from a web
|
* accessible image as a map layer. Create a new image layer with the
|
* <OpenLayers.Layer.Image> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer>
|
*/
|
OpenLayers.Layer.Image = OpenLayers.Class(OpenLayers.Layer, {
|
|
/**
|
* Property: isBaseLayer
|
* {Boolean} The layer is a base layer. Default is true. Set this property
|
* in the layer options
|
*/
|
isBaseLayer: true,
|
|
/**
|
* Property: url
|
* {String} URL of the image to use
|
*/
|
url: null,
|
|
/**
|
* Property: extent
|
* {<OpenLayers.Bounds>} The image bounds in map units. This extent will
|
* also be used as the default maxExtent for the layer. If you wish
|
* to have a maxExtent that is different than the image extent, set the
|
* maxExtent property of the options argument (as with any other layer).
|
*/
|
extent: null,
|
|
/**
|
* Property: size
|
* {<OpenLayers.Size>} The image size in pixels
|
*/
|
size: null,
|
|
/**
|
* Property: tile
|
* {<OpenLayers.Tile.Image>}
|
*/
|
tile: null,
|
|
/**
|
* Property: aspectRatio
|
* {Float} The ratio of height/width represented by a single pixel in the
|
* graphic
|
*/
|
aspectRatio: null,
|
|
/**
|
* Constructor: OpenLayers.Layer.Image
|
* Create a new image layer
|
*
|
* Parameters:
|
* name - {String} A name for the layer.
|
* url - {String} Relative or absolute path to the image
|
* extent - {<OpenLayers.Bounds>} The extent represented by the image
|
* size - {<OpenLayers.Size>} The size (in pixels) of the image
|
* options - {Object} Hashtable of extra options to tag onto the layer
|
*/
|
initialize: function(name, url, extent, size, options) {
|
this.url = url;
|
this.extent = extent;
|
this.maxExtent = extent;
|
this.size = size;
|
OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);
|
|
this.aspectRatio = (this.extent.getHeight() / this.size.h) /
|
(this.extent.getWidth() / this.size.w);
|
},
|
|
/**
|
* Method: destroy
|
* Destroy this layer
|
*/
|
destroy: function() {
|
if (this.tile) {
|
this.removeTileMonitoringHooks(this.tile);
|
this.tile.destroy();
|
this.tile = null;
|
}
|
OpenLayers.Layer.prototype.destroy.apply(this, arguments);
|
},
|
|
/**
|
* Method: clone
|
* Create a clone of this layer
|
*
|
* Paramters:
|
* obj - {Object} An optional layer (is this ever used?)
|
*
|
* Returns:
|
* {<OpenLayers.Layer.Image>} An exact copy of this layer
|
*/
|
clone: function(obj) {
|
|
if(obj == null) {
|
obj = new OpenLayers.Layer.Image(this.name,
|
this.url,
|
this.extent,
|
this.size,
|
this.getOptions());
|
}
|
|
//get all additions from superclasses
|
obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
|
|
// copy/set any non-init, non-simple values here
|
|
return obj;
|
},
|
|
/**
|
* APIMethod: setMap
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
setMap: function(map) {
|
/**
|
* If nothing to do with resolutions has been set, assume a single
|
* resolution determined by ratio*extent/size - if an image has a
|
* pixel aspect ratio different than one (as calculated above), the
|
* image will be stretched in one dimension only.
|
*/
|
if( this.options.maxResolution == null ) {
|
this.options.maxResolution = this.aspectRatio *
|
this.extent.getWidth() /
|
this.size.w;
|
}
|
OpenLayers.Layer.prototype.setMap.apply(this, arguments);
|
},
|
|
/**
|
* Method: moveTo
|
* Create the tile for the image or resize it for the new resolution
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
* zoomChanged - {Boolean}
|
* dragging - {Boolean}
|
*/
|
moveTo:function(bounds, zoomChanged, dragging) {
|
OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
|
|
var firstRendering = (this.tile == null);
|
|
if(zoomChanged || firstRendering) {
|
|
//determine new tile size
|
this.setTileSize();
|
|
//determine new position (upper left corner of new bounds)
|
var ulPx = this.map.getLayerPxFromLonLat({
|
lon: this.extent.left,
|
lat: this.extent.top
|
});
|
|
if(firstRendering) {
|
//create the new tile
|
this.tile = new OpenLayers.Tile.Image(this, ulPx, this.extent,
|
null, this.tileSize);
|
this.addTileMonitoringHooks(this.tile);
|
} else {
|
//just resize the tile and set it's new position
|
this.tile.size = this.tileSize.clone();
|
this.tile.position = ulPx.clone();
|
}
|
this.tile.draw();
|
}
|
},
|
|
/**
|
* Set the tile size based on the map size.
|
*/
|
setTileSize: function() {
|
var tileWidth = this.extent.getWidth() / this.map.getResolution();
|
var tileHeight = this.extent.getHeight() / this.map.getResolution();
|
this.tileSize = new OpenLayers.Size(tileWidth, tileHeight);
|
},
|
|
/**
|
* Method: addTileMonitoringHooks
|
* This function takes a tile as input and adds the appropriate hooks to
|
* the tile so that the layer can keep track of the loading tiles.
|
*
|
* Parameters:
|
* tile - {<OpenLayers.Tile>}
|
*/
|
addTileMonitoringHooks: function(tile) {
|
tile.onLoadStart = function() {
|
this.events.triggerEvent("loadstart");
|
};
|
tile.events.register("loadstart", this, tile.onLoadStart);
|
|
tile.onLoadEnd = function() {
|
this.events.triggerEvent("loadend");
|
};
|
tile.events.register("loadend", this, tile.onLoadEnd);
|
tile.events.register("unload", this, tile.onLoadEnd);
|
},
|
|
/**
|
* Method: removeTileMonitoringHooks
|
* This function takes a tile as input and removes the tile hooks
|
* that were added in <addTileMonitoringHooks>.
|
*
|
* Parameters:
|
* tile - {<OpenLayers.Tile>}
|
*/
|
removeTileMonitoringHooks: function(tile) {
|
tile.unload();
|
tile.events.un({
|
"loadstart": tile.onLoadStart,
|
"loadend": tile.onLoadEnd,
|
"unload": tile.onLoadEnd,
|
scope: this
|
});
|
},
|
|
/**
|
* APIMethod: setUrl
|
*
|
* Parameters:
|
* newUrl - {String}
|
*/
|
setUrl: function(newUrl) {
|
this.url = newUrl;
|
this.tile.draw();
|
},
|
|
/**
|
* APIMethod: getURL
|
* The url we return is always the same (the image itself never changes)
|
* so we can ignore the bounds parameter (it will always be the same,
|
* anyways)
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
*/
|
getURL: function(bounds) {
|
return this.url;
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.Image"
|
});
|
/* ======================================================================
|
OpenLayers/Strategy.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
*/
|
|
/**
|
* Class: OpenLayers.Strategy
|
* Abstract vector layer strategy class. Not to be instantiated directly. Use
|
* one of the strategy subclasses instead.
|
*/
|
OpenLayers.Strategy = OpenLayers.Class({
|
|
/**
|
* Property: layer
|
* {<OpenLayers.Layer.Vector>} The layer this strategy belongs to.
|
*/
|
layer: null,
|
|
/**
|
* Property: options
|
* {Object} Any options sent to the constructor.
|
*/
|
options: null,
|
|
/**
|
* Property: active
|
* {Boolean} The control is active.
|
*/
|
active: null,
|
|
/**
|
* Property: autoActivate
|
* {Boolean} The creator of the strategy can set autoActivate to false
|
* to fully control when the protocol is activated and deactivated.
|
* Defaults to true.
|
*/
|
autoActivate: true,
|
|
/**
|
* Property: autoDestroy
|
* {Boolean} The creator of the strategy can set autoDestroy to false
|
* to fully control when the strategy is destroyed. Defaults to
|
* true.
|
*/
|
autoDestroy: true,
|
|
/**
|
* Constructor: OpenLayers.Strategy
|
* Abstract class for vector strategies. Create instances of a subclass.
|
*
|
* Parameters:
|
* options - {Object} Optional object whose properties will be set on the
|
* instance.
|
*/
|
initialize: function(options) {
|
OpenLayers.Util.extend(this, options);
|
this.options = options;
|
// set the active property here, so that user cannot override it
|
this.active = false;
|
},
|
|
/**
|
* APIMethod: destroy
|
* Clean up the strategy.
|
*/
|
destroy: function() {
|
this.deactivate();
|
this.layer = null;
|
this.options = null;
|
},
|
|
/**
|
* Method: setLayer
|
* Called to set the <layer> property.
|
*
|
* Parameters:
|
* layer - {<OpenLayers.Layer.Vector>}
|
*/
|
setLayer: function(layer) {
|
this.layer = layer;
|
},
|
|
/**
|
* Method: activate
|
* Activate the strategy. Register any listeners, do appropriate setup.
|
*
|
* Returns:
|
* {Boolean} True if the strategy was successfully activated or false if
|
* the strategy was already active.
|
*/
|
activate: function() {
|
if (!this.active) {
|
this.active = true;
|
return true;
|
}
|
return false;
|
},
|
|
/**
|
* Method: deactivate
|
* Deactivate the strategy. Unregister any listeners, do appropriate
|
* tear-down.
|
*
|
* Returns:
|
* {Boolean} True if the strategy was successfully deactivated or false if
|
* the strategy was already inactive.
|
*/
|
deactivate: function() {
|
if (this.active) {
|
this.active = false;
|
return true;
|
}
|
return false;
|
},
|
|
CLASS_NAME: "OpenLayers.Strategy"
|
});
|
/* ======================================================================
|
OpenLayers/Strategy/Save.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Strategy.js
|
*/
|
|
/**
|
* Class: OpenLayers.Strategy.Save
|
* A strategy that commits newly created or modified features. By default
|
* the strategy waits for a call to <save> before persisting changes. By
|
* configuring the strategy with the <auto> option, changes can be saved
|
* automatically.
|
*
|
* Inherits from:
|
* - <OpenLayers.Strategy>
|
*/
|
OpenLayers.Strategy.Save = OpenLayers.Class(OpenLayers.Strategy, {
|
|
/**
|
* APIProperty: events
|
* {<OpenLayers.Events>} An events object that handles all
|
* events on the strategy object.
|
*
|
* Register a listener for a particular event with the following syntax:
|
* (code)
|
* strategy.events.register(type, obj, listener);
|
* (end)
|
*
|
* Supported event types:
|
* start - Triggered before saving
|
* success - Triggered after a successful transaction
|
* fail - Triggered after a failed transaction
|
*
|
*/
|
|
/**
|
* Property: events
|
* {<OpenLayers.Events>} Events instance for triggering this protocol
|
* events.
|
*/
|
events: null,
|
|
/**
|
* APIProperty: auto
|
* {Boolean | Number} Auto-save. Default is false. If true, features will be
|
* saved immediately after being added to the layer and with each
|
* modification or deletion. If auto is a number, features will be
|
* saved on an interval provided by the value (in seconds).
|
*/
|
auto: false,
|
|
/**
|
* Property: timer
|
* {Number} The id of the timer.
|
*/
|
timer: null,
|
|
/**
|
* Constructor: OpenLayers.Strategy.Save
|
* Create a new Save strategy.
|
*
|
* Parameters:
|
* options - {Object} Optional object whose properties will be set on the
|
* instance.
|
*/
|
initialize: function(options) {
|
OpenLayers.Strategy.prototype.initialize.apply(this, [options]);
|
this.events = new OpenLayers.Events(this);
|
},
|
|
/**
|
* APIMethod: activate
|
* Activate the strategy. Register any listeners, do appropriate setup.
|
*
|
* Returns:
|
* {Boolean} The strategy was successfully activated.
|
*/
|
activate: function() {
|
var activated = OpenLayers.Strategy.prototype.activate.call(this);
|
if(activated) {
|
if(this.auto) {
|
if(typeof this.auto === "number") {
|
this.timer = window.setInterval(
|
OpenLayers.Function.bind(this.save, this),
|
this.auto * 1000
|
);
|
} else {
|
this.layer.events.on({
|
"featureadded": this.triggerSave,
|
"afterfeaturemodified": this.triggerSave,
|
scope: this
|
});
|
}
|
}
|
}
|
return activated;
|
},
|
|
/**
|
* APIMethod: deactivate
|
* Deactivate the strategy. Unregister any listeners, do appropriate
|
* tear-down.
|
*
|
* Returns:
|
* {Boolean} The strategy was successfully deactivated.
|
*/
|
deactivate: function() {
|
var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
|
if(deactivated) {
|
if(this.auto) {
|
if(typeof this.auto === "number") {
|
window.clearInterval(this.timer);
|
} else {
|
this.layer.events.un({
|
"featureadded": this.triggerSave,
|
"afterfeaturemodified": this.triggerSave,
|
scope: this
|
});
|
}
|
}
|
}
|
return deactivated;
|
},
|
|
/**
|
* Method: triggerSave
|
* Registered as a listener. Calls save if a feature has insert, update,
|
* or delete state.
|
*
|
* Parameters:
|
* event - {Object} The event this function is listening for.
|
*/
|
triggerSave: function(event) {
|
var feature = event.feature;
|
if(feature.state === OpenLayers.State.INSERT ||
|
feature.state === OpenLayers.State.UPDATE ||
|
feature.state === OpenLayers.State.DELETE) {
|
this.save([event.feature]);
|
}
|
},
|
|
/**
|
* APIMethod: save
|
* Tell the layer protocol to commit unsaved features. If the layer
|
* projection differs from the map projection, features will be
|
* transformed into the layer projection before being committed.
|
*
|
* Parameters:
|
* features - {Array} Features to be saved. If null, then default is all
|
* features in the layer. Features are assumed to be in the map
|
* projection.
|
*/
|
save: function(features) {
|
if(!features) {
|
features = this.layer.features;
|
}
|
this.events.triggerEvent("start", {features:features});
|
var remote = this.layer.projection;
|
var local = this.layer.map.getProjectionObject();
|
if(!local.equals(remote)) {
|
var len = features.length;
|
var clones = new Array(len);
|
var orig, clone;
|
for(var i=0; i<len; ++i) {
|
orig = features[i];
|
clone = orig.clone();
|
clone.fid = orig.fid;
|
clone.state = orig.state;
|
if(orig.url) {
|
clone.url = orig.url;
|
}
|
clone._original = orig;
|
clone.geometry.transform(local, remote);
|
clones[i] = clone;
|
}
|
features = clones;
|
}
|
this.layer.protocol.commit(features, {
|
callback: this.onCommit,
|
scope: this
|
});
|
},
|
|
/**
|
* Method: onCommit
|
* Called after protocol commit.
|
*
|
* Parameters:
|
* response - {<OpenLayers.Protocol.Response>} A response object.
|
*/
|
onCommit: function(response) {
|
var evt = {"response": response};
|
if(response.success()) {
|
var features = response.reqFeatures;
|
// deal with inserts, updates, and deletes
|
var state, feature;
|
var destroys = [];
|
var insertIds = response.insertIds || [];
|
var j = 0;
|
for(var i=0, len=features.length; i<len; ++i) {
|
feature = features[i];
|
// if projection was different, we may be dealing with clones
|
feature = feature._original || feature;
|
state = feature.state;
|
if(state) {
|
if(state == OpenLayers.State.DELETE) {
|
destroys.push(feature);
|
} else if(state == OpenLayers.State.INSERT) {
|
feature.fid = insertIds[j];
|
++j;
|
}
|
feature.state = null;
|
}
|
}
|
|
if(destroys.length > 0) {
|
this.layer.destroyFeatures(destroys);
|
}
|
|
this.events.triggerEvent("success", evt);
|
|
} else {
|
this.events.triggerEvent("fail", evt);
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Strategy.Save"
|
});
|
/* ======================================================================
|
OpenLayers/Events/featureclick.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Events.js
|
*/
|
|
/**
|
* Class: OpenLayers.Events.featureclick
|
*
|
* Extension event type for handling feature click events, including overlapping
|
* features.
|
*
|
* Event types provided by this extension:
|
* - featureclick
|
*/
|
OpenLayers.Events.featureclick = OpenLayers.Class({
|
|
/**
|
* Property: cache
|
* {Object} A cache of features under the mouse.
|
*/
|
cache: null,
|
|
/**
|
* Property: map
|
* {<OpenLayers.Map>} The map to register browser events on.
|
*/
|
map: null,
|
|
/**
|
* Property: provides
|
* {Array(String)} The event types provided by this extension.
|
*/
|
provides: ["featureclick", "nofeatureclick", "featureover", "featureout"],
|
|
/**
|
* Constructor: OpenLayers.Events.featureclick
|
* Create a new featureclick event type.
|
*
|
* Parameters:
|
* target - {<OpenLayers.Events>} The events instance to create the events
|
* for.
|
*/
|
initialize: function(target) {
|
this.target = target;
|
if (target.object instanceof OpenLayers.Map) {
|
this.setMap(target.object);
|
} else if (target.object instanceof OpenLayers.Layer.Vector) {
|
if (target.object.map) {
|
this.setMap(target.object.map);
|
} else {
|
target.object.events.register("added", this, function(evt) {
|
this.setMap(target.object.map);
|
});
|
}
|
} else {
|
throw("Listeners for '" + this.provides.join("', '") +
|
"' events can only be registered for OpenLayers.Layer.Vector " +
|
"or OpenLayers.Map instances");
|
}
|
for (var i=this.provides.length-1; i>=0; --i) {
|
target.extensions[this.provides[i]] = true;
|
}
|
},
|
|
/**
|
* Method: setMap
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>} The map to register browser events on.
|
*/
|
setMap: function(map) {
|
this.map = map;
|
this.cache = {};
|
map.events.register("mousedown", this, this.start, {extension: true});
|
map.events.register("mouseup", this, this.onClick, {extension: true});
|
map.events.register("touchstart", this, this.start, {extension: true});
|
map.events.register("touchmove", this, this.cancel, {extension: true});
|
map.events.register("touchend", this, this.onClick, {extension: true});
|
map.events.register("mousemove", this, this.onMousemove, {extension: true});
|
},
|
|
/**
|
* Method: start
|
* Sets startEvt = evt.
|
*
|
* Parameters:
|
* evt - {<OpenLayers.Event>}
|
*/
|
start: function(evt) {
|
this.startEvt = evt;
|
},
|
|
/**
|
* Method: cancel
|
* Deletes the start event.
|
*
|
* Parameters:
|
* evt - {<OpenLayers.Event>}
|
*/
|
cancel: function(evt) {
|
delete this.startEvt;
|
},
|
|
/**
|
* Method: onClick
|
* Listener for the click event.
|
*
|
* Parameters:
|
* evt - {<OpenLayers.Event>}
|
*/
|
onClick: function(evt) {
|
if (!this.startEvt || evt.type !== "touchend" &&
|
!OpenLayers.Event.isLeftClick(evt)) {
|
return;
|
}
|
var features = this.getFeatures(this.startEvt);
|
delete this.startEvt;
|
// fire featureclick events
|
var feature, layer, more, clicked = {};
|
for (var i=0, len=features.length; i<len; ++i) {
|
feature = features[i];
|
layer = feature.layer;
|
clicked[layer.id] = true;
|
more = this.triggerEvent("featureclick", {feature: feature});
|
if (more === false) {
|
break;
|
}
|
}
|
// fire nofeatureclick events on all vector layers with no targets
|
for (i=0, len=this.map.layers.length; i<len; ++i) {
|
layer = this.map.layers[i];
|
if (layer instanceof OpenLayers.Layer.Vector && !clicked[layer.id]) {
|
this.triggerEvent("nofeatureclick", {layer: layer});
|
}
|
}
|
},
|
|
/**
|
* Method: onMousemove
|
* Listener for the mousemove event.
|
*
|
* Parameters:
|
* evt - {<OpenLayers.Event>}
|
*/
|
onMousemove: function(evt) {
|
delete this.startEvt;
|
var features = this.getFeatures(evt);
|
var over = {}, newly = [], feature;
|
for (var i=0, len=features.length; i<len; ++i) {
|
feature = features[i];
|
over[feature.id] = feature;
|
if (!this.cache[feature.id]) {
|
newly.push(feature);
|
}
|
}
|
// check if already over features
|
var out = [];
|
for (var id in this.cache) {
|
feature = this.cache[id];
|
if (feature.layer && feature.layer.map) {
|
if (!over[feature.id]) {
|
out.push(feature);
|
}
|
} else {
|
// removed
|
delete this.cache[id];
|
}
|
}
|
// fire featureover events
|
var more;
|
for (i=0, len=newly.length; i<len; ++i) {
|
feature = newly[i];
|
this.cache[feature.id] = feature;
|
more = this.triggerEvent("featureover", {feature: feature});
|
if (more === false) {
|
break;
|
}
|
}
|
// fire featureout events
|
for (i=0, len=out.length; i<len; ++i) {
|
feature = out[i];
|
delete this.cache[feature.id];
|
more = this.triggerEvent("featureout", {feature: feature});
|
if (more === false) {
|
break;
|
}
|
}
|
},
|
|
/**
|
* Method: triggerEvent
|
* Determines where to trigger the event and triggers it.
|
*
|
* Parameters:
|
* type - {String} The event type to trigger
|
* evt - {Object} The listener argument
|
*
|
* Returns:
|
* {Boolean} The last listener return.
|
*/
|
triggerEvent: function(type, evt) {
|
var layer = evt.feature ? evt.feature.layer : evt.layer,
|
object = this.target.object;
|
if (object instanceof OpenLayers.Map || object === layer) {
|
return this.target.triggerEvent(type, evt);
|
}
|
},
|
|
/**
|
* Method: getFeatures
|
* Get all features at the given screen location.
|
*
|
* Parameters:
|
* evt - {Object} Event object.
|
*
|
* Returns:
|
* {Array(<OpenLayers.Feature.Vector>)} List of features at the given point.
|
*/
|
getFeatures: function(evt) {
|
var x = evt.clientX, y = evt.clientY,
|
features = [], targets = [], layers = [],
|
layer, target, feature, i, len;
|
// go through all layers looking for targets
|
for (i=this.map.layers.length-1; i>=0; --i) {
|
layer = this.map.layers[i];
|
if (layer.div.style.display !== "none") {
|
if (layer.renderer instanceof OpenLayers.Renderer.Elements) {
|
if (layer instanceof OpenLayers.Layer.Vector) {
|
target = document.elementFromPoint(x, y);
|
while (target && target._featureId) {
|
feature = layer.getFeatureById(target._featureId);
|
if (feature) {
|
features.push(feature);
|
target.style.display = "none";
|
targets.push(target);
|
target = document.elementFromPoint(x, y);
|
} else {
|
// sketch, all bets off
|
target = false;
|
}
|
}
|
}
|
layers.push(layer);
|
layer.div.style.display = "none";
|
} else if (layer.renderer instanceof OpenLayers.Renderer.Canvas) {
|
feature = layer.renderer.getFeatureIdFromEvent(evt);
|
if (feature) {
|
features.push(feature);
|
layers.push(layer);
|
}
|
}
|
}
|
}
|
// restore feature visibility
|
for (i=0, len=targets.length; i<len; ++i) {
|
targets[i].style.display = "";
|
}
|
// restore layer visibility
|
for (i=layers.length-1; i>=0; --i) {
|
layers[i].div.style.display = "block";
|
}
|
return features;
|
},
|
|
/**
|
* APIMethod: destroy
|
* Clean up.
|
*/
|
destroy: function() {
|
for (var i=this.provides.length-1; i>=0; --i) {
|
delete this.target.extensions[this.provides[i]];
|
}
|
this.map.events.un({
|
mousemove: this.onMousemove,
|
mousedown: this.start,
|
mouseup: this.onClick,
|
touchstart: this.start,
|
touchmove: this.cancel,
|
touchend: this.onClick,
|
scope: this
|
});
|
delete this.cache;
|
delete this.map;
|
delete this.target;
|
}
|
|
});
|
|
/**
|
* Class: OpenLayers.Events.nofeatureclick
|
*
|
* Extension event type for handling click events that do not hit a feature.
|
*
|
* Event types provided by this extension:
|
* - nofeatureclick
|
*/
|
OpenLayers.Events.nofeatureclick = OpenLayers.Events.featureclick;
|
|
/**
|
* Class: OpenLayers.Events.featureover
|
*
|
* Extension event type for handling hovering over a feature.
|
*
|
* Event types provided by this extension:
|
* - featureover
|
*/
|
OpenLayers.Events.featureover = OpenLayers.Events.featureclick;
|
|
/**
|
* Class: OpenLayers.Events.featureout
|
*
|
* Extension event type for handling leaving a feature.
|
*
|
* Event types provided by this extension:
|
* - featureout
|
*/
|
OpenLayers.Events.featureout = OpenLayers.Events.featureclick;
|
/* ======================================================================
|
OpenLayers/Format/GPX.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML.js
|
* @requires OpenLayers/Feature/Vector.js
|
* @requires OpenLayers/Geometry/Point.js
|
* @requires OpenLayers/Geometry/LineString.js
|
* @requires OpenLayers/Projection.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.GPX
|
* Read/write GPX parser. Create a new instance with the
|
* <OpenLayers.Format.GPX> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.GPX = OpenLayers.Class(OpenLayers.Format.XML, {
|
|
|
/**
|
* APIProperty: defaultDesc
|
* {String} Default description for the waypoints/tracks in the case
|
* where the feature has no "description" attribute.
|
* Default is "No description available".
|
*/
|
defaultDesc: "No description available",
|
|
/**
|
* APIProperty: extractWaypoints
|
* {Boolean} Extract waypoints from GPX. (default: true)
|
*/
|
extractWaypoints: true,
|
|
/**
|
* APIProperty: extractTracks
|
* {Boolean} Extract tracks from GPX. (default: true)
|
*/
|
extractTracks: true,
|
|
/**
|
* APIProperty: extractRoutes
|
* {Boolean} Extract routes from GPX. (default: true)
|
*/
|
extractRoutes: true,
|
|
/**
|
* APIProperty: extractAttributes
|
* {Boolean} Extract feature attributes from GPX. (default: true)
|
* NOTE: Attributes as part of extensions to the GPX standard may not
|
* be extracted.
|
*/
|
extractAttributes: true,
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
gpx: "http://www.topografix.com/GPX/1/1",
|
xsi: "http://www.w3.org/2001/XMLSchema-instance"
|
},
|
|
/**
|
* Property: schemaLocation
|
* {String} Schema location. Defaults to
|
* "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"
|
*/
|
schemaLocation: "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd",
|
|
/**
|
* APIProperty: creator
|
* {String} The creator attribute to be added to the written GPX files.
|
* Defaults to "OpenLayers"
|
*/
|
creator: "OpenLayers",
|
|
/**
|
* Constructor: OpenLayers.Format.GPX
|
* Create a new parser for GPX.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
initialize: function(options) {
|
// GPX coordinates are always in longlat WGS84
|
this.externalProjection = new OpenLayers.Projection("EPSG:4326");
|
|
OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
|
},
|
|
/**
|
* APIMethod: read
|
* Return a list of features from a GPX doc
|
*
|
* Parameters:
|
* doc - {Element}
|
*
|
* Returns:
|
* Array({<OpenLayers.Feature.Vector>})
|
*/
|
read: function(doc) {
|
if (typeof doc == "string") {
|
doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);
|
}
|
var features = [];
|
|
if(this.extractTracks) {
|
var tracks = doc.getElementsByTagName("trk");
|
for (var i=0, len=tracks.length; i<len; i++) {
|
// Attributes are only in trk nodes, not trkseg nodes
|
var attrs = {};
|
if(this.extractAttributes) {
|
attrs = this.parseAttributes(tracks[i]);
|
}
|
|
var segs = this.getElementsByTagNameNS(tracks[i], tracks[i].namespaceURI, "trkseg");
|
for (var j = 0, seglen = segs.length; j < seglen; j++) {
|
// We don't yet support extraction of trkpt attributes
|
// All trksegs of a trk get that trk's attributes
|
var track = this.extractSegment(segs[j], "trkpt");
|
features.push(new OpenLayers.Feature.Vector(track, attrs));
|
}
|
}
|
}
|
|
if(this.extractRoutes) {
|
var routes = doc.getElementsByTagName("rte");
|
for (var k=0, klen=routes.length; k<klen; k++) {
|
var attrs = {};
|
if(this.extractAttributes) {
|
attrs = this.parseAttributes(routes[k]);
|
}
|
var route = this.extractSegment(routes[k], "rtept");
|
features.push(new OpenLayers.Feature.Vector(route, attrs));
|
}
|
}
|
|
if(this.extractWaypoints) {
|
var waypoints = doc.getElementsByTagName("wpt");
|
for (var l = 0, len = waypoints.length; l < len; l++) {
|
var attrs = {};
|
if(this.extractAttributes) {
|
attrs = this.parseAttributes(waypoints[l]);
|
}
|
var wpt = new OpenLayers.Geometry.Point(waypoints[l].getAttribute("lon"), waypoints[l].getAttribute("lat"));
|
features.push(new OpenLayers.Feature.Vector(wpt, attrs));
|
}
|
}
|
|
if (this.internalProjection && this.externalProjection) {
|
for (var g = 0, featLength = features.length; g < featLength; g++) {
|
features[g].geometry.transform(this.externalProjection,
|
this.internalProjection);
|
}
|
}
|
|
return features;
|
},
|
|
/**
|
* Method: extractSegment
|
*
|
* Parameters:
|
* segment - {DOMElement} a trkseg or rte node to parse
|
* segmentType - {String} nodeName of waypoints that form the line
|
*
|
* Returns:
|
* {<OpenLayers.Geometry.LineString>} A linestring geometry
|
*/
|
extractSegment: function(segment, segmentType) {
|
var points = this.getElementsByTagNameNS(segment, segment.namespaceURI, segmentType);
|
var point_features = [];
|
for (var i = 0, len = points.length; i < len; i++) {
|
point_features.push(new OpenLayers.Geometry.Point(points[i].getAttribute("lon"), points[i].getAttribute("lat")));
|
}
|
return new OpenLayers.Geometry.LineString(point_features);
|
},
|
|
/**
|
* Method: parseAttributes
|
*
|
* Parameters:
|
* node - {<DOMElement>}
|
*
|
* Returns:
|
* {Object} An attributes object.
|
*/
|
parseAttributes: function(node) {
|
// node is either a wpt, trk or rte
|
// attributes are children of the form <attr>value</attr>
|
var attributes = {};
|
var attrNode = node.firstChild, value, name;
|
while(attrNode) {
|
if(attrNode.nodeType == 1 && attrNode.firstChild) {
|
value = attrNode.firstChild;
|
if(value.nodeType == 3 || value.nodeType == 4) {
|
name = (attrNode.prefix) ?
|
attrNode.nodeName.split(":")[1] :
|
attrNode.nodeName;
|
if(name != "trkseg" && name != "rtept") {
|
attributes[name] = value.nodeValue;
|
}
|
}
|
}
|
attrNode = attrNode.nextSibling;
|
}
|
return attributes;
|
},
|
|
/**
|
* APIMethod: write
|
* Accepts Feature Collection, and returns a string.
|
*
|
* Parameters:
|
* features - {Array(<OpenLayers.Feature.Vector>)} List of features to serialize into a string.
|
* metadata - {Object} A key/value pairs object to build a metadata node to
|
* add to the gpx. Supported keys are 'name', 'desc', 'author'.
|
*/
|
write: function(features, metadata) {
|
features = OpenLayers.Util.isArray(features) ?
|
features : [features];
|
var gpx = this.createElementNS(this.namespaces.gpx, "gpx");
|
gpx.setAttribute("version", "1.1");
|
gpx.setAttribute("creator", this.creator);
|
this.setAttributes(gpx, {
|
"xsi:schemaLocation": this.schemaLocation
|
});
|
|
if (metadata && typeof metadata == 'object') {
|
gpx.appendChild(this.buildMetadataNode(metadata));
|
}
|
for(var i=0, len=features.length; i<len; i++) {
|
gpx.appendChild(this.buildFeatureNode(features[i]));
|
}
|
return OpenLayers.Format.XML.prototype.write.apply(this, [gpx]);
|
},
|
|
/**
|
* Method: buildMetadataNode
|
* Creates a "metadata" node.
|
*
|
* Returns:
|
* {DOMElement}
|
*/
|
buildMetadataNode: function(metadata) {
|
var types = ['name', 'desc', 'author'],
|
node = this.createElementNS(this.namespaces.gpx, 'metadata');
|
for (var i=0; i < types.length; i++) {
|
var type = types[i];
|
if (metadata[type]) {
|
var n = this.createElementNS(this.namespaces.gpx, type);
|
n.appendChild(this.createTextNode(metadata[type]));
|
node.appendChild(n);
|
}
|
}
|
return node;
|
},
|
|
/**
|
* Method: buildFeatureNode
|
* Accepts an <OpenLayers.Feature.Vector>, and builds a node for it.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
*
|
* Returns:
|
* {DOMElement} - The created node, either a 'wpt' or a 'trk'.
|
*/
|
buildFeatureNode: function(feature) {
|
var geometry = feature.geometry;
|
geometry = geometry.clone();
|
if (this.internalProjection && this.externalProjection) {
|
geometry.transform(this.internalProjection,
|
this.externalProjection);
|
}
|
if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
|
var wpt = this.buildWptNode(geometry);
|
this.appendAttributesNode(wpt, feature);
|
return wpt;
|
} else {
|
var trkNode = this.createElementNS(this.namespaces.gpx, "trk");
|
this.appendAttributesNode(trkNode, feature);
|
var trkSegNodes = this.buildTrkSegNode(geometry);
|
trkSegNodes = OpenLayers.Util.isArray(trkSegNodes) ?
|
trkSegNodes : [trkSegNodes];
|
for (var i = 0, len = trkSegNodes.length; i < len; i++) {
|
trkNode.appendChild(trkSegNodes[i]);
|
}
|
return trkNode;
|
}
|
},
|
|
/**
|
* Method: buildTrkSegNode
|
* Builds trkseg node(s) given a geometry
|
*
|
* Parameters:
|
* trknode
|
* geometry - {<OpenLayers.Geometry>}
|
*/
|
buildTrkSegNode: function(geometry) {
|
var node,
|
i,
|
len,
|
point,
|
nodes;
|
if (geometry.CLASS_NAME == "OpenLayers.Geometry.LineString" ||
|
geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
|
node = this.createElementNS(this.namespaces.gpx, "trkseg");
|
for (i = 0, len=geometry.components.length; i < len; i++) {
|
point = geometry.components[i];
|
node.appendChild(this.buildTrkPtNode(point));
|
}
|
return node;
|
} else {
|
nodes = [];
|
for (i = 0, len = geometry.components.length; i < len; i++) {
|
nodes.push(this.buildTrkSegNode(geometry.components[i]));
|
}
|
return nodes;
|
}
|
},
|
|
/**
|
* Method: buildTrkPtNode
|
* Builds a trkpt node given a point
|
*
|
* Parameters:
|
* point - {<OpenLayers.Geometry.Point>}
|
*
|
* Returns:
|
* {DOMElement} A trkpt node
|
*/
|
buildTrkPtNode: function(point) {
|
var node = this.createElementNS(this.namespaces.gpx, "trkpt");
|
node.setAttribute("lon", point.x);
|
node.setAttribute("lat", point.y);
|
return node;
|
},
|
|
/**
|
* Method: buildWptNode
|
* Builds a wpt node given a point
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry.Point>}
|
*
|
* Returns:
|
* {DOMElement} A wpt node
|
*/
|
buildWptNode: function(geometry) {
|
var node = this.createElementNS(this.namespaces.gpx, "wpt");
|
node.setAttribute("lon", geometry.x);
|
node.setAttribute("lat", geometry.y);
|
return node;
|
},
|
|
/**
|
* Method: appendAttributesNode
|
* Adds some attributes node.
|
*
|
* Parameters:
|
* node - {DOMElement} the node to append the attribute nodes to.
|
* feature - {<OpenLayers.Feature.Vector>}
|
*/
|
appendAttributesNode: function(node, feature) {
|
var name = this.createElementNS(this.namespaces.gpx, 'name');
|
name.appendChild(this.createTextNode(
|
feature.attributes.name || feature.id));
|
node.appendChild(name);
|
var desc = this.createElementNS(this.namespaces.gpx, 'desc');
|
desc.appendChild(this.createTextNode(
|
feature.attributes.description || this.defaultDesc));
|
node.appendChild(desc);
|
// TBD - deal with remaining (non name/description) attributes.
|
},
|
|
CLASS_NAME: "OpenLayers.Format.GPX"
|
});
|
/* ======================================================================
|
OpenLayers/Format/WMSDescribeLayer.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML/VersionedOGC.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WMSDescribeLayer
|
* Read SLD WMS DescribeLayer response
|
* DescribeLayer is meant to couple WMS to WFS and WCS
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML.VersionedOGC>
|
*/
|
OpenLayers.Format.WMSDescribeLayer = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
|
|
/**
|
* APIProperty: defaultVersion
|
* {String} Version number to assume if none found. Default is "1.1.1".
|
*/
|
defaultVersion: "1.1.1",
|
|
/**
|
* Constructor: OpenLayers.Format.WMSDescribeLayer
|
* Create a new parser for WMS DescribeLayer responses.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* APIMethod: read
|
* Read DescribeLayer data from a string, and return the response.
|
* The OGC currently defines 2 formats which are allowed for output,
|
* so we need to parse these 2 types
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
*
|
* Returns:
|
* {Array} Array of {<LayerDescription>} objects which have:
|
* - {String} owsType: WFS/WCS
|
* - {String} owsURL: the online resource
|
* - {String} typeName: the name of the typename on the service
|
*/
|
|
CLASS_NAME: "OpenLayers.Format.WMSDescribeLayer"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/WMSDescribeLayer/v1_1.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/WMSDescribeLayer.js
|
* @requires OpenLayers/Format/OGCExceptionReport.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WMSDescribeLayer.v1_1_1
|
* Read SLD WMS DescribeLayer response for WMS 1.1.X
|
* WMS 1.1.X is tightly coupled to SLD 1.0.0
|
*
|
* Example DescribeLayer request:
|
* http://demo.opengeo.org/geoserver/wms?request=DescribeLayer&version=1.1.1&layers=topp:states
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.WMSDescribeLayer>
|
*/
|
OpenLayers.Format.WMSDescribeLayer.v1_1_1 = OpenLayers.Class(
|
OpenLayers.Format.WMSDescribeLayer, {
|
|
/**
|
* Constructor: OpenLayers.Format.WMSDescribeLayer
|
* Create a new parser for WMS DescribeLayer responses.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
initialize: function(options) {
|
OpenLayers.Format.WMSDescribeLayer.prototype.initialize.apply(this,
|
[options]);
|
},
|
|
/**
|
* APIMethod: read
|
* Read DescribeLayer data from a string, and return the response.
|
* The OGC defines 2 formats which are allowed for output,
|
* so we need to parse these 2 types for version 1.1.X
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
*
|
* Returns:
|
* {Object} Object with a layerDescriptions property, which holds an Array
|
* of {<LayerDescription>} objects which have:
|
* - {String} owsType: WFS/WCS
|
* - {String} owsURL: the online resource
|
* - {String} typeName: the name of the typename on the owsType service
|
* - {String} layerName: the name of the WMS layer we did a lookup for
|
*/
|
read: function(data) {
|
if(typeof data == "string") {
|
data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
|
}
|
var root = data.documentElement;
|
var children = root.childNodes;
|
var describelayer = {layerDescriptions: []};
|
var childNode, nodeName;
|
for(var i=0; i<children.length; ++i) {
|
childNode = children[i];
|
nodeName = childNode.nodeName;
|
if (nodeName == 'LayerDescription') {
|
var layerName = childNode.getAttribute('name');
|
var owsType = '';
|
var owsURL = '';
|
var typeName = '';
|
// check for owsType and owsURL attributes
|
if (childNode.getAttribute('owsType')) {
|
owsType = childNode.getAttribute('owsType');
|
owsURL = childNode.getAttribute('owsURL');
|
} else {
|
// look for wfs or wcs attribute
|
if (childNode.getAttribute('wfs') != '') {
|
owsType = 'WFS';
|
owsURL = childNode.getAttribute('wfs');
|
} else if (childNode.getAttribute('wcs') != '') {
|
owsType = 'WCS';
|
owsURL = childNode.getAttribute('wcs');
|
}
|
}
|
// look for Query child
|
var query = childNode.getElementsByTagName('Query');
|
if(query.length > 0) {
|
typeName = query[0].getAttribute('typeName');
|
if (!typeName) {
|
// because of Ionic bug
|
typeName = query[0].getAttribute('typename');
|
}
|
}
|
var layerDescription = {
|
layerName: layerName, owsType: owsType,
|
owsURL: owsURL, typeName: typeName
|
};
|
describelayer.layerDescriptions.push(layerDescription);
|
|
//TODO do this in deprecated.js instead:
|
// array style index for backwards compatibility
|
describelayer.length = describelayer.layerDescriptions.length;
|
describelayer[describelayer.length - 1] = layerDescription;
|
|
} else if (nodeName == 'ServiceException') {
|
// an exception must have occurred, so parse it
|
var parser = new OpenLayers.Format.OGCExceptionReport();
|
return {
|
error: parser.read(data)
|
};
|
}
|
}
|
return describelayer;
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WMSDescribeLayer.v1_1_1"
|
|
});
|
|
// Version alias - workaround for http://trac.osgeo.org/mapserver/ticket/2257
|
OpenLayers.Format.WMSDescribeLayer.v1_1_0 =
|
OpenLayers.Format.WMSDescribeLayer.v1_1_1;
|
/* ======================================================================
|
OpenLayers/Layer/XYZ.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Layer/Grid.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.XYZ
|
* The XYZ class is designed to make it easier for people who have tiles
|
* arranged by a standard XYZ grid.
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer.Grid>
|
*/
|
OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, {
|
|
/**
|
* APIProperty: isBaseLayer
|
* Default is true, as this is designed to be a base tile source.
|
*/
|
isBaseLayer: true,
|
|
/**
|
* APIProperty: sphericalMercator
|
* Whether the tile extents should be set to the defaults for
|
* spherical mercator. Useful for things like OpenStreetMap.
|
* Default is false, except for the OSM subclass.
|
*/
|
sphericalMercator: false,
|
|
/**
|
* APIProperty: zoomOffset
|
* {Number} If your cache has more zoom levels than you want to provide
|
* access to with this layer, supply a zoomOffset. This zoom offset
|
* is added to the current map zoom level to determine the level
|
* for a requested tile. For example, if you supply a zoomOffset
|
* of 3, when the map is at the zoom 0, tiles will be requested from
|
* level 3 of your cache. Default is 0 (assumes cache level and map
|
* zoom are equivalent). Using <zoomOffset> is an alternative to
|
* setting <serverResolutions> if you only want to expose a subset
|
* of the server resolutions.
|
*/
|
zoomOffset: 0,
|
|
/**
|
* APIProperty: serverResolutions
|
* {Array} A list of all resolutions available on the server. Only set this
|
* property if the map resolutions differ from the server. This
|
* property serves two purposes. (a) <serverResolutions> can include
|
* resolutions that the server supports and that you don't want to
|
* provide with this layer; you can also look at <zoomOffset>, which is
|
* an alternative to <serverResolutions> for that specific purpose.
|
* (b) The map can work with resolutions that aren't supported by
|
* the server, i.e. that aren't in <serverResolutions>. When the
|
* map is displayed in such a resolution data for the closest
|
* server-supported resolution is loaded and the layer div is
|
* stretched as necessary.
|
*/
|
serverResolutions: null,
|
|
/**
|
* Constructor: OpenLayers.Layer.XYZ
|
*
|
* Parameters:
|
* name - {String}
|
* url - {String}
|
* options - {Object} Hashtable of extra options to tag onto the layer
|
*/
|
initialize: function(name, url, options) {
|
if (options && options.sphericalMercator || this.sphericalMercator) {
|
options = OpenLayers.Util.extend({
|
projection: "EPSG:900913",
|
numZoomLevels: 19
|
}, options);
|
}
|
OpenLayers.Layer.Grid.prototype.initialize.apply(this, [
|
name || this.name, url || this.url, {}, options
|
]);
|
},
|
|
/**
|
* APIMethod: clone
|
* Create a clone of this layer
|
*
|
* Parameters:
|
* obj - {Object} Is this ever used?
|
*
|
* Returns:
|
* {<OpenLayers.Layer.XYZ>} An exact clone of this OpenLayers.Layer.XYZ
|
*/
|
clone: function (obj) {
|
|
if (obj == null) {
|
obj = new OpenLayers.Layer.XYZ(this.name,
|
this.url,
|
this.getOptions());
|
}
|
|
//get all additions from superclasses
|
obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
|
|
return obj;
|
},
|
|
/**
|
* Method: getURL
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
*
|
* Returns:
|
* {String} A string with the layer's url and parameters and also the
|
* passed-in bounds and appropriate tile size specified as
|
* parameters
|
*/
|
getURL: function (bounds) {
|
var xyz = this.getXYZ(bounds);
|
var url = this.url;
|
if (OpenLayers.Util.isArray(url)) {
|
var s = '' + xyz.x + xyz.y + xyz.z;
|
url = this.selectUrl(s, url);
|
}
|
|
return OpenLayers.String.format(url, xyz);
|
},
|
|
/**
|
* Method: getXYZ
|
* Calculates x, y and z for the given bounds.
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
*
|
* Returns:
|
* {Object} - an object with x, y and z properties.
|
*/
|
getXYZ: function(bounds) {
|
var res = this.getServerResolution();
|
var x = Math.round((bounds.left - this.maxExtent.left) /
|
(res * this.tileSize.w));
|
var y = Math.round((this.maxExtent.top - bounds.top) /
|
(res * this.tileSize.h));
|
var z = this.getServerZoom();
|
|
if (this.wrapDateLine) {
|
var limit = Math.pow(2, z);
|
x = ((x % limit) + limit) % limit;
|
}
|
|
return {'x': x, 'y': y, 'z': z};
|
},
|
|
/* APIMethod: setMap
|
* When the layer is added to a map, then we can fetch our origin
|
* (if we don't have one.)
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
setMap: function(map) {
|
OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);
|
if (!this.tileOrigin) {
|
this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left,
|
this.maxExtent.bottom);
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.XYZ"
|
});
|
/* ======================================================================
|
OpenLayers/Layer/OSM.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Layer/XYZ.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.OSM
|
* This layer allows accessing OpenStreetMap tiles. By default the OpenStreetMap
|
* hosted tile.openstreetmap.org Mapnik tileset is used. If you wish to use
|
* a different layer instead, you need to provide a different
|
* URL to the constructor. Here's an example for using OpenCycleMap:
|
*
|
* (code)
|
* new OpenLayers.Layer.OSM("OpenCycleMap",
|
* ["http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png",
|
* "http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png",
|
* "http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"]);
|
* (end)
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer.XYZ>
|
*/
|
OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {
|
|
/**
|
* APIProperty: name
|
* {String} The layer name. Defaults to "OpenStreetMap" if the first
|
* argument to the constructor is null or undefined.
|
*/
|
name: "OpenStreetMap",
|
|
/**
|
* APIProperty: url
|
* {String} The tileset URL scheme. Defaults to
|
* : http://[a|b|c].tile.openstreetmap.org/${z}/${x}/${y}.png
|
* (the official OSM tileset) if the second argument to the constructor
|
* is null or undefined. To use another tileset you can have something
|
* like this:
|
* (code)
|
* new OpenLayers.Layer.OSM("OpenCycleMap",
|
* ["http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png",
|
* "http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png",
|
* "http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"]);
|
* (end)
|
*/
|
url: [
|
'http://a.tile.openstreetmap.org/${z}/${x}/${y}.png',
|
'http://b.tile.openstreetmap.org/${z}/${x}/${y}.png',
|
'http://c.tile.openstreetmap.org/${z}/${x}/${y}.png'
|
],
|
|
/**
|
* Property: attribution
|
* {String} The layer attribution.
|
*/
|
attribution: "© <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors",
|
|
/**
|
* Property: sphericalMercator
|
* {Boolean}
|
*/
|
sphericalMercator: true,
|
|
/**
|
* Property: wrapDateLine
|
* {Boolean}
|
*/
|
wrapDateLine: true,
|
|
/** APIProperty: tileOptions
|
* {Object} optional configuration options for <OpenLayers.Tile> instances
|
* created by this Layer. Default is
|
*
|
* (code)
|
* {crossOriginKeyword: 'anonymous'}
|
* (end)
|
*
|
* When using OSM tilesets other than the default ones, it may be
|
* necessary to set this to
|
*
|
* (code)
|
* {crossOriginKeyword: null}
|
* (end)
|
*
|
* if the server does not send Access-Control-Allow-Origin headers.
|
*/
|
tileOptions: null,
|
|
/**
|
* Constructor: OpenLayers.Layer.OSM
|
*
|
* Parameters:
|
* name - {String} The layer name.
|
* url - {String} The tileset URL scheme.
|
* options - {Object} Configuration options for the layer. Any inherited
|
* layer option can be set in this object (e.g.
|
* <OpenLayers.Layer.Grid.buffer>).
|
*/
|
initialize: function(name, url, options) {
|
OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments);
|
this.tileOptions = OpenLayers.Util.extend({
|
crossOriginKeyword: 'anonymous'
|
}, this.options && this.options.tileOptions);
|
},
|
|
/**
|
* Method: clone
|
*/
|
clone: function(obj) {
|
if (obj == null) {
|
obj = new OpenLayers.Layer.OSM(
|
this.name, this.url, this.getOptions());
|
}
|
obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);
|
return obj;
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.OSM"
|
});
|
/* ======================================================================
|
OpenLayers/Renderer.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
*/
|
|
/**
|
* Class: OpenLayers.Renderer
|
* This is the base class for all renderers.
|
*
|
* This is based on a merger code written by Paul Spencer and Bertil Chapuis.
|
* It is largely composed of virtual functions that are to be implemented
|
* in technology-specific subclasses, but there is some generic code too.
|
*
|
* The functions that *are* implemented here merely deal with the maintenance
|
* of the size and extent variables, as well as the cached 'resolution'
|
* value.
|
*
|
* A note to the user that all subclasses should use getResolution() instead
|
* of directly accessing this.resolution in order to correctly use the
|
* cacheing system.
|
*
|
*/
|
OpenLayers.Renderer = OpenLayers.Class({
|
|
/**
|
* Property: container
|
* {DOMElement}
|
*/
|
container: null,
|
|
/**
|
* Property: root
|
* {DOMElement}
|
*/
|
root: null,
|
|
/**
|
* Property: extent
|
* {<OpenLayers.Bounds>}
|
*/
|
extent: null,
|
|
/**
|
* Property: locked
|
* {Boolean} If the renderer is currently in a state where many things
|
* are changing, the 'locked' property is set to true. This means
|
* that renderers can expect at least one more drawFeature event to be
|
* called with the 'locked' property set to 'true': In some renderers,
|
* this might make sense to use as a 'only update local information'
|
* flag.
|
*/
|
locked: false,
|
|
/**
|
* Property: size
|
* {<OpenLayers.Size>}
|
*/
|
size: null,
|
|
/**
|
* Property: resolution
|
* {Float} cache of current map resolution
|
*/
|
resolution: null,
|
|
/**
|
* Property: map
|
* {<OpenLayers.Map>} Reference to the map -- this is set in Vector's setMap()
|
*/
|
map: null,
|
|
/**
|
* Property: featureDx
|
* {Number} Feature offset in x direction. Will be calculated for and
|
* applied to the current feature while rendering (see
|
* <calculateFeatureDx>).
|
*/
|
featureDx: 0,
|
|
/**
|
* Constructor: OpenLayers.Renderer
|
*
|
* Parameters:
|
* containerID - {<String>}
|
* options - {Object} options for this renderer. See sublcasses for
|
* supported options.
|
*/
|
initialize: function(containerID, options) {
|
this.container = OpenLayers.Util.getElement(containerID);
|
OpenLayers.Util.extend(this, options);
|
},
|
|
/**
|
* APIMethod: destroy
|
*/
|
destroy: function() {
|
this.container = null;
|
this.extent = null;
|
this.size = null;
|
this.resolution = null;
|
this.map = null;
|
},
|
|
/**
|
* APIMethod: supported
|
* This should be overridden by specific subclasses
|
*
|
* Returns:
|
* {Boolean} Whether or not the browser supports the renderer class
|
*/
|
supported: function() {
|
return false;
|
},
|
|
/**
|
* Method: setExtent
|
* Set the visible part of the layer.
|
*
|
* Resolution has probably changed, so we nullify the resolution
|
* cache (this.resolution) -- this way it will be re-computed when
|
* next it is needed.
|
* We nullify the resolution cache (this.resolution) if resolutionChanged
|
* is set to true - this way it will be re-computed on the next
|
* getResolution() request.
|
*
|
* Parameters:
|
* extent - {<OpenLayers.Bounds>}
|
* resolutionChanged - {Boolean}
|
*
|
* Returns:
|
* {Boolean} true to notify the layer that the new extent does not exceed
|
* the coordinate range, and the features will not need to be redrawn.
|
* False otherwise.
|
*/
|
setExtent: function(extent, resolutionChanged) {
|
this.extent = extent.clone();
|
if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
|
var ratio = extent.getWidth() / this.map.getExtent().getWidth(),
|
extent = extent.scale(1 / ratio);
|
this.extent = extent.wrapDateLine(this.map.getMaxExtent()).scale(ratio);
|
}
|
if (resolutionChanged) {
|
this.resolution = null;
|
}
|
return true;
|
},
|
|
/**
|
* Method: setSize
|
* Sets the size of the drawing surface.
|
*
|
* Resolution has probably changed, so we nullify the resolution
|
* cache (this.resolution) -- this way it will be re-computed when
|
* next it is needed.
|
*
|
* Parameters:
|
* size - {<OpenLayers.Size>}
|
*/
|
setSize: function(size) {
|
this.size = size.clone();
|
this.resolution = null;
|
},
|
|
/**
|
* Method: getResolution
|
* Uses cached copy of resolution if available to minimize computing
|
*
|
* Returns:
|
* {Float} The current map's resolution
|
*/
|
getResolution: function() {
|
this.resolution = this.resolution || this.map.getResolution();
|
return this.resolution;
|
},
|
|
/**
|
* Method: drawFeature
|
* Draw the feature. The optional style argument can be used
|
* to override the feature's own style. This method should only
|
* be called from layer.drawFeature().
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
* style - {<Object>}
|
*
|
* Returns:
|
* {Boolean} true if the feature has been drawn completely, false if not,
|
* undefined if the feature had no geometry
|
*/
|
drawFeature: function(feature, style) {
|
if(style == null) {
|
style = feature.style;
|
}
|
if (feature.geometry) {
|
var bounds = feature.geometry.getBounds();
|
if(bounds) {
|
var worldBounds;
|
if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
|
worldBounds = this.map.getMaxExtent();
|
}
|
if (!bounds.intersectsBounds(this.extent, {worldBounds: worldBounds})) {
|
style = {display: "none"};
|
} else {
|
this.calculateFeatureDx(bounds, worldBounds);
|
}
|
var rendered = this.drawGeometry(feature.geometry, style, feature.id);
|
if(style.display != "none" && style.label && rendered !== false) {
|
|
var location = feature.geometry.getCentroid();
|
if(style.labelXOffset || style.labelYOffset) {
|
var xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset;
|
var yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset;
|
var res = this.getResolution();
|
location.move(xOffset*res, yOffset*res);
|
}
|
this.drawText(feature.id, style, location);
|
} else {
|
this.removeText(feature.id);
|
}
|
return rendered;
|
}
|
}
|
},
|
|
/**
|
* Method: calculateFeatureDx
|
* {Number} Calculates the feature offset in x direction. Looking at the
|
* center of the feature bounds and the renderer extent, we calculate how
|
* many world widths the two are away from each other. This distance is
|
* used to shift the feature as close as possible to the center of the
|
* current enderer extent, which ensures that the feature is visible in the
|
* current viewport.
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>} Bounds of the feature
|
* worldBounds - {<OpenLayers.Bounds>} Bounds of the world
|
*/
|
calculateFeatureDx: function(bounds, worldBounds) {
|
this.featureDx = 0;
|
if (worldBounds) {
|
var worldWidth = worldBounds.getWidth(),
|
rendererCenterX = (this.extent.left + this.extent.right) / 2,
|
featureCenterX = (bounds.left + bounds.right) / 2,
|
worldsAway = Math.round((featureCenterX - rendererCenterX) / worldWidth);
|
this.featureDx = worldsAway * worldWidth;
|
}
|
},
|
|
/**
|
* Method: drawGeometry
|
*
|
* Draw a geometry. This should only be called from the renderer itself.
|
* Use layer.drawFeature() from outside the renderer.
|
* virtual function
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
* style - {Object}
|
* featureId - {<String>}
|
*/
|
drawGeometry: function(geometry, style, featureId) {},
|
|
/**
|
* Method: drawText
|
* Function for drawing text labels.
|
* This method is only called by the renderer itself.
|
*
|
* Parameters:
|
* featureId - {String}
|
* style -
|
* location - {<OpenLayers.Geometry.Point>}
|
*/
|
drawText: function(featureId, style, location) {},
|
|
/**
|
* Method: removeText
|
* Function for removing text labels.
|
* This method is only called by the renderer itself.
|
*
|
* Parameters:
|
* featureId - {String}
|
*/
|
removeText: function(featureId) {},
|
|
/**
|
* Method: clear
|
* Clear all vectors from the renderer.
|
* virtual function.
|
*/
|
clear: function() {},
|
|
/**
|
* Method: getFeatureIdFromEvent
|
* Returns a feature id from an event on the renderer.
|
* How this happens is specific to the renderer. This should be
|
* called from layer.getFeatureFromEvent().
|
* Virtual function.
|
*
|
* Parameters:
|
* evt - {<OpenLayers.Event>}
|
*
|
* Returns:
|
* {String} A feature id or undefined.
|
*/
|
getFeatureIdFromEvent: function(evt) {},
|
|
/**
|
* Method: eraseFeatures
|
* This is called by the layer to erase features
|
*
|
* Parameters:
|
* features - {Array(<OpenLayers.Feature.Vector>)}
|
*/
|
eraseFeatures: function(features) {
|
if(!(OpenLayers.Util.isArray(features))) {
|
features = [features];
|
}
|
for(var i=0, len=features.length; i<len; ++i) {
|
var feature = features[i];
|
this.eraseGeometry(feature.geometry, feature.id);
|
this.removeText(feature.id);
|
}
|
},
|
|
/**
|
* Method: eraseGeometry
|
* Remove a geometry from the renderer (by id).
|
* virtual function.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
* featureId - {String}
|
*/
|
eraseGeometry: function(geometry, featureId) {},
|
|
/**
|
* Method: moveRoot
|
* moves this renderer's root to a (different) renderer.
|
* To be implemented by subclasses that require a common renderer root for
|
* feature selection.
|
*
|
* Parameters:
|
* renderer - {<OpenLayers.Renderer>} target renderer for the moved root
|
*/
|
moveRoot: function(renderer) {},
|
|
/**
|
* Method: getRenderLayerId
|
* Gets the layer that this renderer's output appears on. If moveRoot was
|
* used, this will be different from the id of the layer containing the
|
* features rendered by this renderer.
|
*
|
* Returns:
|
* {String} the id of the output layer.
|
*/
|
getRenderLayerId: function() {
|
return this.container.id;
|
},
|
|
/**
|
* Method: applyDefaultSymbolizer
|
*
|
* Parameters:
|
* symbolizer - {Object}
|
*
|
* Returns:
|
* {Object}
|
*/
|
applyDefaultSymbolizer: function(symbolizer) {
|
var result = OpenLayers.Util.extend({},
|
OpenLayers.Renderer.defaultSymbolizer);
|
if(symbolizer.stroke === false) {
|
delete result.strokeWidth;
|
delete result.strokeColor;
|
}
|
if(symbolizer.fill === false) {
|
delete result.fillColor;
|
}
|
OpenLayers.Util.extend(result, symbolizer);
|
return result;
|
},
|
|
CLASS_NAME: "OpenLayers.Renderer"
|
});
|
|
/**
|
* Constant: OpenLayers.Renderer.defaultSymbolizer
|
* {Object} Properties from this symbolizer will be applied to symbolizers
|
* with missing properties. This can also be used to set a global
|
* symbolizer default in OpenLayers. To be SLD 1.x compliant, add the
|
* following code before rendering any vector features:
|
* (code)
|
* OpenLayers.Renderer.defaultSymbolizer = {
|
* fillColor: "#808080",
|
* fillOpacity: 1,
|
* strokeColor: "#000000",
|
* strokeOpacity: 1,
|
* strokeWidth: 1,
|
* pointRadius: 3,
|
* graphicName: "square"
|
* };
|
* (end)
|
*/
|
OpenLayers.Renderer.defaultSymbolizer = {
|
fillColor: "#000000",
|
strokeColor: "#000000",
|
strokeWidth: 2,
|
fillOpacity: 1,
|
strokeOpacity: 1,
|
pointRadius: 0,
|
labelAlign: 'cm'
|
};
|
|
|
|
/**
|
* Constant: OpenLayers.Renderer.symbol
|
* Coordinate arrays for well known (named) symbols.
|
*/
|
OpenLayers.Renderer.symbol = {
|
"star": [350,75, 379,161, 469,161, 397,215, 423,301, 350,250, 277,301,
|
303,215, 231,161, 321,161, 350,75],
|
"cross": [4,0, 6,0, 6,4, 10,4, 10,6, 6,6, 6,10, 4,10, 4,6, 0,6, 0,4, 4,4,
|
4,0],
|
"x": [0,0, 25,0, 50,35, 75,0, 100,0, 65,50, 100,100, 75,100, 50,65, 25,100, 0,100, 35,50, 0,0],
|
"square": [0,0, 0,1, 1,1, 1,0, 0,0],
|
"triangle": [0,10, 10,10, 5,0, 0,10]
|
};
|
/* ======================================================================
|
OpenLayers/Renderer/Canvas.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Renderer.js
|
*/
|
|
/**
|
* Class: OpenLayers.Renderer.Canvas
|
* A renderer based on the 2D 'canvas' drawing element.
|
*
|
* Inherits:
|
* - <OpenLayers.Renderer>
|
*/
|
OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {
|
|
/**
|
* APIProperty: hitDetection
|
* {Boolean} Allow for hit detection of features. Default is true.
|
*/
|
hitDetection: true,
|
|
/**
|
* Property: hitOverflow
|
* {Number} The method for converting feature identifiers to color values
|
* supports 16777215 sequential values. Two features cannot be
|
* predictably detected if their identifiers differ by more than this
|
* value. The hitOverflow allows for bigger numbers (but the
|
* difference in values is still limited).
|
*/
|
hitOverflow: 0,
|
|
/**
|
* Property: canvas
|
* {Canvas} The canvas context object.
|
*/
|
canvas: null,
|
|
/**
|
* Property: features
|
* {Object} Internal object of feature/style pairs for use in redrawing the layer.
|
*/
|
features: null,
|
|
/**
|
* Property: pendingRedraw
|
* {Boolean} The renderer needs a redraw call to render features added while
|
* the renderer was locked.
|
*/
|
pendingRedraw: false,
|
|
/**
|
* Property: cachedSymbolBounds
|
* {Object} Internal cache of calculated symbol extents.
|
*/
|
cachedSymbolBounds: {},
|
|
/**
|
* Constructor: OpenLayers.Renderer.Canvas
|
*
|
* Parameters:
|
* containerID - {<String>}
|
* options - {Object} Optional properties to be set on the renderer.
|
*/
|
initialize: function(containerID, options) {
|
OpenLayers.Renderer.prototype.initialize.apply(this, arguments);
|
this.root = document.createElement("canvas");
|
this.container.appendChild(this.root);
|
this.canvas = this.root.getContext("2d");
|
this.features = {};
|
if (this.hitDetection) {
|
this.hitCanvas = document.createElement("canvas");
|
this.hitContext = this.hitCanvas.getContext("2d");
|
}
|
},
|
|
/**
|
* Method: setExtent
|
* Set the visible part of the layer.
|
*
|
* Parameters:
|
* extent - {<OpenLayers.Bounds>}
|
* resolutionChanged - {Boolean}
|
*
|
* Returns:
|
* {Boolean} true to notify the layer that the new extent does not exceed
|
* the coordinate range, and the features will not need to be redrawn.
|
* False otherwise.
|
*/
|
setExtent: function() {
|
OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);
|
// always redraw features
|
return false;
|
},
|
|
/**
|
* Method: eraseGeometry
|
* Erase a geometry from the renderer. Because the Canvas renderer has
|
* 'memory' of the features that it has drawn, we have to remove the
|
* feature so it doesn't redraw.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
* featureId - {String}
|
*/
|
eraseGeometry: function(geometry, featureId) {
|
this.eraseFeatures(this.features[featureId][0]);
|
},
|
|
/**
|
* APIMethod: supported
|
*
|
* Returns:
|
* {Boolean} Whether or not the browser supports the renderer class
|
*/
|
supported: function() {
|
return OpenLayers.CANVAS_SUPPORTED;
|
},
|
|
/**
|
* Method: setSize
|
* Sets the size of the drawing surface.
|
*
|
* Once the size is updated, redraw the canvas.
|
*
|
* Parameters:
|
* size - {<OpenLayers.Size>}
|
*/
|
setSize: function(size) {
|
this.size = size.clone();
|
var root = this.root;
|
root.style.width = size.w + "px";
|
root.style.height = size.h + "px";
|
root.width = size.w;
|
root.height = size.h;
|
this.resolution = null;
|
if (this.hitDetection) {
|
var hitCanvas = this.hitCanvas;
|
hitCanvas.style.width = size.w + "px";
|
hitCanvas.style.height = size.h + "px";
|
hitCanvas.width = size.w;
|
hitCanvas.height = size.h;
|
}
|
},
|
|
/**
|
* Method: drawFeature
|
* Draw the feature. Stores the feature in the features list,
|
* then redraws the layer.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
* style - {<Object>}
|
*
|
* Returns:
|
* {Boolean} The feature has been drawn completely. If the feature has no
|
* geometry, undefined will be returned. If the feature is not rendered
|
* for other reasons, false will be returned.
|
*/
|
drawFeature: function(feature, style) {
|
var rendered;
|
if (feature.geometry) {
|
style = this.applyDefaultSymbolizer(style || feature.style);
|
// don't render if display none or feature outside extent
|
var bounds = feature.geometry.getBounds();
|
|
var worldBounds;
|
if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
|
worldBounds = this.map.getMaxExtent();
|
}
|
|
var intersects = bounds && bounds.intersectsBounds(this.extent, {worldBounds: worldBounds});
|
|
rendered = (style.display !== "none") && !!bounds && intersects;
|
if (rendered) {
|
// keep track of what we have rendered for redraw
|
this.features[feature.id] = [feature, style];
|
}
|
else {
|
// remove from features tracked for redraw
|
delete(this.features[feature.id]);
|
}
|
this.pendingRedraw = true;
|
}
|
if (this.pendingRedraw && !this.locked) {
|
this.redraw();
|
this.pendingRedraw = false;
|
}
|
return rendered;
|
},
|
|
/**
|
* Method: drawGeometry
|
* Used when looping (in redraw) over the features; draws
|
* the canvas.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
* style - {Object}
|
*/
|
drawGeometry: function(geometry, style, featureId) {
|
var className = geometry.CLASS_NAME;
|
if ((className == "OpenLayers.Geometry.Collection") ||
|
(className == "OpenLayers.Geometry.MultiPoint") ||
|
(className == "OpenLayers.Geometry.MultiLineString") ||
|
(className == "OpenLayers.Geometry.MultiPolygon")) {
|
for (var i = 0; i < geometry.components.length; i++) {
|
this.drawGeometry(geometry.components[i], style, featureId);
|
}
|
return;
|
}
|
switch (geometry.CLASS_NAME) {
|
case "OpenLayers.Geometry.Point":
|
this.drawPoint(geometry, style, featureId);
|
break;
|
case "OpenLayers.Geometry.LineString":
|
this.drawLineString(geometry, style, featureId);
|
break;
|
case "OpenLayers.Geometry.LinearRing":
|
this.drawLinearRing(geometry, style, featureId);
|
break;
|
case "OpenLayers.Geometry.Polygon":
|
this.drawPolygon(geometry, style, featureId);
|
break;
|
default:
|
break;
|
}
|
},
|
|
/**
|
* Method: drawExternalGraphic
|
* Called to draw External graphics.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
* style - {Object}
|
* featureId - {String}
|
*/
|
drawExternalGraphic: function(geometry, style, featureId) {
|
var img = new Image();
|
|
var title = style.title || style.graphicTitle;
|
if (title) {
|
img.title = title;
|
}
|
|
var width = style.graphicWidth || style.graphicHeight;
|
var height = style.graphicHeight || style.graphicWidth;
|
width = width ? width : style.pointRadius * 2;
|
height = height ? height : style.pointRadius * 2;
|
var xOffset = (style.graphicXOffset != undefined) ?
|
style.graphicXOffset : -(0.5 * width);
|
var yOffset = (style.graphicYOffset != undefined) ?
|
style.graphicYOffset : -(0.5 * height);
|
|
var opacity = style.graphicOpacity || style.fillOpacity;
|
|
var onLoad = function() {
|
if(!this.features[featureId]) {
|
return;
|
}
|
var pt = this.getLocalXY(geometry);
|
var p0 = pt[0];
|
var p1 = pt[1];
|
if(!isNaN(p0) && !isNaN(p1)) {
|
var x = (p0 + xOffset) | 0;
|
var y = (p1 + yOffset) | 0;
|
var canvas = this.canvas;
|
canvas.globalAlpha = opacity;
|
var factor = OpenLayers.Renderer.Canvas.drawImageScaleFactor ||
|
(OpenLayers.Renderer.Canvas.drawImageScaleFactor =
|
/android 2.1/.test(navigator.userAgent.toLowerCase()) ?
|
// 320 is the screen width of the G1 phone, for
|
// which drawImage works out of the box.
|
320 / window.screen.width : 1
|
);
|
canvas.drawImage(
|
img, x*factor, y*factor, width*factor, height*factor
|
);
|
if (this.hitDetection) {
|
this.setHitContextStyle("fill", featureId);
|
this.hitContext.fillRect(x, y, width, height);
|
}
|
}
|
};
|
|
img.onload = OpenLayers.Function.bind(onLoad, this);
|
img.src = style.externalGraphic;
|
},
|
|
/**
|
* Method: drawNamedSymbol
|
* Called to draw Well Known Graphic Symbol Name.
|
* This method is only called by the renderer itself.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
* style - {Object}
|
* featureId - {String}
|
*/
|
drawNamedSymbol: function(geometry, style, featureId) {
|
var x, y, cx, cy, i, symbolBounds, scaling, angle;
|
var unscaledStrokeWidth;
|
var deg2rad = Math.PI / 180.0;
|
|
var symbol = OpenLayers.Renderer.symbol[style.graphicName];
|
|
if (!symbol) {
|
throw new Error(style.graphicName + ' is not a valid symbol name');
|
}
|
|
if (!symbol.length || symbol.length < 2) return;
|
|
var pt = this.getLocalXY(geometry);
|
var p0 = pt[0];
|
var p1 = pt[1];
|
|
if (isNaN(p0) || isNaN(p1)) return;
|
|
// Use rounded line caps
|
this.canvas.lineCap = "round";
|
this.canvas.lineJoin = "round";
|
|
if (this.hitDetection) {
|
this.hitContext.lineCap = "round";
|
this.hitContext.lineJoin = "round";
|
}
|
|
// Scale and rotate symbols, using precalculated bounds whenever possible.
|
if (style.graphicName in this.cachedSymbolBounds) {
|
symbolBounds = this.cachedSymbolBounds[style.graphicName];
|
} else {
|
symbolBounds = new OpenLayers.Bounds();
|
for(i = 0; i < symbol.length; i+=2) {
|
symbolBounds.extend(new OpenLayers.LonLat(symbol[i], symbol[i+1]));
|
}
|
this.cachedSymbolBounds[style.graphicName] = symbolBounds;
|
}
|
|
// Push symbol scaling, translation and rotation onto the transformation stack in reverse order.
|
// Don't forget to apply all canvas transformations to the hitContext canvas as well(!)
|
this.canvas.save();
|
if (this.hitDetection) { this.hitContext.save(); }
|
|
// Step 3: place symbol at the desired location
|
this.canvas.translate(p0,p1);
|
if (this.hitDetection) { this.hitContext.translate(p0,p1); }
|
|
// Step 2a. rotate the symbol if necessary
|
angle = deg2rad * style.rotation; // will be NaN when style.rotation is undefined.
|
if (!isNaN(angle)) {
|
this.canvas.rotate(angle);
|
if (this.hitDetection) { this.hitContext.rotate(angle); }
|
}
|
|
// // Step 2: scale symbol such that pointRadius equals half the maximum symbol dimension.
|
scaling = 2.0 * style.pointRadius / Math.max(symbolBounds.getWidth(), symbolBounds.getHeight());
|
this.canvas.scale(scaling,scaling);
|
if (this.hitDetection) { this.hitContext.scale(scaling,scaling); }
|
|
// Step 1: center the symbol at the origin
|
cx = symbolBounds.getCenterLonLat().lon;
|
cy = symbolBounds.getCenterLonLat().lat;
|
this.canvas.translate(-cx,-cy);
|
if (this.hitDetection) { this.hitContext.translate(-cx,-cy); }
|
|
// Don't forget to scale stroke widths, because they are affected by canvas scale transformations as well(!)
|
// Alternative: scale symbol coordinates manually, so stroke width scaling is not needed anymore.
|
unscaledStrokeWidth = style.strokeWidth;
|
style.strokeWidth = unscaledStrokeWidth / scaling;
|
|
if (style.fill !== false) {
|
this.setCanvasStyle("fill", style);
|
this.canvas.beginPath();
|
for (i=0; i<symbol.length; i=i+2) {
|
x = symbol[i];
|
y = symbol[i+1];
|
if (i == 0) this.canvas.moveTo(x,y);
|
this.canvas.lineTo(x,y);
|
}
|
this.canvas.closePath();
|
this.canvas.fill();
|
|
if (this.hitDetection) {
|
this.setHitContextStyle("fill", featureId, style);
|
this.hitContext.beginPath();
|
for (i=0; i<symbol.length; i=i+2) {
|
x = symbol[i];
|
y = symbol[i+1];
|
if (i == 0) this.canvas.moveTo(x,y);
|
this.hitContext.lineTo(x,y);
|
}
|
this.hitContext.closePath();
|
this.hitContext.fill();
|
}
|
}
|
|
if (style.stroke !== false) {
|
this.setCanvasStyle("stroke", style);
|
this.canvas.beginPath();
|
for (i=0; i<symbol.length; i=i+2) {
|
x = symbol[i];
|
y = symbol[i+1];
|
if (i == 0) this.canvas.moveTo(x,y);
|
this.canvas.lineTo(x,y);
|
}
|
this.canvas.closePath();
|
this.canvas.stroke();
|
|
|
if (this.hitDetection) {
|
this.setHitContextStyle("stroke", featureId, style, scaling);
|
this.hitContext.beginPath();
|
for (i=0; i<symbol.length; i=i+2) {
|
x = symbol[i];
|
y = symbol[i+1];
|
if (i == 0) this.hitContext.moveTo(x,y);
|
this.hitContext.lineTo(x,y);
|
}
|
this.hitContext.closePath();
|
this.hitContext.stroke();
|
}
|
|
}
|
|
style.strokeWidth = unscaledStrokeWidth;
|
this.canvas.restore();
|
if (this.hitDetection) { this.hitContext.restore(); }
|
this.setCanvasStyle("reset");
|
},
|
|
/**
|
* Method: setCanvasStyle
|
* Prepare the canvas for drawing by setting various global settings.
|
*
|
* Parameters:
|
* type - {String} one of 'stroke', 'fill', or 'reset'
|
* style - {Object} Symbolizer hash
|
*/
|
setCanvasStyle: function(type, style) {
|
if (type === "fill") {
|
this.canvas.globalAlpha = style['fillOpacity'];
|
this.canvas.fillStyle = style['fillColor'];
|
} else if (type === "stroke") {
|
this.canvas.globalAlpha = style['strokeOpacity'];
|
this.canvas.strokeStyle = style['strokeColor'];
|
this.canvas.lineWidth = style['strokeWidth'];
|
} else {
|
this.canvas.globalAlpha = 0;
|
this.canvas.lineWidth = 1;
|
}
|
},
|
|
/**
|
* Method: featureIdToHex
|
* Convert a feature ID string into an RGB hex string.
|
*
|
* Parameters:
|
* featureId - {String} Feature id
|
*
|
* Returns:
|
* {String} RGB hex string.
|
*/
|
featureIdToHex: function(featureId) {
|
var id = Number(featureId.split("_").pop()) + 1; // zero for no feature
|
if (id >= 16777216) {
|
this.hitOverflow = id - 16777215;
|
id = id % 16777216 + 1;
|
}
|
var hex = "000000" + id.toString(16);
|
var len = hex.length;
|
hex = "#" + hex.substring(len-6, len);
|
return hex;
|
},
|
|
/**
|
* Method: setHitContextStyle
|
* Prepare the hit canvas for drawing by setting various global settings.
|
*
|
* Parameters:
|
* type - {String} one of 'stroke', 'fill', or 'reset'
|
* featureId - {String} The feature id.
|
* symbolizer - {<OpenLayers.Symbolizer>} The symbolizer.
|
*/
|
setHitContextStyle: function(type, featureId, symbolizer, strokeScaling) {
|
var hex = this.featureIdToHex(featureId);
|
if (type == "fill") {
|
this.hitContext.globalAlpha = 1.0;
|
this.hitContext.fillStyle = hex;
|
} else if (type == "stroke") {
|
this.hitContext.globalAlpha = 1.0;
|
this.hitContext.strokeStyle = hex;
|
// bump up stroke width to deal with antialiasing. If strokeScaling is defined, we're rendering a symbol
|
// on a transformed canvas, so the antialias width bump has to scale as well.
|
if (typeof strokeScaling === "undefined") {
|
this.hitContext.lineWidth = symbolizer.strokeWidth + 2;
|
} else {
|
if (!isNaN(strokeScaling)) { this.hitContext.lineWidth = symbolizer.strokeWidth + 2.0 / strokeScaling; }
|
}
|
} else {
|
this.hitContext.globalAlpha = 0;
|
this.hitContext.lineWidth = 1;
|
}
|
},
|
|
/**
|
* Method: drawPoint
|
* This method is only called by the renderer itself.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
* style - {Object}
|
* featureId - {String}
|
*/
|
drawPoint: function(geometry, style, featureId) {
|
if(style.graphic !== false) {
|
if(style.externalGraphic) {
|
this.drawExternalGraphic(geometry, style, featureId);
|
} else if (style.graphicName && (style.graphicName != "circle")) {
|
this.drawNamedSymbol(geometry, style, featureId);
|
} else {
|
var pt = this.getLocalXY(geometry);
|
var p0 = pt[0];
|
var p1 = pt[1];
|
if(!isNaN(p0) && !isNaN(p1)) {
|
var twoPi = Math.PI*2;
|
var radius = style.pointRadius;
|
if(style.fill !== false) {
|
this.setCanvasStyle("fill", style);
|
this.canvas.beginPath();
|
this.canvas.arc(p0, p1, radius, 0, twoPi, true);
|
this.canvas.fill();
|
if (this.hitDetection) {
|
this.setHitContextStyle("fill", featureId, style);
|
this.hitContext.beginPath();
|
this.hitContext.arc(p0, p1, radius, 0, twoPi, true);
|
this.hitContext.fill();
|
}
|
}
|
|
if(style.stroke !== false) {
|
this.setCanvasStyle("stroke", style);
|
this.canvas.beginPath();
|
this.canvas.arc(p0, p1, radius, 0, twoPi, true);
|
this.canvas.stroke();
|
if (this.hitDetection) {
|
this.setHitContextStyle("stroke", featureId, style);
|
this.hitContext.beginPath();
|
this.hitContext.arc(p0, p1, radius, 0, twoPi, true);
|
this.hitContext.stroke();
|
}
|
this.setCanvasStyle("reset");
|
}
|
}
|
}
|
}
|
},
|
|
/**
|
* Method: drawLineString
|
* This method is only called by the renderer itself.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
* style - {Object}
|
* featureId - {String}
|
*/
|
drawLineString: function(geometry, style, featureId) {
|
style = OpenLayers.Util.applyDefaults({fill: false}, style);
|
this.drawLinearRing(geometry, style, featureId);
|
},
|
|
/**
|
* Method: drawLinearRing
|
* This method is only called by the renderer itself.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
* style - {Object}
|
* featureId - {String}
|
*/
|
drawLinearRing: function(geometry, style, featureId) {
|
if (style.fill !== false) {
|
this.setCanvasStyle("fill", style);
|
this.renderPath(this.canvas, geometry, style, featureId, "fill");
|
if (this.hitDetection) {
|
this.setHitContextStyle("fill", featureId, style);
|
this.renderPath(this.hitContext, geometry, style, featureId, "fill");
|
}
|
}
|
if (style.stroke !== false) {
|
this.setCanvasStyle("stroke", style);
|
this.renderPath(this.canvas, geometry, style, featureId, "stroke");
|
if (this.hitDetection) {
|
this.setHitContextStyle("stroke", featureId, style);
|
this.renderPath(this.hitContext, geometry, style, featureId, "stroke");
|
}
|
}
|
this.setCanvasStyle("reset");
|
},
|
|
/**
|
* Method: renderPath
|
* Render a path with stroke and optional fill.
|
*/
|
renderPath: function(context, geometry, style, featureId, type) {
|
var components = geometry.components;
|
var len = components.length;
|
context.beginPath();
|
var start = this.getLocalXY(components[0]);
|
var x = start[0];
|
var y = start[1];
|
if (!isNaN(x) && !isNaN(y)) {
|
context.moveTo(start[0], start[1]);
|
for (var i=1; i<len; ++i) {
|
var pt = this.getLocalXY(components[i]);
|
context.lineTo(pt[0], pt[1]);
|
}
|
if (type === "fill") {
|
context.fill();
|
} else {
|
context.stroke();
|
}
|
}
|
},
|
|
/**
|
* Method: drawPolygon
|
* This method is only called by the renderer itself.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
* style - {Object}
|
* featureId - {String}
|
*/
|
drawPolygon: function(geometry, style, featureId) {
|
var components = geometry.components;
|
var len = components.length;
|
this.drawLinearRing(components[0], style, featureId);
|
// erase inner rings
|
for (var i=1; i<len; ++i) {
|
/**
|
* Note that this is overly agressive. Here we punch holes through
|
* all previously rendered features on the same canvas. A better
|
* solution for polygons with interior rings would be to draw the
|
* polygon on a sketch canvas first. We could erase all holes
|
* there and then copy the drawing to the layer canvas.
|
* TODO: http://trac.osgeo.org/openlayers/ticket/3130
|
*/
|
this.canvas.globalCompositeOperation = "destination-out";
|
if (this.hitDetection) {
|
this.hitContext.globalCompositeOperation = "destination-out";
|
}
|
this.drawLinearRing(
|
components[i],
|
OpenLayers.Util.applyDefaults({stroke: false, fillOpacity: 1.0}, style),
|
featureId
|
);
|
this.canvas.globalCompositeOperation = "source-over";
|
if (this.hitDetection) {
|
this.hitContext.globalCompositeOperation = "source-over";
|
}
|
this.drawLinearRing(
|
components[i],
|
OpenLayers.Util.applyDefaults({fill: false}, style),
|
featureId
|
);
|
}
|
},
|
|
/**
|
* Method: drawText
|
* This method is only called by the renderer itself.
|
*
|
* Parameters:
|
* location - {<OpenLayers.Point>}
|
* style - {Object}
|
*/
|
drawText: function(location, style) {
|
var pt = this.getLocalXY(location);
|
|
this.setCanvasStyle("reset");
|
this.canvas.fillStyle = style.fontColor;
|
this.canvas.globalAlpha = style.fontOpacity || 1.0;
|
var fontStyle = [style.fontStyle ? style.fontStyle : "normal",
|
"normal", // "font-variant" not supported
|
style.fontWeight ? style.fontWeight : "normal",
|
style.fontSize ? style.fontSize : "1em",
|
style.fontFamily ? style.fontFamily : "sans-serif"].join(" ");
|
var labelRows = style.label.split('\n');
|
var numRows = labelRows.length;
|
if (this.canvas.fillText) {
|
// HTML5
|
this.canvas.font = fontStyle;
|
this.canvas.textAlign =
|
OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[0]] ||
|
"center";
|
this.canvas.textBaseline =
|
OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[1]] ||
|
"middle";
|
var vfactor =
|
OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];
|
if (vfactor == null) {
|
vfactor = -.5;
|
}
|
var lineHeight =
|
this.canvas.measureText('Mg').height ||
|
this.canvas.measureText('xx').width;
|
pt[1] += lineHeight*vfactor*(numRows-1);
|
for (var i = 0; i < numRows; i++) {
|
if (style.labelOutlineWidth) {
|
this.canvas.save();
|
this.canvas.globalAlpha = style.labelOutlineOpacity || style.fontOpacity || 1.0;
|
this.canvas.strokeStyle = style.labelOutlineColor;
|
this.canvas.lineWidth = style.labelOutlineWidth;
|
this.canvas.strokeText(labelRows[i], pt[0], pt[1] + (lineHeight*i) + 1);
|
this.canvas.restore();
|
}
|
this.canvas.fillText(labelRows[i], pt[0], pt[1] + (lineHeight*i));
|
}
|
} else if (this.canvas.mozDrawText) {
|
// Mozilla pre-Gecko1.9.1 (<FF3.1)
|
this.canvas.mozTextStyle = fontStyle;
|
// No built-in text alignment, so we measure and adjust the position
|
var hfactor =
|
OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[0]];
|
if (hfactor == null) {
|
hfactor = -.5;
|
}
|
var vfactor =
|
OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];
|
if (vfactor == null) {
|
vfactor = -.5;
|
}
|
var lineHeight = this.canvas.mozMeasureText('xx');
|
pt[1] += lineHeight*(1 + (vfactor*numRows));
|
for (var i = 0; i < numRows; i++) {
|
var x = pt[0] + (hfactor*this.canvas.mozMeasureText(labelRows[i]));
|
var y = pt[1] + (i*lineHeight);
|
this.canvas.translate(x, y);
|
this.canvas.mozDrawText(labelRows[i]);
|
this.canvas.translate(-x, -y);
|
}
|
}
|
this.setCanvasStyle("reset");
|
},
|
|
/**
|
* Method: getLocalXY
|
* transform geographic xy into pixel xy
|
*
|
* Parameters:
|
* point - {<OpenLayers.Geometry.Point>}
|
*/
|
getLocalXY: function(point) {
|
var resolution = this.getResolution();
|
var extent = this.extent;
|
var x = ((point.x - this.featureDx) / resolution + (-extent.left / resolution));
|
var y = ((extent.top / resolution) - point.y / resolution);
|
return [x, y];
|
},
|
|
/**
|
* Method: clear
|
* Clear all vectors from the renderer.
|
*/
|
clear: function() {
|
var height = this.root.height;
|
var width = this.root.width;
|
this.canvas.clearRect(0, 0, width, height);
|
this.features = {};
|
if (this.hitDetection) {
|
this.hitContext.clearRect(0, 0, width, height);
|
}
|
},
|
|
/**
|
* Method: getFeatureIdFromEvent
|
* Returns a feature id from an event on the renderer.
|
*
|
* Parameters:
|
* evt - {<OpenLayers.Event>}
|
*
|
* Returns:
|
* {<OpenLayers.Feature.Vector} A feature or undefined. This method returns a
|
* feature instead of a feature id to avoid an unnecessary lookup on the
|
* layer.
|
*/
|
getFeatureIdFromEvent: function(evt) {
|
var featureId, feature;
|
|
if (this.hitDetection && this.root.style.display !== "none") {
|
// this dragging check should go in the feature handler
|
if (!this.map.dragging) {
|
var xy = evt.xy;
|
var x = xy.x | 0;
|
var y = xy.y | 0;
|
var data = this.hitContext.getImageData(x, y, 1, 1).data;
|
if (data[3] === 255) { // antialiased
|
var id = data[2] + (256 * (data[1] + (256 * data[0])));
|
if (id) {
|
featureId = "OpenLayers_Feature_Vector_" + (id - 1 + this.hitOverflow);
|
try {
|
feature = this.features[featureId][0];
|
} catch(err) {
|
// Because of antialiasing on the canvas, when the hit location is at a point where the edge of
|
// one symbol intersects the interior of another symbol, a wrong hit color (and therefore id) results.
|
// todo: set Antialiasing = 'off' on the hitContext as soon as browsers allow it.
|
}
|
}
|
}
|
}
|
}
|
return feature;
|
},
|
|
/**
|
* Method: eraseFeatures
|
* This is called by the layer to erase features; removes the feature from
|
* the list, then redraws the layer.
|
*
|
* Parameters:
|
* features - {Array(<OpenLayers.Feature.Vector>)}
|
*/
|
eraseFeatures: function(features) {
|
if(!(OpenLayers.Util.isArray(features))) {
|
features = [features];
|
}
|
for(var i=0; i<features.length; ++i) {
|
delete this.features[features[i].id];
|
}
|
this.redraw();
|
},
|
|
/**
|
* Method: redraw
|
* The real 'meat' of the function: any time things have changed,
|
* redraw() can be called to loop over all the data and (you guessed
|
* it) redraw it. Unlike Elements-based Renderers, we can't interact
|
* with things once they're drawn, to remove them, for example, so
|
* instead we have to just clear everything and draw from scratch.
|
*/
|
redraw: function() {
|
if (!this.locked) {
|
var height = this.root.height;
|
var width = this.root.width;
|
this.canvas.clearRect(0, 0, width, height);
|
if (this.hitDetection) {
|
this.hitContext.clearRect(0, 0, width, height);
|
}
|
var labelMap = [];
|
var feature, geometry, style;
|
var worldBounds = (this.map.baseLayer && this.map.baseLayer.wrapDateLine) && this.map.getMaxExtent();
|
for (var id in this.features) {
|
if (!this.features.hasOwnProperty(id)) { continue; }
|
feature = this.features[id][0];
|
geometry = feature.geometry;
|
this.calculateFeatureDx(geometry.getBounds(), worldBounds);
|
style = this.features[id][1];
|
this.drawGeometry(geometry, style, feature.id);
|
if(style.label) {
|
labelMap.push([feature, style]);
|
}
|
}
|
var item;
|
for (var i=0, len=labelMap.length; i<len; ++i) {
|
item = labelMap[i];
|
this.drawText(item[0].geometry.getCentroid(), item[1]);
|
}
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Renderer.Canvas"
|
});
|
|
/**
|
* Constant: OpenLayers.Renderer.Canvas.LABEL_ALIGN
|
* {Object}
|
*/
|
OpenLayers.Renderer.Canvas.LABEL_ALIGN = {
|
"l": "left",
|
"r": "right",
|
"t": "top",
|
"b": "bottom"
|
};
|
|
/**
|
* Constant: OpenLayers.Renderer.Canvas.LABEL_FACTOR
|
* {Object}
|
*/
|
OpenLayers.Renderer.Canvas.LABEL_FACTOR = {
|
"l": 0,
|
"r": -1,
|
"t": 0,
|
"b": -1
|
};
|
|
/**
|
* Constant: OpenLayers.Renderer.Canvas.drawImageScaleFactor
|
* {Number} Scale factor to apply to the canvas drawImage arguments. This
|
* is always 1 except for Android 2.1 devices, to work around
|
* http://code.google.com/p/android/issues/detail?id=5141.
|
*/
|
OpenLayers.Renderer.Canvas.drawImageScaleFactor = null;
|
/* ======================================================================
|
OpenLayers/Format/OSM.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML.js
|
* @requires OpenLayers/Feature/Vector.js
|
* @requires OpenLayers/Geometry/Point.js
|
* @requires OpenLayers/Geometry/LineString.js
|
* @requires OpenLayers/Geometry/Polygon.js
|
* @requires OpenLayers/Projection.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.OSM
|
* OSM parser. Create a new instance with the
|
* <OpenLayers.Format.OSM> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.OSM = OpenLayers.Class(OpenLayers.Format.XML, {
|
|
/**
|
* APIProperty: checkTags
|
* {Boolean} Should tags be checked to determine whether something
|
* should be treated as a seperate node. Will slow down parsing.
|
* Default is false.
|
*/
|
checkTags: false,
|
|
/**
|
* Property: interestingTagsExclude
|
* {Array} List of tags to exclude from 'interesting' checks on nodes.
|
* Must be set when creating the format. Will only be used if checkTags
|
* is set.
|
*/
|
interestingTagsExclude: null,
|
|
/**
|
* APIProperty: areaTags
|
* {Array} List of tags indicating that something is an area.
|
* Must be set when creating the format. Will only be used if
|
* checkTags is true.
|
*/
|
areaTags: null,
|
|
/**
|
* Constructor: OpenLayers.Format.OSM
|
* Create a new parser for OSM.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
initialize: function(options) {
|
var layer_defaults = {
|
'interestingTagsExclude': ['source', 'source_ref',
|
'source:ref', 'history', 'attribution', 'created_by'],
|
'areaTags': ['area', 'building', 'leisure', 'tourism', 'ruins',
|
'historic', 'landuse', 'military', 'natural', 'sport']
|
};
|
|
layer_defaults = OpenLayers.Util.extend(layer_defaults, options);
|
|
var interesting = {};
|
for (var i = 0; i < layer_defaults.interestingTagsExclude.length; i++) {
|
interesting[layer_defaults.interestingTagsExclude[i]] = true;
|
}
|
layer_defaults.interestingTagsExclude = interesting;
|
|
var area = {};
|
for (var i = 0; i < layer_defaults.areaTags.length; i++) {
|
area[layer_defaults.areaTags[i]] = true;
|
}
|
layer_defaults.areaTags = area;
|
|
// OSM coordinates are always in longlat WGS84
|
this.externalProjection = new OpenLayers.Projection("EPSG:4326");
|
|
OpenLayers.Format.XML.prototype.initialize.apply(this, [layer_defaults]);
|
},
|
|
/**
|
* APIMethod: read
|
* Return a list of features from a OSM doc
|
|
* Parameters:
|
* doc - {Element}
|
*
|
* Returns:
|
* Array({<OpenLayers.Feature.Vector>})
|
*/
|
read: function(doc) {
|
if (typeof doc == "string") {
|
doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);
|
}
|
|
var nodes = this.getNodes(doc);
|
var ways = this.getWays(doc);
|
|
// Geoms will contain at least ways.length entries.
|
var feat_list = new Array(ways.length);
|
|
for (var i = 0; i < ways.length; i++) {
|
// We know the minimal of this one ahead of time. (Could be -1
|
// due to areas/polygons)
|
var point_list = new Array(ways[i].nodes.length);
|
|
var poly = this.isWayArea(ways[i]) ? 1 : 0;
|
for (var j = 0; j < ways[i].nodes.length; j++) {
|
var node = nodes[ways[i].nodes[j]];
|
|
var point = new OpenLayers.Geometry.Point(node.lon, node.lat);
|
|
// Since OSM is topological, we stash the node ID internally.
|
point.osm_id = parseInt(ways[i].nodes[j]);
|
point_list[j] = point;
|
|
// We don't display nodes if they're used inside other
|
// elements.
|
node.used = true;
|
}
|
var geometry = null;
|
if (poly) {
|
geometry = new OpenLayers.Geometry.Polygon(
|
new OpenLayers.Geometry.LinearRing(point_list));
|
} else {
|
geometry = new OpenLayers.Geometry.LineString(point_list);
|
}
|
if (this.internalProjection && this.externalProjection) {
|
geometry.transform(this.externalProjection,
|
this.internalProjection);
|
}
|
var feat = new OpenLayers.Feature.Vector(geometry,
|
ways[i].tags);
|
feat.osm_id = parseInt(ways[i].id);
|
feat.fid = "way." + feat.osm_id;
|
feat_list[i] = feat;
|
}
|
for (var node_id in nodes) {
|
var node = nodes[node_id];
|
if (!node.used || this.checkTags) {
|
var tags = null;
|
|
if (this.checkTags) {
|
var result = this.getTags(node.node, true);
|
if (node.used && !result[1]) {
|
continue;
|
}
|
tags = result[0];
|
} else {
|
tags = this.getTags(node.node);
|
}
|
|
var feat = new OpenLayers.Feature.Vector(
|
new OpenLayers.Geometry.Point(node['lon'], node['lat']),
|
tags);
|
if (this.internalProjection && this.externalProjection) {
|
feat.geometry.transform(this.externalProjection,
|
this.internalProjection);
|
}
|
feat.osm_id = parseInt(node_id);
|
feat.fid = "node." + feat.osm_id;
|
feat_list.push(feat);
|
}
|
// Memory cleanup
|
node.node = null;
|
}
|
return feat_list;
|
},
|
|
/**
|
* Method: getNodes
|
* Return the node items from a doc.
|
*
|
* Parameters:
|
* doc - {DOMElement} node to parse tags from
|
*/
|
getNodes: function(doc) {
|
var node_list = doc.getElementsByTagName("node");
|
var nodes = {};
|
for (var i = 0; i < node_list.length; i++) {
|
var node = node_list[i];
|
var id = node.getAttribute("id");
|
nodes[id] = {
|
'lat': node.getAttribute("lat"),
|
'lon': node.getAttribute("lon"),
|
'node': node
|
};
|
}
|
return nodes;
|
},
|
|
/**
|
* Method: getWays
|
* Return the way items from a doc.
|
*
|
* Parameters:
|
* doc - {DOMElement} node to parse tags from
|
*/
|
getWays: function(doc) {
|
var way_list = doc.getElementsByTagName("way");
|
var return_ways = [];
|
for (var i = 0; i < way_list.length; i++) {
|
var way = way_list[i];
|
var way_object = {
|
id: way.getAttribute("id")
|
};
|
|
way_object.tags = this.getTags(way);
|
|
var node_list = way.getElementsByTagName("nd");
|
|
way_object.nodes = new Array(node_list.length);
|
|
for (var j = 0; j < node_list.length; j++) {
|
way_object.nodes[j] = node_list[j].getAttribute("ref");
|
}
|
return_ways.push(way_object);
|
}
|
return return_ways;
|
|
},
|
|
/**
|
* Method: getTags
|
* Return the tags list attached to a specific DOM element.
|
*
|
* Parameters:
|
* dom_node - {DOMElement} node to parse tags from
|
* interesting_tags - {Boolean} whether the return from this function should
|
* return a boolean indicating that it has 'interesting tags' --
|
* tags like attribution and source are ignored. (To change the list
|
* of tags, see interestingTagsExclude)
|
*
|
* Returns:
|
* tags - {Object} hash of tags
|
* interesting - {Boolean} if interesting_tags is passed, returns
|
* whether there are any interesting tags on this element.
|
*/
|
getTags: function(dom_node, interesting_tags) {
|
var tag_list = dom_node.getElementsByTagName("tag");
|
var tags = {};
|
var interesting = false;
|
for (var j = 0; j < tag_list.length; j++) {
|
var key = tag_list[j].getAttribute("k");
|
tags[key] = tag_list[j].getAttribute("v");
|
if (interesting_tags) {
|
if (!this.interestingTagsExclude[key]) {
|
interesting = true;
|
}
|
}
|
}
|
return interesting_tags ? [tags, interesting] : tags;
|
},
|
|
/**
|
* Method: isWayArea
|
* Given a way object from getWays, check whether the tags and geometry
|
* indicate something is an area.
|
*
|
* Returns:
|
* {Boolean}
|
*/
|
isWayArea: function(way) {
|
var poly_shaped = false;
|
var poly_tags = false;
|
|
if (way.nodes[0] == way.nodes[way.nodes.length - 1]) {
|
poly_shaped = true;
|
}
|
if (this.checkTags) {
|
for(var key in way.tags) {
|
if (this.areaTags[key]) {
|
poly_tags = true;
|
break;
|
}
|
}
|
}
|
return poly_shaped && (this.checkTags ? poly_tags : true);
|
},
|
|
/**
|
* APIMethod: write
|
* Takes a list of features, returns a serialized OSM format file for use
|
* in tools like JOSM.
|
*
|
* Parameters:
|
* features - {Array(<OpenLayers.Feature.Vector>)}
|
*/
|
write: function(features) {
|
if (!(OpenLayers.Util.isArray(features))) {
|
features = [features];
|
}
|
|
this.osm_id = 1;
|
this.created_nodes = {};
|
var root_node = this.createElementNS(null, "osm");
|
root_node.setAttribute("version", "0.5");
|
root_node.setAttribute("generator", "OpenLayers "+ OpenLayers.VERSION_NUMBER);
|
|
// Loop backwards, because the deserializer puts nodes last, and
|
// we want them first if possible
|
for(var i = features.length - 1; i >= 0; i--) {
|
var nodes = this.createFeatureNodes(features[i]);
|
for (var j = 0; j < nodes.length; j++) {
|
root_node.appendChild(nodes[j]);
|
}
|
}
|
return OpenLayers.Format.XML.prototype.write.apply(this, [root_node]);
|
},
|
|
/**
|
* Method: createFeatureNodes
|
* Takes a feature, returns a list of nodes from size 0->n.
|
* Will include all pieces of the serialization that are required which
|
* have not already been created. Calls out to createXML based on geometry
|
* type.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
*/
|
createFeatureNodes: function(feature) {
|
var nodes = [];
|
var className = feature.geometry.CLASS_NAME;
|
var type = className.substring(className.lastIndexOf(".") + 1);
|
type = type.toLowerCase();
|
var builder = this.createXML[type];
|
if (builder) {
|
nodes = builder.apply(this, [feature]);
|
}
|
return nodes;
|
},
|
|
/**
|
* Method: createXML
|
* Takes a feature, returns a list of nodes from size 0->n.
|
* Will include all pieces of the serialization that are required which
|
* have not already been created.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
*/
|
createXML: {
|
'point': function(point) {
|
var id = null;
|
var geometry = point.geometry ? point.geometry : point;
|
|
if (this.internalProjection && this.externalProjection) {
|
geometry = geometry.clone();
|
geometry.transform(this.internalProjection,
|
this.externalProjection);
|
}
|
|
var already_exists = false; // We don't return anything if the node
|
// has already been created
|
if (point.osm_id) {
|
id = point.osm_id;
|
if (this.created_nodes[id]) {
|
already_exists = true;
|
}
|
} else {
|
id = -this.osm_id;
|
this.osm_id++;
|
}
|
if (already_exists) {
|
node = this.created_nodes[id];
|
} else {
|
var node = this.createElementNS(null, "node");
|
}
|
this.created_nodes[id] = node;
|
node.setAttribute("id", id);
|
node.setAttribute("lon", geometry.x);
|
node.setAttribute("lat", geometry.y);
|
if (point.attributes) {
|
this.serializeTags(point, node);
|
}
|
this.setState(point, node);
|
return already_exists ? [] : [node];
|
},
|
linestring: function(feature) {
|
var id;
|
var nodes = [];
|
var geometry = feature.geometry;
|
if (feature.osm_id) {
|
id = feature.osm_id;
|
} else {
|
id = -this.osm_id;
|
this.osm_id++;
|
}
|
var way = this.createElementNS(null, "way");
|
way.setAttribute("id", id);
|
for (var i = 0; i < geometry.components.length; i++) {
|
var node = this.createXML['point'].apply(this, [geometry.components[i]]);
|
if (node.length) {
|
node = node[0];
|
var node_ref = node.getAttribute("id");
|
nodes.push(node);
|
} else {
|
node_ref = geometry.components[i].osm_id;
|
node = this.created_nodes[node_ref];
|
}
|
this.setState(feature, node);
|
var nd_dom = this.createElementNS(null, "nd");
|
nd_dom.setAttribute("ref", node_ref);
|
way.appendChild(nd_dom);
|
}
|
this.serializeTags(feature, way);
|
nodes.push(way);
|
|
return nodes;
|
},
|
polygon: function(feature) {
|
var attrs = OpenLayers.Util.extend({'area':'yes'}, feature.attributes);
|
var feat = new OpenLayers.Feature.Vector(feature.geometry.components[0], attrs);
|
feat.osm_id = feature.osm_id;
|
return this.createXML['linestring'].apply(this, [feat]);
|
}
|
},
|
|
/**
|
* Method: serializeTags
|
* Given a feature, serialize the attributes onto the given node.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
* node - {DOMNode}
|
*/
|
serializeTags: function(feature, node) {
|
for (var key in feature.attributes) {
|
var tag = this.createElementNS(null, "tag");
|
tag.setAttribute("k", key);
|
tag.setAttribute("v", feature.attributes[key]);
|
node.appendChild(tag);
|
}
|
},
|
|
/**
|
* Method: setState
|
* OpenStreetMap has a convention that 'state' is stored for modification or deletion.
|
* This allows the file to be uploaded via JOSM or the bulk uploader tool.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
* node - {DOMNode}
|
*/
|
setState: function(feature, node) {
|
if (feature.state) {
|
var state = null;
|
switch(feature.state) {
|
case OpenLayers.State.UPDATE:
|
state = "modify";
|
case OpenLayers.State.DELETE:
|
state = "delete";
|
}
|
if (state) {
|
node.setAttribute("action", state);
|
}
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Format.OSM"
|
});
|
/* ======================================================================
|
OpenLayers/Handler/Keyboard.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Handler.js
|
* @requires OpenLayers/Events.js
|
*/
|
|
/**
|
* Class: OpenLayers.handler.Keyboard
|
* A handler for keyboard events. Create a new instance with the
|
* <OpenLayers.Handler.Keyboard> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Handler>
|
*/
|
OpenLayers.Handler.Keyboard = OpenLayers.Class(OpenLayers.Handler, {
|
|
/* http://www.quirksmode.org/js/keys.html explains key x-browser
|
key handling quirks in pretty nice detail */
|
|
/**
|
* Constant: KEY_EVENTS
|
* keydown, keypress, keyup
|
*/
|
KEY_EVENTS: ["keydown", "keyup"],
|
|
/**
|
* Property: eventListener
|
* {Function}
|
*/
|
eventListener: null,
|
|
/**
|
* Property: observeElement
|
* {DOMElement|String} The DOM element on which we listen for
|
* key events. Default to the document.
|
*/
|
observeElement: null,
|
|
/**
|
* Constructor: OpenLayers.Handler.Keyboard
|
* Returns a new keyboard handler.
|
*
|
* Parameters:
|
* control - {<OpenLayers.Control>} The control that is making use of
|
* this handler. If a handler is being used without a control, the
|
* handlers setMap method must be overridden to deal properly with
|
* the map.
|
* callbacks - {Object} An object containing a single function to be
|
* called when the drag operation is finished. The callback should
|
* expect to recieve a single argument, the pixel location of the event.
|
* Callbacks for 'keydown', 'keypress', and 'keyup' are supported.
|
* options - {Object} Optional object whose properties will be set on the
|
* handler.
|
*/
|
initialize: function(control, callbacks, options) {
|
OpenLayers.Handler.prototype.initialize.apply(this, arguments);
|
// cache the bound event listener method so it can be unobserved later
|
this.eventListener = OpenLayers.Function.bindAsEventListener(
|
this.handleKeyEvent, this
|
);
|
},
|
|
/**
|
* Method: destroy
|
*/
|
destroy: function() {
|
this.deactivate();
|
this.eventListener = null;
|
OpenLayers.Handler.prototype.destroy.apply(this, arguments);
|
},
|
|
/**
|
* Method: activate
|
*/
|
activate: function() {
|
if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
|
this.observeElement = this.observeElement || document;
|
for (var i=0, len=this.KEY_EVENTS.length; i<len; i++) {
|
OpenLayers.Event.observe(
|
this.observeElement, this.KEY_EVENTS[i], this.eventListener);
|
}
|
return true;
|
} else {
|
return false;
|
}
|
},
|
|
/**
|
* Method: deactivate
|
*/
|
deactivate: function() {
|
var deactivated = false;
|
if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
|
for (var i=0, len=this.KEY_EVENTS.length; i<len; i++) {
|
OpenLayers.Event.stopObserving(
|
this.observeElement, this.KEY_EVENTS[i], this.eventListener);
|
}
|
deactivated = true;
|
}
|
return deactivated;
|
},
|
|
/**
|
* Method: handleKeyEvent
|
*/
|
handleKeyEvent: function (evt) {
|
if (this.checkModifiers(evt)) {
|
this.callback(evt.type, [evt]);
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Handler.Keyboard"
|
});
|
/* ======================================================================
|
OpenLayers/Control/ModifyFeature.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/Handler/Drag.js
|
* @requires OpenLayers/Handler/Keyboard.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.ModifyFeature
|
* Control to modify features. When activated, a click renders the vertices
|
* of a feature - these vertices can then be dragged. By default, the
|
* delete key will delete the vertex under the mouse. New features are
|
* added by dragging "virtual vertices" between vertices. Create a new
|
* control with the <OpenLayers.Control.ModifyFeature> constructor.
|
*
|
* Inherits From:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* APIProperty: documentDrag
|
* {Boolean} If set to true, dragging vertices will continue even if the
|
* mouse cursor leaves the map viewport. Default is false.
|
*/
|
documentDrag: false,
|
|
/**
|
* APIProperty: geometryTypes
|
* {Array(String)} To restrict modification to a limited set of geometry
|
* types, send a list of strings corresponding to the geometry class
|
* names.
|
*/
|
geometryTypes: null,
|
|
/**
|
* APIProperty: clickout
|
* {Boolean} Unselect features when clicking outside any feature.
|
* Default is true.
|
*/
|
clickout: true,
|
|
/**
|
* APIProperty: toggle
|
* {Boolean} Unselect a selected feature on click.
|
* Default is true.
|
*/
|
toggle: true,
|
|
/**
|
* APIProperty: standalone
|
* {Boolean} Set to true to create a control without SelectFeature
|
* capabilities. Default is false. If standalone is true, to modify
|
* a feature, call the <selectFeature> method with the target feature.
|
* Note that you must call the <unselectFeature> method to finish
|
* feature modification in standalone mode (before starting to modify
|
* another feature).
|
*/
|
standalone: false,
|
|
/**
|
* Property: layer
|
* {<OpenLayers.Layer.Vector>}
|
*/
|
layer: null,
|
|
/**
|
* Property: feature
|
* {<OpenLayers.Feature.Vector>} Feature currently available for modification.
|
*/
|
feature: null,
|
|
/**
|
* Property: vertex
|
* {<OpenLayers.Feature.Vector>} Vertex currently being modified.
|
*/
|
vertex: null,
|
|
/**
|
* Property: vertices
|
* {Array(<OpenLayers.Feature.Vector>)} Verticies currently available
|
* for dragging.
|
*/
|
vertices: null,
|
|
/**
|
* Property: virtualVertices
|
* {Array(<OpenLayers.Feature.Vector>)} Virtual vertices in the middle
|
* of each edge.
|
*/
|
virtualVertices: null,
|
|
/**
|
* Property: handlers
|
* {Object}
|
*/
|
handlers: null,
|
|
/**
|
* APIProperty: deleteCodes
|
* {Array(Integer)} Keycodes for deleting verticies. Set to null to disable
|
* vertex deltion by keypress. If non-null, keypresses with codes
|
* in this array will delete vertices under the mouse. Default
|
* is 46 and 68, the 'delete' and lowercase 'd' keys.
|
*/
|
deleteCodes: null,
|
|
/**
|
* APIProperty: virtualStyle
|
* {Object} A symbolizer to be used for virtual vertices.
|
*/
|
virtualStyle: null,
|
|
/**
|
* APIProperty: vertexRenderIntent
|
* {String} The renderIntent to use for vertices. If no <virtualStyle> is
|
* provided, this renderIntent will also be used for virtual vertices, with
|
* a fillOpacity and strokeOpacity of 0.3. Default is null, which means
|
* that the layer's default style will be used for vertices.
|
*/
|
vertexRenderIntent: null,
|
|
/**
|
* APIProperty: mode
|
* {Integer} Bitfields specifying the modification mode. Defaults to
|
* OpenLayers.Control.ModifyFeature.RESHAPE. To set the mode to a
|
* combination of options, use the | operator. For example, to allow
|
* the control to both resize and rotate features, use the following
|
* syntax
|
* (code)
|
* control.mode = OpenLayers.Control.ModifyFeature.RESIZE |
|
* OpenLayers.Control.ModifyFeature.ROTATE;
|
* (end)
|
*/
|
mode: null,
|
|
/**
|
* APIProperty: createVertices
|
* {Boolean} Create new vertices by dragging the virtual vertices
|
* in the middle of each edge. Default is true.
|
*/
|
createVertices: true,
|
|
/**
|
* Property: modified
|
* {Boolean} The currently selected feature has been modified.
|
*/
|
modified: false,
|
|
/**
|
* Property: radiusHandle
|
* {<OpenLayers.Feature.Vector>} A handle for rotating/resizing a feature.
|
*/
|
radiusHandle: null,
|
|
/**
|
* Property: dragHandle
|
* {<OpenLayers.Feature.Vector>} A handle for dragging a feature.
|
*/
|
dragHandle: null,
|
|
/**
|
* APIProperty: onModificationStart
|
* {Function} *Deprecated*. Register for "beforefeaturemodified" instead.
|
* The "beforefeaturemodified" event is triggered on the layer before
|
* any modification begins.
|
*
|
* Optional function to be called when a feature is selected
|
* to be modified. The function should expect to be called with a
|
* feature. This could be used for example to allow to lock the
|
* feature on server-side.
|
*/
|
onModificationStart: function() {},
|
|
/**
|
* APIProperty: onModification
|
* {Function} *Deprecated*. Register for "featuremodified" instead.
|
* The "featuremodified" event is triggered on the layer with each
|
* feature modification.
|
*
|
* Optional function to be called when a feature has been
|
* modified. The function should expect to be called with a feature.
|
*/
|
onModification: function() {},
|
|
/**
|
* APIProperty: onModificationEnd
|
* {Function} *Deprecated*. Register for "afterfeaturemodified" instead.
|
* The "afterfeaturemodified" event is triggered on the layer after
|
* a feature has been modified.
|
*
|
* Optional function to be called when a feature is finished
|
* being modified. The function should expect to be called with a
|
* feature.
|
*/
|
onModificationEnd: function() {},
|
|
/**
|
* Constructor: OpenLayers.Control.ModifyFeature
|
* Create a new modify feature control.
|
*
|
* Parameters:
|
* layer - {<OpenLayers.Layer.Vector>} Layer that contains features that
|
* will be modified.
|
* options - {Object} Optional object whose properties will be set on the
|
* control.
|
*/
|
initialize: function(layer, options) {
|
options = options || {};
|
this.layer = layer;
|
this.vertices = [];
|
this.virtualVertices = [];
|
this.virtualStyle = OpenLayers.Util.extend({},
|
this.layer.style ||
|
this.layer.styleMap.createSymbolizer(null, options.vertexRenderIntent)
|
);
|
this.virtualStyle.fillOpacity = 0.3;
|
this.virtualStyle.strokeOpacity = 0.3;
|
this.deleteCodes = [46, 68];
|
this.mode = OpenLayers.Control.ModifyFeature.RESHAPE;
|
OpenLayers.Control.prototype.initialize.apply(this, [options]);
|
if(!(OpenLayers.Util.isArray(this.deleteCodes))) {
|
this.deleteCodes = [this.deleteCodes];
|
}
|
|
// configure the drag handler
|
var dragCallbacks = {
|
down: function(pixel) {
|
this.vertex = null;
|
var feature = this.layer.getFeatureFromEvent(
|
this.handlers.drag.evt);
|
if (feature) {
|
this.dragStart(feature);
|
} else if (this.clickout) {
|
this._unselect = this.feature;
|
}
|
},
|
move: function(pixel) {
|
delete this._unselect;
|
if (this.vertex) {
|
this.dragVertex(this.vertex, pixel);
|
}
|
},
|
up: function() {
|
this.handlers.drag.stopDown = false;
|
if (this._unselect) {
|
this.unselectFeature(this._unselect);
|
delete this._unselect;
|
}
|
},
|
done: function(pixel) {
|
if (this.vertex) {
|
this.dragComplete(this.vertex);
|
}
|
}
|
};
|
var dragOptions = {
|
documentDrag: this.documentDrag,
|
stopDown: false
|
};
|
|
// configure the keyboard handler
|
var keyboardOptions = {
|
keydown: this.handleKeypress
|
};
|
this.handlers = {
|
keyboard: new OpenLayers.Handler.Keyboard(this, keyboardOptions),
|
drag: new OpenLayers.Handler.Drag(this, dragCallbacks, dragOptions)
|
};
|
},
|
|
/**
|
* APIMethod: destroy
|
* Take care of things that are not handled in superclass.
|
*/
|
destroy: function() {
|
if (this.map) {
|
this.map.events.un({
|
"removelayer": this.handleMapEvents,
|
"changelayer": this.handleMapEvents,
|
scope: this
|
});
|
}
|
this.layer = null;
|
OpenLayers.Control.prototype.destroy.apply(this, []);
|
},
|
|
/**
|
* APIMethod: activate
|
* Activate the control.
|
*
|
* Returns:
|
* {Boolean} Successfully activated the control.
|
*/
|
activate: function() {
|
this.moveLayerToTop();
|
this.map.events.on({
|
"removelayer": this.handleMapEvents,
|
"changelayer": this.handleMapEvents,
|
scope: this
|
});
|
return (this.handlers.keyboard.activate() &&
|
this.handlers.drag.activate() &&
|
OpenLayers.Control.prototype.activate.apply(this, arguments));
|
},
|
|
/**
|
* APIMethod: deactivate
|
* Deactivate the control.
|
*
|
* Returns:
|
* {Boolean} Successfully deactivated the control.
|
*/
|
deactivate: function() {
|
var deactivated = false;
|
// the return from the controls is unimportant in this case
|
if(OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {
|
this.moveLayerBack();
|
this.map.events.un({
|
"removelayer": this.handleMapEvents,
|
"changelayer": this.handleMapEvents,
|
scope: this
|
});
|
this.layer.removeFeatures(this.vertices, {silent: true});
|
this.layer.removeFeatures(this.virtualVertices, {silent: true});
|
this.vertices = [];
|
this.handlers.drag.deactivate();
|
this.handlers.keyboard.deactivate();
|
var feature = this.feature;
|
if (feature && feature.geometry && feature.layer) {
|
this.unselectFeature(feature);
|
}
|
deactivated = true;
|
}
|
return deactivated;
|
},
|
|
/**
|
* Method: beforeSelectFeature
|
* Called before a feature is selected.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>} The feature about to be selected.
|
*/
|
beforeSelectFeature: function(feature) {
|
return this.layer.events.triggerEvent(
|
"beforefeaturemodified", {feature: feature}
|
);
|
},
|
|
/**
|
* APIMethod: selectFeature
|
* Select a feature for modification in standalone mode. In non-standalone
|
* mode, this method is called when a feature is selected by clicking.
|
* Register a listener to the beforefeaturemodified event and return false
|
* to prevent feature modification.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>} the selected feature.
|
*/
|
selectFeature: function(feature) {
|
if (this.feature === feature ||
|
(this.geometryTypes && OpenLayers.Util.indexOf(this.geometryTypes,
|
feature.geometry.CLASS_NAME) == -1)) {
|
return;
|
}
|
if (this.beforeSelectFeature(feature) !== false) {
|
if (this.feature) {
|
this.unselectFeature(this.feature);
|
}
|
this.feature = feature;
|
this.layer.selectedFeatures.push(feature);
|
this.layer.drawFeature(feature, 'select');
|
this.modified = false;
|
this.resetVertices();
|
this.onModificationStart(this.feature);
|
}
|
// keep track of geometry modifications
|
var modified = feature.modified;
|
if (feature.geometry && !(modified && modified.geometry)) {
|
this._originalGeometry = feature.geometry.clone();
|
}
|
},
|
|
/**
|
* APIMethod: unselectFeature
|
* Called when the select feature control unselects a feature.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>} The unselected feature.
|
*/
|
unselectFeature: function(feature) {
|
this.layer.removeFeatures(this.vertices, {silent: true});
|
this.vertices = [];
|
this.layer.destroyFeatures(this.virtualVertices, {silent: true});
|
this.virtualVertices = [];
|
if(this.dragHandle) {
|
this.layer.destroyFeatures([this.dragHandle], {silent: true});
|
delete this.dragHandle;
|
}
|
if(this.radiusHandle) {
|
this.layer.destroyFeatures([this.radiusHandle], {silent: true});
|
delete this.radiusHandle;
|
}
|
this.layer.drawFeature(this.feature, 'default');
|
this.feature = null;
|
OpenLayers.Util.removeItem(this.layer.selectedFeatures, feature);
|
this.onModificationEnd(feature);
|
this.layer.events.triggerEvent("afterfeaturemodified", {
|
feature: feature,
|
modified: this.modified
|
});
|
this.modified = false;
|
},
|
|
|
/**
|
* Method: dragStart
|
* Called by the drag handler before a feature is dragged. This method is
|
* used to differentiate between points and vertices
|
* of higher order geometries.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>} The point or vertex about to be
|
* dragged.
|
*/
|
dragStart: function(feature) {
|
var isPoint = feature.geometry.CLASS_NAME ==
|
'OpenLayers.Geometry.Point';
|
if (!this.standalone &&
|
((!feature._sketch && isPoint) || !feature._sketch)) {
|
if (this.toggle && this.feature === feature) {
|
// mark feature for unselection
|
this._unselect = feature;
|
}
|
this.selectFeature(feature);
|
}
|
if (feature._sketch || isPoint) {
|
// feature is a drag or virtual handle or point
|
this.vertex = feature;
|
this.handlers.drag.stopDown = true;
|
}
|
},
|
|
/**
|
* Method: dragVertex
|
* Called by the drag handler with each drag move of a vertex.
|
*
|
* Parameters:
|
* vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged.
|
* pixel - {<OpenLayers.Pixel>} Pixel location of the mouse event.
|
*/
|
dragVertex: function(vertex, pixel) {
|
var pos = this.map.getLonLatFromViewPortPx(pixel);
|
var geom = vertex.geometry;
|
geom.move(pos.lon - geom.x, pos.lat - geom.y);
|
this.modified = true;
|
/**
|
* Five cases:
|
* 1) dragging a simple point
|
* 2) dragging a virtual vertex
|
* 3) dragging a drag handle
|
* 4) dragging a real vertex
|
* 5) dragging a radius handle
|
*/
|
if(this.feature.geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
|
// dragging a simple point
|
this.layer.events.triggerEvent("vertexmodified", {
|
vertex: vertex.geometry,
|
feature: this.feature,
|
pixel: pixel
|
});
|
} else {
|
if(vertex._index) {
|
// dragging a virtual vertex
|
vertex.geometry.parent.addComponent(vertex.geometry,
|
vertex._index);
|
// move from virtual to real vertex
|
delete vertex._index;
|
OpenLayers.Util.removeItem(this.virtualVertices, vertex);
|
this.vertices.push(vertex);
|
} else if(vertex == this.dragHandle) {
|
// dragging a drag handle
|
this.layer.removeFeatures(this.vertices, {silent: true});
|
this.vertices = [];
|
if(this.radiusHandle) {
|
this.layer.destroyFeatures([this.radiusHandle], {silent: true});
|
this.radiusHandle = null;
|
}
|
} else if(vertex !== this.radiusHandle) {
|
// dragging a real vertex
|
this.layer.events.triggerEvent("vertexmodified", {
|
vertex: vertex.geometry,
|
feature: this.feature,
|
pixel: pixel
|
});
|
}
|
// dragging a radius handle - no special treatment
|
if(this.virtualVertices.length > 0) {
|
this.layer.destroyFeatures(this.virtualVertices, {silent: true});
|
this.virtualVertices = [];
|
}
|
this.layer.drawFeature(this.feature, this.standalone ? undefined :
|
'select');
|
}
|
// keep the vertex on top so it gets the mouseout after dragging
|
// this should be removed in favor of an option to draw under or
|
// maintain node z-index
|
this.layer.drawFeature(vertex);
|
},
|
|
/**
|
* Method: dragComplete
|
* Called by the drag handler when the feature dragging is complete.
|
*
|
* Parameters:
|
* vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged.
|
*/
|
dragComplete: function(vertex) {
|
this.resetVertices();
|
this.setFeatureState();
|
this.onModification(this.feature);
|
this.layer.events.triggerEvent("featuremodified",
|
{feature: this.feature});
|
},
|
|
/**
|
* Method: setFeatureState
|
* Called when the feature is modified. If the current state is not
|
* INSERT or DELETE, the state is set to UPDATE.
|
*/
|
setFeatureState: function() {
|
if(this.feature.state != OpenLayers.State.INSERT &&
|
this.feature.state != OpenLayers.State.DELETE) {
|
this.feature.state = OpenLayers.State.UPDATE;
|
if (this.modified && this._originalGeometry) {
|
var feature = this.feature;
|
feature.modified = OpenLayers.Util.extend(feature.modified, {
|
geometry: this._originalGeometry
|
});
|
delete this._originalGeometry;
|
}
|
}
|
},
|
|
/**
|
* Method: resetVertices
|
*/
|
resetVertices: function() {
|
if(this.vertices.length > 0) {
|
this.layer.removeFeatures(this.vertices, {silent: true});
|
this.vertices = [];
|
}
|
if(this.virtualVertices.length > 0) {
|
this.layer.removeFeatures(this.virtualVertices, {silent: true});
|
this.virtualVertices = [];
|
}
|
if(this.dragHandle) {
|
this.layer.destroyFeatures([this.dragHandle], {silent: true});
|
this.dragHandle = null;
|
}
|
if(this.radiusHandle) {
|
this.layer.destroyFeatures([this.radiusHandle], {silent: true});
|
this.radiusHandle = null;
|
}
|
if(this.feature &&
|
this.feature.geometry.CLASS_NAME != "OpenLayers.Geometry.Point") {
|
if((this.mode & OpenLayers.Control.ModifyFeature.DRAG)) {
|
this.collectDragHandle();
|
}
|
if((this.mode & (OpenLayers.Control.ModifyFeature.ROTATE |
|
OpenLayers.Control.ModifyFeature.RESIZE))) {
|
this.collectRadiusHandle();
|
}
|
if(this.mode & OpenLayers.Control.ModifyFeature.RESHAPE){
|
// Don't collect vertices when we're resizing
|
if (!(this.mode & OpenLayers.Control.ModifyFeature.RESIZE)){
|
this.collectVertices();
|
}
|
}
|
}
|
},
|
|
/**
|
* Method: handleKeypress
|
* Called by the feature handler on keypress. This is used to delete
|
* vertices. If the <deleteCode> property is set, vertices will
|
* be deleted when a feature is selected for modification and
|
* the mouse is over a vertex.
|
*
|
* Parameters:
|
* evt - {Event} Keypress event.
|
*/
|
handleKeypress: function(evt) {
|
var code = evt.keyCode;
|
|
// check for delete key
|
if(this.feature &&
|
OpenLayers.Util.indexOf(this.deleteCodes, code) != -1) {
|
var vertex = this.layer.getFeatureFromEvent(this.handlers.drag.evt);
|
if (vertex &&
|
OpenLayers.Util.indexOf(this.vertices, vertex) != -1 &&
|
!this.handlers.drag.dragging && vertex.geometry.parent) {
|
// remove the vertex
|
vertex.geometry.parent.removeComponent(vertex.geometry);
|
this.layer.events.triggerEvent("vertexremoved", {
|
vertex: vertex.geometry,
|
feature: this.feature,
|
pixel: evt.xy
|
});
|
this.layer.drawFeature(this.feature, this.standalone ?
|
undefined : 'select');
|
this.modified = true;
|
this.resetVertices();
|
this.setFeatureState();
|
this.onModification(this.feature);
|
this.layer.events.triggerEvent("featuremodified",
|
{feature: this.feature});
|
}
|
}
|
},
|
|
/**
|
* Method: collectVertices
|
* Collect the vertices from the modifiable feature's geometry and push
|
* them on to the control's vertices array.
|
*/
|
collectVertices: function() {
|
this.vertices = [];
|
this.virtualVertices = [];
|
var control = this;
|
function collectComponentVertices(geometry) {
|
var i, vertex, component, len;
|
if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
|
vertex = new OpenLayers.Feature.Vector(geometry);
|
vertex._sketch = true;
|
vertex.renderIntent = control.vertexRenderIntent;
|
control.vertices.push(vertex);
|
} else {
|
var numVert = geometry.components.length;
|
if(geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
|
numVert -= 1;
|
}
|
for(i=0; i<numVert; ++i) {
|
component = geometry.components[i];
|
if(component.CLASS_NAME == "OpenLayers.Geometry.Point") {
|
vertex = new OpenLayers.Feature.Vector(component);
|
vertex._sketch = true;
|
vertex.renderIntent = control.vertexRenderIntent;
|
control.vertices.push(vertex);
|
} else {
|
collectComponentVertices(component);
|
}
|
}
|
|
// add virtual vertices in the middle of each edge
|
if (control.createVertices && geometry.CLASS_NAME != "OpenLayers.Geometry.MultiPoint") {
|
for(i=0, len=geometry.components.length; i<len-1; ++i) {
|
var prevVertex = geometry.components[i];
|
var nextVertex = geometry.components[i + 1];
|
if(prevVertex.CLASS_NAME == "OpenLayers.Geometry.Point" &&
|
nextVertex.CLASS_NAME == "OpenLayers.Geometry.Point") {
|
var x = (prevVertex.x + nextVertex.x) / 2;
|
var y = (prevVertex.y + nextVertex.y) / 2;
|
var point = new OpenLayers.Feature.Vector(
|
new OpenLayers.Geometry.Point(x, y),
|
null, control.virtualStyle
|
);
|
// set the virtual parent and intended index
|
point.geometry.parent = geometry;
|
point._index = i + 1;
|
point._sketch = true;
|
control.virtualVertices.push(point);
|
}
|
}
|
}
|
}
|
}
|
collectComponentVertices.call(this, this.feature.geometry);
|
this.layer.addFeatures(this.virtualVertices, {silent: true});
|
this.layer.addFeatures(this.vertices, {silent: true});
|
},
|
|
/**
|
* Method: collectDragHandle
|
* Collect the drag handle for the selected geometry.
|
*/
|
collectDragHandle: function() {
|
var geometry = this.feature.geometry;
|
var center = geometry.getBounds().getCenterLonLat();
|
var originGeometry = new OpenLayers.Geometry.Point(
|
center.lon, center.lat
|
);
|
var origin = new OpenLayers.Feature.Vector(originGeometry);
|
originGeometry.move = function(x, y) {
|
OpenLayers.Geometry.Point.prototype.move.call(this, x, y);
|
geometry.move(x, y);
|
};
|
origin._sketch = true;
|
this.dragHandle = origin;
|
this.dragHandle.renderIntent = this.vertexRenderIntent;
|
this.layer.addFeatures([this.dragHandle], {silent: true});
|
},
|
|
/**
|
* Method: collectRadiusHandle
|
* Collect the radius handle for the selected geometry.
|
*/
|
collectRadiusHandle: function() {
|
var geometry = this.feature.geometry;
|
var bounds = geometry.getBounds();
|
var center = bounds.getCenterLonLat();
|
var originGeometry = new OpenLayers.Geometry.Point(
|
center.lon, center.lat
|
);
|
var radiusGeometry = new OpenLayers.Geometry.Point(
|
bounds.right, bounds.bottom
|
);
|
var radius = new OpenLayers.Feature.Vector(radiusGeometry);
|
var resize = (this.mode & OpenLayers.Control.ModifyFeature.RESIZE);
|
var reshape = (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE);
|
var rotate = (this.mode & OpenLayers.Control.ModifyFeature.ROTATE);
|
|
radiusGeometry.move = function(x, y) {
|
OpenLayers.Geometry.Point.prototype.move.call(this, x, y);
|
var dx1 = this.x - originGeometry.x;
|
var dy1 = this.y - originGeometry.y;
|
var dx0 = dx1 - x;
|
var dy0 = dy1 - y;
|
if(rotate) {
|
var a0 = Math.atan2(dy0, dx0);
|
var a1 = Math.atan2(dy1, dx1);
|
var angle = a1 - a0;
|
angle *= 180 / Math.PI;
|
geometry.rotate(angle, originGeometry);
|
}
|
if(resize) {
|
var scale, ratio;
|
// 'resize' together with 'reshape' implies that the aspect
|
// ratio of the geometry will not be preserved whilst resizing
|
if (reshape) {
|
scale = dy1 / dy0;
|
ratio = (dx1 / dx0) / scale;
|
} else {
|
var l0 = Math.sqrt((dx0 * dx0) + (dy0 * dy0));
|
var l1 = Math.sqrt((dx1 * dx1) + (dy1 * dy1));
|
scale = l1 / l0;
|
}
|
geometry.resize(scale, originGeometry, ratio);
|
}
|
};
|
radius._sketch = true;
|
this.radiusHandle = radius;
|
this.radiusHandle.renderIntent = this.vertexRenderIntent;
|
this.layer.addFeatures([this.radiusHandle], {silent: true});
|
},
|
|
/**
|
* Method: setMap
|
* Set the map property for the control and all handlers.
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>} The control's map.
|
*/
|
setMap: function(map) {
|
this.handlers.drag.setMap(map);
|
OpenLayers.Control.prototype.setMap.apply(this, arguments);
|
},
|
|
/**
|
* Method: handleMapEvents
|
*
|
* Parameters:
|
* evt - {Object}
|
*/
|
handleMapEvents: function(evt) {
|
if (evt.type == "removelayer" || evt.property == "order") {
|
this.moveLayerToTop();
|
}
|
},
|
|
/**
|
* Method: moveLayerToTop
|
* Moves the layer for this handler to the top, so mouse events can reach
|
* it.
|
*/
|
moveLayerToTop: function() {
|
var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1,
|
this.layer.getZIndex()) + 1;
|
this.layer.setZIndex(index);
|
|
},
|
|
/**
|
* Method: moveLayerBack
|
* Moves the layer back to the position determined by the map's layers
|
* array.
|
*/
|
moveLayerBack: function() {
|
var index = this.layer.getZIndex() - 1;
|
if (index >= this.map.Z_INDEX_BASE['Feature']) {
|
this.layer.setZIndex(index);
|
} else {
|
this.map.setLayerZIndex(this.layer,
|
this.map.getLayerIndex(this.layer));
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Control.ModifyFeature"
|
});
|
|
/**
|
* Constant: RESHAPE
|
* {Integer} Constant used to make the control work in reshape mode
|
*/
|
OpenLayers.Control.ModifyFeature.RESHAPE = 1;
|
/**
|
* Constant: RESIZE
|
* {Integer} Constant used to make the control work in resize mode
|
*/
|
OpenLayers.Control.ModifyFeature.RESIZE = 2;
|
/**
|
* Constant: ROTATE
|
* {Integer} Constant used to make the control work in rotate mode
|
*/
|
OpenLayers.Control.ModifyFeature.ROTATE = 4;
|
/**
|
* Constant: DRAG
|
* {Integer} Constant used to make the control work in drag mode
|
*/
|
OpenLayers.Control.ModifyFeature.DRAG = 8;
|
/* ======================================================================
|
OpenLayers/Layer/Bing.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Layer/XYZ.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.Bing
|
* Bing layer using direct tile access as provided by Bing Maps REST Services.
|
* See http://msdn.microsoft.com/en-us/library/ff701713.aspx for more
|
* information. Note: Terms of Service compliant use requires the map to be
|
* configured with an <OpenLayers.Control.Attribution> control and the
|
* attribution placed on or near the map.
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer.XYZ>
|
*/
|
OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, {
|
|
/**
|
* Property: key
|
* {String} API key for Bing maps, get your own key
|
* at http://bingmapsportal.com/ .
|
*/
|
key: null,
|
|
/**
|
* Property: serverResolutions
|
* {Array} the resolutions provided by the Bing servers.
|
*/
|
serverResolutions: [
|
156543.03390625, 78271.516953125, 39135.7584765625,
|
19567.87923828125, 9783.939619140625, 4891.9698095703125,
|
2445.9849047851562, 1222.9924523925781, 611.4962261962891,
|
305.74811309814453, 152.87405654907226, 76.43702827453613,
|
38.218514137268066, 19.109257068634033, 9.554628534317017,
|
4.777314267158508, 2.388657133579254, 1.194328566789627,
|
0.5971642833948135, 0.29858214169740677, 0.14929107084870338,
|
0.07464553542435169
|
],
|
|
/**
|
* Property: attributionTemplate
|
* {String}
|
*/
|
attributionTemplate: '<span class="olBingAttribution ${type}">' +
|
'<div><a target="_blank" href="http://www.bing.com/maps/">' +
|
'<img src="${logo}" /></a></div>${copyrights}' +
|
'<a style="white-space: nowrap" target="_blank" '+
|
'href="http://www.microsoft.com/maps/product/terms.html">' +
|
'Terms of Use</a></span>',
|
|
/**
|
* Property: metadata
|
* {Object} Metadata for this layer, as returned by the callback script
|
*/
|
metadata: null,
|
|
/**
|
* Property: protocolRegex
|
* {RegExp} Regular expression to match and replace http: in bing urls
|
*/
|
protocolRegex: /^http:/i,
|
|
/**
|
* APIProperty: type
|
* {String} The layer identifier. Any non-birdseye imageryType
|
* from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be
|
* used. Default is "Road".
|
*/
|
type: "Road",
|
|
/**
|
* APIProperty: culture
|
* {String} The culture identifier. See http://msdn.microsoft.com/en-us/library/ff701709.aspx
|
* for the definition and the possible values. Default is "en-US".
|
*/
|
culture: "en-US",
|
|
/**
|
* APIProperty: metadataParams
|
* {Object} Optional url parameters for the Get Imagery Metadata request
|
* as described here: http://msdn.microsoft.com/en-us/library/ff701716.aspx
|
*/
|
metadataParams: null,
|
|
/** APIProperty: tileOptions
|
* {Object} optional configuration options for <OpenLayers.Tile> instances
|
* created by this Layer. Default is
|
*
|
* (code)
|
* {crossOriginKeyword: 'anonymous'}
|
* (end)
|
*/
|
tileOptions: null,
|
|
/** APIProperty: protocol
|
* {String} Protocol to use to fetch Imagery Metadata, tiles and bing logo
|
* Can be 'http:' 'https:' or ''
|
*
|
* Warning: tiles may not be available under both HTTP and HTTPS protocols.
|
* Microsoft approved use of both HTTP and HTTPS urls for tiles. However
|
* this is undocumented and the Imagery Metadata API always returns HTTP
|
* urls.
|
*
|
* Default is '', unless when executed from a file:/// uri, in which case
|
* it is 'http:'.
|
*/
|
protocol: ~window.location.href.indexOf('http') ? '' : 'http:',
|
|
/**
|
* Constructor: OpenLayers.Layer.Bing
|
* Create a new Bing layer.
|
*
|
* Example:
|
* (code)
|
* var road = new OpenLayers.Layer.Bing({
|
* name: "My Bing Aerial Layer",
|
* type: "Aerial",
|
* key: "my-api-key-here",
|
* });
|
* (end)
|
*
|
* Parameters:
|
* options - {Object} Configuration properties for the layer.
|
*
|
* Required configuration properties:
|
* key - {String} Bing Maps API key for your application. Get one at
|
* http://bingmapsportal.com/.
|
* type - {String} The layer identifier. Any non-birdseye imageryType
|
* from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be
|
* used.
|
*
|
* Any other documented layer properties can be provided in the config object.
|
*/
|
initialize: function(options) {
|
options = OpenLayers.Util.applyDefaults({
|
sphericalMercator: true
|
}, options);
|
var name = options.name || "Bing " + (options.type || this.type);
|
|
var newArgs = [name, null, options];
|
OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs);
|
this.tileOptions = OpenLayers.Util.extend({
|
crossOriginKeyword: 'anonymous'
|
}, this.options.tileOptions);
|
this.loadMetadata();
|
},
|
|
/**
|
* Method: loadMetadata
|
*/
|
loadMetadata: function() {
|
this._callbackId = "_callback_" + this.id.replace(/\./g, "_");
|
// link the processMetadata method to the global scope and bind it
|
// to this instance
|
window[this._callbackId] = OpenLayers.Function.bind(
|
OpenLayers.Layer.Bing.processMetadata, this
|
);
|
var params = OpenLayers.Util.applyDefaults({
|
key: this.key,
|
jsonp: this._callbackId,
|
include: "ImageryProviders"
|
}, this.metadataParams);
|
var url = this.protocol + "//dev.virtualearth.net/REST/v1/Imagery/Metadata/" +
|
this.type + "?" + OpenLayers.Util.getParameterString(params);
|
var script = document.createElement("script");
|
script.type = "text/javascript";
|
script.src = url;
|
script.id = this._callbackId;
|
document.getElementsByTagName("head")[0].appendChild(script);
|
},
|
|
/**
|
* Method: initLayer
|
*
|
* Sets layer properties according to the metadata provided by the API
|
*/
|
initLayer: function() {
|
var res = this.metadata.resourceSets[0].resources[0];
|
var url = res.imageUrl.replace("{quadkey}", "${quadkey}");
|
url = url.replace("{culture}", this.culture);
|
url = url.replace(this.protocolRegex, this.protocol);
|
this.url = [];
|
for (var i=0; i<res.imageUrlSubdomains.length; ++i) {
|
this.url.push(url.replace("{subdomain}", res.imageUrlSubdomains[i]));
|
}
|
this.addOptions({
|
maxResolution: Math.min(
|
this.serverResolutions[res.zoomMin],
|
this.maxResolution || Number.POSITIVE_INFINITY
|
),
|
numZoomLevels: Math.min(
|
res.zoomMax + 1 - res.zoomMin, this.numZoomLevels
|
)
|
}, true);
|
if (!this.isBaseLayer) {
|
this.redraw();
|
}
|
this.updateAttribution();
|
},
|
|
/**
|
* Method: getURL
|
*
|
* Paramters:
|
* bounds - {<OpenLayers.Bounds>}
|
*/
|
getURL: function(bounds) {
|
if (!this.url) {
|
return;
|
}
|
var xyz = this.getXYZ(bounds), x = xyz.x, y = xyz.y, z = xyz.z;
|
var quadDigits = [];
|
for (var i = z; i > 0; --i) {
|
var digit = '0';
|
var mask = 1 << (i - 1);
|
if ((x & mask) != 0) {
|
digit++;
|
}
|
if ((y & mask) != 0) {
|
digit++;
|
digit++;
|
}
|
quadDigits.push(digit);
|
}
|
var quadKey = quadDigits.join("");
|
var url = this.selectUrl('' + x + y + z, this.url);
|
|
return OpenLayers.String.format(url, {'quadkey': quadKey});
|
},
|
|
/**
|
* Method: updateAttribution
|
* Updates the attribution according to the requirements outlined in
|
* http://gis.638310.n2.nabble.com/Bing-imagery-td5789168.html
|
*/
|
updateAttribution: function() {
|
var metadata = this.metadata;
|
if (!metadata.resourceSets || !this.map || !this.map.center) {
|
return;
|
}
|
var res = metadata.resourceSets[0].resources[0];
|
var extent = this.map.getExtent().transform(
|
this.map.getProjectionObject(),
|
new OpenLayers.Projection("EPSG:4326")
|
);
|
var providers = res.imageryProviders || [],
|
zoom = OpenLayers.Util.indexOf(this.serverResolutions,
|
this.getServerResolution()),
|
copyrights = "", provider, i, ii, j, jj, bbox, coverage;
|
for (i=0,ii=providers.length; i<ii; ++i) {
|
provider = providers[i];
|
for (j=0,jj=provider.coverageAreas.length; j<jj; ++j) {
|
coverage = provider.coverageAreas[j];
|
// axis order provided is Y,X
|
bbox = OpenLayers.Bounds.fromArray(coverage.bbox, true);
|
if (extent.intersectsBounds(bbox) &&
|
zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) {
|
copyrights += provider.attribution + " ";
|
}
|
}
|
}
|
var logo = metadata.brandLogoUri.replace(this.protocolRegex, this.protocol);
|
this.attribution = OpenLayers.String.format(this.attributionTemplate, {
|
type: this.type.toLowerCase(),
|
logo: logo,
|
copyrights: copyrights
|
});
|
this.map && this.map.events.triggerEvent("changelayer", {
|
layer: this,
|
property: "attribution"
|
});
|
},
|
|
/**
|
* Method: setMap
|
*/
|
setMap: function() {
|
OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments);
|
this.map.events.register("moveend", this, this.updateAttribution);
|
},
|
|
/**
|
* APIMethod: clone
|
*
|
* Parameters:
|
* obj - {Object}
|
*
|
* Returns:
|
* {<OpenLayers.Layer.Bing>} An exact clone of this <OpenLayers.Layer.Bing>
|
*/
|
clone: function(obj) {
|
if (obj == null) {
|
obj = new OpenLayers.Layer.Bing(this.options);
|
}
|
//get all additions from superclasses
|
obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);
|
// copy/set any non-init, non-simple values here
|
return obj;
|
},
|
|
/**
|
* Method: destroy
|
*/
|
destroy: function() {
|
this.map &&
|
this.map.events.unregister("moveend", this, this.updateAttribution);
|
OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments);
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.Bing"
|
});
|
|
/**
|
* Function: OpenLayers.Layer.Bing.processMetadata
|
* This function will be bound to an instance, linked to the global scope with
|
* an id, and called by the JSONP script returned by the API.
|
*
|
* Parameters:
|
* metadata - {Object} metadata as returned by the API
|
*/
|
OpenLayers.Layer.Bing.processMetadata = function(metadata) {
|
this.metadata = metadata;
|
this.initLayer();
|
var script = document.getElementById(this._callbackId);
|
script.parentNode.removeChild(script);
|
window[this._callbackId] = undefined; // cannot delete from window in IE
|
delete this._callbackId;
|
};
|
/* ======================================================================
|
OpenLayers/StyleMap.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
* @requires OpenLayers/Style.js
|
* @requires OpenLayers/Feature/Vector.js
|
*/
|
|
/**
|
* Class: OpenLayers.StyleMap
|
*/
|
OpenLayers.StyleMap = OpenLayers.Class({
|
|
/**
|
* Property: styles
|
* {Object} Hash of {<OpenLayers.Style>}, keyed by names of well known
|
* rendering intents (e.g. "default", "temporary", "select", "delete").
|
*/
|
styles: null,
|
|
/**
|
* Property: extendDefault
|
* {Boolean} if true, every render intent will extend the symbolizers
|
* specified for the "default" intent at rendering time. Otherwise, every
|
* rendering intent will be treated as a completely independent style.
|
*/
|
extendDefault: true,
|
|
/**
|
* Constructor: OpenLayers.StyleMap
|
*
|
* Parameters:
|
* style - {Object} Optional. Either a style hash, or a style object, or
|
* a hash of style objects (style hashes) keyed by rendering
|
* intent. If just one style hash or style object is passed,
|
* this will be used for all known render intents (default,
|
* select, temporary)
|
* options - {Object} optional hash of additional options for this
|
* instance
|
*/
|
initialize: function (style, options) {
|
this.styles = {
|
"default": new OpenLayers.Style(
|
OpenLayers.Feature.Vector.style["default"]),
|
"select": new OpenLayers.Style(
|
OpenLayers.Feature.Vector.style["select"]),
|
"temporary": new OpenLayers.Style(
|
OpenLayers.Feature.Vector.style["temporary"]),
|
"delete": new OpenLayers.Style(
|
OpenLayers.Feature.Vector.style["delete"])
|
};
|
|
// take whatever the user passed as style parameter and convert it
|
// into parts of stylemap.
|
if(style instanceof OpenLayers.Style) {
|
// user passed a style object
|
this.styles["default"] = style;
|
this.styles["select"] = style;
|
this.styles["temporary"] = style;
|
this.styles["delete"] = style;
|
} else if(typeof style == "object") {
|
for(var key in style) {
|
if(style[key] instanceof OpenLayers.Style) {
|
// user passed a hash of style objects
|
this.styles[key] = style[key];
|
} else if(typeof style[key] == "object") {
|
// user passsed a hash of style hashes
|
this.styles[key] = new OpenLayers.Style(style[key]);
|
} else {
|
// user passed a style hash (i.e. symbolizer)
|
this.styles["default"] = new OpenLayers.Style(style);
|
this.styles["select"] = new OpenLayers.Style(style);
|
this.styles["temporary"] = new OpenLayers.Style(style);
|
this.styles["delete"] = new OpenLayers.Style(style);
|
break;
|
}
|
}
|
}
|
OpenLayers.Util.extend(this, options);
|
},
|
|
/**
|
* Method: destroy
|
*/
|
destroy: function() {
|
for(var key in this.styles) {
|
this.styles[key].destroy();
|
}
|
this.styles = null;
|
},
|
|
/**
|
* Method: createSymbolizer
|
* Creates the symbolizer for a feature for a render intent.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature>} The feature to evaluate the rules
|
* of the intended style against.
|
* intent - {String} The intent determines the symbolizer that will be
|
* used to draw the feature. Well known intents are "default"
|
* (for just drawing the features), "select" (for selected
|
* features) and "temporary" (for drawing features).
|
*
|
* Returns:
|
* {Object} symbolizer hash
|
*/
|
createSymbolizer: function(feature, intent) {
|
if(!feature) {
|
feature = new OpenLayers.Feature.Vector();
|
}
|
if(!this.styles[intent]) {
|
intent = "default";
|
}
|
feature.renderIntent = intent;
|
var defaultSymbolizer = {};
|
if(this.extendDefault && intent != "default") {
|
defaultSymbolizer = this.styles["default"].createSymbolizer(feature);
|
}
|
return OpenLayers.Util.extend(defaultSymbolizer,
|
this.styles[intent].createSymbolizer(feature));
|
},
|
|
/**
|
* Method: addUniqueValueRules
|
* Convenience method to create comparison rules for unique values of a
|
* property. The rules will be added to the style object for a specified
|
* rendering intent. This method is a shortcut for creating something like
|
* the "unique value legends" familiar from well known desktop GIS systems
|
*
|
* Parameters:
|
* renderIntent - {String} rendering intent to add the rules to
|
* property - {String} values of feature attributes to create the
|
* rules for
|
* symbolizers - {Object} Hash of symbolizers, keyed by the desired
|
* property values
|
* context - {Object} An optional object with properties that
|
* symbolizers' property values should be evaluated
|
* against. If no context is specified, feature.attributes
|
* will be used
|
*/
|
addUniqueValueRules: function(renderIntent, property, symbolizers, context) {
|
var rules = [];
|
for (var value in symbolizers) {
|
rules.push(new OpenLayers.Rule({
|
symbolizer: symbolizers[value],
|
context: context,
|
filter: new OpenLayers.Filter.Comparison({
|
type: OpenLayers.Filter.Comparison.EQUAL_TO,
|
property: property,
|
value: value
|
})
|
}));
|
}
|
this.styles[renderIntent].addRules(rules);
|
},
|
|
CLASS_NAME: "OpenLayers.StyleMap"
|
});
|
/* ======================================================================
|
OpenLayers/Layer/Vector.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Layer.js
|
* @requires OpenLayers/Renderer.js
|
* @requires OpenLayers/StyleMap.js
|
* @requires OpenLayers/Feature/Vector.js
|
* @requires OpenLayers/Console.js
|
* @requires OpenLayers/Lang.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.Vector
|
* Instances of OpenLayers.Layer.Vector are used to render vector data from
|
* a variety of sources. Create a new vector layer with the
|
* <OpenLayers.Layer.Vector> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer>
|
*/
|
OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
|
|
/**
|
* APIProperty: events
|
* {<OpenLayers.Events>}
|
*
|
* Register a listener for a particular event with the following syntax:
|
* (code)
|
* layer.events.register(type, obj, listener);
|
* (end)
|
*
|
* Listeners will be called with a reference to an event object. The
|
* properties of this event depends on exactly what happened.
|
*
|
* All event objects have at least the following properties:
|
* object - {Object} A reference to layer.events.object.
|
* element - {DOMElement} A reference to layer.events.element.
|
*
|
* Supported map event types (in addition to those from <OpenLayers.Layer.events>):
|
* beforefeatureadded - Triggered before a feature is added. Listeners
|
* will receive an object with a *feature* property referencing the
|
* feature to be added. To stop the feature from being added, a
|
* listener should return false.
|
* beforefeaturesadded - Triggered before an array of features is added.
|
* Listeners will receive an object with a *features* property
|
* referencing the feature to be added. To stop the features from
|
* being added, a listener should return false.
|
* featureadded - Triggered after a feature is added. The event
|
* object passed to listeners will have a *feature* property with a
|
* reference to the added feature.
|
* featuresadded - Triggered after features are added. The event
|
* object passed to listeners will have a *features* property with a
|
* reference to an array of added features.
|
* beforefeatureremoved - Triggered before a feature is removed. Listeners
|
* will receive an object with a *feature* property referencing the
|
* feature to be removed.
|
* beforefeaturesremoved - Triggered before multiple features are removed.
|
* Listeners will receive an object with a *features* property
|
* referencing the features to be removed.
|
* featureremoved - Triggerd after a feature is removed. The event
|
* object passed to listeners will have a *feature* property with a
|
* reference to the removed feature.
|
* featuresremoved - Triggered after features are removed. The event
|
* object passed to listeners will have a *features* property with a
|
* reference to an array of removed features.
|
* beforefeatureselected - Triggered before a feature is selected. Listeners
|
* will receive an object with a *feature* property referencing the
|
* feature to be selected. To stop the feature from being selectd, a
|
* listener should return false.
|
* featureselected - Triggered after a feature is selected. Listeners
|
* will receive an object with a *feature* property referencing the
|
* selected feature.
|
* featureunselected - Triggered after a feature is unselected.
|
* Listeners will receive an object with a *feature* property
|
* referencing the unselected feature.
|
* beforefeaturemodified - Triggered when a feature is selected to
|
* be modified. Listeners will receive an object with a *feature*
|
* property referencing the selected feature.
|
* featuremodified - Triggered when a feature has been modified.
|
* Listeners will receive an object with a *feature* property referencing
|
* the modified feature.
|
* afterfeaturemodified - Triggered when a feature is finished being modified.
|
* Listeners will receive an object with a *feature* property referencing
|
* the modified feature.
|
* vertexmodified - Triggered when a vertex within any feature geometry
|
* has been modified. Listeners will receive an object with a
|
* *feature* property referencing the modified feature, a *vertex*
|
* property referencing the vertex modified (always a point geometry),
|
* and a *pixel* property referencing the pixel location of the
|
* modification.
|
* vertexremoved - Triggered when a vertex within any feature geometry
|
* has been deleted. Listeners will receive an object with a
|
* *feature* property referencing the modified feature, a *vertex*
|
* property referencing the vertex modified (always a point geometry),
|
* and a *pixel* property referencing the pixel location of the
|
* removal.
|
* sketchstarted - Triggered when a feature sketch bound for this layer
|
* is started. Listeners will receive an object with a *feature*
|
* property referencing the new sketch feature and a *vertex* property
|
* referencing the creation point.
|
* sketchmodified - Triggered when a feature sketch bound for this layer
|
* is modified. Listeners will receive an object with a *vertex*
|
* property referencing the modified vertex and a *feature* property
|
* referencing the sketch feature.
|
* sketchcomplete - Triggered when a feature sketch bound for this layer
|
* is complete. Listeners will receive an object with a *feature*
|
* property referencing the sketch feature. By returning false, a
|
* listener can stop the sketch feature from being added to the layer.
|
* refresh - Triggered when something wants a strategy to ask the protocol
|
* for a new set of features.
|
*/
|
|
/**
|
* APIProperty: isBaseLayer
|
* {Boolean} The layer is a base layer. Default is false. Set this property
|
* in the layer options.
|
*/
|
isBaseLayer: false,
|
|
/**
|
* APIProperty: isFixed
|
* {Boolean} Whether the layer remains in one place while dragging the
|
* map. Note that setting this to true will move the layer to the bottom
|
* of the layer stack.
|
*/
|
isFixed: false,
|
|
/**
|
* APIProperty: features
|
* {Array(<OpenLayers.Feature.Vector>)}
|
*/
|
features: null,
|
|
/**
|
* Property: filter
|
* {<OpenLayers.Filter>} The filter set in this layer,
|
* a strategy launching read requests can combined
|
* this filter with its own filter.
|
*/
|
filter: null,
|
|
/**
|
* Property: selectedFeatures
|
* {Array(<OpenLayers.Feature.Vector>)}
|
*/
|
selectedFeatures: null,
|
|
/**
|
* Property: unrenderedFeatures
|
* {Object} hash of features, keyed by feature.id, that the renderer
|
* failed to draw
|
*/
|
unrenderedFeatures: null,
|
|
/**
|
* APIProperty: reportError
|
* {Boolean} report friendly error message when loading of renderer
|
* fails.
|
*/
|
reportError: true,
|
|
/**
|
* APIProperty: style
|
* {Object} Default style for the layer
|
*/
|
style: null,
|
|
/**
|
* Property: styleMap
|
* {<OpenLayers.StyleMap>}
|
*/
|
styleMap: null,
|
|
/**
|
* Property: strategies
|
* {Array(<OpenLayers.Strategy>})} Optional list of strategies for the layer.
|
*/
|
strategies: null,
|
|
/**
|
* Property: protocol
|
* {<OpenLayers.Protocol>} Optional protocol for the layer.
|
*/
|
protocol: null,
|
|
/**
|
* Property: renderers
|
* {Array(String)} List of supported Renderer classes. Add to this list to
|
* add support for additional renderers. This list is ordered:
|
* the first renderer which returns true for the 'supported()'
|
* method will be used, if not defined in the 'renderer' option.
|
*/
|
renderers: ['SVG', 'VML', 'Canvas'],
|
|
/**
|
* Property: renderer
|
* {<OpenLayers.Renderer>}
|
*/
|
renderer: null,
|
|
/**
|
* APIProperty: rendererOptions
|
* {Object} Options for the renderer. See {<OpenLayers.Renderer>} for
|
* supported options.
|
*/
|
rendererOptions: null,
|
|
/**
|
* APIProperty: geometryType
|
* {String} geometryType allows you to limit the types of geometries this
|
* layer supports. This should be set to something like
|
* "OpenLayers.Geometry.Point" to limit types.
|
*/
|
geometryType: null,
|
|
/**
|
* Property: drawn
|
* {Boolean} Whether the Vector Layer features have been drawn yet.
|
*/
|
drawn: false,
|
|
/**
|
* APIProperty: ratio
|
* {Float} This specifies the ratio of the size of the visiblity of the Vector Layer features to the size of the map.
|
*/
|
ratio: 1,
|
|
/**
|
* Constructor: OpenLayers.Layer.Vector
|
* Create a new vector layer
|
*
|
* Parameters:
|
* name - {String} A name for the layer
|
* options - {Object} Optional object with non-default properties to set on
|
* the layer.
|
*
|
* Returns:
|
* {<OpenLayers.Layer.Vector>} A new vector layer
|
*/
|
initialize: function(name, options) {
|
OpenLayers.Layer.prototype.initialize.apply(this, arguments);
|
|
// allow user-set renderer, otherwise assign one
|
if (!this.renderer || !this.renderer.supported()) {
|
this.assignRenderer();
|
}
|
|
// if no valid renderer found, display error
|
if (!this.renderer || !this.renderer.supported()) {
|
this.renderer = null;
|
this.displayError();
|
}
|
|
if (!this.styleMap) {
|
this.styleMap = new OpenLayers.StyleMap();
|
}
|
|
this.features = [];
|
this.selectedFeatures = [];
|
this.unrenderedFeatures = {};
|
|
// Allow for custom layer behavior
|
if(this.strategies){
|
for(var i=0, len=this.strategies.length; i<len; i++) {
|
this.strategies[i].setLayer(this);
|
}
|
}
|
|
},
|
|
/**
|
* APIMethod: destroy
|
* Destroy this layer
|
*/
|
destroy: function() {
|
if (this.strategies) {
|
var strategy, i, len;
|
for(i=0, len=this.strategies.length; i<len; i++) {
|
strategy = this.strategies[i];
|
if(strategy.autoDestroy) {
|
strategy.destroy();
|
}
|
}
|
this.strategies = null;
|
}
|
if (this.protocol) {
|
if(this.protocol.autoDestroy) {
|
this.protocol.destroy();
|
}
|
this.protocol = null;
|
}
|
this.destroyFeatures();
|
this.features = null;
|
this.selectedFeatures = null;
|
this.unrenderedFeatures = null;
|
if (this.renderer) {
|
this.renderer.destroy();
|
}
|
this.renderer = null;
|
this.geometryType = null;
|
this.drawn = null;
|
OpenLayers.Layer.prototype.destroy.apply(this, arguments);
|
},
|
|
/**
|
* Method: clone
|
* Create a clone of this layer.
|
*
|
* Note: Features of the layer are also cloned.
|
*
|
* Returns:
|
* {<OpenLayers.Layer.Vector>} An exact clone of this layer
|
*/
|
clone: function (obj) {
|
|
if (obj == null) {
|
obj = new OpenLayers.Layer.Vector(this.name, this.getOptions());
|
}
|
|
//get all additions from superclasses
|
obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
|
|
// copy/set any non-init, non-simple values here
|
var features = this.features;
|
var len = features.length;
|
var clonedFeatures = new Array(len);
|
for(var i=0; i<len; ++i) {
|
clonedFeatures[i] = features[i].clone();
|
}
|
obj.features = clonedFeatures;
|
|
return obj;
|
},
|
|
/**
|
* Method: refresh
|
* Ask the layer to request features again and redraw them. Triggers
|
* the refresh event if the layer is in range and visible.
|
*
|
* Parameters:
|
* obj - {Object} Optional object with properties for any listener of
|
* the refresh event.
|
*/
|
refresh: function(obj) {
|
if(this.calculateInRange() && this.visibility) {
|
this.events.triggerEvent("refresh", obj);
|
}
|
},
|
|
/**
|
* Method: assignRenderer
|
* Iterates through the available renderer implementations and selects
|
* and assigns the first one whose "supported()" function returns true.
|
*/
|
assignRenderer: function() {
|
for (var i=0, len=this.renderers.length; i<len; i++) {
|
var rendererClass = this.renderers[i];
|
var renderer = (typeof rendererClass == "function") ?
|
rendererClass :
|
OpenLayers.Renderer[rendererClass];
|
if (renderer && renderer.prototype.supported()) {
|
this.renderer = new renderer(this.div, this.rendererOptions);
|
break;
|
}
|
}
|
},
|
|
/**
|
* Method: displayError
|
* Let the user know their browser isn't supported.
|
*/
|
displayError: function() {
|
if (this.reportError) {
|
OpenLayers.Console.userError(OpenLayers.i18n("browserNotSupported",
|
{renderers: this. renderers.join('\n')}));
|
}
|
},
|
|
/**
|
* Method: setMap
|
* The layer has been added to the map.
|
*
|
* If there is no renderer set, the layer can't be used. Remove it.
|
* Otherwise, give the renderer a reference to the map and set its size.
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
setMap: function(map) {
|
OpenLayers.Layer.prototype.setMap.apply(this, arguments);
|
|
if (!this.renderer) {
|
this.map.removeLayer(this);
|
} else {
|
this.renderer.map = this.map;
|
|
var newSize = this.map.getSize();
|
newSize.w = newSize.w * this.ratio;
|
newSize.h = newSize.h * this.ratio;
|
this.renderer.setSize(newSize);
|
}
|
},
|
|
/**
|
* Method: afterAdd
|
* Called at the end of the map.addLayer sequence. At this point, the map
|
* will have a base layer. Any autoActivate strategies will be
|
* activated here.
|
*/
|
afterAdd: function() {
|
if(this.strategies) {
|
var strategy, i, len;
|
for(i=0, len=this.strategies.length; i<len; i++) {
|
strategy = this.strategies[i];
|
if(strategy.autoActivate) {
|
strategy.activate();
|
}
|
}
|
}
|
},
|
|
/**
|
* Method: removeMap
|
* The layer has been removed from the map.
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
removeMap: function(map) {
|
this.drawn = false;
|
if(this.strategies) {
|
var strategy, i, len;
|
for(i=0, len=this.strategies.length; i<len; i++) {
|
strategy = this.strategies[i];
|
if(strategy.autoActivate) {
|
strategy.deactivate();
|
}
|
}
|
}
|
},
|
|
/**
|
* Method: onMapResize
|
* Notify the renderer of the change in size.
|
*
|
*/
|
onMapResize: function() {
|
OpenLayers.Layer.prototype.onMapResize.apply(this, arguments);
|
|
var newSize = this.map.getSize();
|
newSize.w = newSize.w * this.ratio;
|
newSize.h = newSize.h * this.ratio;
|
this.renderer.setSize(newSize);
|
},
|
|
/**
|
* Method: moveTo
|
* Reset the vector layer's div so that it once again is lined up with
|
* the map. Notify the renderer of the change of extent, and in the
|
* case of a change of zoom level (resolution), have the
|
* renderer redraw features.
|
*
|
* If the layer has not yet been drawn, cycle through the layer's
|
* features and draw each one.
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
* zoomChanged - {Boolean}
|
* dragging - {Boolean}
|
*/
|
moveTo: function(bounds, zoomChanged, dragging) {
|
OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
|
|
var coordSysUnchanged = true;
|
if (!dragging) {
|
this.renderer.root.style.visibility = 'hidden';
|
|
var viewSize = this.map.getSize(),
|
viewWidth = viewSize.w,
|
viewHeight = viewSize.h,
|
offsetLeft = (viewWidth / 2 * this.ratio) - viewWidth / 2,
|
offsetTop = (viewHeight / 2 * this.ratio) - viewHeight / 2;
|
offsetLeft += this.map.layerContainerOriginPx.x;
|
offsetLeft = -Math.round(offsetLeft);
|
offsetTop += this.map.layerContainerOriginPx.y;
|
offsetTop = -Math.round(offsetTop);
|
|
this.div.style.left = offsetLeft + 'px';
|
this.div.style.top = offsetTop + 'px';
|
|
var extent = this.map.getExtent().scale(this.ratio);
|
coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged);
|
|
this.renderer.root.style.visibility = 'visible';
|
|
// Force a reflow on gecko based browsers to prevent jump/flicker.
|
// This seems to happen on only certain configurations; it was originally
|
// noticed in FF 2.0 and Linux.
|
if (OpenLayers.IS_GECKO === true) {
|
this.div.scrollLeft = this.div.scrollLeft;
|
}
|
|
if (!zoomChanged && coordSysUnchanged) {
|
for (var i in this.unrenderedFeatures) {
|
var feature = this.unrenderedFeatures[i];
|
this.drawFeature(feature);
|
}
|
}
|
}
|
if (!this.drawn || zoomChanged || !coordSysUnchanged) {
|
this.drawn = true;
|
var feature;
|
for(var i=0, len=this.features.length; i<len; i++) {
|
this.renderer.locked = (i !== (len - 1));
|
feature = this.features[i];
|
this.drawFeature(feature);
|
}
|
}
|
},
|
|
/**
|
* APIMethod: display
|
* Hide or show the Layer
|
*
|
* Parameters:
|
* display - {Boolean}
|
*/
|
display: function(display) {
|
OpenLayers.Layer.prototype.display.apply(this, arguments);
|
// we need to set the display style of the root in case it is attached
|
// to a foreign layer
|
var currentDisplay = this.div.style.display;
|
if(currentDisplay != this.renderer.root.style.display) {
|
this.renderer.root.style.display = currentDisplay;
|
}
|
},
|
|
/**
|
* APIMethod: addFeatures
|
* Add Features to the layer.
|
*
|
* Parameters:
|
* features - {Array(<OpenLayers.Feature.Vector>)}
|
* options - {Object}
|
*/
|
addFeatures: function(features, options) {
|
if (!(OpenLayers.Util.isArray(features))) {
|
features = [features];
|
}
|
|
var notify = !options || !options.silent;
|
if(notify) {
|
var event = {features: features};
|
var ret = this.events.triggerEvent("beforefeaturesadded", event);
|
if(ret === false) {
|
return;
|
}
|
features = event.features;
|
}
|
|
// Track successfully added features for featuresadded event, since
|
// beforefeatureadded can veto single features.
|
var featuresAdded = [];
|
for (var i=0, len=features.length; i<len; i++) {
|
if (i != (features.length - 1)) {
|
this.renderer.locked = true;
|
} else {
|
this.renderer.locked = false;
|
}
|
var feature = features[i];
|
|
if (this.geometryType &&
|
!(feature.geometry instanceof this.geometryType)) {
|
throw new TypeError('addFeatures: component should be an ' +
|
this.geometryType.prototype.CLASS_NAME);
|
}
|
|
//give feature reference to its layer
|
feature.layer = this;
|
|
if (!feature.style && this.style) {
|
feature.style = OpenLayers.Util.extend({}, this.style);
|
}
|
|
if (notify) {
|
if(this.events.triggerEvent("beforefeatureadded",
|
{feature: feature}) === false) {
|
continue;
|
}
|
this.preFeatureInsert(feature);
|
}
|
|
featuresAdded.push(feature);
|
this.features.push(feature);
|
this.drawFeature(feature);
|
|
if (notify) {
|
this.events.triggerEvent("featureadded", {
|
feature: feature
|
});
|
this.onFeatureInsert(feature);
|
}
|
}
|
|
if(notify) {
|
this.events.triggerEvent("featuresadded", {features: featuresAdded});
|
}
|
},
|
|
|
/**
|
* APIMethod: removeFeatures
|
* Remove features from the layer. This erases any drawn features and
|
* removes them from the layer's control. The beforefeatureremoved
|
* and featureremoved events will be triggered for each feature. The
|
* featuresremoved event will be triggered after all features have
|
* been removed. To supress event triggering, use the silent option.
|
*
|
* Parameters:
|
* features - {Array(<OpenLayers.Feature.Vector>)} List of features to be
|
* removed.
|
* options - {Object} Optional properties for changing behavior of the
|
* removal.
|
*
|
* Valid options:
|
* silent - {Boolean} Supress event triggering. Default is false.
|
*/
|
removeFeatures: function(features, options) {
|
if(!features || features.length === 0) {
|
return;
|
}
|
if (features === this.features) {
|
return this.removeAllFeatures(options);
|
}
|
if (!(OpenLayers.Util.isArray(features))) {
|
features = [features];
|
}
|
if (features === this.selectedFeatures) {
|
features = features.slice();
|
}
|
|
var notify = !options || !options.silent;
|
|
if (notify) {
|
this.events.triggerEvent(
|
"beforefeaturesremoved", {features: features}
|
);
|
}
|
|
for (var i = features.length - 1; i >= 0; i--) {
|
// We remain locked so long as we're not at 0
|
// and the 'next' feature has a geometry. We do the geometry check
|
// because if all the features after the current one are 'null', we
|
// won't call eraseGeometry, so we break the 'renderer functions
|
// will always be called with locked=false *last*' rule. The end result
|
// is a possible gratiutious unlocking to save a loop through the rest
|
// of the list checking the remaining features every time. So long as
|
// null geoms are rare, this is probably okay.
|
if (i != 0 && features[i-1].geometry) {
|
this.renderer.locked = true;
|
} else {
|
this.renderer.locked = false;
|
}
|
|
var feature = features[i];
|
delete this.unrenderedFeatures[feature.id];
|
|
if (notify) {
|
this.events.triggerEvent("beforefeatureremoved", {
|
feature: feature
|
});
|
}
|
|
this.features = OpenLayers.Util.removeItem(this.features, feature);
|
// feature has no layer at this point
|
feature.layer = null;
|
|
if (feature.geometry) {
|
this.renderer.eraseFeatures(feature);
|
}
|
|
//in the case that this feature is one of the selected features,
|
// remove it from that array as well.
|
if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1){
|
OpenLayers.Util.removeItem(this.selectedFeatures, feature);
|
}
|
|
if (notify) {
|
this.events.triggerEvent("featureremoved", {
|
feature: feature
|
});
|
}
|
}
|
|
if (notify) {
|
this.events.triggerEvent("featuresremoved", {features: features});
|
}
|
},
|
|
/**
|
* APIMethod: removeAllFeatures
|
* Remove all features from the layer.
|
*
|
* Parameters:
|
* options - {Object} Optional properties for changing behavior of the
|
* removal.
|
*
|
* Valid options:
|
* silent - {Boolean} Supress event triggering. Default is false.
|
*/
|
removeAllFeatures: function(options) {
|
var notify = !options || !options.silent;
|
var features = this.features;
|
if (notify) {
|
this.events.triggerEvent(
|
"beforefeaturesremoved", {features: features}
|
);
|
}
|
var feature;
|
for (var i = features.length-1; i >= 0; i--) {
|
feature = features[i];
|
if (notify) {
|
this.events.triggerEvent("beforefeatureremoved", {
|
feature: feature
|
});
|
}
|
feature.layer = null;
|
if (notify) {
|
this.events.triggerEvent("featureremoved", {
|
feature: feature
|
});
|
}
|
}
|
this.renderer.clear();
|
this.features = [];
|
this.unrenderedFeatures = {};
|
this.selectedFeatures = [];
|
if (notify) {
|
this.events.triggerEvent("featuresremoved", {features: features});
|
}
|
},
|
|
/**
|
* APIMethod: destroyFeatures
|
* Erase and destroy features on the layer.
|
*
|
* Parameters:
|
* features - {Array(<OpenLayers.Feature.Vector>)} An optional array of
|
* features to destroy. If not supplied, all features on the layer
|
* will be destroyed.
|
* options - {Object}
|
*/
|
destroyFeatures: function(features, options) {
|
var all = (features == undefined); // evaluates to true if
|
// features is null
|
if(all) {
|
features = this.features;
|
}
|
if(features) {
|
this.removeFeatures(features, options);
|
for(var i=features.length-1; i>=0; i--) {
|
features[i].destroy();
|
}
|
}
|
},
|
|
/**
|
* APIMethod: drawFeature
|
* Draw (or redraw) a feature on the layer. If the optional style argument
|
* is included, this style will be used. If no style is included, the
|
* feature's style will be used. If the feature doesn't have a style,
|
* the layer's style will be used.
|
*
|
* This function is not designed to be used when adding features to
|
* the layer (use addFeatures instead). It is meant to be used when
|
* the style of a feature has changed, or in some other way needs to
|
* visually updated *after* it has already been added to a layer. You
|
* must add the feature to the layer for most layer-related events to
|
* happen.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
* style - {String | Object} Named render intent or full symbolizer object.
|
*/
|
drawFeature: function(feature, style) {
|
// don't try to draw the feature with the renderer if the layer is not
|
// drawn itself
|
if (!this.drawn) {
|
return;
|
}
|
if (typeof style != "object") {
|
if(!style && feature.state === OpenLayers.State.DELETE) {
|
style = "delete";
|
}
|
var renderIntent = style || feature.renderIntent;
|
style = feature.style || this.style;
|
if (!style) {
|
style = this.styleMap.createSymbolizer(feature, renderIntent);
|
}
|
}
|
|
var drawn = this.renderer.drawFeature(feature, style);
|
//TODO remove the check for null when we get rid of Renderer.SVG
|
if (drawn === false || drawn === null) {
|
this.unrenderedFeatures[feature.id] = feature;
|
} else {
|
delete this.unrenderedFeatures[feature.id];
|
}
|
},
|
|
/**
|
* Method: eraseFeatures
|
* Erase features from the layer.
|
*
|
* Parameters:
|
* features - {Array(<OpenLayers.Feature.Vector>)}
|
*/
|
eraseFeatures: function(features) {
|
this.renderer.eraseFeatures(features);
|
},
|
|
/**
|
* Method: getFeatureFromEvent
|
* Given an event, return a feature if the event occurred over one.
|
* Otherwise, return null.
|
*
|
* Parameters:
|
* evt - {Event}
|
*
|
* Returns:
|
* {<OpenLayers.Feature.Vector>} A feature if one was under the event.
|
*/
|
getFeatureFromEvent: function(evt) {
|
if (!this.renderer) {
|
throw new Error('getFeatureFromEvent called on layer with no ' +
|
'renderer. This usually means you destroyed a ' +
|
'layer, but not some handler which is associated ' +
|
'with it.');
|
}
|
var feature = null;
|
var featureId = this.renderer.getFeatureIdFromEvent(evt);
|
if (featureId) {
|
if (typeof featureId === "string") {
|
feature = this.getFeatureById(featureId);
|
} else {
|
feature = featureId;
|
}
|
}
|
return feature;
|
},
|
|
/**
|
* APIMethod: getFeatureBy
|
* Given a property value, return the feature if it exists in the features array
|
*
|
* Parameters:
|
* property - {String}
|
* value - {String}
|
*
|
* Returns:
|
* {<OpenLayers.Feature.Vector>} A feature corresponding to the given
|
* property value or null if there is no such feature.
|
*/
|
getFeatureBy: function(property, value) {
|
//TBD - would it be more efficient to use a hash for this.features?
|
var feature = null;
|
for(var i=0, len=this.features.length; i<len; ++i) {
|
if(this.features[i][property] == value) {
|
feature = this.features[i];
|
break;
|
}
|
}
|
return feature;
|
},
|
|
/**
|
* APIMethod: getFeatureById
|
* Given a feature id, return the feature if it exists in the features array
|
*
|
* Parameters:
|
* featureId - {String}
|
*
|
* Returns:
|
* {<OpenLayers.Feature.Vector>} A feature corresponding to the given
|
* featureId or null if there is no such feature.
|
*/
|
getFeatureById: function(featureId) {
|
return this.getFeatureBy('id', featureId);
|
},
|
|
/**
|
* APIMethod: getFeatureByFid
|
* Given a feature fid, return the feature if it exists in the features array
|
*
|
* Parameters:
|
* featureFid - {String}
|
*
|
* Returns:
|
* {<OpenLayers.Feature.Vector>} A feature corresponding to the given
|
* featureFid or null if there is no such feature.
|
*/
|
getFeatureByFid: function(featureFid) {
|
return this.getFeatureBy('fid', featureFid);
|
},
|
|
/**
|
* APIMethod: getFeaturesByAttribute
|
* Returns an array of features that have the given attribute key set to the
|
* given value. Comparison of attribute values takes care of datatypes, e.g.
|
* the string '1234' is not equal to the number 1234.
|
*
|
* Parameters:
|
* attrName - {String}
|
* attrValue - {Mixed}
|
*
|
* Returns:
|
* Array({<OpenLayers.Feature.Vector>}) An array of features that have the
|
* passed named attribute set to the given value.
|
*/
|
getFeaturesByAttribute: function(attrName, attrValue) {
|
var i,
|
feature,
|
len = this.features.length,
|
foundFeatures = [];
|
for(i = 0; i < len; i++) {
|
feature = this.features[i];
|
if(feature && feature.attributes) {
|
if (feature.attributes[attrName] === attrValue) {
|
foundFeatures.push(feature);
|
}
|
}
|
}
|
return foundFeatures;
|
},
|
|
/**
|
* Unselect the selected features
|
* i.e. clears the featureSelection array
|
* change the style back
|
clearSelection: function() {
|
|
var vectorLayer = this.map.vectorLayer;
|
for (var i = 0; i < this.map.featureSelection.length; i++) {
|
var featureSelection = this.map.featureSelection[i];
|
vectorLayer.drawFeature(featureSelection, vectorLayer.style);
|
}
|
this.map.featureSelection = [];
|
},
|
*/
|
|
|
/**
|
* APIMethod: onFeatureInsert
|
* method called after a feature is inserted.
|
* Does nothing by default. Override this if you
|
* need to do something on feature updates.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
*/
|
onFeatureInsert: function(feature) {
|
},
|
|
/**
|
* APIMethod: preFeatureInsert
|
* method called before a feature is inserted.
|
* Does nothing by default. Override this if you
|
* need to do something when features are first added to the
|
* layer, but before they are drawn, such as adjust the style.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
*/
|
preFeatureInsert: function(feature) {
|
},
|
|
/**
|
* APIMethod: getDataExtent
|
* Calculates the max extent which includes all of the features.
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} or null if the layer has no features with
|
* geometries.
|
*/
|
getDataExtent: function () {
|
var maxExtent = null;
|
var features = this.features;
|
if(features && (features.length > 0)) {
|
var geometry = null;
|
for(var i=0, len=features.length; i<len; i++) {
|
geometry = features[i].geometry;
|
if (geometry) {
|
if (maxExtent === null) {
|
maxExtent = new OpenLayers.Bounds();
|
}
|
maxExtent.extend(geometry.getBounds());
|
}
|
}
|
}
|
return maxExtent;
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.Vector"
|
});
|
/* ======================================================================
|
OpenLayers/Layer/PointGrid.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Layer/Vector.js
|
* @requires OpenLayers/Geometry/Polygon.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.PointGrid
|
* A point grid layer dynamically generates a regularly spaced grid of point
|
* features. This is a specialty layer for cases where an application needs
|
* a regular grid of points. It can be used, for example, in an editing
|
* environment to snap to a grid.
|
*
|
* Create a new vector layer with the <OpenLayers.Layer.PointGrid> constructor.
|
* (code)
|
* // create a grid with points spaced at 10 map units
|
* var points = new OpenLayers.Layer.PointGrid({dx: 10, dy: 10});
|
*
|
* // create a grid with different x/y spacing rotated 15 degrees clockwise.
|
* var points = new OpenLayers.Layer.PointGrid({dx: 5, dy: 10, rotation: 15});
|
* (end)
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer.Vector>
|
*/
|
OpenLayers.Layer.PointGrid = OpenLayers.Class(OpenLayers.Layer.Vector, {
|
|
/**
|
* APIProperty: dx
|
* {Number} Point grid spacing in the x-axis direction (map units).
|
* Read-only. Use the <setSpacing> method to modify this value.
|
*/
|
dx: null,
|
|
/**
|
* APIProperty: dy
|
* {Number} Point grid spacing in the y-axis direction (map units).
|
* Read-only. Use the <setSpacing> method to modify this value.
|
*/
|
dy: null,
|
|
/**
|
* APIProperty: ratio
|
* {Number} Ratio of the desired grid size to the map viewport size.
|
* Default is 1.5. Larger ratios mean the grid is recalculated less often
|
* while panning. The <maxFeatures> setting has precedence when determining
|
* grid size. Read-only. Use the <setRatio> method to modify this value.
|
*/
|
ratio: 1.5,
|
|
/**
|
* APIProperty: maxFeatures
|
* {Number} The maximum number of points to generate in the grid. Default
|
* is 250. Read-only. Use the <setMaxFeatures> method to modify this value.
|
*/
|
maxFeatures: 250,
|
|
/**
|
* APIProperty: rotation
|
* {Number} Grid rotation (in degrees clockwise from the positive x-axis).
|
* Default is 0. Read-only. Use the <setRotation> method to modify this
|
* value.
|
*/
|
rotation: 0,
|
|
/**
|
* APIProperty: origin
|
* {<OpenLayers.LonLat>} Grid origin. The grid lattice will be aligned with
|
* the origin. If not set at construction, the center of the map's maximum
|
* extent is used. Read-only. Use the <setOrigin> method to modify this
|
* value.
|
*/
|
origin: null,
|
|
/**
|
* Property: gridBounds
|
* {<OpenLayers.Bounds>} Internally cached grid bounds (with optional
|
* rotation applied).
|
*/
|
gridBounds: null,
|
|
/**
|
* Constructor: OpenLayers.Layer.PointGrid
|
* Creates a new point grid layer.
|
*
|
* Parameters:
|
* config - {Object} An object containing all configuration properties for
|
* the layer. The <dx> and <dy> properties are required to be set at
|
* construction. Any other layer properties may be set in this object.
|
*/
|
initialize: function(config) {
|
config = config || {};
|
OpenLayers.Layer.Vector.prototype.initialize.apply(this, [config.name, config]);
|
},
|
|
/**
|
* Method: setMap
|
* The layer has been added to the map.
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
setMap: function(map) {
|
OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);
|
map.events.register("moveend", this, this.onMoveEnd);
|
},
|
|
/**
|
* Method: removeMap
|
* The layer has been removed from the map.
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
removeMap: function(map) {
|
map.events.unregister("moveend", this, this.onMoveEnd);
|
OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments);
|
},
|
|
/**
|
* APIMethod: setRatio
|
* Set the grid <ratio> property and update the grid. Can only be called
|
* after the layer has been added to a map with a center/extent.
|
*
|
* Parameters:
|
* ratio - {Number}
|
*/
|
setRatio: function(ratio) {
|
this.ratio = ratio;
|
this.updateGrid(true);
|
},
|
|
/**
|
* APIMethod: setMaxFeatures
|
* Set the grid <maxFeatures> property and update the grid. Can only be
|
* called after the layer has been added to a map with a center/extent.
|
*
|
* Parameters:
|
* maxFeatures - {Number}
|
*/
|
setMaxFeatures: function(maxFeatures) {
|
this.maxFeatures = maxFeatures;
|
this.updateGrid(true);
|
},
|
|
/**
|
* APIMethod: setSpacing
|
* Set the grid <dx> and <dy> properties and update the grid. If only one
|
* argument is provided, it will be set as <dx> and <dy>. Can only be
|
* called after the layer has been added to a map with a center/extent.
|
*
|
* Parameters:
|
* dx - {Number}
|
* dy - {Number}
|
*/
|
setSpacing: function(dx, dy) {
|
this.dx = dx;
|
this.dy = dy || dx;
|
this.updateGrid(true);
|
},
|
|
/**
|
* APIMethod: setOrigin
|
* Set the grid <origin> property and update the grid. Can only be called
|
* after the layer has been added to a map with a center/extent.
|
*
|
* Parameters:
|
* origin - {<OpenLayers.LonLat>}
|
*/
|
setOrigin: function(origin) {
|
this.origin = origin;
|
this.updateGrid(true);
|
},
|
|
/**
|
* APIMethod: getOrigin
|
* Get the grid <origin> property.
|
*
|
* Returns:
|
* {<OpenLayers.LonLat>} The grid origin.
|
*/
|
getOrigin: function() {
|
if (!this.origin) {
|
this.origin = this.map.getExtent().getCenterLonLat();
|
}
|
return this.origin;
|
},
|
|
/**
|
* APIMethod: setRotation
|
* Set the grid <rotation> property and update the grid. Rotation values
|
* are in degrees clockwise from the positive x-axis (negative values
|
* for counter-clockwise rotation). Can only be called after the layer
|
* has been added to a map with a center/extent.
|
*
|
* Parameters:
|
* rotation - {Number} Degrees clockwise from the positive x-axis.
|
*/
|
setRotation: function(rotation) {
|
this.rotation = rotation;
|
this.updateGrid(true);
|
},
|
|
/**
|
* Method: onMoveEnd
|
* Listener for map "moveend" events.
|
*/
|
onMoveEnd: function() {
|
this.updateGrid();
|
},
|
|
/**
|
* Method: getViewBounds
|
* Gets the (potentially rotated) view bounds for grid calculations.
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>}
|
*/
|
getViewBounds: function() {
|
var bounds = this.map.getExtent();
|
if (this.rotation) {
|
var origin = this.getOrigin();
|
var rotationOrigin = new OpenLayers.Geometry.Point(origin.lon, origin.lat);
|
var rect = bounds.toGeometry();
|
rect.rotate(-this.rotation, rotationOrigin);
|
bounds = rect.getBounds();
|
}
|
return bounds;
|
},
|
|
/**
|
* Method: updateGrid
|
* Update the grid.
|
*
|
* Parameters:
|
* force - {Boolean} Update the grid even if the previous bounds are still
|
* valid.
|
*/
|
updateGrid: function(force) {
|
if (force || this.invalidBounds()) {
|
var viewBounds = this.getViewBounds();
|
var origin = this.getOrigin();
|
var rotationOrigin = new OpenLayers.Geometry.Point(origin.lon, origin.lat);
|
var viewBoundsWidth = viewBounds.getWidth();
|
var viewBoundsHeight = viewBounds.getHeight();
|
var aspectRatio = viewBoundsWidth / viewBoundsHeight;
|
var maxHeight = Math.sqrt(this.dx * this.dy * this.maxFeatures / aspectRatio);
|
var maxWidth = maxHeight * aspectRatio;
|
var gridWidth = Math.min(viewBoundsWidth * this.ratio, maxWidth);
|
var gridHeight = Math.min(viewBoundsHeight * this.ratio, maxHeight);
|
var center = viewBounds.getCenterLonLat();
|
this.gridBounds = new OpenLayers.Bounds(
|
center.lon - (gridWidth / 2),
|
center.lat - (gridHeight / 2),
|
center.lon + (gridWidth / 2),
|
center.lat + (gridHeight / 2)
|
);
|
var rows = Math.floor(gridHeight / this.dy);
|
var cols = Math.floor(gridWidth / this.dx);
|
var gridLeft = origin.lon + (this.dx * Math.ceil((this.gridBounds.left - origin.lon) / this.dx));
|
var gridBottom = origin.lat + (this.dy * Math.ceil((this.gridBounds.bottom - origin.lat) / this.dy));
|
var features = new Array(rows * cols);
|
var x, y, point;
|
for (var i=0; i<cols; ++i) {
|
x = gridLeft + (i * this.dx);
|
for (var j=0; j<rows; ++j) {
|
y = gridBottom + (j * this.dy);
|
point = new OpenLayers.Geometry.Point(x, y);
|
if (this.rotation) {
|
point.rotate(this.rotation, rotationOrigin);
|
}
|
features[(i*rows)+j] = new OpenLayers.Feature.Vector(point);
|
}
|
}
|
this.destroyFeatures(this.features, {silent: true});
|
this.addFeatures(features, {silent: true});
|
}
|
},
|
|
/**
|
* Method: invalidBounds
|
* Determine whether the previously generated point grid is invalid.
|
* This occurs when the map bounds extends beyond the previously
|
* generated grid bounds.
|
*
|
* Returns:
|
* {Boolean}
|
*/
|
invalidBounds: function() {
|
return !this.gridBounds || !this.gridBounds.containsBounds(this.getViewBounds());
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.PointGrid"
|
|
});
|
/* ======================================================================
|
OpenLayers/Handler/MouseWheel.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Handler.js
|
*/
|
|
/**
|
* Class: OpenLayers.Handler.MouseWheel
|
* Handler for wheel up/down events.
|
*
|
* Inherits from:
|
* - <OpenLayers.Handler>
|
*/
|
OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, {
|
/**
|
* Property: wheelListener
|
* {function}
|
*/
|
wheelListener: null,
|
|
/**
|
* Property: interval
|
* {Integer} In order to increase server performance, an interval (in
|
* milliseconds) can be set to reduce the number of up/down events
|
* called. If set, a new up/down event will not be set until the
|
* interval has passed.
|
* Defaults to 0, meaning no interval.
|
*/
|
interval: 0,
|
|
/**
|
* Property: maxDelta
|
* {Integer} Maximum delta to collect before breaking from the current
|
* interval. In cumulative mode, this also limits the maximum delta
|
* returned from the handler. Default is Number.POSITIVE_INFINITY.
|
*/
|
maxDelta: Number.POSITIVE_INFINITY,
|
|
/**
|
* Property: delta
|
* {Integer} When interval is set, delta collects the mousewheel z-deltas
|
* of the events that occur within the interval.
|
* See also the cumulative option
|
*/
|
delta: 0,
|
|
/**
|
* Property: cumulative
|
* {Boolean} When interval is set: true to collect all the mousewheel
|
* z-deltas, false to only record the delta direction (positive or
|
* negative)
|
*/
|
cumulative: true,
|
|
/**
|
* Constructor: OpenLayers.Handler.MouseWheel
|
*
|
* Parameters:
|
* control - {<OpenLayers.Control>}
|
* callbacks - {Object} An object containing a single function to be
|
* called when the drag operation is finished.
|
* The callback should expect to recieve a single
|
* argument, the point geometry.
|
* options - {Object}
|
*/
|
initialize: function(control, callbacks, options) {
|
OpenLayers.Handler.prototype.initialize.apply(this, arguments);
|
this.wheelListener = OpenLayers.Function.bindAsEventListener(
|
this.onWheelEvent, this
|
);
|
},
|
|
/**
|
* Method: destroy
|
*/
|
destroy: function() {
|
OpenLayers.Handler.prototype.destroy.apply(this, arguments);
|
this.wheelListener = null;
|
},
|
|
/**
|
* Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/
|
*/
|
|
/**
|
* Method: onWheelEvent
|
* Catch the wheel event and handle it xbrowserly
|
*
|
* Parameters:
|
* e - {Event}
|
*/
|
onWheelEvent: function(e){
|
|
// make sure we have a map and check keyboard modifiers
|
if (!this.map || !this.checkModifiers(e)) {
|
return;
|
}
|
|
// Ride up the element's DOM hierarchy to determine if it or any of
|
// its ancestors was:
|
// * specifically marked as scrollable (CSS overflow property)
|
// * one of our layer divs or a div marked as scrollable
|
// ('olScrollable' CSS class)
|
// * the map div
|
//
|
var overScrollableDiv = false;
|
var allowScroll = false;
|
var overMapDiv = false;
|
|
var elem = OpenLayers.Event.element(e);
|
while((elem != null) && !overMapDiv && !overScrollableDiv) {
|
|
if (!overScrollableDiv) {
|
try {
|
var overflow;
|
if (elem.currentStyle) {
|
overflow = elem.currentStyle["overflow"];
|
} else {
|
var style =
|
document.defaultView.getComputedStyle(elem, null);
|
overflow = style.getPropertyValue("overflow");
|
}
|
overScrollableDiv = ( overflow &&
|
(overflow == "auto") || (overflow == "scroll") );
|
} catch(err) {
|
//sometimes when scrolling in a popup, this causes
|
// obscure browser error
|
}
|
}
|
|
if (!allowScroll) {
|
allowScroll = OpenLayers.Element.hasClass(elem, 'olScrollable');
|
if (!allowScroll) {
|
for (var i = 0, len = this.map.layers.length; i < len; i++) {
|
// Are we in the layer div? Note that we have two cases
|
// here: one is to catch EventPane layers, which have a
|
// pane above the layer (layer.pane)
|
var layer = this.map.layers[i];
|
if (elem == layer.div || elem == layer.pane) {
|
allowScroll = true;
|
break;
|
}
|
}
|
}
|
}
|
overMapDiv = (elem == this.map.div);
|
|
elem = elem.parentNode;
|
}
|
|
// Logic below is the following:
|
//
|
// If we are over a scrollable div or not over the map div:
|
// * do nothing (let the browser handle scrolling)
|
//
|
// otherwise
|
//
|
// If we are over the layer div or a 'olScrollable' div:
|
// * zoom/in out
|
// then
|
// * kill event (so as not to also scroll the page after zooming)
|
//
|
// otherwise
|
//
|
// Kill the event (dont scroll the page if we wheel over the
|
// layerswitcher or the pan/zoom control)
|
//
|
if (!overScrollableDiv && overMapDiv) {
|
if (allowScroll) {
|
var delta = 0;
|
|
if (e.wheelDelta) {
|
delta = e.wheelDelta;
|
if (delta % 160 === 0) {
|
// opera have steps of 160 instead of 120
|
delta = delta * 0.75;
|
}
|
delta = delta / 120;
|
} else if (e.detail) {
|
// detail in Firefox on OS X is 1/3 of Windows
|
// so force delta 1 / -1
|
delta = - (e.detail / Math.abs(e.detail));
|
}
|
this.delta += delta;
|
|
window.clearTimeout(this._timeoutId);
|
if(this.interval && Math.abs(this.delta) < this.maxDelta) {
|
// store e because window.event might change during delay
|
var evt = OpenLayers.Util.extend({}, e);
|
this._timeoutId = window.setTimeout(
|
OpenLayers.Function.bind(function(){
|
this.wheelZoom(evt);
|
}, this),
|
this.interval
|
);
|
} else {
|
this.wheelZoom(e);
|
}
|
}
|
OpenLayers.Event.stop(e);
|
}
|
},
|
|
/**
|
* Method: wheelZoom
|
* Given the wheel event, we carry out the appropriate zooming in or out,
|
* based on the 'wheelDelta' or 'detail' property of the event.
|
*
|
* Parameters:
|
* e - {Event}
|
*/
|
wheelZoom: function(e) {
|
var delta = this.delta;
|
this.delta = 0;
|
|
if (delta) {
|
e.xy = this.map.events.getMousePosition(e);
|
if (delta < 0) {
|
this.callback("down",
|
[e, this.cumulative ? Math.max(-this.maxDelta, delta) : -1]);
|
} else {
|
this.callback("up",
|
[e, this.cumulative ? Math.min(this.maxDelta, delta) : 1]);
|
}
|
}
|
},
|
|
/**
|
* Method: activate
|
*/
|
activate: function (evt) {
|
if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
|
//register mousewheel events specifically on the window and document
|
var wheelListener = this.wheelListener;
|
OpenLayers.Event.observe(window, "DOMMouseScroll", wheelListener);
|
OpenLayers.Event.observe(window, "mousewheel", wheelListener);
|
OpenLayers.Event.observe(document, "mousewheel", wheelListener);
|
return true;
|
} else {
|
return false;
|
}
|
},
|
|
/**
|
* Method: deactivate
|
*/
|
deactivate: function (evt) {
|
if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
|
// unregister mousewheel events specifically on the window and document
|
var wheelListener = this.wheelListener;
|
OpenLayers.Event.stopObserving(window, "DOMMouseScroll", wheelListener);
|
OpenLayers.Event.stopObserving(window, "mousewheel", wheelListener);
|
OpenLayers.Event.stopObserving(document, "mousewheel", wheelListener);
|
return true;
|
} else {
|
return false;
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Handler.MouseWheel"
|
});
|
/* ======================================================================
|
OpenLayers/Symbolizer.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
*/
|
|
/**
|
* Class: OpenLayers.Symbolizer
|
* Base class representing a symbolizer used for feature rendering.
|
*/
|
OpenLayers.Symbolizer = OpenLayers.Class({
|
|
|
/**
|
* APIProperty: zIndex
|
* {Number} The zIndex determines the rendering order for a symbolizer.
|
* Symbolizers with larger zIndex values are rendered over symbolizers
|
* with smaller zIndex values. Default is 0.
|
*/
|
zIndex: 0,
|
|
/**
|
* Constructor: OpenLayers.Symbolizer
|
* Instances of this class are not useful. See one of the subclasses.
|
*
|
* Parameters:
|
* config - {Object} An object containing properties to be set on the
|
* symbolizer. Any documented symbolizer property can be set at
|
* construction.
|
*
|
* Returns:
|
* A new symbolizer.
|
*/
|
initialize: function(config) {
|
OpenLayers.Util.extend(this, config);
|
},
|
|
/**
|
* APIMethod: clone
|
* Create a copy of this symbolizer.
|
*
|
* Returns a symbolizer of the same type with the same properties.
|
*/
|
clone: function() {
|
var Type = eval(this.CLASS_NAME);
|
return new Type(OpenLayers.Util.extend({}, this));
|
},
|
|
CLASS_NAME: "OpenLayers.Symbolizer"
|
|
});
|
|
/* ======================================================================
|
OpenLayers/Symbolizer/Raster.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Symbolizer.js
|
*/
|
|
/**
|
* Class: OpenLayers.Symbolizer.Raster
|
* A symbolizer used to render raster images.
|
*/
|
OpenLayers.Symbolizer.Raster = OpenLayers.Class(OpenLayers.Symbolizer, {
|
|
/**
|
* Constructor: OpenLayers.Symbolizer.Raster
|
* Create a symbolizer for rendering rasters.
|
*
|
* Parameters:
|
* config - {Object} An object containing properties to be set on the
|
* symbolizer. Any documented symbolizer property can be set at
|
* construction.
|
*
|
* Returns:
|
* A new raster symbolizer.
|
*/
|
initialize: function(config) {
|
OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);
|
},
|
|
CLASS_NAME: "OpenLayers.Symbolizer.Raster"
|
|
});
|
/* ======================================================================
|
OpenLayers/Rule.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
* @requires OpenLayers/Util.js
|
* @requires OpenLayers/Style.js
|
*/
|
|
/**
|
* Class: OpenLayers.Rule
|
* This class represents an SLD Rule, as being used for rule-based SLD styling.
|
*/
|
OpenLayers.Rule = OpenLayers.Class({
|
|
/**
|
* Property: id
|
* {String} A unique id for this session.
|
*/
|
id: null,
|
|
/**
|
* APIProperty: name
|
* {String} name of this rule
|
*/
|
name: null,
|
|
/**
|
* Property: title
|
* {String} Title of this rule (set if included in SLD)
|
*/
|
title: null,
|
|
/**
|
* Property: description
|
* {String} Description of this rule (set if abstract is included in SLD)
|
*/
|
description: null,
|
|
/**
|
* Property: context
|
* {Object} An optional object with properties that the rule should be
|
* evaluated against. If no context is specified, feature.attributes will
|
* be used.
|
*/
|
context: null,
|
|
/**
|
* Property: filter
|
* {<OpenLayers.Filter>} Optional filter for the rule.
|
*/
|
filter: null,
|
|
/**
|
* Property: elseFilter
|
* {Boolean} Determines whether this rule is only to be applied only if
|
* no other rules match (ElseFilter according to the SLD specification).
|
* Default is false. For instances of OpenLayers.Rule, if elseFilter is
|
* false, the rule will always apply. For subclasses, the else property is
|
* ignored.
|
*/
|
elseFilter: false,
|
|
/**
|
* Property: symbolizer
|
* {Object} Symbolizer or hash of symbolizers for this rule. If hash of
|
* symbolizers, keys are one or more of ["Point", "Line", "Polygon"]. The
|
* latter if useful if it is required to style e.g. vertices of a line
|
* with a point symbolizer. Note, however, that this is not implemented
|
* yet in OpenLayers, but it is the way how symbolizers are defined in
|
* SLD.
|
*/
|
symbolizer: null,
|
|
/**
|
* Property: symbolizers
|
* {Array} Collection of symbolizers associated with this rule. If
|
* provided at construction, the symbolizers array has precedence
|
* over the deprecated symbolizer property. Note that multiple
|
* symbolizers are not currently supported by the vector renderers.
|
* Rules with multiple symbolizers are currently only useful for
|
* maintaining elements in an SLD document.
|
*/
|
symbolizers: null,
|
|
/**
|
* APIProperty: minScaleDenominator
|
* {Number} or {String} minimum scale at which to draw the feature.
|
* In the case of a String, this can be a combination of text and
|
* propertyNames in the form "literal ${propertyName}"
|
*/
|
minScaleDenominator: null,
|
|
/**
|
* APIProperty: maxScaleDenominator
|
* {Number} or {String} maximum scale at which to draw the feature.
|
* In the case of a String, this can be a combination of text and
|
* propertyNames in the form "literal ${propertyName}"
|
*/
|
maxScaleDenominator: null,
|
|
/**
|
* Constructor: OpenLayers.Rule
|
* Creates a Rule.
|
*
|
* Parameters:
|
* options - {Object} An optional object with properties to set on the
|
* rule
|
*
|
* Returns:
|
* {<OpenLayers.Rule>}
|
*/
|
initialize: function(options) {
|
this.symbolizer = {};
|
OpenLayers.Util.extend(this, options);
|
if (this.symbolizers) {
|
delete this.symbolizer;
|
}
|
this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
|
},
|
|
/**
|
* APIMethod: destroy
|
* nullify references to prevent circular references and memory leaks
|
*/
|
destroy: function() {
|
for (var i in this.symbolizer) {
|
this.symbolizer[i] = null;
|
}
|
this.symbolizer = null;
|
delete this.symbolizers;
|
},
|
|
/**
|
* APIMethod: evaluate
|
* evaluates this rule for a specific feature
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature>} feature to apply the rule to.
|
*
|
* Returns:
|
* {Boolean} true if the rule applies, false if it does not.
|
* This rule is the default rule and always returns true.
|
*/
|
evaluate: function(feature) {
|
var context = this.getContext(feature);
|
var applies = true;
|
|
if (this.minScaleDenominator || this.maxScaleDenominator) {
|
var scale = feature.layer.map.getScale();
|
}
|
|
// check if within minScale/maxScale bounds
|
if (this.minScaleDenominator) {
|
applies = scale >= OpenLayers.Style.createLiteral(
|
this.minScaleDenominator, context);
|
}
|
if (applies && this.maxScaleDenominator) {
|
applies = scale < OpenLayers.Style.createLiteral(
|
this.maxScaleDenominator, context);
|
}
|
|
// check if optional filter applies
|
if(applies && this.filter) {
|
// feature id filters get the feature, others get the context
|
if(this.filter.CLASS_NAME == "OpenLayers.Filter.FeatureId") {
|
applies = this.filter.evaluate(feature);
|
} else {
|
applies = this.filter.evaluate(context);
|
}
|
}
|
|
return applies;
|
},
|
|
/**
|
* Method: getContext
|
* Gets the context for evaluating this rule
|
*
|
* Paramters:
|
* feature - {<OpenLayers.Feature>} feature to take the context from if
|
* none is specified.
|
*/
|
getContext: function(feature) {
|
var context = this.context;
|
if (!context) {
|
context = feature.attributes || feature.data;
|
}
|
if (typeof this.context == "function") {
|
context = this.context(feature);
|
}
|
return context;
|
},
|
|
/**
|
* APIMethod: clone
|
* Clones this rule.
|
*
|
* Returns:
|
* {<OpenLayers.Rule>} Clone of this rule.
|
*/
|
clone: function() {
|
var options = OpenLayers.Util.extend({}, this);
|
if (this.symbolizers) {
|
// clone symbolizers
|
var len = this.symbolizers.length;
|
options.symbolizers = new Array(len);
|
for (var i=0; i<len; ++i) {
|
options.symbolizers[i] = this.symbolizers[i].clone();
|
}
|
} else {
|
// clone symbolizer
|
options.symbolizer = {};
|
var value, type;
|
for(var key in this.symbolizer) {
|
value = this.symbolizer[key];
|
type = typeof value;
|
if(type === "object") {
|
options.symbolizer[key] = OpenLayers.Util.extend({}, value);
|
} else if(type === "string") {
|
options.symbolizer[key] = value;
|
}
|
}
|
}
|
// clone filter
|
options.filter = this.filter && this.filter.clone();
|
// clone context
|
options.context = this.context && OpenLayers.Util.extend({}, this.context);
|
return new OpenLayers.Rule(options);
|
},
|
|
CLASS_NAME: "OpenLayers.Rule"
|
});
|
/* ======================================================================
|
OpenLayers/Format/SLD.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML/VersionedOGC.js
|
* @requires OpenLayers/Style.js
|
* @requires OpenLayers/Rule.js
|
* @requires OpenLayers/Filter/FeatureId.js
|
* @requires OpenLayers/Filter/Logical.js
|
* @requires OpenLayers/Filter/Comparison.js
|
* @requires OpenLayers/Filter/Spatial.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.SLD
|
* Read/Write SLD. Create a new instance with the <OpenLayers.Format.SLD>
|
* constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML.VersionedOGC>
|
*/
|
OpenLayers.Format.SLD = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
|
|
/**
|
* APIProperty: profile
|
* {String} If provided, use a custom profile.
|
*
|
* Currently supported profiles:
|
* - GeoServer - parses GeoServer vendor specific capabilities for SLD.
|
*/
|
profile: null,
|
|
/**
|
* APIProperty: defaultVersion
|
* {String} Version number to assume if none found. Default is "1.0.0".
|
*/
|
defaultVersion: "1.0.0",
|
|
/**
|
* APIProperty: stringifyOutput
|
* {Boolean} If true, write will return a string otherwise a DOMElement.
|
* Default is true.
|
*/
|
stringifyOutput: true,
|
|
/**
|
* APIProperty: namedLayersAsArray
|
* {Boolean} Generate a namedLayers array. If false, the namedLayers
|
* property value will be an object keyed by layer name. Default is
|
* false.
|
*/
|
namedLayersAsArray: false,
|
|
/**
|
* APIMethod: write
|
* Write a SLD document given a list of styles.
|
*
|
* Parameters:
|
* sld - {Object} An object representing the SLD.
|
* options - {Object} Optional configuration object.
|
*
|
* Returns:
|
* {String} An SLD document string.
|
*/
|
|
/**
|
* APIMethod: read
|
* Read and SLD doc and return an object representing the SLD.
|
*
|
* Parameters:
|
* data - {String | DOMElement} Data to read.
|
* options - {Object} Options for the reader.
|
*
|
* Returns:
|
* {Object} An object representing the SLD.
|
*/
|
|
CLASS_NAME: "OpenLayers.Format.SLD"
|
});
|
/* ======================================================================
|
OpenLayers/Symbolizer/Polygon.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Symbolizer.js
|
*/
|
|
/**
|
* Class: OpenLayers.Symbolizer.Polygon
|
* A symbolizer used to render line features.
|
*/
|
OpenLayers.Symbolizer.Polygon = OpenLayers.Class(OpenLayers.Symbolizer, {
|
|
/**
|
* APIProperty: strokeColor
|
* {String} Color for line stroke. This is a RGB hex value (e.g. "#ff0000"
|
* for red).
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* APIProperty: strokeOpacity
|
* {Number} Stroke opacity (0-1).
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* APIProperty: strokeWidth
|
* {Number} Pixel stroke width.
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* APIProperty: strokeLinecap
|
* {String} Stroke cap type ("butt", "round", or "square").
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* Property: strokeDashstyle
|
* {String} Stroke dash style according to the SLD spec. Note that the
|
* OpenLayers values for strokeDashstyle ("dot", "dash", "dashdot",
|
* "longdash", "longdashdot", or "solid") will not work in SLD, but
|
* most SLD patterns will render correctly in OpenLayers.
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* APIProperty: fillColor
|
* {String} RGB hex fill color (e.g. "#ff0000" for red).
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* APIProperty: fillOpacity
|
* {Number} Fill opacity (0-1).
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* Constructor: OpenLayers.Symbolizer.Polygon
|
* Create a symbolizer for rendering polygons.
|
*
|
* Parameters:
|
* config - {Object} An object containing properties to be set on the
|
* symbolizer. Any documented symbolizer property can be set at
|
* construction.
|
*
|
* Returns:
|
* A new polygon symbolizer.
|
*/
|
initialize: function(config) {
|
OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);
|
},
|
|
CLASS_NAME: "OpenLayers.Symbolizer.Polygon"
|
|
});
|
|
/* ======================================================================
|
OpenLayers/Format/GML/v2.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/GML/Base.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.GML.v2
|
* Parses GML version 2.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.GML.Base>
|
*/
|
OpenLayers.Format.GML.v2 = OpenLayers.Class(OpenLayers.Format.GML.Base, {
|
|
/**
|
* Property: schemaLocation
|
* {String} Schema location for a particular minor version.
|
*/
|
schemaLocation: "http://www.opengis.net/gml http://schemas.opengis.net/gml/2.1.2/feature.xsd",
|
|
/**
|
* Constructor: OpenLayers.Format.GML.v2
|
* Create a parser for GML v2.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*
|
* Valid options properties:
|
* featureType - {String} Local (without prefix) feature typeName (required).
|
* featureNS - {String} Feature namespace (required).
|
* geometryName - {String} Geometry element name.
|
*/
|
initialize: function(options) {
|
OpenLayers.Format.GML.Base.prototype.initialize.apply(this, [options]);
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"gml": OpenLayers.Util.applyDefaults({
|
"outerBoundaryIs": function(node, container) {
|
var obj = {};
|
this.readChildNodes(node, obj);
|
container.outer = obj.components[0];
|
},
|
"innerBoundaryIs": function(node, container) {
|
var obj = {};
|
this.readChildNodes(node, obj);
|
container.inner.push(obj.components[0]);
|
},
|
"Box": function(node, container) {
|
var obj = {};
|
this.readChildNodes(node, obj);
|
if(!container.components) {
|
container.components = [];
|
}
|
var min = obj.points[0];
|
var max = obj.points[1];
|
container.components.push(
|
new OpenLayers.Bounds(min.x, min.y, max.x, max.y)
|
);
|
}
|
}, OpenLayers.Format.GML.Base.prototype.readers["gml"]),
|
"feature": OpenLayers.Format.GML.Base.prototype.readers["feature"],
|
"wfs": OpenLayers.Format.GML.Base.prototype.readers["wfs"]
|
},
|
|
/**
|
* Method: write
|
*
|
* Parameters:
|
* features - {Array(<OpenLayers.Feature.Vector>) | OpenLayers.Feature.Vector}
|
* An array of features or a single feature.
|
*
|
* Returns:
|
* {String} Given an array of features, a doc with a gml:featureMembers
|
* element will be returned. Given a single feature, a doc with a
|
* gml:featureMember element will be returned.
|
*/
|
write: function(features) {
|
var name;
|
if(OpenLayers.Util.isArray(features)) {
|
// GML2 only has abstract feature collections
|
// wfs provides a feature collection from a well-known schema
|
name = "wfs:FeatureCollection";
|
} else {
|
name = "gml:featureMember";
|
}
|
var root = this.writeNode(name, features);
|
this.setAttributeNS(
|
root, this.namespaces["xsi"],
|
"xsi:schemaLocation", this.schemaLocation
|
);
|
|
return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
|
},
|
|
/**
|
* Property: writers
|
* As a compliment to the readers property, this structure contains public
|
* writing functions grouped by namespace alias and named like the
|
* node names they produce.
|
*/
|
writers: {
|
"gml": OpenLayers.Util.applyDefaults({
|
"Point": function(geometry) {
|
var node = this.createElementNSPlus("gml:Point");
|
this.writeNode("coordinates", [geometry], node);
|
return node;
|
},
|
"coordinates": function(points) {
|
var numPoints = points.length;
|
var parts = new Array(numPoints);
|
var point;
|
for(var i=0; i<numPoints; ++i) {
|
point = points[i];
|
if(this.xy) {
|
parts[i] = point.x + "," + point.y;
|
} else {
|
parts[i] = point.y + "," + point.x;
|
}
|
if(point.z != undefined) { // allow null or undefined
|
parts[i] += "," + point.z;
|
}
|
}
|
return this.createElementNSPlus("gml:coordinates", {
|
attributes: {
|
decimal: ".", cs: ",", ts: " "
|
},
|
value: (numPoints == 1) ? parts[0] : parts.join(" ")
|
});
|
},
|
"LineString": function(geometry) {
|
var node = this.createElementNSPlus("gml:LineString");
|
this.writeNode("coordinates", geometry.components, node);
|
return node;
|
},
|
"Polygon": function(geometry) {
|
var node = this.createElementNSPlus("gml:Polygon");
|
this.writeNode("outerBoundaryIs", geometry.components[0], node);
|
for(var i=1; i<geometry.components.length; ++i) {
|
this.writeNode(
|
"innerBoundaryIs", geometry.components[i], node
|
);
|
}
|
return node;
|
},
|
"outerBoundaryIs": function(ring) {
|
var node = this.createElementNSPlus("gml:outerBoundaryIs");
|
this.writeNode("LinearRing", ring, node);
|
return node;
|
},
|
"innerBoundaryIs": function(ring) {
|
var node = this.createElementNSPlus("gml:innerBoundaryIs");
|
this.writeNode("LinearRing", ring, node);
|
return node;
|
},
|
"LinearRing": function(ring) {
|
var node = this.createElementNSPlus("gml:LinearRing");
|
this.writeNode("coordinates", ring.components, node);
|
return node;
|
},
|
"Box": function(bounds) {
|
var node = this.createElementNSPlus("gml:Box");
|
this.writeNode("coordinates", [
|
{x: bounds.left, y: bounds.bottom},
|
{x: bounds.right, y: bounds.top}
|
], node);
|
// srsName attribute is optional for gml:Box
|
if(this.srsName) {
|
node.setAttribute("srsName", this.srsName);
|
}
|
return node;
|
}
|
}, OpenLayers.Format.GML.Base.prototype.writers["gml"]),
|
"feature": OpenLayers.Format.GML.Base.prototype.writers["feature"],
|
"wfs": OpenLayers.Format.GML.Base.prototype.writers["wfs"]
|
},
|
|
CLASS_NAME: "OpenLayers.Format.GML.v2"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/Filter/v1_0_0.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/GML/v2.js
|
* @requires OpenLayers/Format/Filter/v1.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.Filter.v1_0_0
|
* Write ogc:Filter version 1.0.0.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.GML.v2>
|
* - <OpenLayers.Format.Filter.v1>
|
*/
|
OpenLayers.Format.Filter.v1_0_0 = OpenLayers.Class(
|
OpenLayers.Format.GML.v2, OpenLayers.Format.Filter.v1, {
|
|
/**
|
* Constant: VERSION
|
* {String} 1.0.0
|
*/
|
VERSION: "1.0.0",
|
|
/**
|
* Property: schemaLocation
|
* {String} http://www.opengis.net/ogc/filter/1.0.0/filter.xsd
|
*/
|
schemaLocation: "http://www.opengis.net/ogc/filter/1.0.0/filter.xsd",
|
|
/**
|
* Constructor: OpenLayers.Format.Filter.v1_0_0
|
* Instances of this class are not created directly. Use the
|
* <OpenLayers.Format.Filter> constructor instead.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
initialize: function(options) {
|
OpenLayers.Format.GML.v2.prototype.initialize.apply(
|
this, [options]
|
);
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"ogc": OpenLayers.Util.applyDefaults({
|
"PropertyIsEqualTo": function(node, obj) {
|
var filter = new OpenLayers.Filter.Comparison({
|
type: OpenLayers.Filter.Comparison.EQUAL_TO
|
});
|
this.readChildNodes(node, filter);
|
obj.filters.push(filter);
|
},
|
"PropertyIsNotEqualTo": function(node, obj) {
|
var filter = new OpenLayers.Filter.Comparison({
|
type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO
|
});
|
this.readChildNodes(node, filter);
|
obj.filters.push(filter);
|
},
|
"PropertyIsLike": function(node, obj) {
|
var filter = new OpenLayers.Filter.Comparison({
|
type: OpenLayers.Filter.Comparison.LIKE
|
});
|
this.readChildNodes(node, filter);
|
var wildCard = node.getAttribute("wildCard");
|
var singleChar = node.getAttribute("singleChar");
|
var esc = node.getAttribute("escape");
|
filter.value2regex(wildCard, singleChar, esc);
|
obj.filters.push(filter);
|
}
|
}, OpenLayers.Format.Filter.v1.prototype.readers["ogc"]),
|
"gml": OpenLayers.Format.GML.v2.prototype.readers["gml"],
|
"feature": OpenLayers.Format.GML.v2.prototype.readers["feature"]
|
},
|
|
/**
|
* Property: writers
|
* As a compliment to the readers property, this structure contains public
|
* writing functions grouped by namespace alias and named like the
|
* node names they produce.
|
*/
|
writers: {
|
"ogc": OpenLayers.Util.applyDefaults({
|
"PropertyIsEqualTo": function(filter) {
|
var node = this.createElementNSPlus("ogc:PropertyIsEqualTo");
|
// no ogc:expression handling for PropertyName for now
|
this.writeNode("PropertyName", filter, node);
|
// handle Literals or Functions for now
|
this.writeOgcExpression(filter.value, node);
|
return node;
|
},
|
"PropertyIsNotEqualTo": function(filter) {
|
var node = this.createElementNSPlus("ogc:PropertyIsNotEqualTo");
|
// no ogc:expression handling for PropertyName for now
|
this.writeNode("PropertyName", filter, node);
|
// handle Literals or Functions for now
|
this.writeOgcExpression(filter.value, node);
|
return node;
|
},
|
"PropertyIsLike": function(filter) {
|
var node = this.createElementNSPlus("ogc:PropertyIsLike", {
|
attributes: {
|
wildCard: "*", singleChar: ".", escape: "!"
|
}
|
});
|
// no ogc:expression handling for now
|
this.writeNode("PropertyName", filter, node);
|
// convert regex string to ogc string
|
this.writeNode("Literal", filter.regex2value(), node);
|
return node;
|
},
|
"BBOX": function(filter) {
|
var node = this.createElementNSPlus("ogc:BBOX");
|
// PropertyName is mandatory in 1.0.0, but e.g. GeoServer also
|
// accepts filters without it. When this is used with
|
// OpenLayers.Protocol.WFS, OpenLayers.Format.WFST will set a
|
// missing filter.property to the geometryName that is
|
// configured with the protocol, which defaults to "the_geom".
|
// So the only way to omit this mandatory property is to not
|
// set the property on the filter and to set the geometryName
|
// on the WFS protocol to null. The latter also happens when
|
// the protocol is configured without a geometryName and a
|
// featureNS.
|
filter.property && this.writeNode("PropertyName", filter, node);
|
var box = this.writeNode("gml:Box", filter.value, node);
|
if(filter.projection) {
|
box.setAttribute("srsName", filter.projection);
|
}
|
return node;
|
}
|
}, OpenLayers.Format.Filter.v1.prototype.writers["ogc"]),
|
"gml": OpenLayers.Format.GML.v2.prototype.writers["gml"],
|
"feature": OpenLayers.Format.GML.v2.prototype.writers["feature"]
|
},
|
|
/**
|
* Method: writeSpatial
|
*
|
* Read a {<OpenLayers.Filter.Spatial>} filter and converts it into XML.
|
*
|
* Parameters:
|
* filter - {<OpenLayers.Filter.Spatial>} The filter.
|
* name - {String} Name of the generated XML element.
|
*
|
* Returns:
|
* {DOMElement} The created XML element.
|
*/
|
writeSpatial: function(filter, name) {
|
var node = this.createElementNSPlus("ogc:"+name);
|
this.writeNode("PropertyName", filter, node);
|
if(filter.value instanceof OpenLayers.Filter.Function) {
|
this.writeNode("Function", filter.value, node);
|
} else {
|
var child;
|
if(filter.value instanceof OpenLayers.Geometry) {
|
child = this.writeNode("feature:_geometry", filter.value).firstChild;
|
} else {
|
child = this.writeNode("gml:Box", filter.value);
|
}
|
if(filter.projection) {
|
child.setAttribute("srsName", filter.projection);
|
}
|
node.appendChild(child);
|
}
|
return node;
|
},
|
|
|
CLASS_NAME: "OpenLayers.Format.Filter.v1_0_0"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/WFST/v1_0_0.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/WFST/v1.js
|
* @requires OpenLayers/Format/Filter/v1_0_0.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WFST.v1_0_0
|
* A format for creating WFS v1.0.0 transactions. Create a new instance with the
|
* <OpenLayers.Format.WFST.v1_0_0> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.Filter.v1_0_0>
|
* - <OpenLayers.Format.WFST.v1>
|
*/
|
OpenLayers.Format.WFST.v1_0_0 = OpenLayers.Class(
|
OpenLayers.Format.Filter.v1_0_0, OpenLayers.Format.WFST.v1, {
|
|
/**
|
* Property: version
|
* {String} WFS version number.
|
*/
|
version: "1.0.0",
|
|
/**
|
* APIProperty: srsNameInQuery
|
* {Boolean} If true the reference system is passed in Query requests
|
* via the "srsName" attribute to the "wfs:Query" element, this
|
* property defaults to false as it isn't WFS 1.0.0 compliant.
|
*/
|
srsNameInQuery: false,
|
|
/**
|
* Property: schemaLocations
|
* {Object} Properties are namespace aliases, values are schema locations.
|
*/
|
schemaLocations: {
|
"wfs": "http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd"
|
},
|
|
/**
|
* Constructor: OpenLayers.Format.WFST.v1_0_0
|
* A class for parsing and generating WFS v1.0.0 transactions.
|
*
|
* Parameters:
|
* options - {Object} Optional object whose properties will be set on the
|
* instance.
|
*
|
* Valid options properties:
|
* featureType - {String} Local (without prefix) feature typeName (required).
|
* featureNS - {String} Feature namespace (optional).
|
* featurePrefix - {String} Feature namespace alias (optional - only used
|
* if featureNS is provided). Default is 'feature'.
|
* geometryName - {String} Name of geometry attribute. Default is 'the_geom'.
|
*/
|
initialize: function(options) {
|
OpenLayers.Format.Filter.v1_0_0.prototype.initialize.apply(this, [options]);
|
OpenLayers.Format.WFST.v1.prototype.initialize.apply(this, [options]);
|
},
|
|
/**
|
* Method: readNode
|
* Shorthand for applying one of the named readers given the node
|
* namespace and local name. Readers take two args (node, obj) and
|
* generally extend or modify the second.
|
*
|
* Parameters:
|
* node - {DOMElement} The node to be read (required).
|
* obj - {Object} The object to be modified (optional).
|
* first - {Boolean} Should be set to true for the first node read. This
|
* is usually the readNode call in the read method. Without this being
|
* set, auto-configured properties will stick on subsequent reads.
|
*
|
* Returns:
|
* {Object} The input object, modified (or a new one if none was provided).
|
*/
|
readNode: function(node, obj, first) {
|
// Not the superclass, only the mixin classes inherit from
|
// Format.GML.v2. We need this because we don't want to get readNode
|
// from the superclass's superclass, which is OpenLayers.Format.XML.
|
return OpenLayers.Format.GML.v2.prototype.readNode.apply(this, arguments);
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"wfs": OpenLayers.Util.applyDefaults({
|
"WFS_TransactionResponse": function(node, obj) {
|
obj.insertIds = [];
|
obj.success = false;
|
this.readChildNodes(node, obj);
|
},
|
"InsertResult": function(node, container) {
|
var obj = {fids: []};
|
this.readChildNodes(node, obj);
|
container.insertIds = container.insertIds.concat(obj.fids);
|
},
|
"TransactionResult": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"Status": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"SUCCESS": function(node, obj) {
|
obj.success = true;
|
}
|
}, OpenLayers.Format.WFST.v1.prototype.readers["wfs"]),
|
"gml": OpenLayers.Format.GML.v2.prototype.readers["gml"],
|
"feature": OpenLayers.Format.GML.v2.prototype.readers["feature"],
|
"ogc": OpenLayers.Format.Filter.v1_0_0.prototype.readers["ogc"]
|
},
|
|
/**
|
* Property: writers
|
* As a compliment to the readers property, this structure contains public
|
* writing functions grouped by namespace alias and named like the
|
* node names they produce.
|
*/
|
writers: {
|
"wfs": OpenLayers.Util.applyDefaults({
|
"Query": function(options) {
|
options = OpenLayers.Util.extend({
|
featureNS: this.featureNS,
|
featurePrefix: this.featurePrefix,
|
featureType: this.featureType,
|
srsName: this.srsName,
|
srsNameInQuery: this.srsNameInQuery
|
}, options);
|
var prefix = options.featurePrefix;
|
var node = this.createElementNSPlus("wfs:Query", {
|
attributes: {
|
typeName: (prefix ? prefix + ":" : "") +
|
options.featureType
|
}
|
});
|
if(options.srsNameInQuery && options.srsName) {
|
node.setAttribute("srsName", options.srsName);
|
}
|
if(options.featureNS) {
|
node.setAttribute("xmlns:" + prefix, options.featureNS);
|
}
|
if(options.propertyNames) {
|
for(var i=0,len = options.propertyNames.length; i<len; i++) {
|
this.writeNode(
|
"ogc:PropertyName",
|
{property: options.propertyNames[i]},
|
node
|
);
|
}
|
}
|
if(options.filter) {
|
this.setFilterProperty(options.filter);
|
this.writeNode("ogc:Filter", options.filter, node);
|
}
|
return node;
|
}
|
}, OpenLayers.Format.WFST.v1.prototype.writers["wfs"]),
|
"gml": OpenLayers.Format.GML.v2.prototype.writers["gml"],
|
"feature": OpenLayers.Format.GML.v2.prototype.writers["feature"],
|
"ogc": OpenLayers.Format.Filter.v1_0_0.prototype.writers["ogc"]
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WFST.v1_0_0"
|
});
|
/* ======================================================================
|
OpenLayers/Renderer/Elements.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Renderer.js
|
*/
|
|
/**
|
* Class: OpenLayers.ElementsIndexer
|
* This class takes care of figuring out which order elements should be
|
* placed in the DOM based on given indexing methods.
|
*/
|
OpenLayers.ElementsIndexer = OpenLayers.Class({
|
|
/**
|
* Property: maxZIndex
|
* {Integer} This is the largest-most z-index value for a node
|
* contained within the indexer.
|
*/
|
maxZIndex: null,
|
|
/**
|
* Property: order
|
* {Array<String>} This is an array of node id's stored in the
|
* order that they should show up on screen. Id's higher up in the
|
* array (higher array index) represent nodes with higher z-indeces.
|
*/
|
order: null,
|
|
/**
|
* Property: indices
|
* {Object} This is a hash that maps node ids to their z-index value
|
* stored in the indexer. This is done to make finding a nodes z-index
|
* value O(1).
|
*/
|
indices: null,
|
|
/**
|
* Property: compare
|
* {Function} This is the function used to determine placement of
|
* of a new node within the indexer. If null, this defaults to to
|
* the Z_ORDER_DRAWING_ORDER comparison method.
|
*/
|
compare: null,
|
|
/**
|
* APIMethod: initialize
|
* Create a new indexer with
|
*
|
* Parameters:
|
* yOrdering - {Boolean} Whether to use y-ordering.
|
*/
|
initialize: function(yOrdering) {
|
|
this.compare = yOrdering ?
|
OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER :
|
OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER;
|
|
this.clear();
|
},
|
|
/**
|
* APIMethod: insert
|
* Insert a new node into the indexer. In order to find the correct
|
* positioning for the node to be inserted, this method uses a binary
|
* search. This makes inserting O(log(n)).
|
*
|
* Parameters:
|
* newNode - {DOMElement} The new node to be inserted.
|
*
|
* Returns
|
* {DOMElement} the node before which we should insert our newNode, or
|
* null if newNode can just be appended.
|
*/
|
insert: function(newNode) {
|
// If the node is known to the indexer, remove it so we can
|
// recalculate where it should go.
|
if (this.exists(newNode)) {
|
this.remove(newNode);
|
}
|
|
var nodeId = newNode.id;
|
|
this.determineZIndex(newNode);
|
|
var leftIndex = -1;
|
var rightIndex = this.order.length;
|
var middle;
|
|
while (rightIndex - leftIndex > 1) {
|
middle = parseInt((leftIndex + rightIndex) / 2);
|
|
var placement = this.compare(this, newNode,
|
OpenLayers.Util.getElement(this.order[middle]));
|
|
if (placement > 0) {
|
leftIndex = middle;
|
} else {
|
rightIndex = middle;
|
}
|
}
|
|
this.order.splice(rightIndex, 0, nodeId);
|
this.indices[nodeId] = this.getZIndex(newNode);
|
|
// If the new node should be before another in the index
|
// order, return the node before which we have to insert the new one;
|
// else, return null to indicate that the new node can be appended.
|
return this.getNextElement(rightIndex);
|
},
|
|
/**
|
* APIMethod: remove
|
*
|
* Parameters:
|
* node - {DOMElement} The node to be removed.
|
*/
|
remove: function(node) {
|
var nodeId = node.id;
|
var arrayIndex = OpenLayers.Util.indexOf(this.order, nodeId);
|
if (arrayIndex >= 0) {
|
// Remove it from the order array, as well as deleting the node
|
// from the indeces hash.
|
this.order.splice(arrayIndex, 1);
|
delete this.indices[nodeId];
|
|
// Reset the maxium z-index based on the last item in the
|
// order array.
|
if (this.order.length > 0) {
|
var lastId = this.order[this.order.length - 1];
|
this.maxZIndex = this.indices[lastId];
|
} else {
|
this.maxZIndex = 0;
|
}
|
}
|
},
|
|
/**
|
* APIMethod: clear
|
*/
|
clear: function() {
|
this.order = [];
|
this.indices = {};
|
this.maxZIndex = 0;
|
},
|
|
/**
|
* APIMethod: exists
|
*
|
* Parameters:
|
* node - {DOMElement} The node to test for existence.
|
*
|
* Returns:
|
* {Boolean} Whether or not the node exists in the indexer?
|
*/
|
exists: function(node) {
|
return (this.indices[node.id] != null);
|
},
|
|
/**
|
* APIMethod: getZIndex
|
* Get the z-index value for the current node from the node data itself.
|
*
|
* Parameters:
|
* node - {DOMElement} The node whose z-index to get.
|
*
|
* Returns:
|
* {Integer} The z-index value for the specified node (from the node
|
* data itself).
|
*/
|
getZIndex: function(node) {
|
return node._style.graphicZIndex;
|
},
|
|
/**
|
* Method: determineZIndex
|
* Determine the z-index for the current node if there isn't one,
|
* and set the maximum value if we've found a new maximum.
|
*
|
* Parameters:
|
* node - {DOMElement}
|
*/
|
determineZIndex: function(node) {
|
var zIndex = node._style.graphicZIndex;
|
|
// Everything must have a zIndex. If none is specified,
|
// this means the user *must* (hint: assumption) want this
|
// node to succomb to drawing order. To enforce drawing order
|
// over all indexing methods, we'll create a new z-index that's
|
// greater than any currently in the indexer.
|
if (zIndex == null) {
|
zIndex = this.maxZIndex;
|
node._style.graphicZIndex = zIndex;
|
} else if (zIndex > this.maxZIndex) {
|
this.maxZIndex = zIndex;
|
}
|
},
|
|
/**
|
* APIMethod: getNextElement
|
* Get the next element in the order stack.
|
*
|
* Parameters:
|
* index - {Integer} The index of the current node in this.order.
|
*
|
* Returns:
|
* {DOMElement} the node following the index passed in, or
|
* null.
|
*/
|
getNextElement: function(index) {
|
var nextIndex = index + 1;
|
if (nextIndex < this.order.length) {
|
var nextElement = OpenLayers.Util.getElement(this.order[nextIndex]);
|
if (nextElement == undefined) {
|
nextElement = this.getNextElement(nextIndex);
|
}
|
return nextElement;
|
} else {
|
return null;
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.ElementsIndexer"
|
});
|
|
/**
|
* Namespace: OpenLayers.ElementsIndexer.IndexingMethods
|
* These are the compare methods for figuring out where a new node should be
|
* placed within the indexer. These methods are very similar to general
|
* sorting methods in that they return -1, 0, and 1 to specify the
|
* direction in which new nodes fall in the ordering.
|
*/
|
OpenLayers.ElementsIndexer.IndexingMethods = {
|
|
/**
|
* Method: Z_ORDER
|
* This compare method is used by other comparison methods.
|
* It can be used individually for ordering, but is not recommended,
|
* because it doesn't subscribe to drawing order.
|
*
|
* Parameters:
|
* indexer - {<OpenLayers.ElementsIndexer>}
|
* newNode - {DOMElement}
|
* nextNode - {DOMElement}
|
*
|
* Returns:
|
* {Integer}
|
*/
|
Z_ORDER: function(indexer, newNode, nextNode) {
|
var newZIndex = indexer.getZIndex(newNode);
|
|
var returnVal = 0;
|
if (nextNode) {
|
var nextZIndex = indexer.getZIndex(nextNode);
|
returnVal = newZIndex - nextZIndex;
|
}
|
|
return returnVal;
|
},
|
|
/**
|
* APIMethod: Z_ORDER_DRAWING_ORDER
|
* This method orders nodes by their z-index, but does so in a way
|
* that, if there are other nodes with the same z-index, the newest
|
* drawn will be the front most within that z-index. This is the
|
* default indexing method.
|
*
|
* Parameters:
|
* indexer - {<OpenLayers.ElementsIndexer>}
|
* newNode - {DOMElement}
|
* nextNode - {DOMElement}
|
*
|
* Returns:
|
* {Integer}
|
*/
|
Z_ORDER_DRAWING_ORDER: function(indexer, newNode, nextNode) {
|
var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(
|
indexer,
|
newNode,
|
nextNode
|
);
|
|
// Make Z_ORDER subscribe to drawing order by pushing it above
|
// all of the other nodes with the same z-index.
|
if (nextNode && returnVal == 0) {
|
returnVal = 1;
|
}
|
|
return returnVal;
|
},
|
|
/**
|
* APIMethod: Z_ORDER_Y_ORDER
|
* This one should really be called Z_ORDER_Y_ORDER_DRAWING_ORDER, as it
|
* best describes which ordering methods have precedence (though, the
|
* name would be too long). This method orders nodes by their z-index,
|
* but does so in a way that, if there are other nodes with the same
|
* z-index, the nodes with the lower y position will be "closer" than
|
* those with a higher y position. If two nodes have the exact same y
|
* position, however, then this method will revert to using drawing
|
* order to decide placement.
|
*
|
* Parameters:
|
* indexer - {<OpenLayers.ElementsIndexer>}
|
* newNode - {DOMElement}
|
* nextNode - {DOMElement}
|
*
|
* Returns:
|
* {Integer}
|
*/
|
Z_ORDER_Y_ORDER: function(indexer, newNode, nextNode) {
|
var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(
|
indexer,
|
newNode,
|
nextNode
|
);
|
|
if (nextNode && returnVal === 0) {
|
var result = nextNode._boundsBottom - newNode._boundsBottom;
|
returnVal = (result === 0) ? 1 : result;
|
}
|
|
return returnVal;
|
}
|
};
|
|
/**
|
* Class: OpenLayers.Renderer.Elements
|
* This is another virtual class in that it should never be instantiated by
|
* itself as a Renderer. It exists because there is *tons* of shared
|
* functionality between different vector libraries which use nodes/elements
|
* as a base for rendering vectors.
|
*
|
* The highlevel bits of code that are implemented here are the adding and
|
* removing of geometries, which is essentially the same for any
|
* element-based renderer. The details of creating each node and drawing the
|
* paths are of course different, but the machinery is the same.
|
*
|
* Inherits:
|
* - <OpenLayers.Renderer>
|
*/
|
OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
|
|
/**
|
* Property: rendererRoot
|
* {DOMElement}
|
*/
|
rendererRoot: null,
|
|
/**
|
* Property: root
|
* {DOMElement}
|
*/
|
root: null,
|
|
/**
|
* Property: vectorRoot
|
* {DOMElement}
|
*/
|
vectorRoot: null,
|
|
/**
|
* Property: textRoot
|
* {DOMElement}
|
*/
|
textRoot: null,
|
|
/**
|
* Property: xmlns
|
* {String}
|
*/
|
xmlns: null,
|
|
/**
|
* Property: xOffset
|
* {Number} Offset to apply to the renderer viewport translation in x
|
* direction. If the renderer extent's center is on the right of the
|
* dateline (i.e. exceeds the world bounds), we shift the viewport to the
|
* left by one world width. This avoids that features disappear from the
|
* map viewport. Because our dateline handling logic in other places
|
* ensures that extents crossing the dateline always have a center
|
* exceeding the world bounds on the left, we need this offset to make sure
|
* that the same is true for the renderer extent in pixel space as well.
|
*/
|
xOffset: 0,
|
|
/**
|
* Property: rightOfDateLine
|
* {Boolean} Keeps track of the location of the map extent relative to the
|
* date line. The <setExtent> method compares this value (which is the one
|
* from the previous <setExtent> call) with the current position of the map
|
* extent relative to the date line and updates the xOffset when the extent
|
* has moved from one side of the date line to the other.
|
*/
|
|
/**
|
* Property: Indexer
|
* {<OpenLayers.ElementIndexer>} An instance of OpenLayers.ElementsIndexer
|
* created upon initialization if the zIndexing or yOrdering options
|
* passed to this renderer's constructor are set to true.
|
*/
|
indexer: null,
|
|
/**
|
* Constant: BACKGROUND_ID_SUFFIX
|
* {String}
|
*/
|
BACKGROUND_ID_SUFFIX: "_background",
|
|
/**
|
* Constant: LABEL_ID_SUFFIX
|
* {String}
|
*/
|
LABEL_ID_SUFFIX: "_label",
|
|
/**
|
* Constant: LABEL_OUTLINE_SUFFIX
|
* {String}
|
*/
|
LABEL_OUTLINE_SUFFIX: "_outline",
|
|
/**
|
* Constructor: OpenLayers.Renderer.Elements
|
*
|
* Parameters:
|
* containerID - {String}
|
* options - {Object} options for this renderer.
|
*
|
* Supported options are:
|
* yOrdering - {Boolean} Whether to use y-ordering
|
* zIndexing - {Boolean} Whether to use z-indexing. Will be ignored
|
* if yOrdering is set to true.
|
*/
|
initialize: function(containerID, options) {
|
OpenLayers.Renderer.prototype.initialize.apply(this, arguments);
|
|
this.rendererRoot = this.createRenderRoot();
|
this.root = this.createRoot("_root");
|
this.vectorRoot = this.createRoot("_vroot");
|
this.textRoot = this.createRoot("_troot");
|
|
this.root.appendChild(this.vectorRoot);
|
this.root.appendChild(this.textRoot);
|
|
this.rendererRoot.appendChild(this.root);
|
this.container.appendChild(this.rendererRoot);
|
|
if(options && (options.zIndexing || options.yOrdering)) {
|
this.indexer = new OpenLayers.ElementsIndexer(options.yOrdering);
|
}
|
},
|
|
/**
|
* Method: destroy
|
*/
|
destroy: function() {
|
|
this.clear();
|
|
this.rendererRoot = null;
|
this.root = null;
|
this.xmlns = null;
|
|
OpenLayers.Renderer.prototype.destroy.apply(this, arguments);
|
},
|
|
/**
|
* Method: clear
|
* Remove all the elements from the root
|
*/
|
clear: function() {
|
var child;
|
var root = this.vectorRoot;
|
if (root) {
|
while (child = root.firstChild) {
|
root.removeChild(child);
|
}
|
}
|
root = this.textRoot;
|
if (root) {
|
while (child = root.firstChild) {
|
root.removeChild(child);
|
}
|
}
|
if (this.indexer) {
|
this.indexer.clear();
|
}
|
},
|
|
/**
|
* Method: setExtent
|
* Set the visible part of the layer.
|
*
|
* Parameters:
|
* extent - {<OpenLayers.Bounds>}
|
* resolutionChanged - {Boolean}
|
*
|
* Returns:
|
* {Boolean} true to notify the layer that the new extent does not exceed
|
* the coordinate range, and the features will not need to be redrawn.
|
* False otherwise.
|
*/
|
setExtent: function(extent, resolutionChanged) {
|
var coordSysUnchanged = OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);
|
var resolution = this.getResolution();
|
if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
|
var rightOfDateLine,
|
ratio = extent.getWidth() / this.map.getExtent().getWidth(),
|
extent = extent.scale(1 / ratio),
|
world = this.map.getMaxExtent();
|
if (world.right > extent.left && world.right < extent.right) {
|
rightOfDateLine = true;
|
} else if (world.left > extent.left && world.left < extent.right) {
|
rightOfDateLine = false;
|
}
|
if (rightOfDateLine !== this.rightOfDateLine || resolutionChanged) {
|
coordSysUnchanged = false;
|
this.xOffset = rightOfDateLine === true ?
|
world.getWidth() / resolution : 0;
|
}
|
this.rightOfDateLine = rightOfDateLine;
|
}
|
return coordSysUnchanged;
|
},
|
|
/**
|
* Method: getNodeType
|
* This function is in charge of asking the specific renderer which type
|
* of node to create for the given geometry and style. All geometries
|
* in an Elements-based renderer consist of one node and some
|
* attributes. We have the nodeFactory() function which creates a node
|
* for us, but it takes a 'type' as input, and that is precisely what
|
* this function tells us.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
* style - {Object}
|
*
|
* Returns:
|
* {String} The corresponding node type for the specified geometry
|
*/
|
getNodeType: function(geometry, style) { },
|
|
/**
|
* Method: drawGeometry
|
* Draw the geometry, creating new nodes, setting paths, setting style,
|
* setting featureId on the node. This method should only be called
|
* by the renderer itself.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
* style - {Object}
|
* featureId - {String}
|
*
|
* Returns:
|
* {Boolean} true if the geometry has been drawn completely; null if
|
* incomplete; false otherwise
|
*/
|
drawGeometry: function(geometry, style, featureId) {
|
var className = geometry.CLASS_NAME;
|
var rendered = true;
|
if ((className == "OpenLayers.Geometry.Collection") ||
|
(className == "OpenLayers.Geometry.MultiPoint") ||
|
(className == "OpenLayers.Geometry.MultiLineString") ||
|
(className == "OpenLayers.Geometry.MultiPolygon")) {
|
for (var i = 0, len=geometry.components.length; i<len; i++) {
|
rendered = this.drawGeometry(
|
geometry.components[i], style, featureId) && rendered;
|
}
|
return rendered;
|
}
|
|
rendered = false;
|
var removeBackground = false;
|
if (style.display != "none") {
|
if (style.backgroundGraphic) {
|
this.redrawBackgroundNode(geometry.id, geometry, style,
|
featureId);
|
} else {
|
removeBackground = true;
|
}
|
rendered = this.redrawNode(geometry.id, geometry, style,
|
featureId);
|
}
|
if (rendered == false) {
|
var node = document.getElementById(geometry.id);
|
if (node) {
|
if (node._style.backgroundGraphic) {
|
removeBackground = true;
|
}
|
node.parentNode.removeChild(node);
|
}
|
}
|
if (removeBackground) {
|
var node = document.getElementById(
|
geometry.id + this.BACKGROUND_ID_SUFFIX);
|
if (node) {
|
node.parentNode.removeChild(node);
|
}
|
}
|
return rendered;
|
},
|
|
/**
|
* Method: redrawNode
|
*
|
* Parameters:
|
* id - {String}
|
* geometry - {<OpenLayers.Geometry>}
|
* style - {Object}
|
* featureId - {String}
|
*
|
* Returns:
|
* {Boolean} true if the complete geometry could be drawn, null if parts of
|
* the geometry could not be drawn, false otherwise
|
*/
|
redrawNode: function(id, geometry, style, featureId) {
|
style = this.applyDefaultSymbolizer(style);
|
// Get the node if it's already on the map.
|
var node = this.nodeFactory(id, this.getNodeType(geometry, style));
|
|
// Set the data for the node, then draw it.
|
node._featureId = featureId;
|
node._boundsBottom = geometry.getBounds().bottom;
|
node._geometryClass = geometry.CLASS_NAME;
|
node._style = style;
|
|
var drawResult = this.drawGeometryNode(node, geometry, style);
|
if(drawResult === false) {
|
return false;
|
}
|
|
node = drawResult.node;
|
|
// Insert the node into the indexer so it can show us where to
|
// place it. Note that this operation is O(log(n)). If there's a
|
// performance problem (when dragging, for instance) this is
|
// likely where it would be.
|
if (this.indexer) {
|
var insert = this.indexer.insert(node);
|
if (insert) {
|
this.vectorRoot.insertBefore(node, insert);
|
} else {
|
this.vectorRoot.appendChild(node);
|
}
|
} else {
|
// if there's no indexer, simply append the node to root,
|
// but only if the node is a new one
|
if (node.parentNode !== this.vectorRoot){
|
this.vectorRoot.appendChild(node);
|
}
|
}
|
|
this.postDraw(node);
|
|
return drawResult.complete;
|
},
|
|
/**
|
* Method: redrawBackgroundNode
|
* Redraws the node using special 'background' style properties. Basically
|
* just calls redrawNode(), but instead of directly using the
|
* 'externalGraphic', 'graphicXOffset', 'graphicYOffset', and
|
* 'graphicZIndex' properties directly from the specified 'style'
|
* parameter, we create a new style object and set those properties
|
* from the corresponding 'background'-prefixed properties from
|
* specified 'style' parameter.
|
*
|
* Parameters:
|
* id - {String}
|
* geometry - {<OpenLayers.Geometry>}
|
* style - {Object}
|
* featureId - {String}
|
*
|
* Returns:
|
* {Boolean} true if the complete geometry could be drawn, null if parts of
|
* the geometry could not be drawn, false otherwise
|
*/
|
redrawBackgroundNode: function(id, geometry, style, featureId) {
|
var backgroundStyle = OpenLayers.Util.extend({}, style);
|
|
// Set regular style attributes to apply to the background styles.
|
backgroundStyle.externalGraphic = backgroundStyle.backgroundGraphic;
|
backgroundStyle.graphicXOffset = backgroundStyle.backgroundXOffset;
|
backgroundStyle.graphicYOffset = backgroundStyle.backgroundYOffset;
|
backgroundStyle.graphicZIndex = backgroundStyle.backgroundGraphicZIndex;
|
backgroundStyle.graphicWidth = backgroundStyle.backgroundWidth || backgroundStyle.graphicWidth;
|
backgroundStyle.graphicHeight = backgroundStyle.backgroundHeight || backgroundStyle.graphicHeight;
|
|
// Erase background styles.
|
backgroundStyle.backgroundGraphic = null;
|
backgroundStyle.backgroundXOffset = null;
|
backgroundStyle.backgroundYOffset = null;
|
backgroundStyle.backgroundGraphicZIndex = null;
|
|
return this.redrawNode(
|
id + this.BACKGROUND_ID_SUFFIX,
|
geometry,
|
backgroundStyle,
|
null
|
);
|
},
|
|
/**
|
* Method: drawGeometryNode
|
* Given a node, draw a geometry on the specified layer.
|
* node and geometry are required arguments, style is optional.
|
* This method is only called by the render itself.
|
*
|
* Parameters:
|
* node - {DOMElement}
|
* geometry - {<OpenLayers.Geometry>}
|
* style - {Object}
|
*
|
* Returns:
|
* {Object} a hash with properties "node" (the drawn node) and "complete"
|
* (null if parts of the geometry could not be drawn, false if nothing
|
* could be drawn)
|
*/
|
drawGeometryNode: function(node, geometry, style) {
|
style = style || node._style;
|
|
var options = {
|
'isFilled': style.fill === undefined ?
|
true :
|
style.fill,
|
'isStroked': style.stroke === undefined ?
|
!!style.strokeWidth :
|
style.stroke
|
};
|
var drawn;
|
switch (geometry.CLASS_NAME) {
|
case "OpenLayers.Geometry.Point":
|
if(style.graphic === false) {
|
options.isFilled = false;
|
options.isStroked = false;
|
}
|
drawn = this.drawPoint(node, geometry);
|
break;
|
case "OpenLayers.Geometry.LineString":
|
options.isFilled = false;
|
drawn = this.drawLineString(node, geometry);
|
break;
|
case "OpenLayers.Geometry.LinearRing":
|
drawn = this.drawLinearRing(node, geometry);
|
break;
|
case "OpenLayers.Geometry.Polygon":
|
drawn = this.drawPolygon(node, geometry);
|
break;
|
case "OpenLayers.Geometry.Rectangle":
|
drawn = this.drawRectangle(node, geometry);
|
break;
|
default:
|
break;
|
}
|
|
node._options = options;
|
|
//set style
|
//TBD simplify this
|
if (drawn != false) {
|
return {
|
node: this.setStyle(node, style, options, geometry),
|
complete: drawn
|
};
|
} else {
|
return false;
|
}
|
},
|
|
/**
|
* Method: postDraw
|
* Things that have do be done after the geometry node is appended
|
* to its parent node. To be overridden by subclasses.
|
*
|
* Parameters:
|
* node - {DOMElement}
|
*/
|
postDraw: function(node) {},
|
|
/**
|
* Method: drawPoint
|
* Virtual function for drawing Point Geometry.
|
* Should be implemented by subclasses.
|
* This method is only called by the renderer itself.
|
*
|
* Parameters:
|
* node - {DOMElement}
|
* geometry - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {DOMElement} or false if the renderer could not draw the point
|
*/
|
drawPoint: function(node, geometry) {},
|
|
/**
|
* Method: drawLineString
|
* Virtual function for drawing LineString Geometry.
|
* Should be implemented by subclasses.
|
* This method is only called by the renderer itself.
|
*
|
* Parameters:
|
* node - {DOMElement}
|
* geometry - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {DOMElement} or null if the renderer could not draw all components of
|
* the linestring, or false if nothing could be drawn
|
*/
|
drawLineString: function(node, geometry) {},
|
|
/**
|
* Method: drawLinearRing
|
* Virtual function for drawing LinearRing Geometry.
|
* Should be implemented by subclasses.
|
* This method is only called by the renderer itself.
|
*
|
* Parameters:
|
* node - {DOMElement}
|
* geometry - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {DOMElement} or null if the renderer could not draw all components
|
* of the linear ring, or false if nothing could be drawn
|
*/
|
drawLinearRing: function(node, geometry) {},
|
|
/**
|
* Method: drawPolygon
|
* Virtual function for drawing Polygon Geometry.
|
* Should be implemented by subclasses.
|
* This method is only called by the renderer itself.
|
*
|
* Parameters:
|
* node - {DOMElement}
|
* geometry - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {DOMElement} or null if the renderer could not draw all components
|
* of the polygon, or false if nothing could be drawn
|
*/
|
drawPolygon: function(node, geometry) {},
|
|
/**
|
* Method: drawRectangle
|
* Virtual function for drawing Rectangle Geometry.
|
* Should be implemented by subclasses.
|
* This method is only called by the renderer itself.
|
*
|
* Parameters:
|
* node - {DOMElement}
|
* geometry - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {DOMElement} or false if the renderer could not draw the rectangle
|
*/
|
drawRectangle: function(node, geometry) {},
|
|
/**
|
* Method: drawCircle
|
* Virtual function for drawing Circle Geometry.
|
* Should be implemented by subclasses.
|
* This method is only called by the renderer itself.
|
*
|
* Parameters:
|
* node - {DOMElement}
|
* geometry - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {DOMElement} or false if the renderer could not draw the circle
|
*/
|
drawCircle: function(node, geometry) {},
|
|
/**
|
* Method: removeText
|
* Removes a label
|
*
|
* Parameters:
|
* featureId - {String}
|
*/
|
removeText: function(featureId) {
|
var label = document.getElementById(featureId + this.LABEL_ID_SUFFIX);
|
if (label) {
|
this.textRoot.removeChild(label);
|
}
|
var outline = document.getElementById(featureId + this.LABEL_OUTLINE_SUFFIX);
|
if (outline) {
|
this.textRoot.removeChild(outline);
|
}
|
},
|
|
/**
|
* Method: getFeatureIdFromEvent
|
*
|
* Parameters:
|
* evt - {Object} An <OpenLayers.Event> object
|
*
|
* Returns:
|
* {String} A feature id or undefined.
|
*/
|
getFeatureIdFromEvent: function(evt) {
|
var target = evt.target;
|
var useElement = target && target.correspondingUseElement;
|
var node = useElement ? useElement : (target || evt.srcElement);
|
return node._featureId;
|
},
|
|
/**
|
* Method: eraseGeometry
|
* Erase a geometry from the renderer. In the case of a multi-geometry,
|
* we cycle through and recurse on ourselves. Otherwise, we look for a
|
* node with the geometry.id, destroy its geometry, and remove it from
|
* the DOM.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
* featureId - {String}
|
*/
|
eraseGeometry: function(geometry, featureId) {
|
if ((geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPoint") ||
|
(geometry.CLASS_NAME == "OpenLayers.Geometry.MultiLineString") ||
|
(geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPolygon") ||
|
(geometry.CLASS_NAME == "OpenLayers.Geometry.Collection")) {
|
for (var i=0, len=geometry.components.length; i<len; i++) {
|
this.eraseGeometry(geometry.components[i], featureId);
|
}
|
} else {
|
var element = OpenLayers.Util.getElement(geometry.id);
|
if (element && element.parentNode) {
|
if (element.geometry) {
|
element.geometry.destroy();
|
element.geometry = null;
|
}
|
element.parentNode.removeChild(element);
|
|
if (this.indexer) {
|
this.indexer.remove(element);
|
}
|
|
if (element._style.backgroundGraphic) {
|
var backgroundId = geometry.id + this.BACKGROUND_ID_SUFFIX;
|
var bElem = OpenLayers.Util.getElement(backgroundId);
|
if (bElem && bElem.parentNode) {
|
// No need to destroy the geometry since the element and the background
|
// node share the same geometry.
|
bElem.parentNode.removeChild(bElem);
|
}
|
}
|
}
|
}
|
},
|
|
/**
|
* Method: nodeFactory
|
* Create new node of the specified type, with the (optional) specified id.
|
*
|
* If node already exists with same ID and a different type, we remove it
|
* and then call ourselves again to recreate it.
|
*
|
* Parameters:
|
* id - {String}
|
* type - {String} type Kind of node to draw.
|
*
|
* Returns:
|
* {DOMElement} A new node of the given type and id.
|
*/
|
nodeFactory: function(id, type) {
|
var node = OpenLayers.Util.getElement(id);
|
if (node) {
|
if (!this.nodeTypeCompare(node, type)) {
|
node.parentNode.removeChild(node);
|
node = this.nodeFactory(id, type);
|
}
|
} else {
|
node = this.createNode(type, id);
|
}
|
return node;
|
},
|
|
/**
|
* Method: nodeTypeCompare
|
*
|
* Parameters:
|
* node - {DOMElement}
|
* type - {String} Kind of node
|
*
|
* Returns:
|
* {Boolean} Whether or not the specified node is of the specified type
|
* This function must be overridden by subclasses.
|
*/
|
nodeTypeCompare: function(node, type) {},
|
|
/**
|
* Method: createNode
|
*
|
* Parameters:
|
* type - {String} Kind of node to draw.
|
* id - {String} Id for node.
|
*
|
* Returns:
|
* {DOMElement} A new node of the given type and id.
|
* This function must be overridden by subclasses.
|
*/
|
createNode: function(type, id) {},
|
|
/**
|
* Method: moveRoot
|
* moves this renderer's root to a different renderer.
|
*
|
* Parameters:
|
* renderer - {<OpenLayers.Renderer>} target renderer for the moved root
|
*/
|
moveRoot: function(renderer) {
|
var root = this.root;
|
if(renderer.root.parentNode == this.rendererRoot) {
|
root = renderer.root;
|
}
|
root.parentNode.removeChild(root);
|
renderer.rendererRoot.appendChild(root);
|
},
|
|
/**
|
* Method: getRenderLayerId
|
* Gets the layer that this renderer's output appears on. If moveRoot was
|
* used, this will be different from the id of the layer containing the
|
* features rendered by this renderer.
|
*
|
* Returns:
|
* {String} the id of the output layer.
|
*/
|
getRenderLayerId: function() {
|
return this.root.parentNode.parentNode.id;
|
},
|
|
/**
|
* Method: isComplexSymbol
|
* Determines if a symbol cannot be rendered using drawCircle
|
*
|
* Parameters:
|
* graphicName - {String}
|
*
|
* Returns
|
* {Boolean} true if the symbol is complex, false if not
|
*/
|
isComplexSymbol: function(graphicName) {
|
return (graphicName != "circle") && !!graphicName;
|
},
|
|
CLASS_NAME: "OpenLayers.Renderer.Elements"
|
});
|
|
/* ======================================================================
|
OpenLayers/Control/ArgParser.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Control.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.ArgParser
|
* The ArgParser control adds location bar query string parsing functionality
|
* to an OpenLayers Map.
|
* When added to a Map control, on a page load/refresh, the Map will
|
* automatically take the href string and parse it for lon, lat, zoom, and
|
* layers information.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.ArgParser = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* Property: center
|
* {<OpenLayers.LonLat>}
|
*/
|
center: null,
|
|
/**
|
* Property: zoom
|
* {int}
|
*/
|
zoom: null,
|
|
/**
|
* Property: layers
|
* {String} Each character represents the state of the corresponding layer
|
* on the map.
|
*/
|
layers: null,
|
|
/**
|
* APIProperty: displayProjection
|
* {<OpenLayers.Projection>} Requires proj4js support.
|
* Projection used when reading the coordinates from the URL. This will
|
* reproject the map coordinates from the URL into the map's
|
* projection.
|
*
|
* If you are using this functionality, be aware that any permalink
|
* which is added to the map will determine the coordinate type which
|
* is read from the URL, which means you should not add permalinks with
|
* different displayProjections to the same map.
|
*/
|
displayProjection: null,
|
|
/**
|
* Constructor: OpenLayers.Control.ArgParser
|
*
|
* Parameters:
|
* options - {Object}
|
*/
|
|
/**
|
* Method: getParameters
|
*/
|
getParameters: function(url) {
|
url = url || window.location.href;
|
var parameters = OpenLayers.Util.getParameters(url);
|
|
// If we have an anchor in the url use it to split the url
|
var index = url.indexOf('#');
|
if (index > 0) {
|
// create an url to parse on the getParameters
|
url = '?' + url.substring(index + 1, url.length);
|
|
OpenLayers.Util.extend(parameters,
|
OpenLayers.Util.getParameters(url));
|
}
|
return parameters;
|
},
|
|
/**
|
* Method: setMap
|
* Set the map property for the control.
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
setMap: function(map) {
|
OpenLayers.Control.prototype.setMap.apply(this, arguments);
|
|
//make sure we dont already have an arg parser attached
|
for(var i=0, len=this.map.controls.length; i<len; i++) {
|
var control = this.map.controls[i];
|
if ( (control != this) &&
|
(control.CLASS_NAME == "OpenLayers.Control.ArgParser") ) {
|
|
// If a second argparser is added to the map, then we
|
// override the displayProjection to be the one added to the
|
// map.
|
if (control.displayProjection != this.displayProjection) {
|
this.displayProjection = control.displayProjection;
|
}
|
|
break;
|
}
|
}
|
if (i == this.map.controls.length) {
|
|
var args = this.getParameters();
|
// Be careful to set layer first, to not trigger unnecessary layer loads
|
if (args.layers) {
|
this.layers = args.layers;
|
|
// when we add a new layer, set its visibility
|
this.map.events.register('addlayer', this,
|
this.configureLayers);
|
this.configureLayers();
|
}
|
if (args.lat && args.lon) {
|
this.center = new OpenLayers.LonLat(parseFloat(args.lon),
|
parseFloat(args.lat));
|
if (args.zoom) {
|
this.zoom = parseFloat(args.zoom);
|
}
|
|
// when we add a new baselayer to see when we can set the center
|
this.map.events.register('changebaselayer', this,
|
this.setCenter);
|
this.setCenter();
|
}
|
}
|
},
|
|
/**
|
* Method: setCenter
|
* As soon as a baseLayer has been loaded, we center and zoom
|
* ...and remove the handler.
|
*/
|
setCenter: function() {
|
|
if (this.map.baseLayer) {
|
//dont need to listen for this one anymore
|
this.map.events.unregister('changebaselayer', this,
|
this.setCenter);
|
|
if (this.displayProjection) {
|
this.center.transform(this.displayProjection,
|
this.map.getProjectionObject());
|
}
|
|
this.map.setCenter(this.center, this.zoom);
|
}
|
},
|
|
/**
|
* Method: configureLayers
|
* As soon as all the layers are loaded, cycle through them and
|
* hide or show them.
|
*/
|
configureLayers: function() {
|
|
if (this.layers.length == this.map.layers.length) {
|
this.map.events.unregister('addlayer', this, this.configureLayers);
|
|
for(var i=0, len=this.layers.length; i<len; i++) {
|
|
var layer = this.map.layers[i];
|
var c = this.layers.charAt(i);
|
|
if (c == "B") {
|
this.map.setBaseLayer(layer);
|
} else if ( (c == "T") || (c == "F") ) {
|
layer.setVisibility(c == "T");
|
}
|
}
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Control.ArgParser"
|
});
|
/* ======================================================================
|
OpenLayers/Control/Permalink.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/Control/ArgParser.js
|
* @requires OpenLayers/Lang.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.Permalink
|
* The Permalink control is hyperlink that will return the user to the
|
* current map view. By default it is drawn in the lower right corner of the
|
* map. The href is updated as the map is zoomed, panned and whilst layers
|
* are switched.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.Permalink = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* APIProperty: argParserClass
|
* {Class} The ArgParser control class (not instance) to use with this
|
* control.
|
*/
|
argParserClass: OpenLayers.Control.ArgParser,
|
|
/**
|
* Property: element
|
* {DOMElement}
|
*/
|
element: null,
|
|
/**
|
* APIProperty: anchor
|
* {Boolean} This option changes 3 things:
|
* the character '#' is used in place of the character '?',
|
* the window.href is updated if no element is provided.
|
* When this option is set to true it's not recommend to provide
|
* a base without provide an element.
|
*/
|
anchor: false,
|
|
/**
|
* APIProperty: base
|
* {String}
|
*/
|
base: '',
|
|
/**
|
* APIProperty: displayProjection
|
* {<OpenLayers.Projection>} Requires proj4js support. Projection used
|
* when creating the coordinates in the link. This will reproject the
|
* map coordinates into display coordinates. If you are using this
|
* functionality, the permalink which is last added to the map will
|
* determine the coordinate type which is read from the URL, which
|
* means you should not add permalinks with different
|
* displayProjections to the same map.
|
*/
|
displayProjection: null,
|
|
/**
|
* Constructor: OpenLayers.Control.Permalink
|
*
|
* Parameters:
|
* element - {DOMElement}
|
* base - {String}
|
* options - {Object} options to the control.
|
*
|
* Or for anchor:
|
* options - {Object} options to the control.
|
*/
|
initialize: function(element, base, options) {
|
if (element !== null && typeof element == 'object' && !OpenLayers.Util.isElement(element)) {
|
options = element;
|
this.base = document.location.href;
|
OpenLayers.Control.prototype.initialize.apply(this, [options]);
|
if (this.element != null) {
|
this.element = OpenLayers.Util.getElement(this.element);
|
}
|
}
|
else {
|
OpenLayers.Control.prototype.initialize.apply(this, [options]);
|
this.element = OpenLayers.Util.getElement(element);
|
this.base = base || document.location.href;
|
}
|
},
|
|
/**
|
* APIMethod: destroy
|
*/
|
destroy: function() {
|
if (this.element && this.element.parentNode == this.div) {
|
this.div.removeChild(this.element);
|
this.element = null;
|
}
|
if (this.map) {
|
this.map.events.unregister('moveend', this, this.updateLink);
|
}
|
|
OpenLayers.Control.prototype.destroy.apply(this, arguments);
|
},
|
|
/**
|
* Method: setMap
|
* Set the map property for the control.
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
setMap: function(map) {
|
OpenLayers.Control.prototype.setMap.apply(this, arguments);
|
|
//make sure we have an arg parser attached
|
for(var i=0, len=this.map.controls.length; i<len; i++) {
|
var control = this.map.controls[i];
|
if (control.CLASS_NAME == this.argParserClass.CLASS_NAME) {
|
|
// If a permalink is added to the map, and an ArgParser already
|
// exists, we override the displayProjection to be the one
|
// on the permalink.
|
if (control.displayProjection != this.displayProjection) {
|
this.displayProjection = control.displayProjection;
|
}
|
|
break;
|
}
|
}
|
if (i == this.map.controls.length) {
|
this.map.addControl(new this.argParserClass(
|
{ 'displayProjection': this.displayProjection }));
|
}
|
|
},
|
|
/**
|
* Method: draw
|
*
|
* Returns:
|
* {DOMElement}
|
*/
|
draw: function() {
|
OpenLayers.Control.prototype.draw.apply(this, arguments);
|
|
if (!this.element && !this.anchor) {
|
this.element = document.createElement("a");
|
this.element.innerHTML = OpenLayers.i18n("Permalink");
|
this.element.href="";
|
this.div.appendChild(this.element);
|
}
|
this.map.events.on({
|
'moveend': this.updateLink,
|
'changelayer': this.updateLink,
|
'changebaselayer': this.updateLink,
|
scope: this
|
});
|
|
// Make it so there is at least a link even though the map may not have
|
// moved yet.
|
this.updateLink();
|
|
return this.div;
|
},
|
|
/**
|
* Method: updateLink
|
*/
|
updateLink: function() {
|
var separator = this.anchor ? '#' : '?';
|
var href = this.base;
|
var anchor = null;
|
if (href.indexOf("#") != -1 && this.anchor == false) {
|
anchor = href.substring( href.indexOf("#"), href.length);
|
}
|
if (href.indexOf(separator) != -1) {
|
href = href.substring( 0, href.indexOf(separator) );
|
}
|
var splits = href.split("#");
|
href = splits[0] + separator+ OpenLayers.Util.getParameterString(this.createParams());
|
if (anchor) {
|
href += anchor;
|
}
|
if (this.anchor && !this.element) {
|
window.location.href = href;
|
}
|
else {
|
this.element.href = href;
|
}
|
},
|
|
/**
|
* APIMethod: createParams
|
* Creates the parameters that need to be encoded into the permalink url.
|
*
|
* Parameters:
|
* center - {<OpenLayers.LonLat>} center to encode in the permalink.
|
* Defaults to the current map center.
|
* zoom - {Integer} zoom level to encode in the permalink. Defaults to the
|
* current map zoom level.
|
* layers - {Array(<OpenLayers.Layer>)} layers to encode in the permalink.
|
* Defaults to the current map layers.
|
*
|
* Returns:
|
* {Object} Hash of parameters that will be url-encoded into the
|
* permalink.
|
*/
|
createParams: function(center, zoom, layers) {
|
center = center || this.map.getCenter();
|
|
var params = OpenLayers.Util.getParameters(this.base);
|
|
// If there's still no center, map is not initialized yet.
|
// Break out of this function, and simply return the params from the
|
// base link.
|
if (center) {
|
|
//zoom
|
params.zoom = zoom || this.map.getZoom();
|
|
//lon,lat
|
var lat = center.lat;
|
var lon = center.lon;
|
|
if (this.displayProjection) {
|
var mapPosition = OpenLayers.Projection.transform(
|
{ x: lon, y: lat },
|
this.map.getProjectionObject(),
|
this.displayProjection );
|
lon = mapPosition.x;
|
lat = mapPosition.y;
|
}
|
params.lat = Math.round(lat*100000)/100000;
|
params.lon = Math.round(lon*100000)/100000;
|
|
//layers
|
layers = layers || this.map.layers;
|
params.layers = '';
|
for (var i=0, len=layers.length; i<len; i++) {
|
var layer = layers[i];
|
|
if (layer.isBaseLayer) {
|
params.layers += (layer == this.map.baseLayer) ? "B" : "0";
|
} else {
|
params.layers += (layer.getVisibility()) ? "T" : "F";
|
}
|
}
|
}
|
|
return params;
|
},
|
|
CLASS_NAME: "OpenLayers.Control.Permalink"
|
});
|
/* ======================================================================
|
OpenLayers/Layer/TMS.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Layer/Grid.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.TMS
|
* Create a layer for accessing tiles from services that conform with the
|
* Tile Map Service Specification
|
* (http://wiki.osgeo.org/wiki/Tile_Map_Service_Specification).
|
*
|
* Example:
|
* (code)
|
* var layer = new OpenLayers.Layer.TMS(
|
* "My Layer", // name for display in LayerSwitcher
|
* "http://tilecache.osgeo.org/wms-c/Basic.py/", // service endpoint
|
* {layername: "basic", type: "png"} // required properties
|
* );
|
* (end)
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer.Grid>
|
*/
|
OpenLayers.Layer.TMS = OpenLayers.Class(OpenLayers.Layer.Grid, {
|
|
/**
|
* APIProperty: serviceVersion
|
* {String} Service version for tile requests. Default is "1.0.0".
|
*/
|
serviceVersion: "1.0.0",
|
|
/**
|
* APIProperty: layername
|
* {String} The identifier for the <TileMap> as advertised by the service.
|
* For example, if the service advertises a <TileMap> with
|
* 'href="http://tms.osgeo.org/1.0.0/vmap0"', the <layername> property
|
* would be set to "vmap0".
|
*/
|
layername: null,
|
|
/**
|
* APIProperty: type
|
* {String} The format extension corresponding to the requested tile image
|
* type. This is advertised in a <TileFormat> element as the
|
* "extension" attribute. For example, if the service advertises a
|
* <TileMap> with <TileFormat width="256" height="256" mime-type="image/jpeg" extension="jpg" />,
|
* the <type> property would be set to "jpg".
|
*/
|
type: null,
|
|
/**
|
* APIProperty: isBaseLayer
|
* {Boolean} Make this layer a base layer. Default is true. Set false to
|
* use the layer as an overlay.
|
*/
|
isBaseLayer: true,
|
|
/**
|
* APIProperty: tileOrigin
|
* {<OpenLayers.LonLat>} Optional origin for aligning the grid of tiles.
|
* If provided, requests for tiles at all resolutions will be aligned
|
* with this location (no tiles shall overlap this location). If
|
* not provided, the grid of tiles will be aligned with the bottom-left
|
* corner of the map's <maxExtent>. Default is ``null``.
|
*
|
* Example:
|
* (code)
|
* var layer = new OpenLayers.Layer.TMS(
|
* "My Layer",
|
* "http://tilecache.osgeo.org/wms-c/Basic.py/",
|
* {
|
* layername: "basic",
|
* type: "png",
|
* // set if different than the bottom left of map.maxExtent
|
* tileOrigin: new OpenLayers.LonLat(-180, -90)
|
* }
|
* );
|
* (end)
|
*/
|
tileOrigin: null,
|
|
/**
|
* APIProperty: serverResolutions
|
* {Array} A list of all resolutions available on the server. Only set this
|
* property if the map resolutions differ from the server. This
|
* property serves two purposes. (a) <serverResolutions> can include
|
* resolutions that the server supports and that you don't want to
|
* provide with this layer; you can also look at <zoomOffset>, which is
|
* an alternative to <serverResolutions> for that specific purpose.
|
* (b) The map can work with resolutions that aren't supported by
|
* the server, i.e. that aren't in <serverResolutions>. When the
|
* map is displayed in such a resolution data for the closest
|
* server-supported resolution is loaded and the layer div is
|
* stretched as necessary.
|
*/
|
serverResolutions: null,
|
|
/**
|
* APIProperty: zoomOffset
|
* {Number} If your cache has more zoom levels than you want to provide
|
* access to with this layer, supply a zoomOffset. This zoom offset
|
* is added to the current map zoom level to determine the level
|
* for a requested tile. For example, if you supply a zoomOffset
|
* of 3, when the map is at the zoom 0, tiles will be requested from
|
* level 3 of your cache. Default is 0 (assumes cache level and map
|
* zoom are equivalent). Using <zoomOffset> is an alternative to
|
* setting <serverResolutions> if you only want to expose a subset
|
* of the server resolutions.
|
*/
|
zoomOffset: 0,
|
|
/**
|
* Constructor: OpenLayers.Layer.TMS
|
*
|
* Parameters:
|
* name - {String} Title to be displayed in a <OpenLayers.Control.LayerSwitcher>
|
* url - {String} Service endpoint (without the version number). E.g.
|
* "http://tms.osgeo.org/".
|
* options - {Object} Additional properties to be set on the layer. The
|
* <layername> and <type> properties must be set here.
|
*/
|
initialize: function(name, url, options) {
|
var newArguments = [];
|
newArguments.push(name, url, {}, options);
|
OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
|
},
|
|
/**
|
* APIMethod: clone
|
* Create a complete copy of this layer.
|
*
|
* Parameters:
|
* obj - {Object} Should only be provided by subclasses that call this
|
* method.
|
*
|
* Returns:
|
* {<OpenLayers.Layer.TMS>} An exact clone of this <OpenLayers.Layer.TMS>
|
*/
|
clone: function (obj) {
|
|
if (obj == null) {
|
obj = new OpenLayers.Layer.TMS(this.name,
|
this.url,
|
this.getOptions());
|
}
|
|
//get all additions from superclasses
|
obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
|
|
// copy/set any non-init, non-simple values here
|
|
return obj;
|
},
|
|
/**
|
* Method: getURL
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
*
|
* Returns:
|
* {String} A string with the layer's url and parameters and also the
|
* passed-in bounds and appropriate tile size specified as
|
* parameters
|
*/
|
getURL: function (bounds) {
|
bounds = this.adjustBounds(bounds);
|
var res = this.getServerResolution();
|
var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w));
|
var y = Math.round((bounds.bottom - this.tileOrigin.lat) / (res * this.tileSize.h));
|
var z = this.getServerZoom();
|
var path = this.serviceVersion + "/" + this.layername + "/" + z + "/" + x + "/" + y + "." + this.type;
|
var url = this.url;
|
if (OpenLayers.Util.isArray(url)) {
|
url = this.selectUrl(path, url);
|
}
|
return url + path;
|
},
|
|
/**
|
* Method: setMap
|
* When the layer is added to a map, then we can fetch our origin
|
* (if we don't have one.)
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
setMap: function(map) {
|
OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);
|
if (!this.tileOrigin) {
|
this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left,
|
this.map.maxExtent.bottom);
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.TMS"
|
});
|
/* ======================================================================
|
OpenLayers/Format/WCSCapabilities.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML/VersionedOGC.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WCSCapabilities
|
* Read WCS Capabilities.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML.VersionedOGC>
|
*/
|
OpenLayers.Format.WCSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
|
|
/**
|
* APIProperty: defaultVersion
|
* {String} Version number to assume if none found. Default is "1.1.0".
|
*/
|
defaultVersion: "1.1.0",
|
|
/**
|
* Constructor: OpenLayers.Format.WCSCapabilities
|
* Create a new parser for WCS capabilities.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* APIMethod: read
|
* Read capabilities data from a string, and return a list of coverages.
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
*
|
* Returns:
|
* {Array} List of named coverages.
|
*/
|
|
CLASS_NAME: "OpenLayers.Format.WCSCapabilities"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/WCSCapabilities/v1.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/WCSCapabilities.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WCSCapabilities.v1
|
* Abstract class not to be instantiated directly.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.WCSCapabilities.v1 = OpenLayers.Class(
|
OpenLayers.Format.XML, {
|
|
regExes: {
|
trimSpace: (/^\s*|\s*$/g),
|
splitSpace: (/\s+/)
|
},
|
|
/**
|
* Property: defaultPrefix
|
*/
|
defaultPrefix: "wcs",
|
|
/**
|
* APIMethod: read
|
* Read capabilities data from a string, and return a list of coverages.
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
*
|
* Returns:
|
* {Array} List of named coverages.
|
*/
|
read: function(data) {
|
if(typeof data == "string") {
|
data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
|
}
|
var raw = data;
|
if(data && data.nodeType == 9) {
|
data = data.documentElement;
|
}
|
var capabilities = {};
|
this.readNode(data, capabilities);
|
return capabilities;
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WCSCapabilities.v1"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/WCSCapabilities/v1_0_0.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/WCSCapabilities/v1.js
|
* @requires OpenLayers/Format/GML/v3.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WCSCapabilities/v1_0_0
|
* Read WCS Capabilities version 1.0.0.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.WCSCapabilities.v1>
|
*/
|
OpenLayers.Format.WCSCapabilities.v1_0_0 = OpenLayers.Class(
|
OpenLayers.Format.WCSCapabilities.v1, {
|
|
/**
|
* Constructor: OpenLayers.Format.WCSCapabilities.v1_0_0
|
* Create a new parser for WCS capabilities version 1.0.0.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
wcs: "http://www.opengis.net/wcs",
|
xlink: "http://www.w3.org/1999/xlink",
|
xsi: "http://www.w3.org/2001/XMLSchema-instance",
|
ows: "http://www.opengis.net/ows"
|
},
|
|
/**
|
* Property: errorProperty
|
* {String} Which property of the returned object to check for in order to
|
* determine whether or not parsing has failed. In the case that the
|
* errorProperty is undefined on the returned object, the document will be
|
* run through an OGCExceptionReport parser.
|
*/
|
errorProperty: "service",
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"wcs": {
|
"WCS_Capabilities": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"Service": function(node, obj) {
|
obj.service = {};
|
this.readChildNodes(node, obj.service);
|
},
|
"name": function(node, service) {
|
service.name = this.getChildValue(node);
|
},
|
"label": function(node, service) {
|
service.label = this.getChildValue(node);
|
},
|
"keywords": function(node, service) {
|
service.keywords = [];
|
this.readChildNodes(node, service.keywords);
|
},
|
"keyword": function(node, keywords) {
|
// Append the keyword to the keywords list
|
keywords.push(this.getChildValue(node));
|
},
|
"responsibleParty": function(node, service) {
|
service.responsibleParty = {};
|
this.readChildNodes(node, service.responsibleParty);
|
},
|
"individualName": function(node, responsibleParty) {
|
responsibleParty.individualName = this.getChildValue(node);
|
},
|
"organisationName": function(node, responsibleParty) {
|
responsibleParty.organisationName = this.getChildValue(node);
|
},
|
"positionName": function(node, responsibleParty) {
|
responsibleParty.positionName = this.getChildValue(node);
|
},
|
"contactInfo": function(node, responsibleParty) {
|
responsibleParty.contactInfo = {};
|
this.readChildNodes(node, responsibleParty.contactInfo);
|
},
|
"phone": function(node, contactInfo) {
|
contactInfo.phone = {};
|
this.readChildNodes(node, contactInfo.phone);
|
},
|
"voice": function(node, phone) {
|
phone.voice = this.getChildValue(node);
|
},
|
"facsimile": function(node, phone) {
|
phone.facsimile = this.getChildValue(node);
|
},
|
"address": function(node, contactInfo) {
|
contactInfo.address = {};
|
this.readChildNodes(node, contactInfo.address);
|
},
|
"deliveryPoint": function(node, address) {
|
address.deliveryPoint = this.getChildValue(node);
|
},
|
"city": function(node, address) {
|
address.city = this.getChildValue(node);
|
},
|
"postalCode": function(node, address) {
|
address.postalCode = this.getChildValue(node);
|
},
|
"country": function(node, address) {
|
address.country = this.getChildValue(node);
|
},
|
"electronicMailAddress": function(node, address) {
|
address.electronicMailAddress = this.getChildValue(node);
|
},
|
"fees": function(node, service) {
|
service.fees = this.getChildValue(node);
|
},
|
"accessConstraints": function(node, service) {
|
service.accessConstraints = this.getChildValue(node);
|
},
|
"ContentMetadata": function(node, obj) {
|
obj.contentMetadata = [];
|
this.readChildNodes(node, obj.contentMetadata);
|
},
|
"CoverageOfferingBrief": function(node, contentMetadata) {
|
var coverageOfferingBrief = {};
|
this.readChildNodes(node, coverageOfferingBrief);
|
contentMetadata.push(coverageOfferingBrief);
|
},
|
"name": function(node, coverageOfferingBrief) {
|
coverageOfferingBrief.name = this.getChildValue(node);
|
},
|
"label": function(node, coverageOfferingBrief) {
|
coverageOfferingBrief.label = this.getChildValue(node);
|
},
|
"lonLatEnvelope": function(node, coverageOfferingBrief) {
|
var nodeList = this.getElementsByTagNameNS(node, "http://www.opengis.net/gml", "pos");
|
|
// We expect two nodes here, to create the corners of a bounding box
|
if(nodeList.length == 2) {
|
var min = {};
|
var max = {};
|
|
OpenLayers.Format.GML.v3.prototype.readers["gml"].pos.apply(this, [nodeList[0], min]);
|
OpenLayers.Format.GML.v3.prototype.readers["gml"].pos.apply(this, [nodeList[1], max]);
|
|
coverageOfferingBrief.lonLatEnvelope = {};
|
coverageOfferingBrief.lonLatEnvelope.srsName = node.getAttribute("srsName");
|
coverageOfferingBrief.lonLatEnvelope.min = min.points[0];
|
coverageOfferingBrief.lonLatEnvelope.max = max.points[0];
|
}
|
}
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WCSCapabilities.v1_0_0"
|
|
});
|
/* ======================================================================
|
OpenLayers/Strategy/Fixed.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Strategy.js
|
*/
|
|
/**
|
* Class: OpenLayers.Strategy.Fixed
|
* A simple strategy that requests features once and never requests new data.
|
*
|
* Inherits from:
|
* - <OpenLayers.Strategy>
|
*/
|
OpenLayers.Strategy.Fixed = OpenLayers.Class(OpenLayers.Strategy, {
|
|
/**
|
* APIProperty: preload
|
* {Boolean} Load data before layer made visible. Enabling this may result
|
* in considerable overhead if your application loads many data layers
|
* that are not visible by default. Default is false.
|
*/
|
preload: false,
|
|
/**
|
* Constructor: OpenLayers.Strategy.Fixed
|
* Create a new Fixed strategy.
|
*
|
* Parameters:
|
* options - {Object} Optional object whose properties will be set on the
|
* instance.
|
*/
|
|
/**
|
* Method: activate
|
* Activate the strategy: load data or add listener to load when visible
|
*
|
* Returns:
|
* {Boolean} True if the strategy was successfully activated or false if
|
* the strategy was already active.
|
*/
|
activate: function() {
|
var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments);
|
if(activated) {
|
this.layer.events.on({
|
"refresh": this.load,
|
scope: this
|
});
|
if(this.layer.visibility == true || this.preload) {
|
this.load();
|
} else {
|
this.layer.events.on({
|
"visibilitychanged": this.load,
|
scope: this
|
});
|
}
|
}
|
return activated;
|
},
|
|
/**
|
* Method: deactivate
|
* Deactivate the strategy. Undo what is done in <activate>.
|
*
|
* Returns:
|
* {Boolean} The strategy was successfully deactivated.
|
*/
|
deactivate: function() {
|
var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
|
if(deactivated) {
|
this.layer.events.un({
|
"refresh": this.load,
|
"visibilitychanged": this.load,
|
scope: this
|
});
|
}
|
return deactivated;
|
},
|
|
/**
|
* Method: load
|
* Tells protocol to load data and unhooks the visibilitychanged event
|
*
|
* Parameters:
|
* options - {Object} options to pass to protocol read.
|
*/
|
load: function(options) {
|
var layer = this.layer;
|
layer.events.triggerEvent("loadstart", {filter: layer.filter});
|
layer.protocol.read(OpenLayers.Util.applyDefaults({
|
callback: this.merge,
|
filter: layer.filter,
|
scope: this
|
}, options));
|
layer.events.un({
|
"visibilitychanged": this.load,
|
scope: this
|
});
|
},
|
|
/**
|
* Method: merge
|
* Add all features to the layer.
|
* If the layer projection differs from the map projection, features
|
* will be transformed from the layer projection to the map projection.
|
*
|
* Parameters:
|
* resp - {<OpenLayers.Protocol.Response>} The response object passed
|
* by the protocol.
|
*/
|
merge: function(resp) {
|
var layer = this.layer;
|
layer.destroyFeatures();
|
var features = resp.features;
|
if (features && features.length > 0) {
|
var remote = layer.projection;
|
var local = layer.map.getProjectionObject();
|
if(!local.equals(remote)) {
|
var geom;
|
for(var i=0, len=features.length; i<len; ++i) {
|
geom = features[i].geometry;
|
if(geom) {
|
geom.transform(remote, local);
|
}
|
}
|
}
|
layer.addFeatures(features);
|
}
|
layer.events.triggerEvent("loadend", {response: resp});
|
},
|
|
CLASS_NAME: "OpenLayers.Strategy.Fixed"
|
});
|
/* ======================================================================
|
OpenLayers/Control/Zoom.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/Events/buttonclick.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.Zoom
|
* The Zoom control is a pair of +/- links for zooming in and out.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.Zoom = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* APIProperty: zoomInText
|
* {String}
|
* Text for zoom-in link. Default is "+".
|
*/
|
zoomInText: "+",
|
|
/**
|
* APIProperty: zoomInId
|
* {String}
|
* Instead of having the control create a zoom in link, you can provide
|
* the identifier for an anchor element already added to the document.
|
* By default, an element with id "olZoomInLink" will be searched for
|
* and used if it exists.
|
*/
|
zoomInId: "olZoomInLink",
|
|
/**
|
* APIProperty: zoomOutText
|
* {String}
|
* Text for zoom-out link. Default is "\u2212".
|
*/
|
zoomOutText: "\u2212",
|
|
/**
|
* APIProperty: zoomOutId
|
* {String}
|
* Instead of having the control create a zoom out link, you can provide
|
* the identifier for an anchor element already added to the document.
|
* By default, an element with id "olZoomOutLink" will be searched for
|
* and used if it exists.
|
*/
|
zoomOutId: "olZoomOutLink",
|
|
/**
|
* Method: draw
|
*
|
* Returns:
|
* {DOMElement} A reference to the DOMElement containing the zoom links.
|
*/
|
draw: function() {
|
var div = OpenLayers.Control.prototype.draw.apply(this),
|
links = this.getOrCreateLinks(div),
|
zoomIn = links.zoomIn,
|
zoomOut = links.zoomOut,
|
eventsInstance = this.map.events;
|
|
if (zoomOut.parentNode !== div) {
|
eventsInstance = this.events;
|
eventsInstance.attachToElement(zoomOut.parentNode);
|
}
|
eventsInstance.register("buttonclick", this, this.onZoomClick);
|
|
this.zoomInLink = zoomIn;
|
this.zoomOutLink = zoomOut;
|
return div;
|
},
|
|
/**
|
* Method: getOrCreateLinks
|
*
|
* Parameters:
|
* el - {DOMElement}
|
*
|
* Return:
|
* {Object} Object with zoomIn and zoomOut properties referencing links.
|
*/
|
getOrCreateLinks: function(el) {
|
var zoomIn = document.getElementById(this.zoomInId),
|
zoomOut = document.getElementById(this.zoomOutId);
|
if (!zoomIn) {
|
zoomIn = document.createElement("a");
|
zoomIn.href = "#zoomIn";
|
zoomIn.appendChild(document.createTextNode(this.zoomInText));
|
zoomIn.className = "olControlZoomIn";
|
el.appendChild(zoomIn);
|
}
|
OpenLayers.Element.addClass(zoomIn, "olButton");
|
if (!zoomOut) {
|
zoomOut = document.createElement("a");
|
zoomOut.href = "#zoomOut";
|
zoomOut.appendChild(document.createTextNode(this.zoomOutText));
|
zoomOut.className = "olControlZoomOut";
|
el.appendChild(zoomOut);
|
}
|
OpenLayers.Element.addClass(zoomOut, "olButton");
|
return {
|
zoomIn: zoomIn, zoomOut: zoomOut
|
};
|
},
|
|
/**
|
* Method: onZoomClick
|
* Called when zoomin/out link is clicked.
|
*/
|
onZoomClick: function(evt) {
|
var button = evt.buttonElement;
|
if (button === this.zoomInLink) {
|
this.map.zoomIn();
|
} else if (button === this.zoomOutLink) {
|
this.map.zoomOut();
|
}
|
},
|
|
/**
|
* Method: destroy
|
* Clean up.
|
*/
|
destroy: function() {
|
if (this.map) {
|
this.map.events.unregister("buttonclick", this, this.onZoomClick);
|
}
|
delete this.zoomInLink;
|
delete this.zoomOutLink;
|
OpenLayers.Control.prototype.destroy.apply(this);
|
},
|
|
CLASS_NAME: "OpenLayers.Control.Zoom"
|
});
|
/* ======================================================================
|
OpenLayers/Layer/PointTrack.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Layer/Vector.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.PointTrack
|
* Vector layer to display ordered point features as a line, creating one
|
* LineString feature for each pair of two points.
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer.Vector>
|
*/
|
OpenLayers.Layer.PointTrack = OpenLayers.Class(OpenLayers.Layer.Vector, {
|
|
/**
|
* APIProperty: dataFrom
|
* {<OpenLayers.Layer.PointTrack.TARGET_NODE>} or
|
* {<OpenLayers.Layer.PointTrack.SOURCE_NODE>} optional. If the lines
|
* should get the data/attributes from one of the two points it is
|
* composed of, which one should it be?
|
*/
|
dataFrom: null,
|
|
/**
|
* APIProperty: styleFrom
|
* {<OpenLayers.Layer.PointTrack.TARGET_NODE>} or
|
* {<OpenLayers.Layer.PointTrack.SOURCE_NODE>} optional. If the lines
|
* should get the style from one of the two points it is composed of,
|
* which one should it be?
|
*/
|
styleFrom: null,
|
|
/**
|
* Constructor: OpenLayers.PointTrack
|
* Constructor for a new OpenLayers.PointTrack instance.
|
*
|
* Parameters:
|
* name - {String} name of the layer
|
* options - {Object} Optional object with properties to tag onto the
|
* instance.
|
*/
|
|
/**
|
* APIMethod: addNodes
|
* Adds point features that will be used to create lines from, using point
|
* pairs. The first point of a pair will be the source node, the second
|
* will be the target node.
|
*
|
* Parameters:
|
* pointFeatures - {Array(<OpenLayers.Feature>)}
|
* options - {Object}
|
*
|
* Supported options:
|
* silent - {Boolean} true to suppress (before)feature(s)added events
|
*/
|
addNodes: function(pointFeatures, options) {
|
if (pointFeatures.length < 2) {
|
throw new Error("At least two point features have to be added to " +
|
"create a line from");
|
}
|
|
var lines = new Array(pointFeatures.length-1);
|
|
var pointFeature, startPoint, endPoint;
|
for(var i=0, len=pointFeatures.length; i<len; i++) {
|
pointFeature = pointFeatures[i];
|
endPoint = pointFeature.geometry;
|
|
if (!endPoint) {
|
var lonlat = pointFeature.lonlat;
|
endPoint = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);
|
} else if(endPoint.CLASS_NAME != "OpenLayers.Geometry.Point") {
|
throw new TypeError("Only features with point geometries are supported.");
|
}
|
|
if(i > 0) {
|
var attributes = (this.dataFrom != null) ?
|
(pointFeatures[i+this.dataFrom].data ||
|
pointFeatures[i+this.dataFrom].attributes) :
|
null;
|
var style = (this.styleFrom != null) ?
|
(pointFeatures[i+this.styleFrom].style) :
|
null;
|
var line = new OpenLayers.Geometry.LineString([startPoint,
|
endPoint]);
|
|
lines[i-1] = new OpenLayers.Feature.Vector(line, attributes,
|
style);
|
}
|
|
startPoint = endPoint;
|
}
|
|
this.addFeatures(lines, options);
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.PointTrack"
|
});
|
|
/**
|
* Constant: OpenLayers.Layer.PointTrack.SOURCE_NODE
|
* {Number} value for <OpenLayers.Layer.PointTrack.dataFrom> and
|
* <OpenLayers.Layer.PointTrack.styleFrom>
|
*/
|
OpenLayers.Layer.PointTrack.SOURCE_NODE = -1;
|
|
/**
|
* Constant: OpenLayers.Layer.PointTrack.TARGET_NODE
|
* {Number} value for <OpenLayers.Layer.PointTrack.dataFrom> and
|
* <OpenLayers.Layer.PointTrack.styleFrom>
|
*/
|
OpenLayers.Layer.PointTrack.TARGET_NODE = 0;
|
|
/**
|
* Constant: OpenLayers.Layer.PointTrack.dataFrom
|
* {Object} with the following keys - *deprecated*
|
* - SOURCE_NODE: take data/attributes from the source node of the line
|
* - TARGET_NODE: take data/attributes from the target node of the line
|
*/
|
OpenLayers.Layer.PointTrack.dataFrom = {'SOURCE_NODE': -1, 'TARGET_NODE': 0};
|
/* ======================================================================
|
OpenLayers/Protocol/WFS.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Protocol.js
|
*/
|
|
/**
|
* Class: OpenLayers.Protocol.WFS
|
* Used to create a versioned WFS protocol. Default version is 1.0.0.
|
*
|
* Returns:
|
* {<OpenLayers.Protocol>} A WFS protocol of the given version.
|
*
|
* Example:
|
* (code)
|
* var protocol = new OpenLayers.Protocol.WFS({
|
* version: "1.1.0",
|
* url: "http://demo.opengeo.org/geoserver/wfs",
|
* featureType: "tasmania_roads",
|
* featureNS: "http://www.openplans.org/topp",
|
* geometryName: "the_geom"
|
* });
|
* (end)
|
*
|
* See the protocols for specific WFS versions for more detail.
|
*/
|
OpenLayers.Protocol.WFS = function(options) {
|
options = OpenLayers.Util.applyDefaults(
|
options, OpenLayers.Protocol.WFS.DEFAULTS
|
);
|
var cls = OpenLayers.Protocol.WFS["v"+options.version.replace(/\./g, "_")];
|
if(!cls) {
|
throw "Unsupported WFS version: " + options.version;
|
}
|
return new cls(options);
|
};
|
|
/**
|
* Function: fromWMSLayer
|
* Convenience function to create a WFS protocol from a WMS layer. This makes
|
* the assumption that a WFS requests can be issued at the same URL as
|
* WMS requests and that a WFS featureType exists with the same name as the
|
* WMS layer.
|
*
|
* This function is designed to auto-configure <url>, <featureType>,
|
* <featurePrefix> and <srsName> for WFS <version> 1.1.0. Note that
|
* srsName matching with the WMS layer will not work with WFS 1.0.0.
|
*
|
* Parameters:
|
* layer - {<OpenLayers.Layer.WMS>} WMS layer that has a matching WFS
|
* FeatureType at the same server url with the same typename.
|
* options - {Object} Default properties to be set on the protocol.
|
*
|
* Returns:
|
* {<OpenLayers.Protocol.WFS>}
|
*/
|
OpenLayers.Protocol.WFS.fromWMSLayer = function(layer, options) {
|
var typeName, featurePrefix;
|
var param = layer.params["LAYERS"];
|
var parts = (OpenLayers.Util.isArray(param) ? param[0] : param).split(":");
|
if(parts.length > 1) {
|
featurePrefix = parts[0];
|
}
|
typeName = parts.pop();
|
var protocolOptions = {
|
url: layer.url,
|
featureType: typeName,
|
featurePrefix: featurePrefix,
|
srsName: layer.projection && layer.projection.getCode() ||
|
layer.map && layer.map.getProjectionObject().getCode(),
|
version: "1.1.0"
|
};
|
return new OpenLayers.Protocol.WFS(OpenLayers.Util.applyDefaults(
|
options, protocolOptions
|
));
|
};
|
|
/**
|
* Constant: OpenLayers.Protocol.WFS.DEFAULTS
|
*/
|
OpenLayers.Protocol.WFS.DEFAULTS = {
|
"version": "1.0.0"
|
};
|
/* ======================================================================
|
OpenLayers/Layer/Markers.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Layer.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.Markers
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer>
|
*/
|
OpenLayers.Layer.Markers = OpenLayers.Class(OpenLayers.Layer, {
|
|
/**
|
* APIProperty: isBaseLayer
|
* {Boolean} Markers layer is never a base layer.
|
*/
|
isBaseLayer: false,
|
|
/**
|
* APIProperty: markers
|
* {Array(<OpenLayers.Marker>)} internal marker list
|
*/
|
markers: null,
|
|
|
/**
|
* Property: drawn
|
* {Boolean} internal state of drawing. This is a workaround for the fact
|
* that the map does not call moveTo with a zoomChanged when the map is
|
* first starting up. This lets us catch the case where we have *never*
|
* drawn the layer, and draw it even if the zoom hasn't changed.
|
*/
|
drawn: false,
|
|
/**
|
* Constructor: OpenLayers.Layer.Markers
|
* Create a Markers layer.
|
*
|
* Parameters:
|
* name - {String}
|
* options - {Object} Hashtable of extra options to tag onto the layer
|
*/
|
initialize: function(name, options) {
|
OpenLayers.Layer.prototype.initialize.apply(this, arguments);
|
this.markers = [];
|
},
|
|
/**
|
* APIMethod: destroy
|
*/
|
destroy: function() {
|
this.clearMarkers();
|
this.markers = null;
|
OpenLayers.Layer.prototype.destroy.apply(this, arguments);
|
},
|
|
/**
|
* APIMethod: setOpacity
|
* Sets the opacity for all the markers.
|
*
|
* Parameters:
|
* opacity - {Float}
|
*/
|
setOpacity: function(opacity) {
|
if (opacity != this.opacity) {
|
this.opacity = opacity;
|
for (var i=0, len=this.markers.length; i<len; i++) {
|
this.markers[i].setOpacity(this.opacity);
|
}
|
}
|
},
|
|
/**
|
* Method: moveTo
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
* zoomChanged - {Boolean}
|
* dragging - {Boolean}
|
*/
|
moveTo:function(bounds, zoomChanged, dragging) {
|
OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
|
|
if (zoomChanged || !this.drawn) {
|
for(var i=0, len=this.markers.length; i<len; i++) {
|
this.drawMarker(this.markers[i]);
|
}
|
this.drawn = true;
|
}
|
},
|
|
/**
|
* APIMethod: addMarker
|
*
|
* Parameters:
|
* marker - {<OpenLayers.Marker>}
|
*/
|
addMarker: function(marker) {
|
this.markers.push(marker);
|
|
if (this.opacity < 1) {
|
marker.setOpacity(this.opacity);
|
}
|
|
if (this.map && this.map.getExtent()) {
|
marker.map = this.map;
|
this.drawMarker(marker);
|
}
|
},
|
|
/**
|
* APIMethod: removeMarker
|
*
|
* Parameters:
|
* marker - {<OpenLayers.Marker>}
|
*/
|
removeMarker: function(marker) {
|
if (this.markers && this.markers.length) {
|
OpenLayers.Util.removeItem(this.markers, marker);
|
marker.erase();
|
}
|
},
|
|
/**
|
* Method: clearMarkers
|
* This method removes all markers from a layer. The markers are not
|
* destroyed by this function, but are removed from the list of markers.
|
*/
|
clearMarkers: function() {
|
if (this.markers != null) {
|
while(this.markers.length > 0) {
|
this.removeMarker(this.markers[0]);
|
}
|
}
|
},
|
|
/**
|
* Method: drawMarker
|
* Calculate the pixel location for the marker, create it, and
|
* add it to the layer's div
|
*
|
* Parameters:
|
* marker - {<OpenLayers.Marker>}
|
*/
|
drawMarker: function(marker) {
|
var px = this.map.getLayerPxFromLonLat(marker.lonlat);
|
if (px == null) {
|
marker.display(false);
|
} else {
|
if (!marker.isDrawn()) {
|
var markerImg = marker.draw(px);
|
this.div.appendChild(markerImg);
|
} else if(marker.icon) {
|
marker.icon.moveTo(px);
|
}
|
}
|
},
|
|
/**
|
* APIMethod: getDataExtent
|
* Calculates the max extent which includes all of the markers.
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>}
|
*/
|
getDataExtent: function () {
|
var maxExtent = null;
|
|
if ( this.markers && (this.markers.length > 0)) {
|
var maxExtent = new OpenLayers.Bounds();
|
for(var i=0, len=this.markers.length; i<len; i++) {
|
var marker = this.markers[i];
|
maxExtent.extend(marker.lonlat);
|
}
|
}
|
|
return maxExtent;
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.Markers"
|
});
|
/* ======================================================================
|
OpenLayers/Control/Pan.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control/Button.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.Pan
|
* The Pan control is a single button to pan the map in one direction. For
|
* a more complete control see <OpenLayers.Control.PanPanel>.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.Pan = OpenLayers.Class(OpenLayers.Control.Button, {
|
|
/**
|
* APIProperty: slideFactor
|
* {Integer} Number of pixels by which we'll pan the map in any direction
|
* on clicking the arrow buttons, defaults to 50. If you want to pan
|
* by some ratio of the map dimensions, use <slideRatio> instead.
|
*/
|
slideFactor: 50,
|
|
/**
|
* APIProperty: slideRatio
|
* {Number} The fraction of map width/height by which we'll pan the map
|
* on clicking the arrow buttons. Default is null. If set, will
|
* override <slideFactor>. E.g. if slideRatio is .5, then Pan Up will
|
* pan up half the map height.
|
*/
|
slideRatio: null,
|
|
/**
|
* Property: direction
|
* {String} in {'North', 'South', 'East', 'West'}
|
*/
|
direction: null,
|
|
/**
|
* Constructor: OpenLayers.Control.Pan
|
* Control which handles the panning (in any of the cardinal directions)
|
* of the map by a set px distance.
|
*
|
* Parameters:
|
* direction - {String} The direction this button should pan.
|
* options - {Object} An optional object whose properties will be used
|
* to extend the control.
|
*/
|
initialize: function(direction, options) {
|
|
this.direction = direction;
|
this.CLASS_NAME += this.direction;
|
|
OpenLayers.Control.prototype.initialize.apply(this, [options]);
|
},
|
|
/**
|
* Method: trigger
|
*/
|
trigger: function(){
|
if (this.map) {
|
var getSlideFactor = OpenLayers.Function.bind(function (dim) {
|
return this.slideRatio ?
|
this.map.getSize()[dim] * this.slideRatio :
|
this.slideFactor;
|
}, this);
|
|
switch (this.direction) {
|
case OpenLayers.Control.Pan.NORTH:
|
this.map.pan(0, -getSlideFactor("h"));
|
break;
|
case OpenLayers.Control.Pan.SOUTH:
|
this.map.pan(0, getSlideFactor("h"));
|
break;
|
case OpenLayers.Control.Pan.WEST:
|
this.map.pan(-getSlideFactor("w"), 0);
|
break;
|
case OpenLayers.Control.Pan.EAST:
|
this.map.pan(getSlideFactor("w"), 0);
|
break;
|
}
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Control.Pan"
|
});
|
|
OpenLayers.Control.Pan.NORTH = "North";
|
OpenLayers.Control.Pan.SOUTH = "South";
|
OpenLayers.Control.Pan.EAST = "East";
|
OpenLayers.Control.Pan.WEST = "West";
|
/* ======================================================================
|
OpenLayers/Format/CSWGetDomain.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.CSWGetDomain
|
* Default version is 2.0.2.
|
*
|
* Returns:
|
* {<OpenLayers.Format>} A CSWGetDomain format of the given version.
|
*/
|
OpenLayers.Format.CSWGetDomain = function(options) {
|
options = OpenLayers.Util.applyDefaults(
|
options, OpenLayers.Format.CSWGetDomain.DEFAULTS
|
);
|
var cls = OpenLayers.Format.CSWGetDomain["v"+options.version.replace(/\./g, "_")];
|
if(!cls) {
|
throw "Unsupported CSWGetDomain version: " + options.version;
|
}
|
return new cls(options);
|
};
|
|
/**
|
* Constant: DEFAULTS
|
* {Object} Default properties for the CSWGetDomain format.
|
*/
|
OpenLayers.Format.CSWGetDomain.DEFAULTS = {
|
"version": "2.0.2"
|
};
|
/* ======================================================================
|
OpenLayers/Format/CSWGetDomain/v2_0_2.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML.js
|
* @requires OpenLayers/Format/CSWGetDomain.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.CSWGetDomain.v2_0_2
|
* A format for creating CSWGetDomain v2.0.2 transactions.
|
* Create a new instance with the
|
* <OpenLayers.Format.CSWGetDomain.v2_0_2> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.CSWGetDomain.v2_0_2 = OpenLayers.Class(OpenLayers.Format.XML, {
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
xlink: "http://www.w3.org/1999/xlink",
|
xsi: "http://www.w3.org/2001/XMLSchema-instance",
|
csw: "http://www.opengis.net/cat/csw/2.0.2"
|
},
|
|
/**
|
* Property: defaultPrefix
|
* {String} The default prefix (used by Format.XML).
|
*/
|
defaultPrefix: "csw",
|
|
/**
|
* Property: version
|
* {String} CSW version number.
|
*/
|
version: "2.0.2",
|
|
/**
|
* Property: schemaLocation
|
* {String} http://www.opengis.net/cat/csw/2.0.2
|
* http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd
|
*/
|
schemaLocation: "http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd",
|
|
/**
|
* APIProperty: PropertyName
|
* {String} Value of the csw:PropertyName element, used when
|
* writing a GetDomain document.
|
*/
|
PropertyName: null,
|
|
/**
|
* APIProperty: ParameterName
|
* {String} Value of the csw:ParameterName element, used when
|
* writing a GetDomain document.
|
*/
|
ParameterName: null,
|
|
/**
|
* Constructor: OpenLayers.Format.CSWGetDomain.v2_0_2
|
* A class for parsing and generating CSWGetDomain v2.0.2 transactions.
|
*
|
* Parameters:
|
* options - {Object} Optional object whose properties will be set on the
|
* instance.
|
*
|
* Valid options properties:
|
* - PropertyName
|
* - ParameterName
|
*/
|
|
/**
|
* APIMethod: read
|
* Parse the response from a GetDomain request.
|
*/
|
read: function(data) {
|
if(typeof data == "string") {
|
data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
|
}
|
if(data && data.nodeType == 9) {
|
data = data.documentElement;
|
}
|
var obj = {};
|
this.readNode(data, obj);
|
return obj;
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"csw": {
|
"GetDomainResponse": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"DomainValues": function(node, obj) {
|
if (!(OpenLayers.Util.isArray(obj.DomainValues))) {
|
obj.DomainValues = [];
|
}
|
var attrs = node.attributes;
|
var domainValue = {};
|
for(var i=0, len=attrs.length; i<len; ++i) {
|
domainValue[attrs[i].name] = attrs[i].nodeValue;
|
}
|
this.readChildNodes(node, domainValue);
|
obj.DomainValues.push(domainValue);
|
},
|
"PropertyName": function(node, obj) {
|
obj.PropertyName = this.getChildValue(node);
|
},
|
"ParameterName": function(node, obj) {
|
obj.ParameterName = this.getChildValue(node);
|
},
|
"ListOfValues": function(node, obj) {
|
if (!(OpenLayers.Util.isArray(obj.ListOfValues))) {
|
obj.ListOfValues = [];
|
}
|
this.readChildNodes(node, obj.ListOfValues);
|
},
|
"Value": function(node, obj) {
|
var attrs = node.attributes;
|
var value = {};
|
for(var i=0, len=attrs.length; i<len; ++i) {
|
value[attrs[i].name] = attrs[i].nodeValue;
|
}
|
value.value = this.getChildValue(node);
|
obj.push({Value: value});
|
},
|
"ConceptualScheme": function(node, obj) {
|
obj.ConceptualScheme = {};
|
this.readChildNodes(node, obj.ConceptualScheme);
|
},
|
"Name": function(node, obj) {
|
obj.Name = this.getChildValue(node);
|
},
|
"Document": function(node, obj) {
|
obj.Document = this.getChildValue(node);
|
},
|
"Authority": function(node, obj) {
|
obj.Authority = this.getChildValue(node);
|
},
|
"RangeOfValues": function(node, obj) {
|
obj.RangeOfValues = {};
|
this.readChildNodes(node, obj.RangeOfValues);
|
},
|
"MinValue": function(node, obj) {
|
var attrs = node.attributes;
|
var value = {};
|
for(var i=0, len=attrs.length; i<len; ++i) {
|
value[attrs[i].name] = attrs[i].nodeValue;
|
}
|
value.value = this.getChildValue(node);
|
obj.MinValue = value;
|
},
|
"MaxValue": function(node, obj) {
|
var attrs = node.attributes;
|
var value = {};
|
for(var i=0, len=attrs.length; i<len; ++i) {
|
value[attrs[i].name] = attrs[i].nodeValue;
|
}
|
value.value = this.getChildValue(node);
|
obj.MaxValue = value;
|
}
|
}
|
},
|
|
/**
|
* APIMethod: write
|
* Given an configuration js object, write a CSWGetDomain request.
|
*
|
* Parameters:
|
* options - {Object} A object mapping the request.
|
*
|
* Returns:
|
* {String} A serialized CSWGetDomain request.
|
*/
|
write: function(options) {
|
var node = this.writeNode("csw:GetDomain", options);
|
return OpenLayers.Format.XML.prototype.write.apply(this, [node]);
|
},
|
|
/**
|
* Property: writers
|
* As a compliment to the readers property, this structure contains public
|
* writing functions grouped by namespace alias and named like the
|
* node names they produce.
|
*/
|
writers: {
|
"csw": {
|
"GetDomain": function(options) {
|
var node = this.createElementNSPlus("csw:GetDomain", {
|
attributes: {
|
service: "CSW",
|
version: this.version
|
}
|
});
|
if (options.PropertyName || this.PropertyName) {
|
this.writeNode(
|
"csw:PropertyName",
|
options.PropertyName || this.PropertyName,
|
node
|
);
|
} else if (options.ParameterName || this.ParameterName) {
|
this.writeNode(
|
"csw:ParameterName",
|
options.ParameterName || this.ParameterName,
|
node
|
);
|
}
|
this.readChildNodes(node, options);
|
return node;
|
},
|
"PropertyName": function(value) {
|
var node = this.createElementNSPlus("csw:PropertyName", {
|
value: value
|
});
|
return node;
|
},
|
"ParameterName": function(value) {
|
var node = this.createElementNSPlus("csw:ParameterName", {
|
value: value
|
});
|
return node;
|
}
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Format.CSWGetDomain.v2_0_2"
|
});
|
/* ======================================================================
|
OpenLayers/Format/ArcXML/Features.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/ArcXML.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.ArcXML.Features
|
* Read/Write ArcXML features. Create a new instance with the
|
* <OpenLayers.Format.ArcXML.Features> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.ArcXML.Features = OpenLayers.Class(OpenLayers.Format.XML, {
|
|
/**
|
* Constructor: OpenLayers.Format.ArcXML.Features
|
* Create a new parser/writer for ArcXML Features. Create an instance of this class
|
* to get a set of features from an ArcXML response.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* APIMethod: read
|
* Read data from a string of ArcXML, and return a set of OpenLayers features.
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
*
|
* Returns:
|
* {Array(<OpenLayers.Feature.Vector>)} A collection of features.
|
*/
|
read: function(data) {
|
var axl = new OpenLayers.Format.ArcXML();
|
var parsed = axl.read(data);
|
|
return parsed.features.feature;
|
}
|
});
|
/* ======================================================================
|
OpenLayers/Control/Snapping.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/Layer/Vector.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.Snapping
|
* Acts as a snapping agent while editing vector features.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.Snapping = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* APIProperty: events
|
* {<OpenLayers.Events>} Events instance for listeners and triggering
|
* control specific events.
|
*
|
* Register a listener for a particular event with the following syntax:
|
* (code)
|
* control.events.register(type, obj, listener);
|
* (end)
|
*
|
* Supported event types (in addition to those from <OpenLayers.Control.events>):
|
* beforesnap - Triggered before a snap occurs. Listeners receive an
|
* event object with *point*, *x*, *y*, *distance*, *layer*, and
|
* *snapType* properties. The point property will be original point
|
* geometry considered for snapping. The x and y properties represent
|
* coordinates the point will receive. The distance is the distance
|
* of the snap. The layer is the target layer. The snapType property
|
* will be one of "node", "vertex", or "edge". Return false to stop
|
* snapping from occurring.
|
* snap - Triggered when a snap occurs. Listeners receive an event with
|
* *point*, *snapType*, *layer*, and *distance* properties. The point
|
* will be the location snapped to. The snapType will be one of "node",
|
* "vertex", or "edge". The layer will be the target layer. The
|
* distance will be the distance of the snap in map units.
|
* unsnap - Triggered when a vertex is unsnapped. Listeners receive an
|
* event with a *point* property.
|
*/
|
|
/**
|
* CONSTANT: DEFAULTS
|
* Default target properties.
|
*/
|
DEFAULTS: {
|
tolerance: 10,
|
node: true,
|
edge: true,
|
vertex: true
|
},
|
|
/**
|
* Property: greedy
|
* {Boolean} Snap to closest feature in first layer with an eligible
|
* feature. Default is true.
|
*/
|
greedy: true,
|
|
/**
|
* Property: precedence
|
* {Array} List representing precedence of different snapping types.
|
* Default is "node", "vertex", "edge".
|
*/
|
precedence: ["node", "vertex", "edge"],
|
|
/**
|
* Property: resolution
|
* {Float} The map resolution for the previously considered snap.
|
*/
|
resolution: null,
|
|
/**
|
* Property: geoToleranceCache
|
* {Object} A cache of geo-tolerances. Tolerance values (in map units) are
|
* calculated when the map resolution changes.
|
*/
|
geoToleranceCache: null,
|
|
/**
|
* Property: layer
|
* {<OpenLayers.Layer.Vector>} The current editable layer. Set at
|
* construction or after construction with <setLayer>.
|
*/
|
layer: null,
|
|
/**
|
* Property: feature
|
* {<OpenLayers.Feature.Vector>} The current editable feature.
|
*/
|
feature: null,
|
|
/**
|
* Property: point
|
* {<OpenLayers.Geometry.Point>} The currently snapped vertex.
|
*/
|
point: null,
|
|
/**
|
* Constructor: OpenLayers.Control.Snapping
|
* Creates a new snapping control. A control is constructed with an editable
|
* layer and a set of configuration objects for target layers. While the
|
* control is active, dragging vertices while drawing new features or
|
* modifying existing features on the editable layer will engage
|
* snapping to features on the target layers. Whether a vertex snaps to
|
* a feature on a target layer depends on the target layer configuration.
|
*
|
* Parameters:
|
* options - {Object} An object containing all configuration properties for
|
* the control.
|
*
|
* Valid options:
|
* layer - {<OpenLayers.Layer.Vector>} The editable layer. Features from this
|
* layer that are digitized or modified may have vertices snapped to
|
* features from any of the target layers.
|
* targets - {Array(Object | OpenLayers.Layer.Vector)} A list of objects for
|
* configuring target layers. See valid properties of the target
|
* objects below. If the items in the targets list are vector layers
|
* (instead of configuration objects), the defaults from the <defaults>
|
* property will apply. The editable layer itself may be a target
|
* layer, allowing newly created or edited features to be snapped to
|
* existing features from the same layer. If no targets are provided
|
* the layer given in the constructor (as <layer>) will become the
|
* initial target.
|
* defaults - {Object} An object with default properties to be applied
|
* to all target objects.
|
* greedy - {Boolean} Snap to closest feature in first target layer that
|
* applies. Default is true. If false, all features in all target
|
* layers will be checked and the closest feature in all target layers
|
* will be chosen. The greedy property determines if the order of the
|
* target layers is significant. By default, the order of the target
|
* layers is significant where layers earlier in the target layer list
|
* have precedence over layers later in the list. Within a single
|
* layer, the closest feature is always chosen for snapping. This
|
* property only determines whether the search for a closer feature
|
* continues after an eligible feature is found in a target layer.
|
*
|
* Valid target properties:
|
* layer - {<OpenLayers.Layer.Vector>} A target layer. Features from this
|
* layer will be eligible to act as snapping target for the editable
|
* layer.
|
* tolerance - {Float} The distance (in pixels) at which snapping may occur.
|
* Default is 10.
|
* node - {Boolean} Snap to nodes (first or last point in a geometry) in
|
* target layer. Default is true.
|
* nodeTolerance - {Float} Optional distance at which snapping may occur
|
* for nodes specifically. If none is provided, <tolerance> will be
|
* used.
|
* vertex - {Boolean} Snap to vertices in target layer. Default is true.
|
* vertexTolerance - {Float} Optional distance at which snapping may occur
|
* for vertices specifically. If none is provided, <tolerance> will be
|
* used.
|
* edge - {Boolean} Snap to edges in target layer. Default is true.
|
* edgeTolerance - {Float} Optional distance at which snapping may occur
|
* for edges specifically. If none is provided, <tolerance> will be
|
* used.
|
* filter - {<OpenLayers.Filter>} Optional filter to evaluate to determine if
|
* feature is eligible for snapping. If filter evaluates to true for a
|
* target feature a vertex may be snapped to the feature.
|
* minResolution - {Number} If a minResolution is provided, snapping to this
|
* target will only be considered if the map resolution is greater than
|
* or equal to this value (the minResolution is inclusive). Default is
|
* no minimum resolution limit.
|
* maxResolution - {Number} If a maxResolution is provided, snapping to this
|
* target will only be considered if the map resolution is strictly
|
* less than this value (the maxResolution is exclusive). Default is
|
* no maximum resolution limit.
|
*/
|
initialize: function(options) {
|
OpenLayers.Control.prototype.initialize.apply(this, [options]);
|
this.options = options || {}; // TODO: this could be done by the super
|
|
// set the editable layer if provided
|
if(this.options.layer) {
|
this.setLayer(this.options.layer);
|
}
|
// configure target layers
|
var defaults = OpenLayers.Util.extend({}, this.options.defaults);
|
this.defaults = OpenLayers.Util.applyDefaults(defaults, this.DEFAULTS);
|
this.setTargets(this.options.targets);
|
if(this.targets.length === 0 && this.layer) {
|
this.addTargetLayer(this.layer);
|
}
|
|
this.geoToleranceCache = {};
|
},
|
|
/**
|
* APIMethod: setLayer
|
* Set the editable layer. Call the setLayer method if the editable layer
|
* changes and the same control should be used on a new editable layer.
|
* If the control is already active, it will be active after the new
|
* layer is set.
|
*
|
* Parameters:
|
* layer - {<OpenLayers.Layer.Vector>} The new editable layer.
|
*/
|
setLayer: function(layer) {
|
if(this.active) {
|
this.deactivate();
|
this.layer = layer;
|
this.activate();
|
} else {
|
this.layer = layer;
|
}
|
},
|
|
/**
|
* Method: setTargets
|
* Set the targets for the snapping agent.
|
*
|
* Parameters:
|
* targets - {Array} An array of target configs or target layers.
|
*/
|
setTargets: function(targets) {
|
this.targets = [];
|
if(targets && targets.length) {
|
var target;
|
for(var i=0, len=targets.length; i<len; ++i) {
|
target = targets[i];
|
if(target instanceof OpenLayers.Layer.Vector) {
|
this.addTargetLayer(target);
|
} else {
|
this.addTarget(target);
|
}
|
}
|
}
|
},
|
|
/**
|
* Method: addTargetLayer
|
* Add a target layer with the default target config.
|
*
|
* Parameters:
|
* layer - {<OpenLayers.Layer.Vector>} A target layer.
|
*/
|
addTargetLayer: function(layer) {
|
this.addTarget({layer: layer});
|
},
|
|
/**
|
* Method: addTarget
|
* Add a configured target layer.
|
*
|
* Parameters:
|
* target - {Object} A target config.
|
*/
|
addTarget: function(target) {
|
target = OpenLayers.Util.applyDefaults(target, this.defaults);
|
target.nodeTolerance = target.nodeTolerance || target.tolerance;
|
target.vertexTolerance = target.vertexTolerance || target.tolerance;
|
target.edgeTolerance = target.edgeTolerance || target.tolerance;
|
this.targets.push(target);
|
},
|
|
/**
|
* Method: removeTargetLayer
|
* Remove a target layer.
|
*
|
* Parameters:
|
* layer - {<OpenLayers.Layer.Vector>} The target layer to remove.
|
*/
|
removeTargetLayer: function(layer) {
|
var target;
|
for(var i=this.targets.length-1; i>=0; --i) {
|
target = this.targets[i];
|
if(target.layer === layer) {
|
this.removeTarget(target);
|
}
|
}
|
},
|
|
/**
|
* Method: removeTarget
|
* Remove a target.
|
*
|
* Parameters:
|
* target - {Object} A target config.
|
*
|
* Returns:
|
* {Array} The targets array.
|
*/
|
removeTarget: function(target) {
|
return OpenLayers.Util.removeItem(this.targets, target);
|
},
|
|
/**
|
* APIMethod: activate
|
* Activate the control. Activating the control registers listeners for
|
* editing related events so that during feature creation and
|
* modification, moving vertices will trigger snapping.
|
*/
|
activate: function() {
|
var activated = OpenLayers.Control.prototype.activate.call(this);
|
if(activated) {
|
if(this.layer && this.layer.events) {
|
this.layer.events.on({
|
sketchstarted: this.onSketchModified,
|
sketchmodified: this.onSketchModified,
|
vertexmodified: this.onVertexModified,
|
scope: this
|
});
|
}
|
}
|
return activated;
|
},
|
|
/**
|
* APIMethod: deactivate
|
* Deactivate the control. Deactivating the control unregisters listeners
|
* so feature editing may proceed without engaging the snapping agent.
|
*/
|
deactivate: function() {
|
var deactivated = OpenLayers.Control.prototype.deactivate.call(this);
|
if(deactivated) {
|
if(this.layer && this.layer.events) {
|
this.layer.events.un({
|
sketchstarted: this.onSketchModified,
|
sketchmodified: this.onSketchModified,
|
vertexmodified: this.onVertexModified,
|
scope: this
|
});
|
}
|
}
|
this.feature = null;
|
this.point = null;
|
return deactivated;
|
},
|
|
/**
|
* Method: onSketchModified
|
* Registered as a listener for the sketchmodified event on the editable
|
* layer.
|
*
|
* Parameters:
|
* event - {Object} The sketch modified event.
|
*/
|
onSketchModified: function(event) {
|
this.feature = event.feature;
|
this.considerSnapping(event.vertex, event.vertex);
|
},
|
|
/**
|
* Method: onVertexModified
|
* Registered as a listener for the vertexmodified event on the editable
|
* layer.
|
*
|
* Parameters:
|
* event - {Object} The vertex modified event.
|
*/
|
onVertexModified: function(event) {
|
this.feature = event.feature;
|
var loc = this.layer.map.getLonLatFromViewPortPx(event.pixel);
|
this.considerSnapping(
|
event.vertex, new OpenLayers.Geometry.Point(loc.lon, loc.lat)
|
);
|
},
|
|
/**
|
* Method: considerSnapping
|
*
|
* Parameters:
|
* point - {<OpenLayers.Geometry.Point>} The vertex to be snapped (or
|
* unsnapped).
|
* loc - {<OpenLayers.Geometry.Point>} The location of the mouse in map
|
* coords.
|
*/
|
considerSnapping: function(point, loc) {
|
var best = {
|
rank: Number.POSITIVE_INFINITY,
|
dist: Number.POSITIVE_INFINITY,
|
x: null, y: null
|
};
|
var snapped = false;
|
var result, target;
|
for(var i=0, len=this.targets.length; i<len; ++i) {
|
target = this.targets[i];
|
result = this.testTarget(target, loc);
|
if(result) {
|
if(this.greedy) {
|
best = result;
|
best.target = target;
|
snapped = true;
|
break;
|
} else {
|
if((result.rank < best.rank) ||
|
(result.rank === best.rank && result.dist < best.dist)) {
|
best = result;
|
best.target = target;
|
snapped = true;
|
}
|
}
|
}
|
}
|
if(snapped) {
|
var proceed = this.events.triggerEvent("beforesnap", {
|
point: point, x: best.x, y: best.y, distance: best.dist,
|
layer: best.target.layer, snapType: this.precedence[best.rank]
|
});
|
if(proceed !== false) {
|
point.x = best.x;
|
point.y = best.y;
|
this.point = point;
|
this.events.triggerEvent("snap", {
|
point: point,
|
snapType: this.precedence[best.rank],
|
layer: best.target.layer,
|
distance: best.dist
|
});
|
} else {
|
snapped = false;
|
}
|
}
|
if(this.point && !snapped) {
|
point.x = loc.x;
|
point.y = loc.y;
|
this.point = null;
|
this.events.triggerEvent("unsnap", {point: point});
|
}
|
},
|
|
/**
|
* Method: testTarget
|
*
|
* Parameters:
|
* target - {Object} Object with target layer configuration.
|
* loc - {<OpenLayers.Geometry.Point>} The location of the mouse in map
|
* coords.
|
*
|
* Returns:
|
* {Object} A result object with rank, dist, x, and y properties.
|
* Returns null if candidate is not eligible for snapping.
|
*/
|
testTarget: function(target, loc) {
|
var resolution = this.layer.map.getResolution();
|
if ("minResolution" in target) {
|
if (resolution < target.minResolution) {
|
return null;
|
}
|
}
|
if ("maxResolution" in target) {
|
if (resolution >= target.maxResolution) {
|
return null;
|
}
|
}
|
var tolerance = {
|
node: this.getGeoTolerance(target.nodeTolerance, resolution),
|
vertex: this.getGeoTolerance(target.vertexTolerance, resolution),
|
edge: this.getGeoTolerance(target.edgeTolerance, resolution)
|
};
|
// this could be cached if we don't support setting tolerance values directly
|
var maxTolerance = Math.max(
|
tolerance.node, tolerance.vertex, tolerance.edge
|
);
|
var result = {
|
rank: Number.POSITIVE_INFINITY, dist: Number.POSITIVE_INFINITY
|
};
|
var eligible = false;
|
var features = target.layer.features;
|
var feature, type, vertices, vertex, closest, dist, found;
|
var numTypes = this.precedence.length;
|
var ll = new OpenLayers.LonLat(loc.x, loc.y);
|
for(var i=0, len=features.length; i<len; ++i) {
|
feature = features[i];
|
if(feature !== this.feature && !feature._sketch &&
|
feature.state !== OpenLayers.State.DELETE &&
|
(!target.filter || target.filter.evaluate(feature))) {
|
if(feature.atPoint(ll, maxTolerance, maxTolerance)) {
|
for(var j=0, stop=Math.min(result.rank+1, numTypes); j<stop; ++j) {
|
type = this.precedence[j];
|
if(target[type]) {
|
if(type === "edge") {
|
closest = feature.geometry.distanceTo(loc, {details: true});
|
dist = closest.distance;
|
if(dist <= tolerance[type] && dist < result.dist) {
|
result = {
|
rank: j, dist: dist,
|
x: closest.x0, y: closest.y0 // closest coords on feature
|
};
|
eligible = true;
|
// don't look for lower precedence types for this feature
|
break;
|
}
|
} else {
|
// look for nodes or vertices
|
vertices = feature.geometry.getVertices(type === "node");
|
found = false;
|
for(var k=0, klen=vertices.length; k<klen; ++k) {
|
vertex = vertices[k];
|
dist = vertex.distanceTo(loc);
|
if(dist <= tolerance[type] &&
|
(j < result.rank || (j === result.rank && dist < result.dist))) {
|
result = {
|
rank: j, dist: dist,
|
x: vertex.x, y: vertex.y
|
};
|
eligible = true;
|
found = true;
|
}
|
}
|
if(found) {
|
// don't look for lower precedence types for this feature
|
break;
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
return eligible ? result : null;
|
},
|
|
/**
|
* Method: getGeoTolerance
|
* Calculate a tolerance in map units given a tolerance in pixels. This
|
* takes advantage of the <geoToleranceCache> when the map resolution
|
* has not changed.
|
*
|
* Parameters:
|
* tolerance - {Number} A tolerance value in pixels.
|
* resolution - {Number} Map resolution.
|
*
|
* Returns:
|
* {Number} A tolerance value in map units.
|
*/
|
getGeoTolerance: function(tolerance, resolution) {
|
if(resolution !== this.resolution) {
|
this.resolution = resolution;
|
this.geoToleranceCache = {};
|
}
|
var geoTolerance = this.geoToleranceCache[tolerance];
|
if(geoTolerance === undefined) {
|
geoTolerance = tolerance * resolution;
|
this.geoToleranceCache[tolerance] = geoTolerance;
|
}
|
return geoTolerance;
|
},
|
|
/**
|
* Method: destroy
|
* Clean up the control.
|
*/
|
destroy: function() {
|
if(this.active) {
|
this.deactivate(); // TODO: this should be handled by the super
|
}
|
delete this.layer;
|
delete this.targets;
|
OpenLayers.Control.prototype.destroy.call(this);
|
},
|
|
CLASS_NAME: "OpenLayers.Control.Snapping"
|
});
|
/* ======================================================================
|
OpenLayers/Format/OWSCommon/v1_1_0.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/OWSCommon/v1.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.OWSCommon.v1_1_0
|
* Parser for OWS Common version 1.1.0.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.OWSCommon.v1>
|
*/
|
OpenLayers.Format.OWSCommon.v1_1_0 = OpenLayers.Class(OpenLayers.Format.OWSCommon.v1, {
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
ows: "http://www.opengis.net/ows/1.1",
|
xlink: "http://www.w3.org/1999/xlink"
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"ows": OpenLayers.Util.applyDefaults({
|
"ExceptionReport": function(node, obj) {
|
obj.exceptionReport = {
|
version: node.getAttribute('version'),
|
language: node.getAttribute('xml:lang'),
|
exceptions: []
|
};
|
this.readChildNodes(node, obj.exceptionReport);
|
},
|
"AllowedValues": function(node, parameter) {
|
parameter.allowedValues = {};
|
this.readChildNodes(node, parameter.allowedValues);
|
},
|
"AnyValue": function(node, parameter) {
|
parameter.anyValue = true;
|
},
|
"DataType": function(node, parameter) {
|
parameter.dataType = this.getChildValue(node);
|
},
|
"Range": function(node, allowedValues) {
|
allowedValues.range = {};
|
this.readChildNodes(node, allowedValues.range);
|
},
|
"MinimumValue": function(node, range) {
|
range.minValue = this.getChildValue(node);
|
},
|
"MaximumValue": function(node, range) {
|
range.maxValue = this.getChildValue(node);
|
},
|
"Identifier": function(node, obj) {
|
obj.identifier = this.getChildValue(node);
|
},
|
"SupportedCRS": function(node, obj) {
|
obj.supportedCRS = this.getChildValue(node);
|
}
|
}, OpenLayers.Format.OWSCommon.v1.prototype.readers["ows"])
|
},
|
|
/**
|
* Property: writers
|
* As a compliment to the readers property, this structure contains public
|
* writing functions grouped by namespace alias and named like the
|
* node names they produce.
|
*/
|
writers: {
|
"ows": OpenLayers.Util.applyDefaults({
|
"Range": function(range) {
|
var node = this.createElementNSPlus("ows:Range", {
|
attributes: {
|
'ows:rangeClosure': range.closure
|
}
|
});
|
this.writeNode("ows:MinimumValue", range.minValue, node);
|
this.writeNode("ows:MaximumValue", range.maxValue, node);
|
return node;
|
},
|
"MinimumValue": function(minValue) {
|
var node = this.createElementNSPlus("ows:MinimumValue", {
|
value: minValue
|
});
|
return node;
|
},
|
"MaximumValue": function(maxValue) {
|
var node = this.createElementNSPlus("ows:MaximumValue", {
|
value: maxValue
|
});
|
return node;
|
},
|
"Value": function(value) {
|
var node = this.createElementNSPlus("ows:Value", {
|
value: value
|
});
|
return node;
|
}
|
}, OpenLayers.Format.OWSCommon.v1.prototype.writers["ows"])
|
},
|
|
CLASS_NAME: "OpenLayers.Format.OWSCommon.v1_1_0"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/WCSGetCoverage.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML.js
|
* @requires OpenLayers/Format/OWSCommon/v1_1_0.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WCSGetCoverage version 1.1.0
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.WCSGetCoverage = OpenLayers.Class(OpenLayers.Format.XML, {
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
ows: "http://www.opengis.net/ows/1.1",
|
wcs: "http://www.opengis.net/wcs/1.1",
|
xlink: "http://www.w3.org/1999/xlink",
|
xsi: "http://www.w3.org/2001/XMLSchema-instance"
|
},
|
|
/**
|
* Property: regExes
|
* Compiled regular expressions for manipulating strings.
|
*/
|
regExes: {
|
trimSpace: (/^\s*|\s*$/g),
|
removeSpace: (/\s*/g),
|
splitSpace: (/\s+/),
|
trimComma: (/\s*,\s*/g)
|
},
|
|
/**
|
* Constant: VERSION
|
* {String} 1.1.2
|
*/
|
VERSION: "1.1.2",
|
|
/**
|
* Property: schemaLocation
|
* {String} Schema location
|
*/
|
schemaLocation: "http://www.opengis.net/wcs/1.1 http://schemas.opengis.net/wcs/1.1/wcsGetCoverage.xsd",
|
|
/**
|
* Constructor: OpenLayers.Format.WCSGetCoverage
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* Method: write
|
*
|
* Parameters:
|
* options - {Object} Optional object.
|
*
|
* Returns:
|
* {String} A WCS GetCoverage request XML string.
|
*/
|
write: function(options) {
|
var node = this.writeNode("wcs:GetCoverage", options);
|
this.setAttributeNS(
|
node, this.namespaces.xsi,
|
"xsi:schemaLocation", this.schemaLocation
|
);
|
return OpenLayers.Format.XML.prototype.write.apply(this, [node]);
|
},
|
|
/**
|
* Property: writers
|
* As a compliment to the readers property, this structure contains public
|
* writing functions grouped by namespace alias and named like the
|
* node names they produce.
|
*/
|
writers: {
|
"wcs": {
|
"GetCoverage": function(options) {
|
var node = this.createElementNSPlus("wcs:GetCoverage", {
|
attributes: {
|
version: options.version || this.VERSION,
|
service: 'WCS'
|
}
|
});
|
this.writeNode("ows:Identifier", options.identifier, node);
|
this.writeNode("wcs:DomainSubset", options.domainSubset, node);
|
this.writeNode("wcs:Output", options.output, node);
|
return node;
|
},
|
"DomainSubset": function(domainSubset) {
|
var node = this.createElementNSPlus("wcs:DomainSubset", {});
|
this.writeNode("ows:BoundingBox", domainSubset.boundingBox, node);
|
if (domainSubset.temporalSubset) {
|
this.writeNode("wcs:TemporalSubset", domainSubset.temporalSubset, node);
|
}
|
return node;
|
},
|
"TemporalSubset": function(temporalSubset) {
|
var node = this.createElementNSPlus("wcs:TemporalSubset", {});
|
for (var i=0, len=temporalSubset.timePeriods.length; i<len; ++i) {
|
this.writeNode("wcs:TimePeriod", temporalSubset.timePeriods[i], node);
|
}
|
return node;
|
},
|
"TimePeriod": function(timePeriod) {
|
var node = this.createElementNSPlus("wcs:TimePeriod", {});
|
this.writeNode("wcs:BeginPosition", timePeriod.begin, node);
|
this.writeNode("wcs:EndPosition", timePeriod.end, node);
|
if (timePeriod.resolution) {
|
this.writeNode("wcs:TimeResolution", timePeriod.resolution, node);
|
}
|
return node;
|
},
|
"BeginPosition": function(begin) {
|
var node = this.createElementNSPlus("wcs:BeginPosition", {
|
value: begin
|
});
|
return node;
|
},
|
"EndPosition": function(end) {
|
var node = this.createElementNSPlus("wcs:EndPosition", {
|
value: end
|
});
|
return node;
|
},
|
"TimeResolution": function(resolution) {
|
var node = this.createElementNSPlus("wcs:TimeResolution", {
|
value: resolution
|
});
|
return node;
|
},
|
"Output": function(output) {
|
var node = this.createElementNSPlus("wcs:Output", {
|
attributes: {
|
format: output.format,
|
store: output.store
|
}
|
});
|
if (output.gridCRS) {
|
this.writeNode("wcs:GridCRS", output.gridCRS, node);
|
}
|
return node;
|
},
|
"GridCRS": function(gridCRS) {
|
var node = this.createElementNSPlus("wcs:GridCRS", {});
|
this.writeNode("wcs:GridBaseCRS", gridCRS.baseCRS, node);
|
if (gridCRS.type) {
|
this.writeNode("wcs:GridType", gridCRS.type, node);
|
}
|
if (gridCRS.origin) {
|
this.writeNode("wcs:GridOrigin", gridCRS.origin, node);
|
}
|
this.writeNode("wcs:GridOffsets", gridCRS.offsets, node);
|
if (gridCRS.CS) {
|
this.writeNode("wcs:GridCS", gridCRS.CS, node);
|
}
|
return node;
|
},
|
"GridBaseCRS": function(baseCRS) {
|
return this.createElementNSPlus("wcs:GridBaseCRS", {
|
value: baseCRS
|
});
|
},
|
"GridOrigin": function(origin) {
|
return this.createElementNSPlus("wcs:GridOrigin", {
|
value: origin
|
});
|
},
|
"GridType": function(type) {
|
return this.createElementNSPlus("wcs:GridType", {
|
value: type
|
});
|
},
|
"GridOffsets": function(offsets) {
|
return this.createElementNSPlus("wcs:GridOffsets", {
|
value: offsets
|
});
|
},
|
"GridCS": function(CS) {
|
return this.createElementNSPlus("wcs:GridCS", {
|
value: CS
|
});
|
}
|
},
|
"ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.writers.ows
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WCSGetCoverage"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/KML.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/BaseTypes/Date.js
|
* @requires OpenLayers/Format/XML.js
|
* @requires OpenLayers/Feature/Vector.js
|
* @requires OpenLayers/Geometry/Point.js
|
* @requires OpenLayers/Geometry/LineString.js
|
* @requires OpenLayers/Geometry/Polygon.js
|
* @requires OpenLayers/Geometry/Collection.js
|
* @requires OpenLayers/Request/XMLHttpRequest.js
|
* @requires OpenLayers/Projection.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.KML
|
* Read/Write KML. Create a new instance with the <OpenLayers.Format.KML>
|
* constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, {
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
kml: "http://www.opengis.net/kml/2.2",
|
gx: "http://www.google.com/kml/ext/2.2"
|
},
|
|
/**
|
* APIProperty: kmlns
|
* {String} KML Namespace to use. Defaults to 2.0 namespace.
|
*/
|
kmlns: "http://earth.google.com/kml/2.0",
|
|
/**
|
* APIProperty: placemarksDesc
|
* {String} Name of the placemarks. Default is "No description available".
|
*/
|
placemarksDesc: "No description available",
|
|
/**
|
* APIProperty: foldersName
|
* {String} Name of the folders. Default is "OpenLayers export".
|
* If set to null, no name element will be created.
|
*/
|
foldersName: "OpenLayers export",
|
|
/**
|
* APIProperty: foldersDesc
|
* {String} Description of the folders. Default is "Exported on [date]."
|
* If set to null, no description element will be created.
|
*/
|
foldersDesc: "Exported on " + new Date(),
|
|
/**
|
* APIProperty: extractAttributes
|
* {Boolean} Extract attributes from KML. Default is true.
|
* Extracting styleUrls requires this to be set to true
|
* Note that currently only Data and SimpleData
|
* elements are handled.
|
*/
|
extractAttributes: true,
|
|
/**
|
* APIProperty: kvpAttributes
|
* {Boolean} Only used if extractAttributes is true.
|
* If set to true, attributes will be simple
|
* key-value pairs, compatible with other formats,
|
* Any displayName elements will be ignored.
|
* If set to false, attributes will be objects,
|
* retaining any displayName elements, but not
|
* compatible with other formats. Any CDATA in
|
* displayName will be read in as a string value.
|
* Default is false.
|
*/
|
kvpAttributes: false,
|
|
/**
|
* Property: extractStyles
|
* {Boolean} Extract styles from KML. Default is false.
|
* Extracting styleUrls also requires extractAttributes to be
|
* set to true
|
*/
|
extractStyles: false,
|
|
/**
|
* APIProperty: extractTracks
|
* {Boolean} Extract gx:Track elements from Placemark elements. Default
|
* is false. If true, features will be generated for all points in
|
* all gx:Track elements. Features will have a when (Date) attribute
|
* based on when elements in the track. If tracks include angle
|
* elements, features will have heading, tilt, and roll attributes.
|
* If track point coordinates have three values, features will have
|
* an altitude attribute with the third coordinate value.
|
*/
|
extractTracks: false,
|
|
/**
|
* APIProperty: trackAttributes
|
* {Array} If <extractTracks> is true, points within gx:Track elements will
|
* be parsed as features with when, heading, tilt, and roll attributes.
|
* Any additional attribute names can be provided in <trackAttributes>.
|
*/
|
trackAttributes: null,
|
|
/**
|
* Property: internalns
|
* {String} KML Namespace to use -- defaults to the namespace of the
|
* Placemark node being parsed, but falls back to kmlns.
|
*/
|
internalns: null,
|
|
/**
|
* Property: features
|
* {Array} Array of features
|
*
|
*/
|
features: null,
|
|
/**
|
* Property: styles
|
* {Object} Storage of style objects
|
*
|
*/
|
styles: null,
|
|
/**
|
* Property: styleBaseUrl
|
* {String}
|
*/
|
styleBaseUrl: "",
|
|
/**
|
* Property: fetched
|
* {Object} Storage of KML URLs that have been fetched before
|
* in order to prevent reloading them.
|
*/
|
fetched: null,
|
|
/**
|
* APIProperty: maxDepth
|
* {Integer} Maximum depth for recursive loading external KML URLs
|
* Defaults to 0: do no external fetching
|
*/
|
maxDepth: 0,
|
|
/**
|
* Constructor: OpenLayers.Format.KML
|
* Create a new parser for KML.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
initialize: function(options) {
|
// compile regular expressions once instead of every time they are used
|
this.regExes = {
|
trimSpace: (/^\s*|\s*$/g),
|
removeSpace: (/\s*/g),
|
splitSpace: (/\s+/),
|
trimComma: (/\s*,\s*/g),
|
kmlColor: (/(\w{2})(\w{2})(\w{2})(\w{2})/),
|
kmlIconPalette: (/root:\/\/icons\/palette-(\d+)(\.\w+)/),
|
straightBracket: (/\$\[(.*?)\]/g)
|
};
|
// KML coordinates are always in longlat WGS84
|
this.externalProjection = new OpenLayers.Projection("EPSG:4326");
|
|
OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
|
},
|
|
/**
|
* APIMethod: read
|
* Read data from a string, and return a list of features.
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
*
|
* Returns:
|
* {Array(<OpenLayers.Feature.Vector>)} List of features.
|
*/
|
read: function(data) {
|
this.features = [];
|
this.styles = {};
|
this.fetched = {};
|
|
// Set default options
|
var options = {
|
depth: 0,
|
styleBaseUrl: this.styleBaseUrl
|
};
|
|
return this.parseData(data, options);
|
},
|
|
/**
|
* Method: parseData
|
* Read data from a string, and return a list of features.
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
* options - {Object} Hash of options
|
*
|
* Returns:
|
* {Array(<OpenLayers.Feature.Vector>)} List of features.
|
*/
|
parseData: function(data, options) {
|
if(typeof data == "string") {
|
data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
|
}
|
|
// Loop throught the following node types in this order and
|
// process the nodes found
|
var types = ["Link", "NetworkLink", "Style", "StyleMap", "Placemark"];
|
for(var i=0, len=types.length; i<len; ++i) {
|
var type = types[i];
|
|
var nodes = this.getElementsByTagNameNS(data, "*", type);
|
|
// skip to next type if no nodes are found
|
if(nodes.length == 0) {
|
continue;
|
}
|
|
switch (type.toLowerCase()) {
|
|
// Fetch external links
|
case "link":
|
case "networklink":
|
this.parseLinks(nodes, options);
|
break;
|
|
// parse style information
|
case "style":
|
if (this.extractStyles) {
|
this.parseStyles(nodes, options);
|
}
|
break;
|
case "stylemap":
|
if (this.extractStyles) {
|
this.parseStyleMaps(nodes, options);
|
}
|
break;
|
|
// parse features
|
case "placemark":
|
this.parseFeatures(nodes, options);
|
break;
|
}
|
}
|
|
return this.features;
|
},
|
|
/**
|
* Method: parseLinks
|
* Finds URLs of linked KML documents and fetches them
|
*
|
* Parameters:
|
* nodes - {Array} of {DOMElement} data to read/parse.
|
* options - {Object} Hash of options
|
*
|
*/
|
parseLinks: function(nodes, options) {
|
|
// Fetch external links <NetworkLink> and <Link>
|
// Don't do anything if we have reached our maximum depth for recursion
|
if (options.depth >= this.maxDepth) {
|
return false;
|
}
|
|
// increase depth
|
var newOptions = OpenLayers.Util.extend({}, options);
|
newOptions.depth++;
|
|
for(var i=0, len=nodes.length; i<len; i++) {
|
var href = this.parseProperty(nodes[i], "*", "href");
|
if(href && !this.fetched[href]) {
|
this.fetched[href] = true; // prevent reloading the same urls
|
var data = this.fetchLink(href);
|
if (data) {
|
this.parseData(data, newOptions);
|
}
|
}
|
}
|
|
},
|
|
/**
|
* Method: fetchLink
|
* Fetches a URL and returns the result
|
*
|
* Parameters:
|
* href - {String} url to be fetched
|
*
|
*/
|
fetchLink: function(href) {
|
var request = OpenLayers.Request.GET({url: href, async: false});
|
if (request) {
|
return request.responseText;
|
}
|
},
|
|
/**
|
* Method: parseStyles
|
* Parses <Style> nodes
|
*
|
* Parameters:
|
* nodes - {Array} of {DOMElement} data to read/parse.
|
* options - {Object} Hash of options
|
*
|
*/
|
parseStyles: function(nodes, options) {
|
for(var i=0, len=nodes.length; i<len; i++) {
|
var style = this.parseStyle(nodes[i]);
|
if(style) {
|
var styleName = (options.styleBaseUrl || "") + "#" + style.id;
|
|
this.styles[styleName] = style;
|
}
|
}
|
},
|
|
/**
|
* Method: parseKmlColor
|
* Parses a kml color (in 'aabbggrr' format) and returns the corresponding
|
* color and opacity or null if the color is invalid.
|
*
|
* Parameters:
|
* kmlColor - {String} a kml formated color
|
*
|
* Returns:
|
* {Object}
|
*/
|
parseKmlColor: function(kmlColor) {
|
var color = null;
|
if (kmlColor) {
|
var matches = kmlColor.match(this.regExes.kmlColor);
|
if (matches) {
|
color = {
|
color: '#' + matches[4] + matches[3] + matches[2],
|
opacity: parseInt(matches[1], 16) / 255
|
};
|
}
|
}
|
return color;
|
},
|
|
/**
|
* Method: parseStyle
|
* Parses the children of a <Style> node and builds the style hash
|
* accordingly
|
*
|
* Parameters:
|
* node - {DOMElement} <Style> node
|
*
|
*/
|
parseStyle: function(node) {
|
var style = {};
|
|
var types = ["LineStyle", "PolyStyle", "IconStyle", "BalloonStyle",
|
"LabelStyle"];
|
var type, styleTypeNode, nodeList, geometry, parser;
|
for(var i=0, len=types.length; i<len; ++i) {
|
type = types[i];
|
styleTypeNode = this.getElementsByTagNameNS(node, "*", type)[0];
|
if(!styleTypeNode) {
|
continue;
|
}
|
|
// only deal with first geometry of this type
|
switch (type.toLowerCase()) {
|
case "linestyle":
|
var kmlColor = this.parseProperty(styleTypeNode, "*", "color");
|
var color = this.parseKmlColor(kmlColor);
|
if (color) {
|
style["strokeColor"] = color.color;
|
style["strokeOpacity"] = color.opacity;
|
}
|
|
var width = this.parseProperty(styleTypeNode, "*", "width");
|
if (width) {
|
style["strokeWidth"] = width;
|
}
|
break;
|
|
case "polystyle":
|
var kmlColor = this.parseProperty(styleTypeNode, "*", "color");
|
var color = this.parseKmlColor(kmlColor);
|
if (color) {
|
style["fillOpacity"] = color.opacity;
|
style["fillColor"] = color.color;
|
}
|
// Check if fill is disabled
|
var fill = this.parseProperty(styleTypeNode, "*", "fill");
|
if (fill == "0") {
|
style["fillColor"] = "none";
|
}
|
// Check if outline is disabled
|
var outline = this.parseProperty(styleTypeNode, "*", "outline");
|
if (outline == "0") {
|
style["strokeWidth"] = "0";
|
}
|
|
break;
|
|
case "iconstyle":
|
// set scale
|
var scale = parseFloat(this.parseProperty(styleTypeNode,
|
"*", "scale") || 1);
|
|
// set default width and height of icon
|
var width = 32 * scale;
|
var height = 32 * scale;
|
|
var iconNode = this.getElementsByTagNameNS(styleTypeNode,
|
"*",
|
"Icon")[0];
|
if (iconNode) {
|
var href = this.parseProperty(iconNode, "*", "href");
|
if (href) {
|
|
var w = this.parseProperty(iconNode, "*", "w");
|
var h = this.parseProperty(iconNode, "*", "h");
|
|
// Settings for Google specific icons that are 64x64
|
// We set the width and height to 64 and halve the
|
// scale to prevent icons from being too big
|
var google = "http://maps.google.com/mapfiles/kml";
|
if (OpenLayers.String.startsWith(
|
href, google) && !w && !h) {
|
w = 64;
|
h = 64;
|
scale = scale / 2;
|
}
|
|
// if only dimension is defined, make sure the
|
// other one has the same value
|
w = w || h;
|
h = h || w;
|
|
if (w) {
|
width = parseInt(w) * scale;
|
}
|
|
if (h) {
|
height = parseInt(h) * scale;
|
}
|
|
// support for internal icons
|
// (/root://icons/palette-x.png)
|
// x and y tell the position on the palette:
|
// - in pixels
|
// - starting from the left bottom
|
// We translate that to a position in the list
|
// and request the appropriate icon from the
|
// google maps website
|
var matches = href.match(this.regExes.kmlIconPalette);
|
if (matches) {
|
var palette = matches[1];
|
var file_extension = matches[2];
|
|
var x = this.parseProperty(iconNode, "*", "x");
|
var y = this.parseProperty(iconNode, "*", "y");
|
|
var posX = x ? x/32 : 0;
|
var posY = y ? (7 - y/32) : 7;
|
|
var pos = posY * 8 + posX;
|
href = "http://maps.google.com/mapfiles/kml/pal"
|
+ palette + "/icon" + pos + file_extension;
|
}
|
|
style["graphicOpacity"] = 1; // fully opaque
|
style["externalGraphic"] = href;
|
}
|
|
}
|
|
|
// hotSpots define the offset for an Icon
|
var hotSpotNode = this.getElementsByTagNameNS(styleTypeNode,
|
"*",
|
"hotSpot")[0];
|
if (hotSpotNode) {
|
var x = parseFloat(hotSpotNode.getAttribute("x"));
|
var y = parseFloat(hotSpotNode.getAttribute("y"));
|
|
var xUnits = hotSpotNode.getAttribute("xunits");
|
if (xUnits == "pixels") {
|
style["graphicXOffset"] = -x * scale;
|
}
|
else if (xUnits == "insetPixels") {
|
style["graphicXOffset"] = -width + (x * scale);
|
}
|
else if (xUnits == "fraction") {
|
style["graphicXOffset"] = -width * x;
|
}
|
|
var yUnits = hotSpotNode.getAttribute("yunits");
|
if (yUnits == "pixels") {
|
style["graphicYOffset"] = -height + (y * scale) + 1;
|
}
|
else if (yUnits == "insetPixels") {
|
style["graphicYOffset"] = -(y * scale) + 1;
|
}
|
else if (yUnits == "fraction") {
|
style["graphicYOffset"] = -height * (1 - y) + 1;
|
}
|
}
|
|
style["graphicWidth"] = width;
|
style["graphicHeight"] = height;
|
break;
|
|
case "balloonstyle":
|
var balloonStyle = OpenLayers.Util.getXmlNodeValue(
|
styleTypeNode);
|
if (balloonStyle) {
|
style["balloonStyle"] = balloonStyle.replace(
|
this.regExes.straightBracket, "${$1}");
|
}
|
break;
|
case "labelstyle":
|
var kmlColor = this.parseProperty(styleTypeNode, "*", "color");
|
var color = this.parseKmlColor(kmlColor);
|
if (color) {
|
style["fontColor"] = color.color;
|
style["fontOpacity"] = color.opacity;
|
}
|
break;
|
|
default:
|
}
|
}
|
|
// Some polygons have no line color, so we use the fillColor for that
|
if (!style["strokeColor"] && style["fillColor"]) {
|
style["strokeColor"] = style["fillColor"];
|
}
|
|
var id = node.getAttribute("id");
|
if (id && style) {
|
style.id = id;
|
}
|
|
return style;
|
},
|
|
/**
|
* Method: parseStyleMaps
|
* Parses <StyleMap> nodes, but only uses the 'normal' key
|
*
|
* Parameters:
|
* nodes - {Array} of {DOMElement} data to read/parse.
|
* options - {Object} Hash of options
|
*
|
*/
|
parseStyleMaps: function(nodes, options) {
|
// Only the default or "normal" part of the StyleMap is processed now
|
// To do the select or "highlight" bit, we'd need to change lots more
|
|
for(var i=0, len=nodes.length; i<len; i++) {
|
var node = nodes[i];
|
var pairs = this.getElementsByTagNameNS(node, "*",
|
"Pair");
|
|
var id = node.getAttribute("id");
|
for (var j=0, jlen=pairs.length; j<jlen; j++) {
|
var pair = pairs[j];
|
// Use the shortcut in the SLD format to quickly retrieve the
|
// value of a node. Maybe it's good to have a method in
|
// Format.XML to do this
|
var key = this.parseProperty(pair, "*", "key");
|
var styleUrl = this.parseProperty(pair, "*", "styleUrl");
|
|
if (styleUrl && key == "normal") {
|
this.styles[(options.styleBaseUrl || "") + "#" + id] =
|
this.styles[(options.styleBaseUrl || "") + styleUrl];
|
}
|
|
// TODO: implement the "select" part
|
//if (styleUrl && key == "highlight") {
|
//}
|
|
}
|
}
|
|
},
|
|
|
/**
|
* Method: parseFeatures
|
* Loop through all Placemark nodes and parse them.
|
* Will create a list of features
|
*
|
* Parameters:
|
* nodes - {Array} of {DOMElement} data to read/parse.
|
* options - {Object} Hash of options
|
*
|
*/
|
parseFeatures: function(nodes, options) {
|
var features = [];
|
for(var i=0, len=nodes.length; i<len; i++) {
|
var featureNode = nodes[i];
|
var feature = this.parseFeature.apply(this,[featureNode]) ;
|
if(feature) {
|
|
// Create reference to styleUrl
|
if (this.extractStyles && feature.attributes &&
|
feature.attributes.styleUrl) {
|
feature.style = this.getStyle(feature.attributes.styleUrl, options);
|
}
|
|
if (this.extractStyles) {
|
// Make sure that <Style> nodes within a placemark are
|
// processed as well
|
var inlineStyleNode = this.getElementsByTagNameNS(featureNode,
|
"*",
|
"Style")[0];
|
if (inlineStyleNode) {
|
var inlineStyle= this.parseStyle(inlineStyleNode);
|
if (inlineStyle) {
|
feature.style = OpenLayers.Util.extend(
|
feature.style, inlineStyle
|
);
|
}
|
}
|
}
|
|
// check if gx:Track elements should be parsed
|
if (this.extractTracks) {
|
var tracks = this.getElementsByTagNameNS(
|
featureNode, this.namespaces.gx, "Track"
|
);
|
if (tracks && tracks.length > 0) {
|
var track = tracks[0];
|
var container = {
|
features: [],
|
feature: feature
|
};
|
this.readNode(track, container);
|
if (container.features.length > 0) {
|
features.push.apply(features, container.features);
|
}
|
}
|
} else {
|
// add feature to list of features
|
features.push(feature);
|
}
|
} else {
|
throw "Bad Placemark: " + i;
|
}
|
}
|
|
// add new features to existing feature list
|
this.features = this.features.concat(features);
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"kml": {
|
"when": function(node, container) {
|
container.whens.push(OpenLayers.Date.parse(
|
this.getChildValue(node)
|
));
|
},
|
"_trackPointAttribute": function(node, container) {
|
var name = node.nodeName.split(":").pop();
|
container.attributes[name].push(this.getChildValue(node));
|
}
|
},
|
"gx": {
|
"Track": function(node, container) {
|
var obj = {
|
whens: [],
|
points: [],
|
angles: []
|
};
|
if (this.trackAttributes) {
|
var name;
|
obj.attributes = {};
|
for (var i=0, ii=this.trackAttributes.length; i<ii; ++i) {
|
name = this.trackAttributes[i];
|
obj.attributes[name] = [];
|
if (!(name in this.readers.kml)) {
|
this.readers.kml[name] = this.readers.kml._trackPointAttribute;
|
}
|
}
|
}
|
this.readChildNodes(node, obj);
|
if (obj.whens.length !== obj.points.length) {
|
throw new Error("gx:Track with unequal number of when (" +
|
obj.whens.length + ") and gx:coord (" +
|
obj.points.length + ") elements.");
|
}
|
var hasAngles = obj.angles.length > 0;
|
if (hasAngles && obj.whens.length !== obj.angles.length) {
|
throw new Error("gx:Track with unequal number of when (" +
|
obj.whens.length + ") and gx:angles (" +
|
obj.angles.length + ") elements.");
|
}
|
var feature, point, angles;
|
for (var i=0, ii=obj.whens.length; i<ii; ++i) {
|
feature = container.feature.clone();
|
feature.fid = container.feature.fid || container.feature.id;
|
point = obj.points[i];
|
feature.geometry = point;
|
if ("z" in point) {
|
feature.attributes.altitude = point.z;
|
}
|
if (this.internalProjection && this.externalProjection) {
|
feature.geometry.transform(
|
this.externalProjection, this.internalProjection
|
);
|
}
|
if (this.trackAttributes) {
|
for (var j=0, jj=this.trackAttributes.length; j<jj; ++j) {
|
var name = this.trackAttributes[j];
|
feature.attributes[name] = obj.attributes[name][i];
|
}
|
}
|
feature.attributes.when = obj.whens[i];
|
feature.attributes.trackId = container.feature.id;
|
if (hasAngles) {
|
angles = obj.angles[i];
|
feature.attributes.heading = parseFloat(angles[0]);
|
feature.attributes.tilt = parseFloat(angles[1]);
|
feature.attributes.roll = parseFloat(angles[2]);
|
}
|
container.features.push(feature);
|
}
|
},
|
"coord": function(node, container) {
|
var str = this.getChildValue(node);
|
var coords = str.replace(this.regExes.trimSpace, "").split(/\s+/);
|
var point = new OpenLayers.Geometry.Point(coords[0], coords[1]);
|
if (coords.length > 2) {
|
point.z = parseFloat(coords[2]);
|
}
|
container.points.push(point);
|
},
|
"angles": function(node, container) {
|
var str = this.getChildValue(node);
|
var parts = str.replace(this.regExes.trimSpace, "").split(/\s+/);
|
container.angles.push(parts);
|
}
|
}
|
},
|
|
/**
|
* Method: parseFeature
|
* This function is the core of the KML parsing code in OpenLayers.
|
* It creates the geometries that are then attached to the returned
|
* feature, and calls parseAttributes() to get attribute data out.
|
*
|
* Parameters:
|
* node - {DOMElement}
|
*
|
* Returns:
|
* {<OpenLayers.Feature.Vector>} A vector feature.
|
*/
|
parseFeature: function(node) {
|
// only accept one geometry per feature - look for highest "order"
|
var order = ["MultiGeometry", "Polygon", "LineString", "Point"];
|
var type, nodeList, geometry, parser;
|
for(var i=0, len=order.length; i<len; ++i) {
|
type = order[i];
|
this.internalns = node.namespaceURI ?
|
node.namespaceURI : this.kmlns;
|
nodeList = this.getElementsByTagNameNS(node,
|
this.internalns, type);
|
if(nodeList.length > 0) {
|
// only deal with first geometry of this type
|
var parser = this.parseGeometry[type.toLowerCase()];
|
if(parser) {
|
geometry = parser.apply(this, [nodeList[0]]);
|
if (this.internalProjection && this.externalProjection) {
|
geometry.transform(this.externalProjection,
|
this.internalProjection);
|
}
|
} else {
|
throw new TypeError("Unsupported geometry type: " + type);
|
}
|
// stop looking for different geometry types
|
break;
|
}
|
}
|
|
// construct feature (optionally with attributes)
|
var attributes;
|
if(this.extractAttributes) {
|
attributes = this.parseAttributes(node);
|
}
|
var feature = new OpenLayers.Feature.Vector(geometry, attributes);
|
|
var fid = node.getAttribute("id") || node.getAttribute("name");
|
if(fid != null) {
|
feature.fid = fid;
|
}
|
|
return feature;
|
},
|
|
/**
|
* Method: getStyle
|
* Retrieves a style from a style hash using styleUrl as the key
|
* If the styleUrl doesn't exist yet, we try to fetch it
|
* Internet
|
*
|
* Parameters:
|
* styleUrl - {String} URL of style
|
* options - {Object} Hash of options
|
*
|
* Returns:
|
* {Object} - (reference to) Style hash
|
*/
|
getStyle: function(styleUrl, options) {
|
|
var styleBaseUrl = OpenLayers.Util.removeTail(styleUrl);
|
|
var newOptions = OpenLayers.Util.extend({}, options);
|
newOptions.depth++;
|
newOptions.styleBaseUrl = styleBaseUrl;
|
|
// Fetch remote Style URLs (if not fetched before)
|
if (!this.styles[styleUrl]
|
&& !OpenLayers.String.startsWith(styleUrl, "#")
|
&& newOptions.depth <= this.maxDepth
|
&& !this.fetched[styleBaseUrl] ) {
|
|
var data = this.fetchLink(styleBaseUrl);
|
if (data) {
|
this.parseData(data, newOptions);
|
}
|
|
}
|
|
// return requested style
|
var style = OpenLayers.Util.extend({}, this.styles[styleUrl]);
|
return style;
|
},
|
|
/**
|
* Property: parseGeometry
|
* Properties of this object are the functions that parse geometries based
|
* on their type.
|
*/
|
parseGeometry: {
|
|
/**
|
* Method: parseGeometry.point
|
* Given a KML node representing a point geometry, create an OpenLayers
|
* point geometry.
|
*
|
* Parameters:
|
* node - {DOMElement} A KML Point node.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry.Point>} A point geometry.
|
*/
|
point: function(node) {
|
var nodeList = this.getElementsByTagNameNS(node, this.internalns,
|
"coordinates");
|
var coords = [];
|
if(nodeList.length > 0) {
|
var coordString = nodeList[0].firstChild.nodeValue;
|
coordString = coordString.replace(this.regExes.removeSpace, "");
|
coords = coordString.split(",");
|
}
|
|
var point = null;
|
if(coords.length > 1) {
|
// preserve third dimension
|
if(coords.length == 2) {
|
coords[2] = null;
|
}
|
point = new OpenLayers.Geometry.Point(coords[0], coords[1],
|
coords[2]);
|
} else {
|
throw "Bad coordinate string: " + coordString;
|
}
|
return point;
|
},
|
|
/**
|
* Method: parseGeometry.linestring
|
* Given a KML node representing a linestring geometry, create an
|
* OpenLayers linestring geometry.
|
*
|
* Parameters:
|
* node - {DOMElement} A KML LineString node.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry.LineString>} A linestring geometry.
|
*/
|
linestring: function(node, ring) {
|
var nodeList = this.getElementsByTagNameNS(node, this.internalns,
|
"coordinates");
|
var line = null;
|
if(nodeList.length > 0) {
|
var coordString = this.getChildValue(nodeList[0]);
|
|
coordString = coordString.replace(this.regExes.trimSpace,
|
"");
|
coordString = coordString.replace(this.regExes.trimComma,
|
",");
|
var pointList = coordString.split(this.regExes.splitSpace);
|
var numPoints = pointList.length;
|
var points = new Array(numPoints);
|
var coords, numCoords;
|
for(var i=0; i<numPoints; ++i) {
|
coords = pointList[i].split(",");
|
numCoords = coords.length;
|
if(numCoords > 1) {
|
if(coords.length == 2) {
|
coords[2] = null;
|
}
|
points[i] = new OpenLayers.Geometry.Point(coords[0],
|
coords[1],
|
coords[2]);
|
} else {
|
throw "Bad LineString point coordinates: " +
|
pointList[i];
|
}
|
}
|
if(numPoints) {
|
if(ring) {
|
line = new OpenLayers.Geometry.LinearRing(points);
|
} else {
|
line = new OpenLayers.Geometry.LineString(points);
|
}
|
} else {
|
throw "Bad LineString coordinates: " + coordString;
|
}
|
}
|
|
return line;
|
},
|
|
/**
|
* Method: parseGeometry.polygon
|
* Given a KML node representing a polygon geometry, create an
|
* OpenLayers polygon geometry.
|
*
|
* Parameters:
|
* node - {DOMElement} A KML Polygon node.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry.Polygon>} A polygon geometry.
|
*/
|
polygon: function(node) {
|
var nodeList = this.getElementsByTagNameNS(node, this.internalns,
|
"LinearRing");
|
var numRings = nodeList.length;
|
var components = new Array(numRings);
|
if(numRings > 0) {
|
// this assumes exterior ring first, inner rings after
|
var ring;
|
for(var i=0, len=nodeList.length; i<len; ++i) {
|
ring = this.parseGeometry.linestring.apply(this,
|
[nodeList[i], true]);
|
if(ring) {
|
components[i] = ring;
|
} else {
|
throw "Bad LinearRing geometry: " + i;
|
}
|
}
|
}
|
return new OpenLayers.Geometry.Polygon(components);
|
},
|
|
/**
|
* Method: parseGeometry.multigeometry
|
* Given a KML node representing a multigeometry, create an
|
* OpenLayers geometry collection.
|
*
|
* Parameters:
|
* node - {DOMElement} A KML MultiGeometry node.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry.Collection>} A geometry collection.
|
*/
|
multigeometry: function(node) {
|
var child, parser;
|
var parts = [];
|
var children = node.childNodes;
|
for(var i=0, len=children.length; i<len; ++i ) {
|
child = children[i];
|
if(child.nodeType == 1) {
|
var type = (child.prefix) ?
|
child.nodeName.split(":")[1] :
|
child.nodeName;
|
var parser = this.parseGeometry[type.toLowerCase()];
|
if(parser) {
|
parts.push(parser.apply(this, [child]));
|
}
|
}
|
}
|
return new OpenLayers.Geometry.Collection(parts);
|
}
|
|
},
|
|
/**
|
* Method: parseAttributes
|
*
|
* Parameters:
|
* node - {DOMElement}
|
*
|
* Returns:
|
* {Object} An attributes object.
|
*/
|
parseAttributes: function(node) {
|
var attributes = {};
|
|
// Extended Data is parsed first.
|
var edNodes = node.getElementsByTagName("ExtendedData");
|
if (edNodes.length) {
|
attributes = this.parseExtendedData(edNodes[0]);
|
}
|
|
// assume attribute nodes are type 1 children with a type 3 or 4 child
|
var child, grandchildren, grandchild;
|
var children = node.childNodes;
|
|
for(var i=0, len=children.length; i<len; ++i) {
|
child = children[i];
|
if(child.nodeType == 1) {
|
grandchildren = child.childNodes;
|
if(grandchildren.length >= 1 && grandchildren.length <= 3) {
|
var grandchild;
|
switch (grandchildren.length) {
|
case 1:
|
grandchild = grandchildren[0];
|
break;
|
case 2:
|
var c1 = grandchildren[0];
|
var c2 = grandchildren[1];
|
grandchild = (c1.nodeType == 3 || c1.nodeType == 4) ?
|
c1 : c2;
|
break;
|
case 3:
|
default:
|
grandchild = grandchildren[1];
|
break;
|
}
|
if(grandchild.nodeType == 3 || grandchild.nodeType == 4) {
|
var name = (child.prefix) ?
|
child.nodeName.split(":")[1] :
|
child.nodeName;
|
var value = OpenLayers.Util.getXmlNodeValue(grandchild);
|
if (value) {
|
value = value.replace(this.regExes.trimSpace, "");
|
attributes[name] = value;
|
}
|
}
|
}
|
}
|
}
|
return attributes;
|
},
|
|
/**
|
* Method: parseExtendedData
|
* Parse ExtendedData from KML. Limited support for schemas/datatypes.
|
* See http://code.google.com/apis/kml/documentation/kmlreference.html#extendeddata
|
* for more information on extendeddata.
|
*/
|
parseExtendedData: function(node) {
|
var attributes = {};
|
var i, len, data, key;
|
var dataNodes = node.getElementsByTagName("Data");
|
for (i = 0, len = dataNodes.length; i < len; i++) {
|
data = dataNodes[i];
|
key = data.getAttribute("name");
|
var ed = {};
|
var valueNode = data.getElementsByTagName("value");
|
if (valueNode.length) {
|
ed['value'] = this.getChildValue(valueNode[0]);
|
}
|
if (this.kvpAttributes) {
|
attributes[key] = ed['value'];
|
} else {
|
var nameNode = data.getElementsByTagName("displayName");
|
if (nameNode.length) {
|
ed['displayName'] = this.getChildValue(nameNode[0]);
|
}
|
attributes[key] = ed;
|
}
|
}
|
var simpleDataNodes = node.getElementsByTagName("SimpleData");
|
for (i = 0, len = simpleDataNodes.length; i < len; i++) {
|
var ed = {};
|
data = simpleDataNodes[i];
|
key = data.getAttribute("name");
|
ed['value'] = this.getChildValue(data);
|
if (this.kvpAttributes) {
|
attributes[key] = ed['value'];
|
} else {
|
ed['displayName'] = key;
|
attributes[key] = ed;
|
}
|
}
|
|
return attributes;
|
},
|
|
/**
|
* Method: parseProperty
|
* Convenience method to find a node and return its value
|
*
|
* Parameters:
|
* xmlNode - {<DOMElement>}
|
* namespace - {String} namespace of the node to find
|
* tagName - {String} name of the property to parse
|
*
|
* Returns:
|
* {String} The value for the requested property (defaults to null)
|
*/
|
parseProperty: function(xmlNode, namespace, tagName) {
|
var value;
|
var nodeList = this.getElementsByTagNameNS(xmlNode, namespace, tagName);
|
try {
|
value = OpenLayers.Util.getXmlNodeValue(nodeList[0]);
|
} catch(e) {
|
value = null;
|
}
|
|
return value;
|
},
|
|
/**
|
* APIMethod: write
|
* Accept Feature Collection, and return a string.
|
*
|
* Parameters:
|
* features - {Array(<OpenLayers.Feature.Vector>)} An array of features.
|
*
|
* Returns:
|
* {String} A KML string.
|
*/
|
write: function(features) {
|
if(!(OpenLayers.Util.isArray(features))) {
|
features = [features];
|
}
|
var kml = this.createElementNS(this.kmlns, "kml");
|
var folder = this.createFolderXML();
|
for(var i=0, len=features.length; i<len; ++i) {
|
folder.appendChild(this.createPlacemarkXML(features[i]));
|
}
|
kml.appendChild(folder);
|
return OpenLayers.Format.XML.prototype.write.apply(this, [kml]);
|
},
|
|
/**
|
* Method: createFolderXML
|
* Creates and returns a KML folder node
|
*
|
* Returns:
|
* {DOMElement}
|
*/
|
createFolderXML: function() {
|
// Folder
|
var folder = this.createElementNS(this.kmlns, "Folder");
|
|
// Folder name
|
if (this.foldersName) {
|
var folderName = this.createElementNS(this.kmlns, "name");
|
var folderNameText = this.createTextNode(this.foldersName);
|
folderName.appendChild(folderNameText);
|
folder.appendChild(folderName);
|
}
|
|
// Folder description
|
if (this.foldersDesc) {
|
var folderDesc = this.createElementNS(this.kmlns, "description");
|
var folderDescText = this.createTextNode(this.foldersDesc);
|
folderDesc.appendChild(folderDescText);
|
folder.appendChild(folderDesc);
|
}
|
|
return folder;
|
},
|
|
/**
|
* Method: createPlacemarkXML
|
* Creates and returns a KML placemark node representing the given feature.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
*
|
* Returns:
|
* {DOMElement}
|
*/
|
createPlacemarkXML: function(feature) {
|
// Placemark name
|
var placemarkName = this.createElementNS(this.kmlns, "name");
|
var label = (feature.style && feature.style.label) ? feature.style.label : feature.id;
|
var name = feature.attributes.name || label;
|
placemarkName.appendChild(this.createTextNode(name));
|
|
// Placemark description
|
var placemarkDesc = this.createElementNS(this.kmlns, "description");
|
var desc = feature.attributes.description || this.placemarksDesc;
|
placemarkDesc.appendChild(this.createTextNode(desc));
|
|
// Placemark
|
var placemarkNode = this.createElementNS(this.kmlns, "Placemark");
|
if(feature.fid != null) {
|
placemarkNode.setAttribute("id", feature.fid);
|
}
|
placemarkNode.appendChild(placemarkName);
|
placemarkNode.appendChild(placemarkDesc);
|
|
// Geometry node (Point, LineString, etc. nodes)
|
var geometryNode = this.buildGeometryNode(feature.geometry);
|
placemarkNode.appendChild(geometryNode);
|
|
// output attributes as extendedData
|
if (feature.attributes) {
|
var edNode = this.buildExtendedData(feature.attributes);
|
if (edNode) {
|
placemarkNode.appendChild(edNode);
|
}
|
}
|
|
return placemarkNode;
|
},
|
|
/**
|
* Method: buildGeometryNode
|
* Builds and returns a KML geometry node with the given geometry.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {DOMElement}
|
*/
|
buildGeometryNode: function(geometry) {
|
var className = geometry.CLASS_NAME;
|
var type = className.substring(className.lastIndexOf(".") + 1);
|
var builder = this.buildGeometry[type.toLowerCase()];
|
var node = null;
|
if(builder) {
|
node = builder.apply(this, [geometry]);
|
}
|
return node;
|
},
|
|
/**
|
* Property: buildGeometry
|
* Object containing methods to do the actual geometry node building
|
* based on geometry type.
|
*/
|
buildGeometry: {
|
// TBD: Anybody care about namespace aliases here (these nodes have
|
// no prefixes)?
|
|
/**
|
* Method: buildGeometry.point
|
* Given an OpenLayers point geometry, create a KML point.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry.Point>} A point geometry.
|
*
|
* Returns:
|
* {DOMElement} A KML point node.
|
*/
|
point: function(geometry) {
|
var kml = this.createElementNS(this.kmlns, "Point");
|
kml.appendChild(this.buildCoordinatesNode(geometry));
|
return kml;
|
},
|
|
/**
|
* Method: buildGeometry.multipoint
|
* Given an OpenLayers multipoint geometry, create a KML
|
* GeometryCollection.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry.Point>} A multipoint geometry.
|
*
|
* Returns:
|
* {DOMElement} A KML GeometryCollection node.
|
*/
|
multipoint: function(geometry) {
|
return this.buildGeometry.collection.apply(this, [geometry]);
|
},
|
|
/**
|
* Method: buildGeometry.linestring
|
* Given an OpenLayers linestring geometry, create a KML linestring.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry.LineString>} A linestring geometry.
|
*
|
* Returns:
|
* {DOMElement} A KML linestring node.
|
*/
|
linestring: function(geometry) {
|
var kml = this.createElementNS(this.kmlns, "LineString");
|
kml.appendChild(this.buildCoordinatesNode(geometry));
|
return kml;
|
},
|
|
/**
|
* Method: buildGeometry.multilinestring
|
* Given an OpenLayers multilinestring geometry, create a KML
|
* GeometryCollection.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry.Point>} A multilinestring geometry.
|
*
|
* Returns:
|
* {DOMElement} A KML GeometryCollection node.
|
*/
|
multilinestring: function(geometry) {
|
return this.buildGeometry.collection.apply(this, [geometry]);
|
},
|
|
/**
|
* Method: buildGeometry.linearring
|
* Given an OpenLayers linearring geometry, create a KML linearring.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry.LinearRing>} A linearring geometry.
|
*
|
* Returns:
|
* {DOMElement} A KML linearring node.
|
*/
|
linearring: function(geometry) {
|
var kml = this.createElementNS(this.kmlns, "LinearRing");
|
kml.appendChild(this.buildCoordinatesNode(geometry));
|
return kml;
|
},
|
|
/**
|
* Method: buildGeometry.polygon
|
* Given an OpenLayers polygon geometry, create a KML polygon.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry.Polygon>} A polygon geometry.
|
*
|
* Returns:
|
* {DOMElement} A KML polygon node.
|
*/
|
polygon: function(geometry) {
|
var kml = this.createElementNS(this.kmlns, "Polygon");
|
var rings = geometry.components;
|
var ringMember, ringGeom, type;
|
for(var i=0, len=rings.length; i<len; ++i) {
|
type = (i==0) ? "outerBoundaryIs" : "innerBoundaryIs";
|
ringMember = this.createElementNS(this.kmlns, type);
|
ringGeom = this.buildGeometry.linearring.apply(this,
|
[rings[i]]);
|
ringMember.appendChild(ringGeom);
|
kml.appendChild(ringMember);
|
}
|
return kml;
|
},
|
|
/**
|
* Method: buildGeometry.multipolygon
|
* Given an OpenLayers multipolygon geometry, create a KML
|
* GeometryCollection.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry.Point>} A multipolygon geometry.
|
*
|
* Returns:
|
* {DOMElement} A KML GeometryCollection node.
|
*/
|
multipolygon: function(geometry) {
|
return this.buildGeometry.collection.apply(this, [geometry]);
|
},
|
|
/**
|
* Method: buildGeometry.collection
|
* Given an OpenLayers geometry collection, create a KML MultiGeometry.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry.Collection>} A geometry collection.
|
*
|
* Returns:
|
* {DOMElement} A KML MultiGeometry node.
|
*/
|
collection: function(geometry) {
|
var kml = this.createElementNS(this.kmlns, "MultiGeometry");
|
var child;
|
for(var i=0, len=geometry.components.length; i<len; ++i) {
|
child = this.buildGeometryNode.apply(this,
|
[geometry.components[i]]);
|
if(child) {
|
kml.appendChild(child);
|
}
|
}
|
return kml;
|
}
|
},
|
|
/**
|
* Method: buildCoordinatesNode
|
* Builds and returns the KML coordinates node with the given geometry
|
* <coordinates>...</coordinates>
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {DOMElement}
|
*/
|
buildCoordinatesNode: function(geometry) {
|
var coordinatesNode = this.createElementNS(this.kmlns, "coordinates");
|
|
var path;
|
var points = geometry.components;
|
if(points) {
|
// LineString or LinearRing
|
var point;
|
var numPoints = points.length;
|
var parts = new Array(numPoints);
|
for(var i=0; i<numPoints; ++i) {
|
point = points[i];
|
parts[i] = this.buildCoordinates(point);
|
}
|
path = parts.join(" ");
|
} else {
|
// Point
|
path = this.buildCoordinates(geometry);
|
}
|
|
var txtNode = this.createTextNode(path);
|
coordinatesNode.appendChild(txtNode);
|
|
return coordinatesNode;
|
},
|
|
/**
|
* Method: buildCoordinates
|
*
|
* Parameters:
|
* point - {<OpenLayers.Geometry.Point>}
|
*
|
* Returns
|
* {String} a coordinate pair
|
*/
|
buildCoordinates: function(point) {
|
if (this.internalProjection && this.externalProjection) {
|
point = point.clone();
|
point.transform(this.internalProjection,
|
this.externalProjection);
|
}
|
return point.x + "," + point.y;
|
},
|
|
/**
|
* Method: buildExtendedData
|
*
|
* Parameters:
|
* attributes - {Object}
|
*
|
* Returns
|
* {DOMElement} A KML ExtendedData node or {null} if no attributes.
|
*/
|
buildExtendedData: function(attributes) {
|
var extendedData = this.createElementNS(this.kmlns, "ExtendedData");
|
for (var attributeName in attributes) {
|
// empty, name, description, styleUrl attributes ignored
|
if (attributes[attributeName] && attributeName != "name" && attributeName != "description" && attributeName != "styleUrl") {
|
var data = this.createElementNS(this.kmlns, "Data");
|
data.setAttribute("name", attributeName);
|
var value = this.createElementNS(this.kmlns, "value");
|
if (typeof attributes[attributeName] == "object") {
|
// cater for object attributes with 'value' properties
|
// other object properties will output an empty node
|
if (attributes[attributeName].value) {
|
value.appendChild(this.createTextNode(attributes[attributeName].value));
|
}
|
if (attributes[attributeName].displayName) {
|
var displayName = this.createElementNS(this.kmlns, "displayName");
|
// displayName always written as CDATA
|
displayName.appendChild(this.getXMLDoc().createCDATASection(attributes[attributeName].displayName));
|
data.appendChild(displayName);
|
}
|
} else {
|
value.appendChild(this.createTextNode(attributes[attributeName]));
|
}
|
data.appendChild(value);
|
extendedData.appendChild(data);
|
}
|
}
|
if (this.isSimpleContent(extendedData)) {
|
return null;
|
} else {
|
return extendedData;
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Format.KML"
|
});
|
/* ======================================================================
|
OpenLayers/Format/WMSCapabilities.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML/VersionedOGC.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WMSCapabilities
|
* Read WMS Capabilities.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML.VersionedOGC>
|
*/
|
OpenLayers.Format.WMSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
|
|
/**
|
* APIProperty: defaultVersion
|
* {String} Version number to assume if none found. Default is "1.1.1".
|
*/
|
defaultVersion: "1.1.1",
|
|
/**
|
* APIProperty: profile
|
* {String} If provided, use a custom profile.
|
*
|
* Currently supported profiles:
|
* - WMSC - parses vendor specific capabilities for WMS-C.
|
*/
|
profile: null,
|
|
/**
|
* Constructor: OpenLayers.Format.WMSCapabilities
|
* Create a new parser for WMS capabilities.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* APIMethod: read
|
* Read capabilities data from a string, and return a list of layers.
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
*
|
* Returns:
|
* {Array} List of named layers.
|
*/
|
|
CLASS_NAME: "OpenLayers.Format.WMSCapabilities"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/WMSCapabilities/v1.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/WMSCapabilities.js
|
* @requires OpenLayers/Format/OGCExceptionReport.js
|
* @requires OpenLayers/Format/XML.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WMSCapabilities.v1
|
* Abstract class not to be instantiated directly. Creates
|
* the common parts for both WMS 1.1.X and WMS 1.3.X.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.WMSCapabilities.v1 = OpenLayers.Class(
|
OpenLayers.Format.XML, {
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
wms: "http://www.opengis.net/wms",
|
xlink: "http://www.w3.org/1999/xlink",
|
xsi: "http://www.w3.org/2001/XMLSchema-instance"
|
},
|
|
/**
|
* Property: defaultPrefix
|
*/
|
defaultPrefix: "wms",
|
|
/**
|
* Constructor: OpenLayers.Format.WMSCapabilities.v1
|
* Create an instance of one of the subclasses.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* APIMethod: read
|
* Read capabilities data from a string, and return a list of layers.
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
*
|
* Returns:
|
* {Array} List of named layers.
|
*/
|
read: function(data) {
|
if(typeof data == "string") {
|
data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
|
}
|
var raw = data;
|
if(data && data.nodeType == 9) {
|
data = data.documentElement;
|
}
|
var capabilities = {};
|
this.readNode(data, capabilities);
|
if (capabilities.service === undefined) {
|
// an exception must have occurred, so parse it
|
var parser = new OpenLayers.Format.OGCExceptionReport();
|
capabilities.error = parser.read(raw);
|
}
|
return capabilities;
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"wms": {
|
"Service": function(node, obj) {
|
obj.service = {};
|
this.readChildNodes(node, obj.service);
|
},
|
"Name": function(node, obj) {
|
obj.name = this.getChildValue(node);
|
},
|
"Title": function(node, obj) {
|
obj.title = this.getChildValue(node);
|
},
|
"Abstract": function(node, obj) {
|
obj["abstract"] = this.getChildValue(node);
|
},
|
"BoundingBox": function(node, obj) {
|
var bbox = {};
|
bbox.bbox = [
|
parseFloat(node.getAttribute("minx")),
|
parseFloat(node.getAttribute("miny")),
|
parseFloat(node.getAttribute("maxx")),
|
parseFloat(node.getAttribute("maxy"))
|
];
|
var res = {
|
x: parseFloat(node.getAttribute("resx")),
|
y: parseFloat(node.getAttribute("resy"))
|
};
|
|
if (! (isNaN(res.x) && isNaN(res.y))) {
|
bbox.res = res;
|
}
|
// return the bbox so that descendant classes can set the
|
// CRS and SRS and add it to the obj
|
return bbox;
|
},
|
"OnlineResource": function(node, obj) {
|
obj.href = this.getAttributeNS(node, this.namespaces.xlink,
|
"href");
|
},
|
"ContactInformation": function(node, obj) {
|
obj.contactInformation = {};
|
this.readChildNodes(node, obj.contactInformation);
|
},
|
"ContactPersonPrimary": function(node, obj) {
|
obj.personPrimary = {};
|
this.readChildNodes(node, obj.personPrimary);
|
},
|
"ContactPerson": function(node, obj) {
|
obj.person = this.getChildValue(node);
|
},
|
"ContactOrganization": function(node, obj) {
|
obj.organization = this.getChildValue(node);
|
},
|
"ContactPosition": function(node, obj) {
|
obj.position = this.getChildValue(node);
|
},
|
"ContactAddress": function(node, obj) {
|
obj.contactAddress = {};
|
this.readChildNodes(node, obj.contactAddress);
|
},
|
"AddressType": function(node, obj) {
|
obj.type = this.getChildValue(node);
|
},
|
"Address": function(node, obj) {
|
obj.address = this.getChildValue(node);
|
},
|
"City": function(node, obj) {
|
obj.city = this.getChildValue(node);
|
},
|
"StateOrProvince": function(node, obj) {
|
obj.stateOrProvince = this.getChildValue(node);
|
},
|
"PostCode": function(node, obj) {
|
obj.postcode = this.getChildValue(node);
|
},
|
"Country": function(node, obj) {
|
obj.country = this.getChildValue(node);
|
},
|
"ContactVoiceTelephone": function(node, obj) {
|
obj.phone = this.getChildValue(node);
|
},
|
"ContactFacsimileTelephone": function(node, obj) {
|
obj.fax = this.getChildValue(node);
|
},
|
"ContactElectronicMailAddress": function(node, obj) {
|
obj.email = this.getChildValue(node);
|
},
|
"Fees": function(node, obj) {
|
var fees = this.getChildValue(node);
|
if (fees && fees.toLowerCase() != "none") {
|
obj.fees = fees;
|
}
|
},
|
"AccessConstraints": function(node, obj) {
|
var constraints = this.getChildValue(node);
|
if (constraints && constraints.toLowerCase() != "none") {
|
obj.accessConstraints = constraints;
|
}
|
},
|
"Capability": function(node, obj) {
|
obj.capability = {
|
nestedLayers: [],
|
layers: []
|
};
|
this.readChildNodes(node, obj.capability);
|
},
|
"Request": function(node, obj) {
|
obj.request = {};
|
this.readChildNodes(node, obj.request);
|
},
|
"GetCapabilities": function(node, obj) {
|
obj.getcapabilities = {formats: []};
|
this.readChildNodes(node, obj.getcapabilities);
|
},
|
"Format": function(node, obj) {
|
if (OpenLayers.Util.isArray(obj.formats)) {
|
obj.formats.push(this.getChildValue(node));
|
} else {
|
obj.format = this.getChildValue(node);
|
}
|
},
|
"DCPType": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"HTTP": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"Get": function(node, obj) {
|
obj.get = {};
|
this.readChildNodes(node, obj.get);
|
// backwards compatibility
|
if (!obj.href) {
|
obj.href = obj.get.href;
|
}
|
},
|
"Post": function(node, obj) {
|
obj.post = {};
|
this.readChildNodes(node, obj.post);
|
// backwards compatibility
|
if (!obj.href) {
|
obj.href = obj.get.href;
|
}
|
},
|
"GetMap": function(node, obj) {
|
obj.getmap = {formats: []};
|
this.readChildNodes(node, obj.getmap);
|
},
|
"GetFeatureInfo": function(node, obj) {
|
obj.getfeatureinfo = {formats: []};
|
this.readChildNodes(node, obj.getfeatureinfo);
|
},
|
"Exception": function(node, obj) {
|
obj.exception = {formats: []};
|
this.readChildNodes(node, obj.exception);
|
},
|
"Layer": function(node, obj) {
|
var parentLayer, capability;
|
if (obj.capability) {
|
capability = obj.capability;
|
parentLayer = obj;
|
} else {
|
capability = obj;
|
}
|
var attrNode = node.getAttributeNode("queryable");
|
var queryable = (attrNode && attrNode.specified) ?
|
node.getAttribute("queryable") : null;
|
attrNode = node.getAttributeNode("cascaded");
|
var cascaded = (attrNode && attrNode.specified) ?
|
node.getAttribute("cascaded") : null;
|
attrNode = node.getAttributeNode("opaque");
|
var opaque = (attrNode && attrNode.specified) ?
|
node.getAttribute('opaque') : null;
|
var noSubsets = node.getAttribute('noSubsets');
|
var fixedWidth = node.getAttribute('fixedWidth');
|
var fixedHeight = node.getAttribute('fixedHeight');
|
var parent = parentLayer || {},
|
extend = OpenLayers.Util.extend;
|
var layer = {
|
nestedLayers: [],
|
styles: parentLayer ? [].concat(parentLayer.styles) : [],
|
srs: parentLayer ? extend({}, parent.srs) : {},
|
metadataURLs: [],
|
bbox: parentLayer ? extend({}, parent.bbox) : {},
|
llbbox: parent.llbbox,
|
dimensions: parentLayer ? extend({}, parent.dimensions) : {},
|
authorityURLs: parentLayer ? extend({}, parent.authorityURLs) : {},
|
identifiers: {},
|
keywords: [],
|
queryable: (queryable && queryable !== "") ?
|
(queryable === "1" || queryable === "true" ) :
|
(parent.queryable || false),
|
cascaded: (cascaded !== null) ? parseInt(cascaded) :
|
(parent.cascaded || 0),
|
opaque: opaque ?
|
(opaque === "1" || opaque === "true" ) :
|
(parent.opaque || false),
|
noSubsets: (noSubsets !== null) ?
|
(noSubsets === "1" || noSubsets === "true" ) :
|
(parent.noSubsets || false),
|
fixedWidth: (fixedWidth != null) ?
|
parseInt(fixedWidth) : (parent.fixedWidth || 0),
|
fixedHeight: (fixedHeight != null) ?
|
parseInt(fixedHeight) : (parent.fixedHeight || 0),
|
minScale: parent.minScale,
|
maxScale: parent.maxScale,
|
attribution: parent.attribution
|
};
|
obj.nestedLayers.push(layer);
|
layer.capability = capability;
|
this.readChildNodes(node, layer);
|
delete layer.capability;
|
if(layer.name) {
|
var parts = layer.name.split(":"),
|
request = capability.request,
|
gfi = request.getfeatureinfo;
|
if(parts.length > 0) {
|
layer.prefix = parts[0];
|
}
|
capability.layers.push(layer);
|
if (layer.formats === undefined) {
|
layer.formats = request.getmap.formats;
|
}
|
if (layer.infoFormats === undefined && gfi) {
|
layer.infoFormats = gfi.formats;
|
}
|
}
|
},
|
"Attribution": function(node, obj) {
|
obj.attribution = {};
|
this.readChildNodes(node, obj.attribution);
|
},
|
"LogoURL": function(node, obj) {
|
obj.logo = {
|
width: node.getAttribute("width"),
|
height: node.getAttribute("height")
|
};
|
this.readChildNodes(node, obj.logo);
|
},
|
"Style": function(node, obj) {
|
var style = {};
|
obj.styles.push(style);
|
this.readChildNodes(node, style);
|
},
|
"LegendURL": function(node, obj) {
|
var legend = {
|
width: node.getAttribute("width"),
|
height: node.getAttribute("height")
|
};
|
obj.legend = legend;
|
this.readChildNodes(node, legend);
|
},
|
"MetadataURL": function(node, obj) {
|
var metadataURL = {type: node.getAttribute("type")};
|
obj.metadataURLs.push(metadataURL);
|
this.readChildNodes(node, metadataURL);
|
},
|
"DataURL": function(node, obj) {
|
obj.dataURL = {};
|
this.readChildNodes(node, obj.dataURL);
|
},
|
"FeatureListURL": function(node, obj) {
|
obj.featureListURL = {};
|
this.readChildNodes(node, obj.featureListURL);
|
},
|
"AuthorityURL": function(node, obj) {
|
var name = node.getAttribute("name");
|
var authority = {};
|
this.readChildNodes(node, authority);
|
obj.authorityURLs[name] = authority.href;
|
},
|
"Identifier": function(node, obj) {
|
var authority = node.getAttribute("authority");
|
obj.identifiers[authority] = this.getChildValue(node);
|
},
|
"KeywordList": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"SRS": function(node, obj) {
|
obj.srs[this.getChildValue(node)] = true;
|
}
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/WMSCapabilities/v1_1.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/WMSCapabilities/v1.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WMSCapabilities.v1_1
|
* Abstract class not to be instantiated directly.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.WMSCapabilities.v1>
|
*/
|
OpenLayers.Format.WMSCapabilities.v1_1 = OpenLayers.Class(
|
OpenLayers.Format.WMSCapabilities.v1, {
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"wms": OpenLayers.Util.applyDefaults({
|
"WMT_MS_Capabilities": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"Keyword": function(node, obj) {
|
if (obj.keywords) {
|
obj.keywords.push(this.getChildValue(node));
|
}
|
},
|
"DescribeLayer": function(node, obj) {
|
obj.describelayer = {formats: []};
|
this.readChildNodes(node, obj.describelayer);
|
},
|
"GetLegendGraphic": function(node, obj) {
|
obj.getlegendgraphic = {formats: []};
|
this.readChildNodes(node, obj.getlegendgraphic);
|
},
|
"GetStyles": function(node, obj) {
|
obj.getstyles = {formats: []};
|
this.readChildNodes(node, obj.getstyles);
|
},
|
"PutStyles": function(node, obj) {
|
obj.putstyles = {formats: []};
|
this.readChildNodes(node, obj.putstyles);
|
},
|
"UserDefinedSymbolization": function(node, obj) {
|
var userSymbols = {
|
supportSLD: parseInt(node.getAttribute("SupportSLD")) == 1,
|
userLayer: parseInt(node.getAttribute("UserLayer")) == 1,
|
userStyle: parseInt(node.getAttribute("UserStyle")) == 1,
|
remoteWFS: parseInt(node.getAttribute("RemoteWFS")) == 1
|
};
|
obj.userSymbols = userSymbols;
|
},
|
"LatLonBoundingBox": function(node, obj) {
|
obj.llbbox = [
|
parseFloat(node.getAttribute("minx")),
|
parseFloat(node.getAttribute("miny")),
|
parseFloat(node.getAttribute("maxx")),
|
parseFloat(node.getAttribute("maxy"))
|
];
|
},
|
"BoundingBox": function(node, obj) {
|
var bbox = OpenLayers.Format.WMSCapabilities.v1.prototype.readers["wms"].BoundingBox.apply(this, [node, obj]);
|
bbox.srs = node.getAttribute("SRS");
|
obj.bbox[bbox.srs] = bbox;
|
},
|
"ScaleHint": function(node, obj) {
|
var min = node.getAttribute("min");
|
var max = node.getAttribute("max");
|
var rad2 = Math.pow(2, 0.5);
|
var ipm = OpenLayers.INCHES_PER_UNIT["m"];
|
if (min != 0) {
|
obj.maxScale = parseFloat(
|
((min / rad2) * ipm *
|
OpenLayers.DOTS_PER_INCH).toPrecision(13)
|
);
|
}
|
if (max != Number.POSITIVE_INFINITY) {
|
obj.minScale = parseFloat(
|
((max / rad2) * ipm *
|
OpenLayers.DOTS_PER_INCH).toPrecision(13)
|
);
|
}
|
},
|
"Dimension": function(node, obj) {
|
var name = node.getAttribute("name").toLowerCase();
|
var dim = {
|
name: name,
|
units: node.getAttribute("units"),
|
unitsymbol: node.getAttribute("unitSymbol")
|
};
|
obj.dimensions[dim.name] = dim;
|
},
|
"Extent": function(node, obj) {
|
var name = node.getAttribute("name").toLowerCase();
|
if (name in obj["dimensions"]) {
|
var extent = obj.dimensions[name];
|
extent.nearestVal =
|
node.getAttribute("nearestValue") === "1";
|
extent.multipleVal =
|
node.getAttribute("multipleValues") === "1";
|
extent.current = node.getAttribute("current") === "1";
|
extent["default"] = node.getAttribute("default") || "";
|
var values = this.getChildValue(node);
|
extent.values = values.split(",");
|
}
|
}
|
}, OpenLayers.Format.WMSCapabilities.v1.prototype.readers["wms"])
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_1"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/WMSCapabilities/v1_1_0.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/WMSCapabilities/v1_1.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WMSCapabilities/v1_1_0
|
* Read WMS Capabilities version 1.1.0.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.WMSCapabilities.v1_1>
|
*/
|
OpenLayers.Format.WMSCapabilities.v1_1_0 = OpenLayers.Class(
|
OpenLayers.Format.WMSCapabilities.v1_1, {
|
|
/**
|
* Property: version
|
* {String} The specific parser version.
|
*/
|
version: "1.1.0",
|
|
/**
|
* Constructor: OpenLayers.Format.WMSCapabilities.v1_1_0
|
* Create a new parser for WMS capabilities version 1.1.0.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"wms": OpenLayers.Util.applyDefaults({
|
"SRS": function(node, obj) {
|
var srs = this.getChildValue(node);
|
var values = srs.split(/ +/);
|
for (var i=0, len=values.length; i<len; i++) {
|
obj.srs[values[i]] = true;
|
}
|
}
|
}, OpenLayers.Format.WMSCapabilities.v1_1.prototype.readers["wms"])
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_1_0"
|
|
});
|
/* ======================================================================
|
OpenLayers/Protocol/WFS/v1.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Protocol/WFS.js
|
*/
|
|
/**
|
* Class: OpenLayers.Protocol.WFS.v1
|
* Abstract class for for v1.0.0 and v1.1.0 protocol.
|
*
|
* Inherits from:
|
* - <OpenLayers.Protocol>
|
*/
|
OpenLayers.Protocol.WFS.v1 = OpenLayers.Class(OpenLayers.Protocol, {
|
|
/**
|
* Property: version
|
* {String} WFS version number.
|
*/
|
version: null,
|
|
/**
|
* Property: srsName
|
* {String} Name of spatial reference system. Default is "EPSG:4326".
|
*/
|
srsName: "EPSG:4326",
|
|
/**
|
* Property: featureType
|
* {String} Local feature typeName.
|
*/
|
featureType: null,
|
|
/**
|
* Property: featureNS
|
* {String} Feature namespace.
|
*/
|
featureNS: null,
|
|
/**
|
* Property: geometryName
|
* {String} Name of the geometry attribute for features. Default is
|
* "the_geom" for WFS <version> 1.0, and null for higher versions.
|
*/
|
geometryName: "the_geom",
|
|
/**
|
* Property: maxFeatures
|
* {Integer} Optional maximum number of features to retrieve.
|
*/
|
|
/**
|
* Property: schema
|
* {String} Optional schema location that will be included in the
|
* schemaLocation attribute value. Note that the feature type schema
|
* is required for a strict XML validator (on transactions with an
|
* insert for example), but is *not* required by the WFS specification
|
* (since the server is supposed to know about feature type schemas).
|
*/
|
schema: null,
|
|
/**
|
* Property: featurePrefix
|
* {String} Namespace alias for feature type. Default is "feature".
|
*/
|
featurePrefix: "feature",
|
|
/**
|
* Property: formatOptions
|
* {Object} Optional options for the format. If a format is not provided,
|
* this property can be used to extend the default format options.
|
*/
|
formatOptions: null,
|
|
/**
|
* Property: readFormat
|
* {<OpenLayers.Format>} For WFS requests it is possible to get a
|
* different output format than GML. In that case, we cannot parse
|
* the response with the default format (WFST) and we need a different
|
* format for reading.
|
*/
|
readFormat: null,
|
|
/**
|
* Property: readOptions
|
* {Object} Optional object to pass to format's read.
|
*/
|
readOptions: null,
|
|
/**
|
* Constructor: OpenLayers.Protocol.WFS
|
* A class for giving layers WFS protocol.
|
*
|
* Parameters:
|
* options - {Object} Optional object whose properties will be set on the
|
* instance.
|
*
|
* Valid options properties:
|
* url - {String} URL to send requests to (required).
|
* featureType - {String} Local (without prefix) feature typeName (required).
|
* featureNS - {String} Feature namespace (required, but can be autodetected
|
* during the first query if GML is used as readFormat and
|
* featurePrefix is provided and matches the prefix used by the server
|
* for this featureType).
|
* featurePrefix - {String} Feature namespace alias (optional - only used
|
* for writing if featureNS is provided). Default is 'feature'.
|
* geometryName - {String} Name of geometry attribute. The default is
|
* 'the_geom' for WFS <version> 1.0, and null for higher versions. If
|
* null, it will be set to the name of the first geometry found in the
|
* first read operation.
|
* multi - {Boolean} If set to true, geometries will be casted to Multi
|
* geometries before they are written in a transaction. No casting will
|
* be done when reading features.
|
*/
|
initialize: function(options) {
|
OpenLayers.Protocol.prototype.initialize.apply(this, [options]);
|
if(!options.format) {
|
this.format = OpenLayers.Format.WFST(OpenLayers.Util.extend({
|
version: this.version,
|
featureType: this.featureType,
|
featureNS: this.featureNS,
|
featurePrefix: this.featurePrefix,
|
geometryName: this.geometryName,
|
srsName: this.srsName,
|
schema: this.schema
|
}, this.formatOptions));
|
}
|
if (!options.geometryName && parseFloat(this.format.version) > 1.0) {
|
this.setGeometryName(null);
|
}
|
},
|
|
/**
|
* APIMethod: destroy
|
* Clean up the protocol.
|
*/
|
destroy: function() {
|
if(this.options && !this.options.format) {
|
this.format.destroy();
|
}
|
this.format = null;
|
OpenLayers.Protocol.prototype.destroy.apply(this);
|
},
|
|
/**
|
* APIMethod: read
|
* Construct a request for reading new features. Since WFS splits the
|
* basic CRUD operations into GetFeature requests (for read) and
|
* Transactions (for all others), this method does not make use of the
|
* format's read method (that is only about reading transaction
|
* responses).
|
*
|
* Parameters:
|
* options - {Object} Options for the read operation, in addition to the
|
* options set on the instance (options set here will take precedence).
|
*
|
* To use a configured protocol to get e.g. a WFS hit count, applications
|
* could do the following:
|
*
|
* (code)
|
* protocol.read({
|
* readOptions: {output: "object"},
|
* resultType: "hits",
|
* maxFeatures: null,
|
* callback: function(resp) {
|
* // process resp.numberOfFeatures here
|
* }
|
* });
|
* (end)
|
*
|
* To use a configured protocol to use WFS paging (if supported by the
|
* server), applications could do the following:
|
*
|
* (code)
|
* protocol.read({
|
* startIndex: 0,
|
* count: 50
|
* });
|
* (end)
|
*
|
* To limit the attributes returned by the GetFeature request, applications
|
* can use the propertyNames option to specify the properties to include in
|
* the response:
|
*
|
* (code)
|
* protocol.read({
|
* propertyNames: ["DURATION", "INTENSITY"]
|
* });
|
* (end)
|
*/
|
read: function(options) {
|
OpenLayers.Protocol.prototype.read.apply(this, arguments);
|
options = OpenLayers.Util.extend({}, options);
|
OpenLayers.Util.applyDefaults(options, this.options || {});
|
var response = new OpenLayers.Protocol.Response({requestType: "read"});
|
|
var data = OpenLayers.Format.XML.prototype.write.apply(
|
this.format, [this.format.writeNode("wfs:GetFeature", options)]
|
);
|
|
response.priv = OpenLayers.Request.POST({
|
url: options.url,
|
callback: this.createCallback(this.handleRead, response, options),
|
params: options.params,
|
headers: options.headers,
|
data: data
|
});
|
|
return response;
|
},
|
|
/**
|
* APIMethod: setFeatureType
|
* Change the feature type on the fly.
|
*
|
* Parameters:
|
* featureType - {String} Local (without prefix) feature typeName.
|
*/
|
setFeatureType: function(featureType) {
|
this.featureType = featureType;
|
this.format.featureType = featureType;
|
},
|
|
/**
|
* APIMethod: setGeometryName
|
* Sets the geometryName option after instantiation.
|
*
|
* Parameters:
|
* geometryName - {String} Name of geometry attribute.
|
*/
|
setGeometryName: function(geometryName) {
|
this.geometryName = geometryName;
|
this.format.geometryName = geometryName;
|
},
|
|
/**
|
* Method: handleRead
|
* Deal with response from the read request.
|
*
|
* Parameters:
|
* response - {<OpenLayers.Protocol.Response>} The response object to pass
|
* to the user callback.
|
* options - {Object} The user options passed to the read call.
|
*/
|
handleRead: function(response, options) {
|
options = OpenLayers.Util.extend({}, options);
|
OpenLayers.Util.applyDefaults(options, this.options);
|
|
if(options.callback) {
|
var request = response.priv;
|
if(request.status >= 200 && request.status < 300) {
|
// success
|
var result = this.parseResponse(request, options.readOptions);
|
if (result && result.success !== false) {
|
if (options.readOptions && options.readOptions.output == "object") {
|
OpenLayers.Util.extend(response, result);
|
} else {
|
response.features = result;
|
}
|
response.code = OpenLayers.Protocol.Response.SUCCESS;
|
} else {
|
// failure (service exception)
|
response.code = OpenLayers.Protocol.Response.FAILURE;
|
response.error = result;
|
}
|
} else {
|
// failure
|
response.code = OpenLayers.Protocol.Response.FAILURE;
|
}
|
options.callback.call(options.scope, response);
|
}
|
},
|
|
/**
|
* Method: parseResponse
|
* Read HTTP response body and return features
|
*
|
* Parameters:
|
* request - {XMLHttpRequest} The request object
|
* options - {Object} Optional object to pass to format's read
|
*
|
* Returns:
|
* {Object} or {Array({<OpenLayers.Feature.Vector>})} or
|
* {<OpenLayers.Feature.Vector>}
|
* An object with a features property, an array of features or a single
|
* feature.
|
*/
|
parseResponse: function(request, options) {
|
var doc = request.responseXML;
|
if(!doc || !doc.documentElement) {
|
doc = request.responseText;
|
}
|
if(!doc || doc.length <= 0) {
|
return null;
|
}
|
var result = (this.readFormat !== null) ? this.readFormat.read(doc) :
|
this.format.read(doc, options);
|
if (!this.featureNS) {
|
var format = this.readFormat || this.format;
|
this.featureNS = format.featureNS;
|
// no need to auto-configure again on subsequent reads
|
format.autoConfig = false;
|
if (!this.geometryName) {
|
this.setGeometryName(format.geometryName);
|
}
|
}
|
return result;
|
},
|
|
/**
|
* Method: commit
|
* Given a list of feature, assemble a batch request for update, create,
|
* and delete transactions. A commit call on the prototype amounts
|
* to writing a WFS transaction - so the write method on the format
|
* is used.
|
*
|
* Parameters:
|
* features - {Array(<OpenLayers.Feature.Vector>)}
|
* options - {Object}
|
*
|
* Valid options properties:
|
* nativeElements - {Array({Object})} Array of objects with information for writing
|
* out <Native> elements, these objects have vendorId, safeToIgnore and
|
* value properties. The <Native> element is intended to allow access to
|
* vendor specific capabilities of any particular web feature server or
|
* datastore.
|
*
|
* Returns:
|
* {<OpenLayers.Protocol.Response>} A response object with a features
|
* property containing any insertIds and a priv property referencing
|
* the XMLHttpRequest object.
|
*/
|
commit: function(features, options) {
|
|
options = OpenLayers.Util.extend({}, options);
|
OpenLayers.Util.applyDefaults(options, this.options);
|
|
var response = new OpenLayers.Protocol.Response({
|
requestType: "commit",
|
reqFeatures: features
|
});
|
response.priv = OpenLayers.Request.POST({
|
url: options.url,
|
headers: options.headers,
|
data: this.format.write(features, options),
|
callback: this.createCallback(this.handleCommit, response, options)
|
});
|
|
return response;
|
},
|
|
/**
|
* Method: handleCommit
|
* Called when the commit request returns.
|
*
|
* Parameters:
|
* response - {<OpenLayers.Protocol.Response>} The response object to pass
|
* to the user callback.
|
* options - {Object} The user options passed to the commit call.
|
*/
|
handleCommit: function(response, options) {
|
if(options.callback) {
|
var request = response.priv;
|
|
// ensure that we have an xml doc
|
var data = request.responseXML;
|
if(!data || !data.documentElement) {
|
data = request.responseText;
|
}
|
|
var obj = this.format.read(data) || {};
|
|
response.insertIds = obj.insertIds || [];
|
if (obj.success) {
|
response.code = OpenLayers.Protocol.Response.SUCCESS;
|
} else {
|
response.code = OpenLayers.Protocol.Response.FAILURE;
|
response.error = obj;
|
}
|
options.callback.call(options.scope, response);
|
}
|
},
|
|
/**
|
* Method: filterDelete
|
* Send a request that deletes all features by their filter.
|
*
|
* Parameters:
|
* filter - {<OpenLayers.Filter>} filter
|
*/
|
filterDelete: function(filter, options) {
|
options = OpenLayers.Util.extend({}, options);
|
OpenLayers.Util.applyDefaults(options, this.options);
|
|
var response = new OpenLayers.Protocol.Response({
|
requestType: "commit"
|
});
|
|
var root = this.format.createElementNSPlus("wfs:Transaction", {
|
attributes: {
|
service: "WFS",
|
version: this.version
|
}
|
});
|
|
var deleteNode = this.format.createElementNSPlus("wfs:Delete", {
|
attributes: {
|
typeName: (options.featureNS ? this.featurePrefix + ":" : "") +
|
options.featureType
|
}
|
});
|
|
if(options.featureNS) {
|
deleteNode.setAttribute("xmlns:" + this.featurePrefix, options.featureNS);
|
}
|
var filterNode = this.format.writeNode("ogc:Filter", filter);
|
|
deleteNode.appendChild(filterNode);
|
|
root.appendChild(deleteNode);
|
|
var data = OpenLayers.Format.XML.prototype.write.apply(
|
this.format, [root]
|
);
|
|
return OpenLayers.Request.POST({
|
url: this.url,
|
callback : options.callback || function(){},
|
data: data
|
});
|
|
},
|
|
/**
|
* Method: abort
|
* Abort an ongoing request, the response object passed to
|
* this method must come from this protocol (as a result
|
* of a read, or commit operation).
|
*
|
* Parameters:
|
* response - {<OpenLayers.Protocol.Response>}
|
*/
|
abort: function(response) {
|
if (response) {
|
response.priv.abort();
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Protocol.WFS.v1"
|
});
|
/* ======================================================================
|
OpenLayers/Handler/Feature.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Handler.js
|
*/
|
|
/**
|
* Class: OpenLayers.Handler.Feature
|
* Handler to respond to mouse events related to a drawn feature. Callbacks
|
* with the following keys will be notified of the following events
|
* associated with features: click, clickout, over, out, and dblclick.
|
*
|
* This handler stops event propagation for mousedown and mouseup if those
|
* browser events target features that can be selected.
|
*
|
* Inherits from:
|
* - <OpenLayers.Handler>
|
*/
|
OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, {
|
|
/**
|
* Property: EVENTMAP
|
* {Object} A object mapping the browser events to objects with callback
|
* keys for in and out.
|
*/
|
EVENTMAP: {
|
'click': {'in': 'click', 'out': 'clickout'},
|
'mousemove': {'in': 'over', 'out': 'out'},
|
'dblclick': {'in': 'dblclick', 'out': null},
|
'mousedown': {'in': null, 'out': null},
|
'mouseup': {'in': null, 'out': null},
|
'touchstart': {'in': 'click', 'out': 'clickout'}
|
},
|
|
/**
|
* Property: feature
|
* {<OpenLayers.Feature.Vector>} The last feature that was hovered.
|
*/
|
feature: null,
|
|
/**
|
* Property: lastFeature
|
* {<OpenLayers.Feature.Vector>} The last feature that was handled.
|
*/
|
lastFeature: null,
|
|
/**
|
* Property: down
|
* {<OpenLayers.Pixel>} The location of the last mousedown.
|
*/
|
down: null,
|
|
/**
|
* Property: up
|
* {<OpenLayers.Pixel>} The location of the last mouseup.
|
*/
|
up: null,
|
|
/**
|
* Property: clickTolerance
|
* {Number} The number of pixels the mouse can move between mousedown
|
* and mouseup for the event to still be considered a click.
|
* Dragging the map should not trigger the click and clickout callbacks
|
* unless the map is moved by less than this tolerance. Defaults to 4.
|
*/
|
clickTolerance: 4,
|
|
/**
|
* Property: geometryTypes
|
* To restrict dragging to a limited set of geometry types, send a list
|
* of strings corresponding to the geometry class names.
|
*
|
* @type Array(String)
|
*/
|
geometryTypes: null,
|
|
/**
|
* Property: stopClick
|
* {Boolean} If stopClick is set to true, handled clicks do not
|
* propagate to other click listeners. Otherwise, handled clicks
|
* do propagate. Unhandled clicks always propagate, whatever the
|
* value of stopClick. Defaults to true.
|
*/
|
stopClick: true,
|
|
/**
|
* Property: stopDown
|
* {Boolean} If stopDown is set to true, handled mousedowns do not
|
* propagate to other mousedown listeners. Otherwise, handled
|
* mousedowns do propagate. Unhandled mousedowns always propagate,
|
* whatever the value of stopDown. Defaults to true.
|
*/
|
stopDown: true,
|
|
/**
|
* Property: stopUp
|
* {Boolean} If stopUp is set to true, handled mouseups do not
|
* propagate to other mouseup listeners. Otherwise, handled mouseups
|
* do propagate. Unhandled mouseups always propagate, whatever the
|
* value of stopUp. Defaults to false.
|
*/
|
stopUp: false,
|
|
/**
|
* Constructor: OpenLayers.Handler.Feature
|
*
|
* Parameters:
|
* control - {<OpenLayers.Control>}
|
* layer - {<OpenLayers.Layer.Vector>}
|
* callbacks - {Object} An object with a 'over' property whos value is
|
* a function to be called when the mouse is over a feature. The
|
* callback should expect to recieve a single argument, the feature.
|
* options - {Object}
|
*/
|
initialize: function(control, layer, callbacks, options) {
|
OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]);
|
this.layer = layer;
|
},
|
|
/**
|
* Method: touchstart
|
* Handle touchstart events
|
*
|
* Parameters:
|
* evt - {Event}
|
*
|
* Returns:
|
* {Boolean} Let the event propagate.
|
*/
|
touchstart: function(evt) {
|
this.startTouch();
|
return OpenLayers.Event.isMultiTouch(evt) ?
|
true : this.mousedown(evt);
|
},
|
|
/**
|
* Method: touchmove
|
* Handle touchmove events. We just prevent the browser default behavior,
|
* for Android Webkit not to select text when moving the finger after
|
* selecting a feature.
|
*
|
* Parameters:
|
* evt - {Event}
|
*/
|
touchmove: function(evt) {
|
OpenLayers.Event.preventDefault(evt);
|
},
|
|
/**
|
* Method: mousedown
|
* Handle mouse down. Stop propagation if a feature is targeted by this
|
* event (stops map dragging during feature selection).
|
*
|
* Parameters:
|
* evt - {Event}
|
*/
|
mousedown: function(evt) {
|
// Feature selection is only done with a left click. Other handlers may stop the
|
// propagation of left-click mousedown events but not right-click mousedown events.
|
// This mismatch causes problems when comparing the location of the down and up
|
// events in the click function so it is important ignore right-clicks.
|
if (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt)) {
|
this.down = evt.xy;
|
}
|
return this.handle(evt) ? !this.stopDown : true;
|
},
|
|
/**
|
* Method: mouseup
|
* Handle mouse up. Stop propagation if a feature is targeted by this
|
* event.
|
*
|
* Parameters:
|
* evt - {Event}
|
*/
|
mouseup: function(evt) {
|
this.up = evt.xy;
|
return this.handle(evt) ? !this.stopUp : true;
|
},
|
|
/**
|
* Method: click
|
* Handle click. Call the "click" callback if click on a feature,
|
* or the "clickout" callback if click outside any feature.
|
*
|
* Parameters:
|
* evt - {Event}
|
*
|
* Returns:
|
* {Boolean}
|
*/
|
click: function(evt) {
|
return this.handle(evt) ? !this.stopClick : true;
|
},
|
|
/**
|
* Method: mousemove
|
* Handle mouse moves. Call the "over" callback if moving in to a feature,
|
* or the "out" callback if moving out of a feature.
|
*
|
* Parameters:
|
* evt - {Event}
|
*
|
* Returns:
|
* {Boolean}
|
*/
|
mousemove: function(evt) {
|
if (!this.callbacks['over'] && !this.callbacks['out']) {
|
return true;
|
}
|
this.handle(evt);
|
return true;
|
},
|
|
/**
|
* Method: dblclick
|
* Handle dblclick. Call the "dblclick" callback if dblclick on a feature.
|
*
|
* Parameters:
|
* evt - {Event}
|
*
|
* Returns:
|
* {Boolean}
|
*/
|
dblclick: function(evt) {
|
return !this.handle(evt);
|
},
|
|
/**
|
* Method: geometryTypeMatches
|
* Return true if the geometry type of the passed feature matches
|
* one of the geometry types in the geometryTypes array.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Vector.Feature>}
|
*
|
* Returns:
|
* {Boolean}
|
*/
|
geometryTypeMatches: function(feature) {
|
return this.geometryTypes == null ||
|
OpenLayers.Util.indexOf(this.geometryTypes,
|
feature.geometry.CLASS_NAME) > -1;
|
},
|
|
/**
|
* Method: handle
|
*
|
* Parameters:
|
* evt - {Event}
|
*
|
* Returns:
|
* {Boolean} The event occurred over a relevant feature.
|
*/
|
handle: function(evt) {
|
if(this.feature && !this.feature.layer) {
|
// feature has been destroyed
|
this.feature = null;
|
}
|
var type = evt.type;
|
var handled = false;
|
var previouslyIn = !!(this.feature); // previously in a feature
|
var click = (type == "click" || type == "dblclick" || type == "touchstart");
|
this.feature = this.layer.getFeatureFromEvent(evt);
|
if(this.feature && !this.feature.layer) {
|
// feature has been destroyed
|
this.feature = null;
|
}
|
if(this.lastFeature && !this.lastFeature.layer) {
|
// last feature has been destroyed
|
this.lastFeature = null;
|
}
|
if(this.feature) {
|
if(type === "touchstart") {
|
// stop the event to prevent Android Webkit from
|
// "flashing" the map div
|
OpenLayers.Event.preventDefault(evt);
|
}
|
var inNew = (this.feature != this.lastFeature);
|
if(this.geometryTypeMatches(this.feature)) {
|
// in to a feature
|
if(previouslyIn && inNew) {
|
// out of last feature and in to another
|
if(this.lastFeature) {
|
this.triggerCallback(type, 'out', [this.lastFeature]);
|
}
|
this.triggerCallback(type, 'in', [this.feature]);
|
} else if(!previouslyIn || click) {
|
// in feature for the first time
|
this.triggerCallback(type, 'in', [this.feature]);
|
}
|
this.lastFeature = this.feature;
|
handled = true;
|
} else {
|
// not in to a feature
|
if(this.lastFeature && (previouslyIn && inNew || click)) {
|
// out of last feature for the first time
|
this.triggerCallback(type, 'out', [this.lastFeature]);
|
}
|
// next time the mouse goes in a feature whose geometry type
|
// doesn't match we don't want to call the 'out' callback
|
// again, so let's set this.feature to null so that
|
// previouslyIn will evaluate to false the next time
|
// we enter handle. Yes, a bit hackish...
|
this.feature = null;
|
}
|
} else if(this.lastFeature && (previouslyIn || click)) {
|
this.triggerCallback(type, 'out', [this.lastFeature]);
|
}
|
return handled;
|
},
|
|
/**
|
* Method: triggerCallback
|
* Call the callback keyed in the event map with the supplied arguments.
|
* For click and clickout, the <clickTolerance> is checked first.
|
*
|
* Parameters:
|
* type - {String}
|
*/
|
triggerCallback: function(type, mode, args) {
|
var key = this.EVENTMAP[type][mode];
|
if(key) {
|
if(type == 'click' && this.up && this.down) {
|
// for click/clickout, only trigger callback if tolerance is met
|
var dpx = Math.sqrt(
|
Math.pow(this.up.x - this.down.x, 2) +
|
Math.pow(this.up.y - this.down.y, 2)
|
);
|
if(dpx <= this.clickTolerance) {
|
this.callback(key, args);
|
}
|
// we're done with this set of events now: clear the cached
|
// positions so we can't trip over them later (this can occur
|
// if one of the up/down events gets eaten before it gets to us
|
// but we still get the click)
|
this.up = this.down = null;
|
} else {
|
this.callback(key, args);
|
}
|
}
|
},
|
|
/**
|
* Method: activate
|
* Turn on the handler. Returns false if the handler was already active.
|
*
|
* Returns:
|
* {Boolean}
|
*/
|
activate: function() {
|
var activated = false;
|
if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
|
this.moveLayerToTop();
|
this.map.events.on({
|
"removelayer": this.handleMapEvents,
|
"changelayer": this.handleMapEvents,
|
scope: this
|
});
|
activated = true;
|
}
|
return activated;
|
},
|
|
/**
|
* Method: deactivate
|
* Turn off the handler. Returns false if the handler was already active.
|
*
|
* Returns:
|
* {Boolean}
|
*/
|
deactivate: function() {
|
var deactivated = false;
|
if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
|
this.moveLayerBack();
|
this.feature = null;
|
this.lastFeature = null;
|
this.down = null;
|
this.up = null;
|
this.map.events.un({
|
"removelayer": this.handleMapEvents,
|
"changelayer": this.handleMapEvents,
|
scope: this
|
});
|
deactivated = true;
|
}
|
return deactivated;
|
},
|
|
/**
|
* Method: handleMapEvents
|
*
|
* Parameters:
|
* evt - {Object}
|
*/
|
handleMapEvents: function(evt) {
|
if (evt.type == "removelayer" || evt.property == "order") {
|
this.moveLayerToTop();
|
}
|
},
|
|
/**
|
* Method: moveLayerToTop
|
* Moves the layer for this handler to the top, so mouse events can reach
|
* it.
|
*/
|
moveLayerToTop: function() {
|
var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1,
|
this.layer.getZIndex()) + 1;
|
this.layer.setZIndex(index);
|
|
},
|
|
/**
|
* Method: moveLayerBack
|
* Moves the layer back to the position determined by the map's layers
|
* array.
|
*/
|
moveLayerBack: function() {
|
var index = this.layer.getZIndex() - 1;
|
if (index >= this.map.Z_INDEX_BASE['Feature']) {
|
this.layer.setZIndex(index);
|
} else {
|
this.map.setLayerZIndex(this.layer,
|
this.map.getLayerIndex(this.layer));
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Handler.Feature"
|
});
|
/* ======================================================================
|
OpenLayers/Layer/Vector/RootContainer.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Layer/Vector.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.Vector.RootContainer
|
* A special layer type to combine multiple vector layers inside a single
|
* renderer root container. This class is not supposed to be instantiated
|
* from user space, it is a helper class for controls that require event
|
* processing for multiple vector layers.
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer.Vector>
|
*/
|
OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, {
|
|
/**
|
* Property: displayInLayerSwitcher
|
* Set to false for this layer type
|
*/
|
displayInLayerSwitcher: false,
|
|
/**
|
* APIProperty: layers
|
* Layers that are attached to this container. Required config option.
|
*/
|
layers: null,
|
|
/**
|
* Constructor: OpenLayers.Layer.Vector.RootContainer
|
* Create a new root container for multiple vector layer. This constructor
|
* is not supposed to be used from user space, it is only to be used by
|
* controls that need feature selection across multiple vector layers.
|
*
|
* Parameters:
|
* name - {String} A name for the layer
|
* options - {Object} Optional object with non-default properties to set on
|
* the layer.
|
*
|
* Required options properties:
|
* layers - {Array(<OpenLayers.Layer.Vector>)} The layers managed by this
|
* container
|
*
|
* Returns:
|
* {<OpenLayers.Layer.Vector.RootContainer>} A new vector layer root
|
* container
|
*/
|
|
/**
|
* Method: display
|
*/
|
display: function() {},
|
|
/**
|
* Method: getFeatureFromEvent
|
* walk through the layers to find the feature returned by the event
|
*
|
* Parameters:
|
* evt - {Object} event object with a feature property
|
*
|
* Returns:
|
* {<OpenLayers.Feature.Vector>}
|
*/
|
getFeatureFromEvent: function(evt) {
|
var layers = this.layers;
|
var feature;
|
for(var i=0; i<layers.length; i++) {
|
feature = layers[i].getFeatureFromEvent(evt);
|
if(feature) {
|
return feature;
|
}
|
}
|
},
|
|
/**
|
* Method: setMap
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
setMap: function(map) {
|
OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);
|
this.collectRoots();
|
map.events.register("changelayer", this, this.handleChangeLayer);
|
},
|
|
/**
|
* Method: removeMap
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
removeMap: function(map) {
|
map.events.unregister("changelayer", this, this.handleChangeLayer);
|
this.resetRoots();
|
OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments);
|
},
|
|
/**
|
* Method: collectRoots
|
* Collects the root nodes of all layers this control is configured with
|
* and moveswien the nodes to this control's layer
|
*/
|
collectRoots: function() {
|
var layer;
|
// walk through all map layers, because we want to keep the order
|
for(var i=0; i<this.map.layers.length; ++i) {
|
layer = this.map.layers[i];
|
if(OpenLayers.Util.indexOf(this.layers, layer) != -1) {
|
layer.renderer.moveRoot(this.renderer);
|
}
|
}
|
},
|
|
/**
|
* Method: resetRoots
|
* Resets the root nodes back into the layers they belong to.
|
*/
|
resetRoots: function() {
|
var layer;
|
for(var i=0; i<this.layers.length; ++i) {
|
layer = this.layers[i];
|
if(this.renderer && layer.renderer.getRenderLayerId() == this.id) {
|
this.renderer.moveRoot(layer.renderer);
|
}
|
}
|
},
|
|
/**
|
* Method: handleChangeLayer
|
* Event handler for the map's changelayer event. We need to rebuild
|
* this container's layer dom if order of one of its layers changes.
|
* This handler is added with the setMap method, and removed with the
|
* removeMap method.
|
*
|
* Parameters:
|
* evt - {Object}
|
*/
|
handleChangeLayer: function(evt) {
|
var layer = evt.layer;
|
if(evt.property == "order" &&
|
OpenLayers.Util.indexOf(this.layers, layer) != -1) {
|
this.resetRoots();
|
this.collectRoots();
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.Vector.RootContainer"
|
});
|
/* ======================================================================
|
OpenLayers/Control/SelectFeature.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/Feature/Vector.js
|
* @requires OpenLayers/Handler/Feature.js
|
* @requires OpenLayers/Layer/Vector/RootContainer.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.SelectFeature
|
* The SelectFeature control selects vector features from a given layer on
|
* click or hover.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* APIProperty: events
|
* {<OpenLayers.Events>} Events instance for listeners and triggering
|
* control specific events.
|
*
|
* Register a listener for a particular event with the following syntax:
|
* (code)
|
* control.events.register(type, obj, listener);
|
* (end)
|
*
|
* Supported event types (in addition to those from <OpenLayers.Control.events>):
|
* beforefeaturehighlighted - Triggered before a feature is highlighted
|
* featurehighlighted - Triggered when a feature is highlighted
|
* featureunhighlighted - Triggered when a feature is unhighlighted
|
* boxselectionstart - Triggered before box selection starts
|
* boxselectionend - Triggered after box selection ends
|
*/
|
|
/**
|
* Property: multipleKey
|
* {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets
|
* the <multiple> property to true. Default is null.
|
*/
|
multipleKey: null,
|
|
/**
|
* Property: toggleKey
|
* {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets
|
* the <toggle> property to true. Default is null.
|
*/
|
toggleKey: null,
|
|
/**
|
* APIProperty: multiple
|
* {Boolean} Allow selection of multiple geometries. Default is false.
|
*/
|
multiple: false,
|
|
/**
|
* APIProperty: clickout
|
* {Boolean} Unselect features when clicking outside any feature.
|
* Default is true.
|
*/
|
clickout: true,
|
|
/**
|
* APIProperty: toggle
|
* {Boolean} Unselect a selected feature on click. Default is false. Only
|
* has meaning if hover is false.
|
*/
|
toggle: false,
|
|
/**
|
* APIProperty: hover
|
* {Boolean} Select on mouse over and deselect on mouse out. If true, this
|
* ignores clicks and only listens to mouse moves.
|
*/
|
hover: false,
|
|
/**
|
* APIProperty: highlightOnly
|
* {Boolean} If true do not actually select features (that is place them in
|
* the layer's selected features array), just highlight them. This property
|
* has no effect if hover is false. Defaults to false.
|
*/
|
highlightOnly: false,
|
|
/**
|
* APIProperty: box
|
* {Boolean} Allow feature selection by drawing a box.
|
*/
|
box: false,
|
|
/**
|
* Property: onBeforeSelect
|
* {Function} Optional function to be called before a feature is selected.
|
* The function should expect to be called with a feature.
|
*/
|
onBeforeSelect: function() {},
|
|
/**
|
* APIProperty: onSelect
|
* {Function} Optional function to be called when a feature is selected.
|
* The function should expect to be called with a feature.
|
*/
|
onSelect: function() {},
|
|
/**
|
* APIProperty: onUnselect
|
* {Function} Optional function to be called when a feature is unselected.
|
* The function should expect to be called with a feature.
|
*/
|
onUnselect: function() {},
|
|
/**
|
* Property: scope
|
* {Object} The scope to use with the onBeforeSelect, onSelect, onUnselect
|
* callbacks. If null the scope will be this control.
|
*/
|
scope: null,
|
|
/**
|
* APIProperty: geometryTypes
|
* {Array(String)} To restrict selecting to a limited set of geometry types,
|
* send a list of strings corresponding to the geometry class names.
|
*/
|
geometryTypes: null,
|
|
/**
|
* Property: layer
|
* {<OpenLayers.Layer.Vector>} The vector layer with a common renderer
|
* root for all layers this control is configured with (if an array of
|
* layers was passed to the constructor), or the vector layer the control
|
* was configured with (if a single layer was passed to the constructor).
|
*/
|
layer: null,
|
|
/**
|
* Property: layers
|
* {Array(<OpenLayers.Layer.Vector>)} The layers this control will work on,
|
* or null if the control was configured with a single layer
|
*/
|
layers: null,
|
|
/**
|
* APIProperty: callbacks
|
* {Object} The functions that are sent to the handlers.feature for callback
|
*/
|
callbacks: null,
|
|
/**
|
* APIProperty: selectStyle
|
* {Object} Hash of styles
|
*/
|
selectStyle: null,
|
|
/**
|
* Property: renderIntent
|
* {String} key used to retrieve the select style from the layer's
|
* style map.
|
*/
|
renderIntent: "select",
|
|
/**
|
* Property: handlers
|
* {Object} Object with references to multiple <OpenLayers.Handler>
|
* instances.
|
*/
|
handlers: null,
|
|
/**
|
* Constructor: OpenLayers.Control.SelectFeature
|
* Create a new control for selecting features.
|
*
|
* Parameters:
|
* layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers. The
|
* layer(s) this control will select features from.
|
* options - {Object}
|
*/
|
initialize: function(layers, options) {
|
OpenLayers.Control.prototype.initialize.apply(this, [options]);
|
|
if(this.scope === null) {
|
this.scope = this;
|
}
|
this.initLayer(layers);
|
var callbacks = {
|
click: this.clickFeature,
|
clickout: this.clickoutFeature
|
};
|
if (this.hover) {
|
callbacks.over = this.overFeature;
|
callbacks.out = this.outFeature;
|
}
|
|
this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);
|
this.handlers = {
|
feature: new OpenLayers.Handler.Feature(
|
this, this.layer, this.callbacks,
|
{geometryTypes: this.geometryTypes}
|
)
|
};
|
|
if (this.box) {
|
this.handlers.box = new OpenLayers.Handler.Box(
|
this, {done: this.selectBox},
|
{boxDivClassName: "olHandlerBoxSelectFeature"}
|
);
|
}
|
},
|
|
/**
|
* Method: initLayer
|
* Assign the layer property. If layers is an array, we need to use
|
* a RootContainer.
|
*
|
* Parameters:
|
* layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers.
|
*/
|
initLayer: function(layers) {
|
if(OpenLayers.Util.isArray(layers)) {
|
this.layers = layers;
|
this.layer = new OpenLayers.Layer.Vector.RootContainer(
|
this.id + "_container", {
|
layers: layers
|
}
|
);
|
} else {
|
this.layer = layers;
|
}
|
},
|
|
/**
|
* Method: destroy
|
*/
|
destroy: function() {
|
if(this.active && this.layers) {
|
this.map.removeLayer(this.layer);
|
}
|
OpenLayers.Control.prototype.destroy.apply(this, arguments);
|
if(this.layers) {
|
this.layer.destroy();
|
}
|
},
|
|
/**
|
* Method: activate
|
* Activates the control.
|
*
|
* Returns:
|
* {Boolean} The control was effectively activated.
|
*/
|
activate: function () {
|
if (!this.active) {
|
if(this.layers) {
|
this.map.addLayer(this.layer);
|
}
|
this.handlers.feature.activate();
|
if(this.box && this.handlers.box) {
|
this.handlers.box.activate();
|
}
|
}
|
return OpenLayers.Control.prototype.activate.apply(
|
this, arguments
|
);
|
},
|
|
/**
|
* Method: deactivate
|
* Deactivates the control.
|
*
|
* Returns:
|
* {Boolean} The control was effectively deactivated.
|
*/
|
deactivate: function () {
|
if (this.active) {
|
this.handlers.feature.deactivate();
|
if(this.handlers.box) {
|
this.handlers.box.deactivate();
|
}
|
if(this.layers) {
|
this.map.removeLayer(this.layer);
|
}
|
}
|
return OpenLayers.Control.prototype.deactivate.apply(
|
this, arguments
|
);
|
},
|
|
/**
|
* Method: unselectAll
|
* Unselect all selected features. To unselect all except for a single
|
* feature, set the options.except property to the feature.
|
*
|
* Parameters:
|
* options - {Object} Optional configuration object.
|
*/
|
unselectAll: function(options) {
|
// we'll want an option to supress notification here
|
var layers = this.layers || [this.layer],
|
layer, feature, l, numExcept;
|
for(l=0; l<layers.length; ++l) {
|
layer = layers[l];
|
numExcept = 0;
|
//layer.selectedFeatures is null when layer is destroyed and
|
//one of it's preremovelayer listener calls setLayer
|
//with another layer on this control
|
if(layer.selectedFeatures != null) {
|
while(layer.selectedFeatures.length > numExcept) {
|
feature = layer.selectedFeatures[numExcept];
|
if(!options || options.except != feature) {
|
this.unselect(feature);
|
} else {
|
++numExcept;
|
}
|
}
|
}
|
}
|
},
|
|
/**
|
* Method: clickFeature
|
* Called on click in a feature
|
* Only responds if this.hover is false.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
*/
|
clickFeature: function(feature) {
|
if(!this.hover) {
|
var selected = (OpenLayers.Util.indexOf(
|
feature.layer.selectedFeatures, feature) > -1);
|
if(selected) {
|
if(this.toggleSelect()) {
|
this.unselect(feature);
|
} else if(!this.multipleSelect()) {
|
this.unselectAll({except: feature});
|
}
|
} else {
|
if(!this.multipleSelect()) {
|
this.unselectAll({except: feature});
|
}
|
this.select(feature);
|
}
|
}
|
},
|
|
/**
|
* Method: multipleSelect
|
* Allow for multiple selected features based on <multiple> property and
|
* <multipleKey> event modifier.
|
*
|
* Returns:
|
* {Boolean} Allow for multiple selected features.
|
*/
|
multipleSelect: function() {
|
return this.multiple || (this.handlers.feature.evt &&
|
this.handlers.feature.evt[this.multipleKey]);
|
},
|
|
/**
|
* Method: toggleSelect
|
* Event should toggle the selected state of a feature based on <toggle>
|
* property and <toggleKey> event modifier.
|
*
|
* Returns:
|
* {Boolean} Toggle the selected state of a feature.
|
*/
|
toggleSelect: function() {
|
return this.toggle || (this.handlers.feature.evt &&
|
this.handlers.feature.evt[this.toggleKey]);
|
},
|
|
/**
|
* Method: clickoutFeature
|
* Called on click outside a previously clicked (selected) feature.
|
* Only responds if this.hover is false.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Vector.Feature>}
|
*/
|
clickoutFeature: function(feature) {
|
if(!this.hover && this.clickout) {
|
this.unselectAll();
|
}
|
},
|
|
/**
|
* Method: overFeature
|
* Called on over a feature.
|
* Only responds if this.hover is true.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
*/
|
overFeature: function(feature) {
|
var layer = feature.layer;
|
if(this.hover) {
|
if(this.highlightOnly) {
|
this.highlight(feature);
|
} else if(OpenLayers.Util.indexOf(
|
layer.selectedFeatures, feature) == -1) {
|
this.select(feature);
|
}
|
}
|
},
|
|
/**
|
* Method: outFeature
|
* Called on out of a selected feature.
|
* Only responds if this.hover is true.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
*/
|
outFeature: function(feature) {
|
if(this.hover) {
|
if(this.highlightOnly) {
|
// we do nothing if we're not the last highlighter of the
|
// feature
|
if(feature._lastHighlighter == this.id) {
|
// if another select control had highlighted the feature before
|
// we did it ourself then we use that control to highlight the
|
// feature as it was before we highlighted it, else we just
|
// unhighlight it
|
if(feature._prevHighlighter &&
|
feature._prevHighlighter != this.id) {
|
delete feature._lastHighlighter;
|
var control = this.map.getControl(
|
feature._prevHighlighter);
|
if(control) {
|
control.highlight(feature);
|
}
|
} else {
|
this.unhighlight(feature);
|
}
|
}
|
} else {
|
this.unselect(feature);
|
}
|
}
|
},
|
|
/**
|
* Method: highlight
|
* Redraw feature with the select style.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
*/
|
highlight: function(feature) {
|
var layer = feature.layer;
|
var cont = this.events.triggerEvent("beforefeaturehighlighted", {
|
feature : feature
|
});
|
if(cont !== false) {
|
feature._prevHighlighter = feature._lastHighlighter;
|
feature._lastHighlighter = this.id;
|
var style = this.selectStyle || this.renderIntent;
|
layer.drawFeature(feature, style);
|
this.events.triggerEvent("featurehighlighted", {feature : feature});
|
}
|
},
|
|
/**
|
* Method: unhighlight
|
* Redraw feature with the "default" style
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
*/
|
unhighlight: function(feature) {
|
var layer = feature.layer;
|
// three cases:
|
// 1. there's no other highlighter, in that case _prev is undefined,
|
// and we just need to undef _last
|
// 2. another control highlighted the feature after we did it, in
|
// that case _last references this other control, and we just
|
// need to undef _prev
|
// 3. another control highlighted the feature before we did it, in
|
// that case _prev references this other control, and we need to
|
// set _last to _prev and undef _prev
|
if(feature._prevHighlighter == undefined) {
|
delete feature._lastHighlighter;
|
} else if(feature._prevHighlighter == this.id) {
|
delete feature._prevHighlighter;
|
} else {
|
feature._lastHighlighter = feature._prevHighlighter;
|
delete feature._prevHighlighter;
|
}
|
layer.drawFeature(feature, feature.style || feature.layer.style ||
|
"default");
|
this.events.triggerEvent("featureunhighlighted", {feature : feature});
|
},
|
|
/**
|
* Method: select
|
* Add feature to the layer's selectedFeature array, render the feature as
|
* selected, and call the onSelect function.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
*/
|
select: function(feature) {
|
var cont = this.onBeforeSelect.call(this.scope, feature);
|
var layer = feature.layer;
|
if(cont !== false) {
|
cont = layer.events.triggerEvent("beforefeatureselected", {
|
feature: feature
|
});
|
if(cont !== false) {
|
layer.selectedFeatures.push(feature);
|
this.highlight(feature);
|
// if the feature handler isn't involved in the feature
|
// selection (because the box handler is used or the
|
// feature is selected programatically) we fake the
|
// feature handler to allow unselecting on click
|
if(!this.handlers.feature.lastFeature) {
|
this.handlers.feature.lastFeature = layer.selectedFeatures[0];
|
}
|
layer.events.triggerEvent("featureselected", {feature: feature});
|
this.onSelect.call(this.scope, feature);
|
}
|
}
|
},
|
|
/**
|
* Method: unselect
|
* Remove feature from the layer's selectedFeature array, render the feature as
|
* normal, and call the onUnselect function.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
*/
|
unselect: function(feature) {
|
var layer = feature.layer;
|
// Store feature style for restoration later
|
this.unhighlight(feature);
|
OpenLayers.Util.removeItem(layer.selectedFeatures, feature);
|
layer.events.triggerEvent("featureunselected", {feature: feature});
|
this.onUnselect.call(this.scope, feature);
|
},
|
|
/**
|
* Method: selectBox
|
* Callback from the handlers.box set up when <box> selection is true
|
* on.
|
*
|
* Parameters:
|
* position - {<OpenLayers.Bounds> || <OpenLayers.Pixel> }
|
*/
|
selectBox: function(position) {
|
if (position instanceof OpenLayers.Bounds) {
|
var minXY = this.map.getLonLatFromPixel({
|
x: position.left,
|
y: position.bottom
|
});
|
var maxXY = this.map.getLonLatFromPixel({
|
x: position.right,
|
y: position.top
|
});
|
var bounds = new OpenLayers.Bounds(
|
minXY.lon, minXY.lat, maxXY.lon, maxXY.lat
|
);
|
|
// if multiple is false, first deselect currently selected features
|
if (!this.multipleSelect()) {
|
this.unselectAll();
|
}
|
|
// because we're using a box, we consider we want multiple selection
|
var prevMultiple = this.multiple;
|
this.multiple = true;
|
var layers = this.layers || [this.layer];
|
this.events.triggerEvent("boxselectionstart", {layers: layers});
|
var layer;
|
for(var l=0; l<layers.length; ++l) {
|
layer = layers[l];
|
for(var i=0, len = layer.features.length; i<len; ++i) {
|
var feature = layer.features[i];
|
// check if the feature is displayed
|
if (!feature.getVisibility()) {
|
continue;
|
}
|
|
if (this.geometryTypes == null || OpenLayers.Util.indexOf(
|
this.geometryTypes, feature.geometry.CLASS_NAME) > -1) {
|
if (bounds.toGeometry().intersects(feature.geometry)) {
|
if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) {
|
this.select(feature);
|
}
|
}
|
}
|
}
|
}
|
this.multiple = prevMultiple;
|
this.events.triggerEvent("boxselectionend", {layers: layers});
|
}
|
},
|
|
/**
|
* Method: setMap
|
* Set the map property for the control.
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
setMap: function(map) {
|
this.handlers.feature.setMap(map);
|
if (this.box) {
|
this.handlers.box.setMap(map);
|
}
|
OpenLayers.Control.prototype.setMap.apply(this, arguments);
|
},
|
|
/**
|
* APIMethod: setLayer
|
* Attach a new layer to the control, overriding any existing layers.
|
*
|
* Parameters:
|
* layers - Array of {<OpenLayers.Layer.Vector>} or a single
|
* {<OpenLayers.Layer.Vector>}
|
*/
|
setLayer: function(layers) {
|
var isActive = this.active;
|
this.unselectAll();
|
this.deactivate();
|
if(this.layers) {
|
this.layer.destroy();
|
this.layers = null;
|
}
|
this.initLayer(layers);
|
this.handlers.feature.layer = this.layer;
|
if (isActive) {
|
this.activate();
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Control.SelectFeature"
|
});
|
/* ======================================================================
|
OpenLayers/Handler/Point.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Handler.js
|
* @requires OpenLayers/Geometry/Point.js
|
*/
|
|
/**
|
* Class: OpenLayers.Handler.Point
|
* Handler to draw a point on the map. Point is displayed on activation,
|
* moves on mouse move, and is finished on mouse up. The handler triggers
|
* callbacks for 'done', 'cancel', and 'modify'. The modify callback is
|
* called with each change in the sketch and will receive the latest point
|
* drawn. Create a new instance with the <OpenLayers.Handler.Point>
|
* constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Handler>
|
*/
|
OpenLayers.Handler.Point = OpenLayers.Class(OpenLayers.Handler, {
|
|
/**
|
* Property: point
|
* {<OpenLayers.Feature.Vector>} The currently drawn point
|
*/
|
point: null,
|
|
/**
|
* Property: layer
|
* {<OpenLayers.Layer.Vector>} The temporary drawing layer
|
*/
|
layer: null,
|
|
/**
|
* APIProperty: multi
|
* {Boolean} Cast features to multi-part geometries before passing to the
|
* layer. Default is false.
|
*/
|
multi: false,
|
|
/**
|
* APIProperty: citeCompliant
|
* {Boolean} If set to true, coordinates of features drawn in a map extent
|
* crossing the date line won't exceed the world bounds. Default is false.
|
*/
|
citeCompliant: false,
|
|
/**
|
* Property: mouseDown
|
* {Boolean} The mouse is down
|
*/
|
mouseDown: false,
|
|
/**
|
* Property: stoppedDown
|
* {Boolean} Indicate whether the last mousedown stopped the event
|
* propagation.
|
*/
|
stoppedDown: null,
|
|
/**
|
* Property: lastDown
|
* {<OpenLayers.Pixel>} Location of the last mouse down
|
*/
|
lastDown: null,
|
|
/**
|
* Property: lastUp
|
* {<OpenLayers.Pixel>}
|
*/
|
lastUp: null,
|
|
/**
|
* APIProperty: persist
|
* {Boolean} Leave the feature rendered until destroyFeature is called.
|
* Default is false. If set to true, the feature remains rendered until
|
* destroyFeature is called, typically by deactivating the handler or
|
* starting another drawing.
|
*/
|
persist: false,
|
|
/**
|
* APIProperty: stopDown
|
* {Boolean} Stop event propagation on mousedown. Must be false to
|
* allow "pan while drawing". Defaults to false.
|
*/
|
stopDown: false,
|
|
/**
|
* APIPropery: stopUp
|
* {Boolean} Stop event propagation on mouse. Must be false to
|
* allow "pan while dragging". Defaults to fase.
|
*/
|
stopUp: false,
|
|
/**
|
* Property: layerOptions
|
* {Object} Any optional properties to be set on the sketch layer.
|
*/
|
layerOptions: null,
|
|
/**
|
* APIProperty: pixelTolerance
|
* {Number} Maximum number of pixels between down and up (mousedown
|
* and mouseup, or touchstart and touchend) for the handler to
|
* add a new point. If set to an integer value, if the
|
* displacement between down and up is great to this value
|
* no point will be added. Default value is 5.
|
*/
|
pixelTolerance: 5,
|
|
/**
|
* Property: lastTouchPx
|
* {<OpenLayers.Pixel>} The last pixel used to know the distance between
|
* two touches (for double touch).
|
*/
|
lastTouchPx: null,
|
|
/**
|
* Constructor: OpenLayers.Handler.Point
|
* Create a new point handler.
|
*
|
* Parameters:
|
* control - {<OpenLayers.Control>} The control that owns this handler
|
* callbacks - {Object} An object with a properties whose values are
|
* functions. Various callbacks described below.
|
* options - {Object} An optional object with properties to be set on the
|
* handler
|
*
|
* Named callbacks:
|
* create - Called when a sketch is first created. Callback called with
|
* the creation point geometry and sketch feature.
|
* modify - Called with each move of a vertex with the vertex (point)
|
* geometry and the sketch feature.
|
* done - Called when the point drawing is finished. The callback will
|
* recieve a single argument, the point geometry.
|
* cancel - Called when the handler is deactivated while drawing. The
|
* cancel callback will receive a geometry.
|
*/
|
initialize: function(control, callbacks, options) {
|
if(!(options && options.layerOptions && options.layerOptions.styleMap)) {
|
this.style = OpenLayers.Util.extend(OpenLayers.Feature.Vector.style['default'], {});
|
}
|
|
OpenLayers.Handler.prototype.initialize.apply(this, arguments);
|
},
|
|
/**
|
* APIMethod: activate
|
* turn on the handler
|
*/
|
activate: function() {
|
if(!OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
|
return false;
|
}
|
// create temporary vector layer for rendering geometry sketch
|
// TBD: this could be moved to initialize/destroy - setting visibility here
|
var options = OpenLayers.Util.extend({
|
displayInLayerSwitcher: false,
|
// indicate that the temp vector layer will never be out of range
|
// without this, resolution properties must be specified at the
|
// map-level for this temporary layer to init its resolutions
|
// correctly
|
calculateInRange: OpenLayers.Function.True,
|
wrapDateLine: this.citeCompliant
|
}, this.layerOptions);
|
this.layer = new OpenLayers.Layer.Vector(this.CLASS_NAME, options);
|
this.map.addLayer(this.layer);
|
return true;
|
},
|
|
/**
|
* Method: createFeature
|
* Add temporary features
|
*
|
* Parameters:
|
* pixel - {<OpenLayers.Pixel>} A pixel location on the map.
|
*/
|
createFeature: function(pixel) {
|
var lonlat = this.layer.getLonLatFromViewPortPx(pixel);
|
var geometry = new OpenLayers.Geometry.Point(
|
lonlat.lon, lonlat.lat
|
);
|
this.point = new OpenLayers.Feature.Vector(geometry);
|
this.callback("create", [this.point.geometry, this.point]);
|
this.point.geometry.clearBounds();
|
this.layer.addFeatures([this.point], {silent: true});
|
},
|
|
/**
|
* APIMethod: deactivate
|
* turn off the handler
|
*/
|
deactivate: function() {
|
if(!OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
|
return false;
|
}
|
this.cancel();
|
// If a layer's map property is set to null, it means that that layer
|
// isn't added to the map. Since we ourself added the layer to the map
|
// in activate(), we can assume that if this.layer.map is null it means
|
// that the layer has been destroyed (as a result of map.destroy() for
|
// example.
|
if (this.layer.map != null) {
|
this.destroyFeature(true);
|
this.layer.destroy(false);
|
}
|
this.layer = null;
|
return true;
|
},
|
|
/**
|
* Method: destroyFeature
|
* Destroy the temporary geometries
|
*
|
* Parameters:
|
* force - {Boolean} Destroy even if persist is true.
|
*/
|
destroyFeature: function(force) {
|
if(this.layer && (force || !this.persist)) {
|
this.layer.destroyFeatures();
|
}
|
this.point = null;
|
},
|
|
/**
|
* Method: destroyPersistedFeature
|
* Destroy the persisted feature.
|
*/
|
destroyPersistedFeature: function() {
|
var layer = this.layer;
|
if(layer && layer.features.length > 1) {
|
this.layer.features[0].destroy();
|
}
|
},
|
|
/**
|
* Method: finalize
|
* Finish the geometry and call the "done" callback.
|
*
|
* Parameters:
|
* cancel - {Boolean} Call cancel instead of done callback. Default
|
* is false.
|
*/
|
finalize: function(cancel) {
|
var key = cancel ? "cancel" : "done";
|
this.mouseDown = false;
|
this.lastDown = null;
|
this.lastUp = null;
|
this.lastTouchPx = null;
|
this.callback(key, [this.geometryClone()]);
|
this.destroyFeature(cancel);
|
},
|
|
/**
|
* APIMethod: cancel
|
* Finish the geometry and call the "cancel" callback.
|
*/
|
cancel: function() {
|
this.finalize(true);
|
},
|
|
/**
|
* Method: click
|
* Handle clicks. Clicks are stopped from propagating to other listeners
|
* on map.events or other dom elements.
|
*
|
* Parameters:
|
* evt - {Event} The browser event
|
*
|
* Returns:
|
* {Boolean} Allow event propagation
|
*/
|
click: function(evt) {
|
OpenLayers.Event.stop(evt);
|
return false;
|
},
|
|
/**
|
* Method: dblclick
|
* Handle double-clicks. Double-clicks are stopped from propagating to other
|
* listeners on map.events or other dom elements.
|
*
|
* Parameters:
|
* evt - {Event} The browser event
|
*
|
* Returns:
|
* {Boolean} Allow event propagation
|
*/
|
dblclick: function(evt) {
|
OpenLayers.Event.stop(evt);
|
return false;
|
},
|
|
/**
|
* Method: modifyFeature
|
* Modify the existing geometry given a pixel location.
|
*
|
* Parameters:
|
* pixel - {<OpenLayers.Pixel>} A pixel location on the map.
|
*/
|
modifyFeature: function(pixel) {
|
if(!this.point) {
|
this.createFeature(pixel);
|
}
|
var lonlat = this.layer.getLonLatFromViewPortPx(pixel);
|
this.point.geometry.x = lonlat.lon;
|
this.point.geometry.y = lonlat.lat;
|
this.callback("modify", [this.point.geometry, this.point, false]);
|
this.point.geometry.clearBounds();
|
this.drawFeature();
|
},
|
|
/**
|
* Method: drawFeature
|
* Render features on the temporary layer.
|
*/
|
drawFeature: function() {
|
this.layer.drawFeature(this.point, this.style);
|
},
|
|
/**
|
* Method: getGeometry
|
* Return the sketch geometry. If <multi> is true, this will return
|
* a multi-part geometry.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry.Point>}
|
*/
|
getGeometry: function() {
|
var geometry = this.point && this.point.geometry;
|
if(geometry && this.multi) {
|
geometry = new OpenLayers.Geometry.MultiPoint([geometry]);
|
}
|
return geometry;
|
},
|
|
/**
|
* Method: geometryClone
|
* Return a clone of the relevant geometry.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry>}
|
*/
|
geometryClone: function() {
|
var geom = this.getGeometry();
|
return geom && geom.clone();
|
},
|
|
/**
|
* Method: mousedown
|
* Handle mousedown.
|
*
|
* Parameters:
|
* evt - {Event} The browser event
|
*
|
* Returns:
|
* {Boolean} Allow event propagation
|
*/
|
mousedown: function(evt) {
|
return this.down(evt);
|
},
|
|
/**
|
* Method: touchstart
|
* Handle touchstart.
|
*
|
* Parameters:
|
* evt - {Event} The browser event
|
*
|
* Returns:
|
* {Boolean} Allow event propagation
|
*/
|
touchstart: function(evt) {
|
this.startTouch();
|
this.lastTouchPx = evt.xy;
|
return this.down(evt);
|
},
|
|
/**
|
* Method: mousemove
|
* Handle mousemove.
|
*
|
* Parameters:
|
* evt - {Event} The browser event
|
*
|
* Returns:
|
* {Boolean} Allow event propagation
|
*/
|
mousemove: function(evt) {
|
return this.move(evt);
|
},
|
|
/**
|
* Method: touchmove
|
* Handle touchmove.
|
*
|
* Parameters:
|
* evt - {Event} The browser event
|
*
|
* Returns:
|
* {Boolean} Allow event propagation
|
*/
|
touchmove: function(evt) {
|
this.lastTouchPx = evt.xy;
|
return this.move(evt);
|
},
|
|
/**
|
* Method: mouseup
|
* Handle mouseup.
|
*
|
* Parameters:
|
* evt - {Event} The browser event
|
*
|
* Returns:
|
* {Boolean} Allow event propagation
|
*/
|
mouseup: function(evt) {
|
return this.up(evt);
|
},
|
|
/**
|
* Method: touchend
|
* Handle touchend.
|
*
|
* Parameters:
|
* evt - {Event} The browser event
|
*
|
* Returns:
|
* {Boolean} Allow event propagation
|
*/
|
touchend: function(evt) {
|
evt.xy = this.lastTouchPx;
|
return this.up(evt);
|
},
|
|
/**
|
* Method: down
|
* Handle mousedown and touchstart. Adjust the geometry and redraw.
|
* Return determines whether to propagate the event on the map.
|
*
|
* Parameters:
|
* evt - {Event} The browser event
|
*
|
* Returns:
|
* {Boolean} Allow event propagation
|
*/
|
down: function(evt) {
|
this.mouseDown = true;
|
this.lastDown = evt.xy;
|
if(!this.touch) { // no point displayed until up on touch devices
|
this.modifyFeature(evt.xy);
|
}
|
this.stoppedDown = this.stopDown;
|
return !this.stopDown;
|
},
|
|
/**
|
* Method: move
|
* Handle mousemove and touchmove. Adjust the geometry and redraw.
|
* Return determines whether to propagate the event on the map.
|
*
|
* Parameters:
|
* evt - {Event} The browser event
|
*
|
* Returns:
|
* {Boolean} Allow event propagation
|
*/
|
move: function (evt) {
|
if(!this.touch // no point displayed until up on touch devices
|
&& (!this.mouseDown || this.stoppedDown)) {
|
this.modifyFeature(evt.xy);
|
}
|
return true;
|
},
|
|
/**
|
* Method: up
|
* Handle mouseup and touchend. Send the latest point in the geometry to the control.
|
* Return determines whether to propagate the event on the map.
|
*
|
* Parameters:
|
* evt - {Event} The browser event
|
*
|
* Returns:
|
* {Boolean} Allow event propagation
|
*/
|
up: function (evt) {
|
this.mouseDown = false;
|
this.stoppedDown = this.stopDown;
|
|
// check keyboard modifiers
|
if(!this.checkModifiers(evt)) {
|
return true;
|
}
|
// ignore double-clicks
|
if (this.lastUp && this.lastUp.equals(evt.xy)) {
|
return true;
|
}
|
if (this.lastDown && this.passesTolerance(this.lastDown, evt.xy,
|
this.pixelTolerance)) {
|
if (this.touch) {
|
this.modifyFeature(evt.xy);
|
}
|
if(this.persist) {
|
this.destroyPersistedFeature();
|
}
|
this.lastUp = evt.xy;
|
this.finalize();
|
return !this.stopUp;
|
} else {
|
return true;
|
}
|
},
|
|
/**
|
* Method: mouseout
|
* Handle mouse out. For better user experience reset mouseDown
|
* and stoppedDown when the mouse leaves the map viewport.
|
*
|
* Parameters:
|
* evt - {Event} The browser event
|
*/
|
mouseout: function(evt) {
|
if(OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {
|
this.stoppedDown = this.stopDown;
|
this.mouseDown = false;
|
}
|
},
|
|
/**
|
* Method: passesTolerance
|
* Determine whether the event is within the optional pixel tolerance.
|
*
|
* Returns:
|
* {Boolean} The event is within the pixel tolerance (if specified).
|
*/
|
passesTolerance: function(pixel1, pixel2, tolerance) {
|
var passes = true;
|
|
if (tolerance != null && pixel1 && pixel2) {
|
var dist = pixel1.distanceTo(pixel2);
|
if (dist > tolerance) {
|
passes = false;
|
}
|
}
|
return passes;
|
},
|
|
CLASS_NAME: "OpenLayers.Handler.Point"
|
});
|
/* ======================================================================
|
OpenLayers/Handler/Path.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Handler/Point.js
|
* @requires OpenLayers/Geometry/Point.js
|
* @requires OpenLayers/Geometry/LineString.js
|
*/
|
|
/**
|
* Class: OpenLayers.Handler.Path
|
* Handler to draw a path on the map. Path is displayed on mouse down,
|
* moves on mouse move, and is finished on mouse up.
|
*
|
* Inherits from:
|
* - <OpenLayers.Handler.Point>
|
*/
|
OpenLayers.Handler.Path = OpenLayers.Class(OpenLayers.Handler.Point, {
|
|
/**
|
* Property: line
|
* {<OpenLayers.Feature.Vector>}
|
*/
|
line: null,
|
|
/**
|
* APIProperty: maxVertices
|
* {Number} The maximum number of vertices which can be drawn by this
|
* handler. When the number of vertices reaches maxVertices, the
|
* geometry is automatically finalized. Default is null.
|
*/
|
maxVertices: null,
|
|
/**
|
* Property: doubleTouchTolerance
|
* {Number} Maximum number of pixels between two touches for
|
* the gesture to be considered a "finalize feature" action.
|
* Default is 20.
|
*/
|
doubleTouchTolerance: 20,
|
|
/**
|
* Property: freehand
|
* {Boolean} In freehand mode, the handler starts the path on mouse down,
|
* adds a point for every mouse move, and finishes the path on mouse up.
|
* Outside of freehand mode, a point is added to the path on every mouse
|
* click and double-click finishes the path.
|
*/
|
freehand: false,
|
|
/**
|
* Property: freehandToggle
|
* {String} If set, freehandToggle is checked on mouse events and will set
|
* the freehand mode to the opposite of this.freehand. To disallow
|
* toggling between freehand and non-freehand mode, set freehandToggle to
|
* null. Acceptable toggle values are 'shiftKey', 'ctrlKey', and 'altKey'.
|
*/
|
freehandToggle: 'shiftKey',
|
|
/**
|
* Property: timerId
|
* {Integer} The timer used to test the double touch.
|
*/
|
timerId: null,
|
|
/**
|
* Property: redoStack
|
* {Array} Stack containing points removed with <undo>.
|
*/
|
redoStack: null,
|
|
/**
|
* Constructor: OpenLayers.Handler.Path
|
* Create a new path hander
|
*
|
* Parameters:
|
* control - {<OpenLayers.Control>} The control that owns this handler
|
* callbacks - {Object} An object with a properties whose values are
|
* functions. Various callbacks described below.
|
* options - {Object} An optional object with properties to be set on the
|
* handler
|
*
|
* Named callbacks:
|
* create - Called when a sketch is first created. Callback called with
|
* the creation point geometry and sketch feature.
|
* modify - Called with each move of a vertex with the vertex (point)
|
* geometry and the sketch feature.
|
* point - Called as each point is added. Receives the new point geometry.
|
* done - Called when the point drawing is finished. The callback will
|
* recieve a single argument, the linestring geometry.
|
* cancel - Called when the handler is deactivated while drawing. The
|
* cancel callback will receive a geometry.
|
*/
|
|
/**
|
* Method: createFeature
|
* Add temporary geometries
|
*
|
* Parameters:
|
* pixel - {<OpenLayers.Pixel>} The initial pixel location for the new
|
* feature.
|
*/
|
createFeature: function(pixel) {
|
var lonlat = this.layer.getLonLatFromViewPortPx(pixel);
|
var geometry = new OpenLayers.Geometry.Point(
|
lonlat.lon, lonlat.lat
|
);
|
this.point = new OpenLayers.Feature.Vector(geometry);
|
this.line = new OpenLayers.Feature.Vector(
|
new OpenLayers.Geometry.LineString([this.point.geometry])
|
);
|
this.callback("create", [this.point.geometry, this.getSketch()]);
|
this.point.geometry.clearBounds();
|
this.layer.addFeatures([this.line, this.point], {silent: true});
|
},
|
|
/**
|
* Method: destroyFeature
|
* Destroy temporary geometries
|
*
|
* Parameters:
|
* force - {Boolean} Destroy even if persist is true.
|
*/
|
destroyFeature: function(force) {
|
OpenLayers.Handler.Point.prototype.destroyFeature.call(
|
this, force);
|
this.line = null;
|
},
|
|
/**
|
* Method: destroyPersistedFeature
|
* Destroy the persisted feature.
|
*/
|
destroyPersistedFeature: function() {
|
var layer = this.layer;
|
if(layer && layer.features.length > 2) {
|
this.layer.features[0].destroy();
|
}
|
},
|
|
/**
|
* Method: removePoint
|
* Destroy the temporary point.
|
*/
|
removePoint: function() {
|
if(this.point) {
|
this.layer.removeFeatures([this.point]);
|
}
|
},
|
|
/**
|
* Method: addPoint
|
* Add point to geometry. Send the point index to override
|
* the behavior of LinearRing that disregards adding duplicate points.
|
*
|
* Parameters:
|
* pixel - {<OpenLayers.Pixel>} The pixel location for the new point.
|
*/
|
addPoint: function(pixel) {
|
this.layer.removeFeatures([this.point]);
|
var lonlat = this.layer.getLonLatFromViewPortPx(pixel);
|
this.point = new OpenLayers.Feature.Vector(
|
new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat)
|
);
|
this.line.geometry.addComponent(
|
this.point.geometry, this.line.geometry.components.length
|
);
|
this.layer.addFeatures([this.point]);
|
this.callback("point", [this.point.geometry, this.getGeometry()]);
|
this.callback("modify", [this.point.geometry, this.getSketch()]);
|
this.drawFeature();
|
delete this.redoStack;
|
},
|
|
/**
|
* Method: insertXY
|
* Insert a point in the current sketch given x & y coordinates. The new
|
* point is inserted immediately before the most recently drawn point.
|
*
|
* Parameters:
|
* x - {Number} The x-coordinate of the point.
|
* y - {Number} The y-coordinate of the point.
|
*/
|
insertXY: function(x, y) {
|
this.line.geometry.addComponent(
|
new OpenLayers.Geometry.Point(x, y),
|
this.getCurrentPointIndex()
|
);
|
this.drawFeature();
|
delete this.redoStack;
|
},
|
|
/**
|
* Method: insertDeltaXY
|
* Insert a point given offsets from the previously inserted point.
|
*
|
* Parameters:
|
* dx - {Number} The x-coordinate offset of the point.
|
* dy - {Number} The y-coordinate offset of the point.
|
*/
|
insertDeltaXY: function(dx, dy) {
|
var previousIndex = this.getCurrentPointIndex() - 1;
|
var p0 = this.line.geometry.components[previousIndex];
|
if (p0 && !isNaN(p0.x) && !isNaN(p0.y)) {
|
this.insertXY(p0.x + dx, p0.y + dy);
|
}
|
},
|
|
/**
|
* Method: insertDirectionLength
|
* Insert a point in the current sketch given a direction and a length.
|
*
|
* Parameters:
|
* direction - {Number} Degrees clockwise from the positive x-axis.
|
* length - {Number} Distance from the previously drawn point.
|
*/
|
insertDirectionLength: function(direction, length) {
|
direction *= Math.PI / 180;
|
var dx = length * Math.cos(direction);
|
var dy = length * Math.sin(direction);
|
this.insertDeltaXY(dx, dy);
|
},
|
|
/**
|
* Method: insertDeflectionLength
|
* Insert a point in the current sketch given a deflection and a length.
|
* The deflection should be degrees clockwise from the previously
|
* digitized segment.
|
*
|
* Parameters:
|
* deflection - {Number} Degrees clockwise from the previous segment.
|
* length - {Number} Distance from the previously drawn point.
|
*/
|
insertDeflectionLength: function(deflection, length) {
|
var previousIndex = this.getCurrentPointIndex() - 1;
|
if (previousIndex > 0) {
|
var p1 = this.line.geometry.components[previousIndex];
|
var p0 = this.line.geometry.components[previousIndex-1];
|
var theta = Math.atan2(p1.y - p0.y, p1.x - p0.x);
|
this.insertDirectionLength(
|
(theta * 180 / Math.PI) + deflection, length
|
);
|
}
|
},
|
|
/**
|
* Method: getCurrentPointIndex
|
*
|
* Returns:
|
* {Number} The index of the most recently drawn point.
|
*/
|
getCurrentPointIndex: function() {
|
return this.line.geometry.components.length - 1;
|
},
|
|
|
/**
|
* Method: undo
|
* Remove the most recently added point in the sketch geometry.
|
*
|
* Returns:
|
* {Boolean} A point was removed.
|
*/
|
undo: function() {
|
var geometry = this.line.geometry;
|
var components = geometry.components;
|
var index = this.getCurrentPointIndex() - 1;
|
var target = components[index];
|
var undone = geometry.removeComponent(target);
|
if (undone) {
|
// On touch devices, set the current ("mouse location") point to
|
// match the last digitized point.
|
if (this.touch && index > 0) {
|
components = geometry.components; // safety
|
var lastpt = components[index - 1];
|
var curptidx = this.getCurrentPointIndex();
|
var curpt = components[curptidx];
|
curpt.x = lastpt.x;
|
curpt.y = lastpt.y;
|
}
|
if (!this.redoStack) {
|
this.redoStack = [];
|
}
|
this.redoStack.push(target);
|
this.drawFeature();
|
}
|
return undone;
|
},
|
|
/**
|
* Method: redo
|
* Reinsert the most recently removed point resulting from an <undo> call.
|
* The undo stack is deleted whenever a point is added by other means.
|
*
|
* Returns:
|
* {Boolean} A point was added.
|
*/
|
redo: function() {
|
var target = this.redoStack && this.redoStack.pop();
|
if (target) {
|
this.line.geometry.addComponent(target, this.getCurrentPointIndex());
|
this.drawFeature();
|
}
|
return !!target;
|
},
|
|
/**
|
* Method: freehandMode
|
* Determine whether to behave in freehand mode or not.
|
*
|
* Returns:
|
* {Boolean}
|
*/
|
freehandMode: function(evt) {
|
return (this.freehandToggle && evt[this.freehandToggle]) ?
|
!this.freehand : this.freehand;
|
},
|
|
/**
|
* Method: modifyFeature
|
* Modify the existing geometry given the new point
|
*
|
* Parameters:
|
* pixel - {<OpenLayers.Pixel>} The updated pixel location for the latest
|
* point.
|
* drawing - {Boolean} Indicate if we're currently drawing.
|
*/
|
modifyFeature: function(pixel, drawing) {
|
if(!this.line) {
|
this.createFeature(pixel);
|
}
|
var lonlat = this.layer.getLonLatFromViewPortPx(pixel);
|
this.point.geometry.x = lonlat.lon;
|
this.point.geometry.y = lonlat.lat;
|
this.callback("modify", [this.point.geometry, this.getSketch(), drawing]);
|
this.point.geometry.clearBounds();
|
this.drawFeature();
|
},
|
|
/**
|
* Method: drawFeature
|
* Render geometries on the temporary layer.
|
*/
|
drawFeature: function() {
|
this.layer.drawFeature(this.line, this.style);
|
this.layer.drawFeature(this.point, this.style);
|
},
|
|
/**
|
* Method: getSketch
|
* Return the sketch feature.
|
*
|
* Returns:
|
* {<OpenLayers.Feature.Vector>}
|
*/
|
getSketch: function() {
|
return this.line;
|
},
|
|
/**
|
* Method: getGeometry
|
* Return the sketch geometry. If <multi> is true, this will return
|
* a multi-part geometry.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry.LineString>}
|
*/
|
getGeometry: function() {
|
var geometry = this.line && this.line.geometry;
|
if(geometry && this.multi) {
|
geometry = new OpenLayers.Geometry.MultiLineString([geometry]);
|
}
|
return geometry;
|
},
|
|
/**
|
* method: touchstart
|
* handle touchstart.
|
*
|
* parameters:
|
* evt - {event} the browser event
|
*
|
* returns:
|
* {boolean} allow event propagation
|
*/
|
touchstart: function(evt) {
|
if (this.timerId &&
|
this.passesTolerance(this.lastTouchPx, evt.xy,
|
this.doubleTouchTolerance)) {
|
// double-tap, finalize the geometry
|
this.finishGeometry();
|
window.clearTimeout(this.timerId);
|
this.timerId = null;
|
return false;
|
} else {
|
if (this.timerId) {
|
window.clearTimeout(this.timerId);
|
this.timerId = null;
|
}
|
this.timerId = window.setTimeout(
|
OpenLayers.Function.bind(function() {
|
this.timerId = null;
|
}, this), 300);
|
return OpenLayers.Handler.Point.prototype.touchstart.call(this, evt);
|
}
|
},
|
|
/**
|
* Method: down
|
* Handle mousedown and touchstart. Add a new point to the geometry and
|
* render it. Return determines whether to propagate the event on the map.
|
*
|
* Parameters:
|
* evt - {Event} The browser event
|
*
|
* Returns:
|
* {Boolean} Allow event propagation
|
*/
|
down: function(evt) {
|
var stopDown = this.stopDown;
|
if(this.freehandMode(evt)) {
|
stopDown = true;
|
if (this.touch) {
|
this.modifyFeature(evt.xy, !!this.lastUp);
|
OpenLayers.Event.stop(evt);
|
}
|
}
|
if (!this.touch && (!this.lastDown ||
|
!this.passesTolerance(this.lastDown, evt.xy,
|
this.pixelTolerance))) {
|
this.modifyFeature(evt.xy, !!this.lastUp);
|
}
|
this.mouseDown = true;
|
this.lastDown = evt.xy;
|
this.stoppedDown = stopDown;
|
return !stopDown;
|
},
|
|
/**
|
* Method: move
|
* Handle mousemove and touchmove. Adjust the geometry and redraw.
|
* Return determines whether to propagate the event on the map.
|
*
|
* Parameters:
|
* evt - {Event} The browser event
|
*
|
* Returns:
|
* {Boolean} Allow event propagation
|
*/
|
move: function (evt) {
|
if(this.stoppedDown && this.freehandMode(evt)) {
|
if(this.persist) {
|
this.destroyPersistedFeature();
|
}
|
if(this.maxVertices && this.line &&
|
this.line.geometry.components.length === this.maxVertices) {
|
this.removePoint();
|
this.finalize();
|
} else {
|
this.addPoint(evt.xy);
|
}
|
return false;
|
}
|
if (!this.touch && (!this.mouseDown || this.stoppedDown)) {
|
this.modifyFeature(evt.xy, !!this.lastUp);
|
}
|
return true;
|
},
|
|
/**
|
* Method: up
|
* Handle mouseup and touchend. Send the latest point in the geometry to
|
* the control. Return determines whether to propagate the event on the map.
|
*
|
* Parameters:
|
* evt - {Event} The browser event
|
*
|
* Returns:
|
* {Boolean} Allow event propagation
|
*/
|
up: function (evt) {
|
if (this.mouseDown && (!this.lastUp || !this.lastUp.equals(evt.xy))) {
|
if(this.stoppedDown && this.freehandMode(evt)) {
|
if (this.persist) {
|
this.destroyPersistedFeature();
|
}
|
this.removePoint();
|
this.finalize();
|
} else {
|
if (this.passesTolerance(this.lastDown, evt.xy,
|
this.pixelTolerance)) {
|
if (this.touch) {
|
this.modifyFeature(evt.xy);
|
}
|
if(this.lastUp == null && this.persist) {
|
this.destroyPersistedFeature();
|
}
|
this.addPoint(evt.xy);
|
this.lastUp = evt.xy;
|
if(this.line.geometry.components.length === this.maxVertices + 1) {
|
this.finishGeometry();
|
}
|
}
|
}
|
}
|
this.stoppedDown = this.stopDown;
|
this.mouseDown = false;
|
return !this.stopUp;
|
},
|
|
/**
|
* APIMethod: finishGeometry
|
* Finish the geometry and send it back to the control.
|
*/
|
finishGeometry: function() {
|
var index = this.line.geometry.components.length - 1;
|
this.line.geometry.removeComponent(this.line.geometry.components[index]);
|
this.removePoint();
|
this.finalize();
|
},
|
|
/**
|
* Method: dblclick
|
* Handle double-clicks.
|
*
|
* Parameters:
|
* evt - {Event} The browser event
|
*
|
* Returns:
|
* {Boolean} Allow event propagation
|
*/
|
dblclick: function(evt) {
|
if(!this.freehandMode(evt)) {
|
this.finishGeometry();
|
}
|
return false;
|
},
|
|
CLASS_NAME: "OpenLayers.Handler.Path"
|
});
|
/* ======================================================================
|
OpenLayers/Spherical.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/SingleFile.js
|
*/
|
|
/**
|
* Namespace: Spherical
|
* The OpenLayers.Spherical namespace includes utility functions for
|
* calculations on the basis of a spherical earth (ignoring ellipsoidal
|
* effects), which is accurate enough for most purposes.
|
*
|
* Relevant links:
|
* * http://www.movable-type.co.uk/scripts/latlong.html
|
* * http://code.google.com/apis/maps/documentation/javascript/reference.html#spherical
|
*/
|
|
OpenLayers.Spherical = OpenLayers.Spherical || {};
|
|
OpenLayers.Spherical.DEFAULT_RADIUS = 6378137;
|
|
/**
|
* APIFunction: computeDistanceBetween
|
* Computes the distance between two LonLats.
|
*
|
* Parameters:
|
* from - {<OpenLayers.LonLat>} or {Object} Starting point. A LonLat or
|
* a JavaScript literal with lon lat properties.
|
* to - {<OpenLayers.LonLat>} or {Object} Ending point. A LonLat or a
|
* JavaScript literal with lon lat properties.
|
* radius - {Float} The radius. Optional. Defaults to 6378137 meters.
|
*
|
* Returns:
|
* {Float} The distance in meters.
|
*/
|
OpenLayers.Spherical.computeDistanceBetween = function(from, to, radius) {
|
var R = radius || OpenLayers.Spherical.DEFAULT_RADIUS;
|
var sinHalfDeltaLon = Math.sin(Math.PI * (to.lon - from.lon) / 360);
|
var sinHalfDeltaLat = Math.sin(Math.PI * (to.lat - from.lat) / 360);
|
var a = sinHalfDeltaLat * sinHalfDeltaLat +
|
sinHalfDeltaLon * sinHalfDeltaLon * Math.cos(Math.PI * from.lat / 180) * Math.cos(Math.PI * to.lat / 180);
|
return 2 * R * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
};
|
|
|
/**
|
* APIFunction: computeHeading
|
* Computes the heading from one LonLat to another LonLat.
|
*
|
* Parameters:
|
* from - {<OpenLayers.LonLat>} or {Object} Starting point. A LonLat or
|
* a JavaScript literal with lon lat properties.
|
* to - {<OpenLayers.LonLat>} or {Object} Ending point. A LonLat or a
|
* JavaScript literal with lon lat properties.
|
*
|
* Returns:
|
* {Float} The heading in degrees.
|
*/
|
OpenLayers.Spherical.computeHeading = function(from, to) {
|
var y = Math.sin(Math.PI * (from.lon - to.lon) / 180) * Math.cos(Math.PI * to.lat / 180);
|
var x = Math.cos(Math.PI * from.lat / 180) * Math.sin(Math.PI * to.lat / 180) -
|
Math.sin(Math.PI * from.lat / 180) * Math.cos(Math.PI * to.lat / 180) * Math.cos(Math.PI * (from.lon - to.lon) / 180);
|
return 180 * Math.atan2(y, x) / Math.PI;
|
};
|
/* ======================================================================
|
OpenLayers/Control/CacheWrite.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/Request.js
|
* @requires OpenLayers/Console.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.CacheWrite
|
* A control for caching image tiles in the browser's local storage. The
|
* <OpenLayers.Control.CacheRead> control is used to fetch and use the cached
|
* tile images.
|
*
|
* Note: Before using this control on any layer that is not your own, make sure
|
* that the terms of service of the tile provider allow local storage of tiles.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.CacheWrite = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* APIProperty: events
|
* {<OpenLayers.Events>} Events instance for listeners and triggering
|
* control specific events.
|
*
|
* To register events in the constructor, configure <eventListeners>.
|
*
|
* Register a listener for a particular event with the following syntax:
|
* (code)
|
* control.events.register(type, obj, listener);
|
* (end)
|
*
|
* Supported event types (in addition to those from <OpenLayers.Control.events>):
|
* cachefull - Triggered when the cache is full. Listeners receive an
|
* object with a tile property as first argument. The tile references
|
* the tile that couldn't be cached.
|
*/
|
|
/**
|
* APIProperty: eventListeners
|
* {Object} Object with event listeners, keyed by event name. An optional
|
* scope property defines the scope that listeners will be executed in.
|
*/
|
|
/**
|
* APIProperty: layers
|
* {Array(<OpenLayers.Layer.Grid>)}. Optional. If provided, caching
|
* will be enabled for these layers only, otherwise for all cacheable
|
* layers.
|
*/
|
layers: null,
|
|
/**
|
* APIProperty: imageFormat
|
* {String} The image format used for caching. The default is "image/png".
|
* Supported formats depend on the user agent. If an unsupported
|
* <imageFormat> is provided, "image/png" will be used. For aerial
|
* imagery, "image/jpeg" is recommended.
|
*/
|
imageFormat: "image/png",
|
|
/**
|
* Property: quotaRegEx
|
* {RegExp}
|
*/
|
quotaRegEx: (/quota/i),
|
|
/**
|
* Constructor: OpenLayers.Control.CacheWrite
|
*
|
* Parameters:
|
* options - {Object} Object with API properties for this control.
|
*/
|
|
/**
|
* Method: setMap
|
* Set the map property for the control.
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
setMap: function(map) {
|
OpenLayers.Control.prototype.setMap.apply(this, arguments);
|
var i, layers = this.layers || map.layers;
|
for (i=layers.length-1; i>=0; --i) {
|
this.addLayer({layer: layers[i]});
|
}
|
if (!this.layers) {
|
map.events.on({
|
addlayer: this.addLayer,
|
removeLayer: this.removeLayer,
|
scope: this
|
});
|
}
|
},
|
|
/**
|
* Method: addLayer
|
* Adds a layer to the control. Once added, tiles requested for this layer
|
* will be cached.
|
*
|
* Parameters:
|
* evt - {Object} Object with a layer property referencing an
|
* <OpenLayers.Layer> instance
|
*/
|
addLayer: function(evt) {
|
evt.layer.events.on({
|
tileloadstart: this.makeSameOrigin,
|
tileloaded: this.onTileLoaded,
|
scope: this
|
});
|
},
|
|
/**
|
* Method: removeLayer
|
* Removes a layer from the control. Once removed, tiles requested for this
|
* layer will no longer be cached.
|
*
|
* Parameters:
|
* evt - {Object} Object with a layer property referencing an
|
* <OpenLayers.Layer> instance
|
*/
|
removeLayer: function(evt) {
|
evt.layer.events.un({
|
tileloadstart: this.makeSameOrigin,
|
tileloaded: this.onTileLoaded,
|
scope: this
|
});
|
},
|
|
/**
|
* Method: makeSameOrigin
|
* If the tile does not have CORS image loading enabled and is from a
|
* different origin, use OpenLayers.ProxyHost to make it a same origin url.
|
*
|
* Parameters:
|
* evt - {<OpenLayers.Event>}
|
*/
|
makeSameOrigin: function(evt) {
|
if (this.active) {
|
var tile = evt.tile;
|
if (tile instanceof OpenLayers.Tile.Image &&
|
!tile.crossOriginKeyword &&
|
tile.url.substr(0, 5) !== "data:") {
|
var sameOriginUrl = OpenLayers.Request.makeSameOrigin(
|
tile.url, OpenLayers.ProxyHost
|
);
|
OpenLayers.Control.CacheWrite.urlMap[sameOriginUrl] = tile.url;
|
tile.url = sameOriginUrl;
|
}
|
}
|
},
|
|
/**
|
* Method: onTileLoaded
|
* Decides whether a tile can be cached and calls the cache method.
|
*
|
* Parameters:
|
* evt - {Event}
|
*/
|
onTileLoaded: function(evt) {
|
if (this.active && !evt.aborted &&
|
evt.tile instanceof OpenLayers.Tile.Image &&
|
evt.tile.url.substr(0, 5) !== 'data:') {
|
this.cache({tile: evt.tile});
|
delete OpenLayers.Control.CacheWrite.urlMap[evt.tile.url];
|
}
|
},
|
|
/**
|
* Method: cache
|
* Adds a tile to the cache. When the cache is full, the "cachefull" event
|
* is triggered.
|
*
|
* Parameters:
|
* obj - {Object} Object with a tile property, tile being the
|
* <OpenLayers.Tile.Image> with the data to add to the cache
|
*/
|
cache: function(obj) {
|
if (window.localStorage) {
|
var tile = obj.tile;
|
try {
|
var canvasContext = tile.getCanvasContext();
|
if (canvasContext) {
|
var urlMap = OpenLayers.Control.CacheWrite.urlMap;
|
var url = urlMap[tile.url] || tile.url;
|
window.localStorage.setItem(
|
"olCache_" + url,
|
canvasContext.canvas.toDataURL(this.imageFormat)
|
);
|
}
|
} catch(e) {
|
// local storage full or CORS violation
|
var reason = e.name || e.message;
|
if (reason && this.quotaRegEx.test(reason)) {
|
this.events.triggerEvent("cachefull", {tile: tile});
|
} else {
|
OpenLayers.Console.error(e.toString());
|
}
|
}
|
}
|
},
|
|
/**
|
* Method: destroy
|
* The destroy method is used to perform any clean up before the control
|
* is dereferenced. Typically this is where event listeners are removed
|
* to prevent memory leaks.
|
*/
|
destroy: function() {
|
if (this.layers || this.map) {
|
var i, layers = this.layers || this.map.layers;
|
for (i=layers.length-1; i>=0; --i) {
|
this.removeLayer({layer: layers[i]});
|
}
|
}
|
if (this.map) {
|
this.map.events.un({
|
addlayer: this.addLayer,
|
removeLayer: this.removeLayer,
|
scope: this
|
});
|
}
|
OpenLayers.Control.prototype.destroy.apply(this, arguments);
|
},
|
|
CLASS_NAME: "OpenLayers.Control.CacheWrite"
|
});
|
|
/**
|
* APIFunction: OpenLayers.Control.CacheWrite.clearCache
|
* Clears all tiles cached with <OpenLayers.Control.CacheWrite> from the cache.
|
*/
|
OpenLayers.Control.CacheWrite.clearCache = function() {
|
if (!window.localStorage) { return; }
|
var i, key;
|
for (i=window.localStorage.length-1; i>=0; --i) {
|
key = window.localStorage.key(i);
|
if (key.substr(0, 8) === "olCache_") {
|
window.localStorage.removeItem(key);
|
}
|
}
|
};
|
|
/**
|
* Property: OpenLayers.Control.CacheWrite.urlMap
|
* {Object} Mapping of same origin urls to cache url keys. Entries will be
|
* deleted as soon as a tile was cached.
|
*/
|
OpenLayers.Control.CacheWrite.urlMap = {};
|
|
|
/* ======================================================================
|
OpenLayers/Format/Context.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML/VersionedOGC.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.Context
|
* Base class for both Format.WMC and Format.OWSContext
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML.VersionedOGC>
|
*/
|
OpenLayers.Format.Context = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
|
|
/**
|
* Property: layerOptions
|
* {Object} Default options for layers created by the parser. These
|
* options are overridden by the options which are read from the
|
* capabilities document.
|
*/
|
layerOptions: null,
|
|
/**
|
* Property: layerParams
|
* {Object} Default parameters for layers created by the parser. This
|
* can be used e.g. to override DEFAULT_PARAMS for
|
* OpenLayers.Layer.WMS.
|
*/
|
layerParams: null,
|
|
/**
|
* Constructor: OpenLayers.Format.Context
|
* Create a new parser for Context documents.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* APIMethod: read
|
* Read Context data from a string, and return an object with map
|
* properties and a list of layers.
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
* options - {Object} The options object must contain a map property. If
|
* the map property is a string, it must be the id of a dom element
|
* where the new map will be placed. If the map property is an
|
* <OpenLayers.Map>, the layers from the context document will be added
|
* to the map.
|
*
|
* Returns:
|
* {<OpenLayers.Map>} A map based on the context.
|
*/
|
read: function(data, options) {
|
var context = OpenLayers.Format.XML.VersionedOGC.prototype.read.apply(this,
|
arguments);
|
var map;
|
if(options && options.map) {
|
this.context = context;
|
if(options.map instanceof OpenLayers.Map) {
|
map = this.mergeContextToMap(context, options.map);
|
} else {
|
var mapOptions = options.map;
|
if(OpenLayers.Util.isElement(mapOptions) ||
|
typeof mapOptions == "string") {
|
// we assume mapOptions references a div
|
// element
|
mapOptions = {div: mapOptions};
|
}
|
map = this.contextToMap(context, mapOptions);
|
}
|
} else {
|
// not documented as part of the API, provided as a non-API option
|
map = context;
|
}
|
return map;
|
},
|
|
/**
|
* Method: getLayerFromContext
|
* Create a WMS layer from a layerContext object.
|
*
|
* Parameters:
|
* layerContext - {Object} An object representing a WMS layer.
|
*
|
* Returns:
|
* {<OpenLayers.Layer.WMS>} A WMS layer.
|
*/
|
getLayerFromContext: function(layerContext) {
|
var i, len;
|
// fill initial options object from layerContext
|
var options = {
|
queryable: layerContext.queryable, //keep queryable for api compatibility
|
visibility: layerContext.visibility,
|
maxExtent: layerContext.maxExtent,
|
metadata: OpenLayers.Util.applyDefaults(layerContext.metadata,
|
{styles: layerContext.styles,
|
formats: layerContext.formats,
|
"abstract": layerContext["abstract"],
|
dataURL: layerContext.dataURL
|
}),
|
numZoomLevels: layerContext.numZoomLevels,
|
units: layerContext.units,
|
isBaseLayer: layerContext.isBaseLayer,
|
opacity: layerContext.opacity,
|
displayInLayerSwitcher: layerContext.displayInLayerSwitcher,
|
singleTile: layerContext.singleTile,
|
tileSize: (layerContext.tileSize) ?
|
new OpenLayers.Size(
|
layerContext.tileSize.width,
|
layerContext.tileSize.height
|
) : undefined,
|
minScale: layerContext.minScale || layerContext.maxScaleDenominator,
|
maxScale: layerContext.maxScale || layerContext.minScaleDenominator,
|
srs: layerContext.srs,
|
dimensions: layerContext.dimensions,
|
metadataURL: layerContext.metadataURL
|
};
|
if (this.layerOptions) {
|
OpenLayers.Util.applyDefaults(options, this.layerOptions);
|
}
|
|
var params = {
|
layers: layerContext.name,
|
transparent: layerContext.transparent,
|
version: layerContext.version
|
};
|
if (layerContext.formats && layerContext.formats.length>0) {
|
// set default value for params if current attribute is not positionned
|
params.format = layerContext.formats[0].value;
|
for (i=0, len=layerContext.formats.length; i<len; i++) {
|
var format = layerContext.formats[i];
|
if (format.current == true) {
|
params.format = format.value;
|
break;
|
}
|
}
|
}
|
if (layerContext.styles && layerContext.styles.length>0) {
|
for (i=0, len=layerContext.styles.length; i<len; i++) {
|
var style = layerContext.styles[i];
|
if (style.current == true) {
|
// three style types to consider
|
// 1) linked SLD
|
// 2) inline SLD
|
// 3) named style
|
if(style.href) {
|
params.sld = style.href;
|
} else if(style.body) {
|
params.sld_body = style.body;
|
} else {
|
params.styles = style.name;
|
}
|
break;
|
}
|
}
|
}
|
if (this.layerParams) {
|
OpenLayers.Util.applyDefaults(params, this.layerParams);
|
}
|
|
var layer = null;
|
var service = layerContext.service;
|
if (service == OpenLayers.Format.Context.serviceTypes.WFS) {
|
options.strategies = [new OpenLayers.Strategy.BBOX()];
|
options.protocol = new OpenLayers.Protocol.WFS({
|
url: layerContext.url,
|
// since we do not know featureNS, let the protocol
|
// determine it automagically using featurePrefix
|
featurePrefix: layerContext.name.split(":")[0],
|
featureType: layerContext.name.split(":").pop()
|
});
|
layer = new OpenLayers.Layer.Vector(
|
layerContext.title || layerContext.name,
|
options
|
);
|
} else if (service == OpenLayers.Format.Context.serviceTypes.KML) {
|
// use a vector layer with an HTTP Protcol and a Fixed strategy
|
options.strategies = [new OpenLayers.Strategy.Fixed()];
|
options.protocol = new OpenLayers.Protocol.HTTP({
|
url: layerContext.url,
|
format: new OpenLayers.Format.KML()
|
});
|
layer = new OpenLayers.Layer.Vector(
|
layerContext.title || layerContext.name,
|
options
|
);
|
} else if (service == OpenLayers.Format.Context.serviceTypes.GML) {
|
// use a vector layer with a HTTP Protocol and a Fixed strategy
|
options.strategies = [new OpenLayers.Strategy.Fixed()];
|
options.protocol = new OpenLayers.Protocol.HTTP({
|
url: layerContext.url,
|
format: new OpenLayers.Format.GML()
|
});
|
layer = new OpenLayers.Layer.Vector(
|
layerContext.title || layerContext.name,
|
options
|
);
|
} else if (layerContext.features) {
|
// inline GML or KML features
|
layer = new OpenLayers.Layer.Vector(
|
layerContext.title || layerContext.name,
|
options
|
);
|
layer.addFeatures(layerContext.features);
|
} else if (layerContext.categoryLayer !== true) {
|
layer = new OpenLayers.Layer.WMS(
|
layerContext.title || layerContext.name,
|
layerContext.url,
|
params,
|
options
|
);
|
}
|
return layer;
|
},
|
|
/**
|
* Method: getLayersFromContext
|
* Create an array of layers from an array of layerContext objects.
|
*
|
* Parameters:
|
* layersContext - {Array(Object)} An array of objects representing layers.
|
*
|
* Returns:
|
* {Array(<OpenLayers.Layer>)} An array of layers.
|
*/
|
getLayersFromContext: function(layersContext) {
|
var layers = [];
|
for (var i=0, len=layersContext.length; i<len; i++) {
|
var layer = this.getLayerFromContext(layersContext[i]);
|
if (layer !== null) {
|
layers.push(layer);
|
}
|
}
|
return layers;
|
},
|
|
/**
|
* Method: contextToMap
|
* Create a map given a context object.
|
*
|
* Parameters:
|
* context - {Object} The context object.
|
* options - {Object} Default map options.
|
*
|
* Returns:
|
* {<OpenLayers.Map>} A map based on the context object.
|
*/
|
contextToMap: function(context, options) {
|
options = OpenLayers.Util.applyDefaults({
|
maxExtent: context.maxExtent,
|
projection: context.projection,
|
units: context.units
|
}, options);
|
|
if (options.maxExtent) {
|
options.maxResolution =
|
options.maxExtent.getWidth() / OpenLayers.Map.TILE_WIDTH;
|
}
|
|
var metadata = {
|
contactInformation: context.contactInformation,
|
"abstract": context["abstract"],
|
keywords: context.keywords,
|
logo: context.logo,
|
descriptionURL: context.descriptionURL
|
};
|
|
options.metadata = metadata;
|
|
var map = new OpenLayers.Map(options);
|
map.addLayers(this.getLayersFromContext(context.layersContext));
|
map.setCenter(
|
context.bounds.getCenterLonLat(),
|
map.getZoomForExtent(context.bounds, true)
|
);
|
return map;
|
},
|
|
/**
|
* Method: mergeContextToMap
|
* Add layers from a context object to a map.
|
*
|
* Parameters:
|
* context - {Object} The context object.
|
* map - {<OpenLayers.Map>} The map.
|
*
|
* Returns:
|
* {<OpenLayers.Map>} The same map with layers added.
|
*/
|
mergeContextToMap: function(context, map) {
|
map.addLayers(this.getLayersFromContext(context.layersContext));
|
return map;
|
},
|
|
/**
|
* APIMethod: write
|
* Write a context document given a map.
|
*
|
* Parameters:
|
* obj - {<OpenLayers.Map> | Object} A map or context object.
|
* options - {Object} Optional configuration object.
|
*
|
* Returns:
|
* {String} A context document string.
|
*/
|
write: function(obj, options) {
|
obj = this.toContext(obj);
|
return OpenLayers.Format.XML.VersionedOGC.prototype.write.apply(this,
|
arguments);
|
},
|
|
CLASS_NAME: "OpenLayers.Format.Context"
|
});
|
|
/**
|
* Constant: OpenLayers.Format.Context.serviceTypes
|
* Enumeration for service types
|
*/
|
OpenLayers.Format.Context.serviceTypes = {
|
"WMS": "urn:ogc:serviceType:WMS",
|
"WFS": "urn:ogc:serviceType:WFS",
|
"WCS": "urn:ogc:serviceType:WCS",
|
"GML": "urn:ogc:serviceType:GML",
|
"SLD": "urn:ogc:serviceType:SLD",
|
"FES": "urn:ogc:serviceType:FES",
|
"KML": "urn:ogc:serviceType:KML"
|
};
|
/* ======================================================================
|
OpenLayers/Format/WMC.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML.js
|
* @requires OpenLayers/Format/Context.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WMC
|
* Read and write Web Map Context documents.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.Context>
|
*/
|
OpenLayers.Format.WMC = OpenLayers.Class(OpenLayers.Format.Context, {
|
|
/**
|
* APIProperty: defaultVersion
|
* {String} Version number to assume if none found. Default is "1.1.0".
|
*/
|
defaultVersion: "1.1.0",
|
|
/**
|
* Constructor: OpenLayers.Format.WMC
|
* Create a new parser for Web Map Context documents.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* Method: layerToContext
|
* Create a layer context object given a wms layer object.
|
*
|
* Parameters:
|
* layer - {<OpenLayers.Layer.WMS>} The layer.
|
*
|
* Returns:
|
* {Object} A layer context object.
|
*/
|
layerToContext: function(layer) {
|
var parser = this.getParser();
|
var layerContext = {
|
queryable: layer.queryable,
|
visibility: layer.visibility,
|
name: layer.params["LAYERS"],
|
title: layer.name,
|
"abstract": layer.metadata["abstract"],
|
dataURL: layer.metadata.dataURL,
|
metadataURL: layer.metadataURL,
|
server: {
|
version: layer.params["VERSION"],
|
url: layer.url
|
},
|
maxExtent: layer.maxExtent,
|
transparent: layer.params["TRANSPARENT"],
|
numZoomLevels: layer.numZoomLevels,
|
units: layer.units,
|
isBaseLayer: layer.isBaseLayer,
|
opacity: layer.opacity == 1 ? undefined : layer.opacity,
|
displayInLayerSwitcher: layer.displayInLayerSwitcher,
|
singleTile: layer.singleTile,
|
tileSize: (layer.singleTile || !layer.tileSize) ?
|
undefined : {width: layer.tileSize.w, height: layer.tileSize.h},
|
minScale : (layer.options.resolutions ||
|
layer.options.scales ||
|
layer.options.maxResolution ||
|
layer.options.minScale) ?
|
layer.minScale : undefined,
|
maxScale : (layer.options.resolutions ||
|
layer.options.scales ||
|
layer.options.minResolution ||
|
layer.options.maxScale) ?
|
layer.maxScale : undefined,
|
formats: [],
|
styles: [],
|
srs: layer.srs,
|
dimensions: layer.dimensions
|
};
|
|
|
if (layer.metadata.servertitle) {
|
layerContext.server.title = layer.metadata.servertitle;
|
}
|
|
if (layer.metadata.formats && layer.metadata.formats.length > 0) {
|
for (var i=0, len=layer.metadata.formats.length; i<len; i++) {
|
var format = layer.metadata.formats[i];
|
layerContext.formats.push({
|
value: format.value,
|
current: (format.value == layer.params["FORMAT"])
|
});
|
}
|
} else {
|
layerContext.formats.push({
|
value: layer.params["FORMAT"],
|
current: true
|
});
|
}
|
|
if (layer.metadata.styles && layer.metadata.styles.length > 0) {
|
for (var i=0, len=layer.metadata.styles.length; i<len; i++) {
|
var style = layer.metadata.styles[i];
|
if ((style.href == layer.params["SLD"]) ||
|
(style.body == layer.params["SLD_BODY"]) ||
|
(style.name == layer.params["STYLES"])) {
|
style.current = true;
|
} else {
|
style.current = false;
|
}
|
layerContext.styles.push(style);
|
}
|
} else {
|
layerContext.styles.push({
|
href: layer.params["SLD"],
|
body: layer.params["SLD_BODY"],
|
name: layer.params["STYLES"] || parser.defaultStyleName,
|
title: parser.defaultStyleTitle,
|
current: true
|
});
|
}
|
|
return layerContext;
|
},
|
|
/**
|
* Method: toContext
|
* Create a context object free from layer given a map or a
|
* context object.
|
*
|
* Parameters:
|
* obj - {<OpenLayers.Map> | Object} The map or context.
|
*
|
* Returns:
|
* {Object} A context object.
|
*/
|
toContext: function(obj) {
|
var context = {};
|
var layers = obj.layers;
|
if (obj.CLASS_NAME == "OpenLayers.Map") {
|
var metadata = obj.metadata || {};
|
context.size = obj.getSize();
|
context.bounds = obj.getExtent();
|
context.projection = obj.projection;
|
context.title = obj.title;
|
context.keywords = metadata.keywords;
|
context["abstract"] = metadata["abstract"];
|
context.logo = metadata.logo;
|
context.descriptionURL = metadata.descriptionURL;
|
context.contactInformation = metadata.contactInformation;
|
context.maxExtent = obj.maxExtent;
|
} else {
|
// copy all obj properties except the "layers" property
|
OpenLayers.Util.applyDefaults(context, obj);
|
if (context.layers != undefined) {
|
delete(context.layers);
|
}
|
}
|
|
if (context.layersContext == undefined) {
|
context.layersContext = [];
|
}
|
|
// let's convert layers into layersContext object (if any)
|
if (layers != undefined && OpenLayers.Util.isArray(layers)) {
|
for (var i=0, len=layers.length; i<len; i++) {
|
var layer = layers[i];
|
if (layer instanceof OpenLayers.Layer.WMS) {
|
context.layersContext.push(this.layerToContext(layer));
|
}
|
}
|
}
|
return context;
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WMC"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/WMC/v1.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/WMC.js
|
* @requires OpenLayers/Format/XML.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WMC.v1
|
* Superclass for WMC version 1 parsers.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.WMC.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
ol: "http://openlayers.org/context",
|
wmc: "http://www.opengis.net/context",
|
sld: "http://www.opengis.net/sld",
|
xlink: "http://www.w3.org/1999/xlink",
|
xsi: "http://www.w3.org/2001/XMLSchema-instance"
|
},
|
|
/**
|
* Property: schemaLocation
|
* {String} Schema location for a particular minor version.
|
*/
|
schemaLocation: "",
|
|
/**
|
* Method: getNamespacePrefix
|
* Get the namespace prefix for a given uri from the <namespaces> object.
|
*
|
* Returns:
|
* {String} A namespace prefix or null if none found.
|
*/
|
getNamespacePrefix: function(uri) {
|
var prefix = null;
|
if(uri == null) {
|
prefix = this.namespaces[this.defaultPrefix];
|
} else {
|
for(prefix in this.namespaces) {
|
if(this.namespaces[prefix] == uri) {
|
break;
|
}
|
}
|
}
|
return prefix;
|
},
|
|
/**
|
* Property: defaultPrefix
|
*/
|
defaultPrefix: "wmc",
|
|
/**
|
* Property: rootPrefix
|
* {String} Prefix on the root node that maps to the context namespace URI.
|
*/
|
rootPrefix: null,
|
|
/**
|
* Property: defaultStyleName
|
* {String} Style name used if layer has no style param. Default is "".
|
*/
|
defaultStyleName: "",
|
|
/**
|
* Property: defaultStyleTitle
|
* {String} Default style title. Default is "Default".
|
*/
|
defaultStyleTitle: "Default",
|
|
/**
|
* Constructor: OpenLayers.Format.WMC.v1
|
* Instances of this class are not created directly. Use the
|
* <OpenLayers.Format.WMC> constructor instead.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
initialize: function(options) {
|
OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
|
},
|
|
/**
|
* Method: read
|
* Read capabilities data from a string, and return a list of layers.
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
*
|
* Returns:
|
* {Array} List of named layers.
|
*/
|
read: function(data) {
|
if(typeof data == "string") {
|
data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
|
}
|
var root = data.documentElement;
|
this.rootPrefix = root.prefix;
|
var context = {
|
version: root.getAttribute("version")
|
};
|
this.runChildNodes(context, root);
|
return context;
|
},
|
|
/**
|
* Method: runChildNodes
|
*/
|
runChildNodes: function(obj, node) {
|
var children = node.childNodes;
|
var childNode, processor, prefix, local;
|
for(var i=0, len=children.length; i<len; ++i) {
|
childNode = children[i];
|
if(childNode.nodeType == 1) {
|
prefix = this.getNamespacePrefix(childNode.namespaceURI);
|
local = childNode.nodeName.split(":").pop();
|
processor = this["read_" + prefix + "_" + local];
|
if(processor) {
|
processor.apply(this, [obj, childNode]);
|
}
|
}
|
}
|
},
|
|
/**
|
* Method: read_wmc_General
|
*/
|
read_wmc_General: function(context, node) {
|
this.runChildNodes(context, node);
|
},
|
|
/**
|
* Method: read_wmc_BoundingBox
|
*/
|
read_wmc_BoundingBox: function(context, node) {
|
context.projection = node.getAttribute("SRS");
|
context.bounds = new OpenLayers.Bounds(
|
node.getAttribute("minx"), node.getAttribute("miny"),
|
node.getAttribute("maxx"), node.getAttribute("maxy")
|
);
|
},
|
|
/**
|
* Method: read_wmc_LayerList
|
*/
|
read_wmc_LayerList: function(context, node) {
|
// layersContext is an array containing info for each layer
|
context.layersContext = [];
|
this.runChildNodes(context, node);
|
},
|
|
/**
|
* Method: read_wmc_Layer
|
*/
|
read_wmc_Layer: function(context, node) {
|
var layerContext = {
|
visibility: (node.getAttribute("hidden") != "1"),
|
queryable: (node.getAttribute("queryable") == "1"),
|
formats: [],
|
styles: [],
|
metadata: {}
|
};
|
|
this.runChildNodes(layerContext, node);
|
// set properties common to multiple objects on layer options/params
|
context.layersContext.push(layerContext);
|
},
|
|
/**
|
* Method: read_wmc_Extension
|
*/
|
read_wmc_Extension: function(obj, node) {
|
this.runChildNodes(obj, node);
|
},
|
|
/**
|
* Method: read_ol_units
|
*/
|
read_ol_units: function(layerContext, node) {
|
layerContext.units = this.getChildValue(node);
|
},
|
|
/**
|
* Method: read_ol_maxExtent
|
*/
|
read_ol_maxExtent: function(obj, node) {
|
var bounds = new OpenLayers.Bounds(
|
node.getAttribute("minx"), node.getAttribute("miny"),
|
node.getAttribute("maxx"), node.getAttribute("maxy")
|
);
|
obj.maxExtent = bounds;
|
},
|
|
/**
|
* Method: read_ol_transparent
|
*/
|
read_ol_transparent: function(layerContext, node) {
|
layerContext.transparent = this.getChildValue(node);
|
},
|
|
/**
|
* Method: read_ol_numZoomLevels
|
*/
|
read_ol_numZoomLevels: function(layerContext, node) {
|
layerContext.numZoomLevels = parseInt(this.getChildValue(node));
|
},
|
|
/**
|
* Method: read_ol_opacity
|
*/
|
read_ol_opacity: function(layerContext, node) {
|
layerContext.opacity = parseFloat(this.getChildValue(node));
|
},
|
|
/**
|
* Method: read_ol_singleTile
|
*/
|
read_ol_singleTile: function(layerContext, node) {
|
layerContext.singleTile = (this.getChildValue(node) == "true");
|
},
|
|
/**
|
* Method: read_ol_tileSize
|
*/
|
read_ol_tileSize: function(layerContext, node) {
|
var obj = {"width": node.getAttribute("width"), "height": node.getAttribute("height")};
|
layerContext.tileSize = obj;
|
},
|
|
/**
|
* Method: read_ol_isBaseLayer
|
*/
|
read_ol_isBaseLayer: function(layerContext, node) {
|
layerContext.isBaseLayer = (this.getChildValue(node) == "true");
|
},
|
|
/**
|
* Method: read_ol_displayInLayerSwitcher
|
*/
|
read_ol_displayInLayerSwitcher: function(layerContext, node) {
|
layerContext.displayInLayerSwitcher = (this.getChildValue(node) == "true");
|
},
|
|
/**
|
* Method: read_wmc_Server
|
*/
|
read_wmc_Server: function(layerContext, node) {
|
layerContext.version = node.getAttribute("version");
|
layerContext.url = this.getOnlineResource_href(node);
|
layerContext.metadata.servertitle = node.getAttribute("title");
|
},
|
|
/**
|
* Method: read_wmc_FormatList
|
*/
|
read_wmc_FormatList: function(layerContext, node) {
|
this.runChildNodes(layerContext, node);
|
},
|
|
/**
|
* Method: read_wmc_Format
|
*/
|
read_wmc_Format: function(layerContext, node) {
|
var format = {
|
value: this.getChildValue(node)
|
};
|
if(node.getAttribute("current") == "1") {
|
format.current = true;
|
}
|
layerContext.formats.push(format);
|
},
|
|
/**
|
* Method: read_wmc_StyleList
|
*/
|
read_wmc_StyleList: function(layerContext, node) {
|
this.runChildNodes(layerContext, node);
|
},
|
|
/**
|
* Method: read_wmc_Style
|
*/
|
read_wmc_Style: function(layerContext, node) {
|
var style = {};
|
this.runChildNodes(style, node);
|
if(node.getAttribute("current") == "1") {
|
style.current = true;
|
}
|
layerContext.styles.push(style);
|
},
|
|
/**
|
* Method: read_wmc_SLD
|
*/
|
read_wmc_SLD: function(style, node) {
|
this.runChildNodes(style, node);
|
// style either comes back with an href or a body property
|
},
|
|
/**
|
* Method: read_sld_StyledLayerDescriptor
|
*/
|
read_sld_StyledLayerDescriptor: function(sld, node) {
|
var xml = OpenLayers.Format.XML.prototype.write.apply(this, [node]);
|
sld.body = xml;
|
},
|
|
/**
|
* Method: read_sld_FeatureTypeStyle
|
*/
|
read_sld_FeatureTypeStyle: function(sld, node) {
|
var xml = OpenLayers.Format.XML.prototype.write.apply(this, [node]);
|
sld.body = xml;
|
},
|
|
/**
|
* Method: read_wmc_OnlineResource
|
*/
|
read_wmc_OnlineResource: function(obj, node) {
|
obj.href = this.getAttributeNS(
|
node, this.namespaces.xlink, "href"
|
);
|
},
|
|
/**
|
* Method: read_wmc_Name
|
*/
|
read_wmc_Name: function(obj, node) {
|
var name = this.getChildValue(node);
|
if(name) {
|
obj.name = name;
|
}
|
},
|
|
/**
|
* Method: read_wmc_Title
|
*/
|
read_wmc_Title: function(obj, node) {
|
var title = this.getChildValue(node);
|
if(title) {
|
obj.title = title;
|
}
|
},
|
|
/**
|
* Method: read_wmc_MetadataURL
|
*/
|
read_wmc_MetadataURL: function(layerContext, node) {
|
layerContext.metadataURL = this.getOnlineResource_href(node);
|
},
|
|
/**
|
* Method: read_wmc_KeywordList
|
*/
|
read_wmc_KeywordList: function(context, node) {
|
context.keywords = [];
|
this.runChildNodes(context.keywords, node);
|
},
|
|
/**
|
* Method: read_wmc_Keyword
|
*/
|
read_wmc_Keyword: function(keywords, node) {
|
keywords.push(this.getChildValue(node));
|
},
|
|
/**
|
* Method: read_wmc_Abstract
|
*/
|
read_wmc_Abstract: function(obj, node) {
|
var abst = this.getChildValue(node);
|
if(abst) {
|
obj["abstract"] = abst;
|
}
|
},
|
|
/**
|
* Method: read_wmc_LogoURL
|
*/
|
read_wmc_LogoURL: function(context, node) {
|
context.logo = {
|
width: node.getAttribute("width"),
|
height: node.getAttribute("height"),
|
format: node.getAttribute("format"),
|
href: this.getOnlineResource_href(node)
|
};
|
},
|
|
/**
|
* Method: read_wmc_DescriptionURL
|
*/
|
read_wmc_DescriptionURL: function(context, node) {
|
context.descriptionURL = this.getOnlineResource_href(node);
|
},
|
|
/**
|
* Method: read_wmc_ContactInformation
|
*/
|
read_wmc_ContactInformation: function(obj, node) {
|
var contact = {};
|
this.runChildNodes(contact, node);
|
obj.contactInformation = contact;
|
},
|
|
/**
|
* Method: read_wmc_ContactPersonPrimary
|
*/
|
read_wmc_ContactPersonPrimary: function(contact, node) {
|
var personPrimary = {};
|
this.runChildNodes(personPrimary, node);
|
contact.personPrimary = personPrimary;
|
},
|
|
/**
|
* Method: read_wmc_ContactPerson
|
*/
|
read_wmc_ContactPerson: function(primaryPerson, node) {
|
var person = this.getChildValue(node);
|
if (person) {
|
primaryPerson.person = person;
|
}
|
},
|
|
/**
|
* Method: read_wmc_ContactOrganization
|
*/
|
read_wmc_ContactOrganization: function(primaryPerson, node) {
|
var organization = this.getChildValue(node);
|
if (organization) {
|
primaryPerson.organization = organization;
|
}
|
},
|
|
/**
|
* Method: read_wmc_ContactPosition
|
*/
|
read_wmc_ContactPosition: function(contact, node) {
|
var position = this.getChildValue(node);
|
if (position) {
|
contact.position = position;
|
}
|
},
|
|
/**
|
* Method: read_wmc_ContactAddress
|
*/
|
read_wmc_ContactAddress: function(contact, node) {
|
var contactAddress = {};
|
this.runChildNodes(contactAddress, node);
|
contact.contactAddress = contactAddress;
|
},
|
|
/**
|
* Method: read_wmc_AddressType
|
*/
|
read_wmc_AddressType: function(contactAddress, node) {
|
var type = this.getChildValue(node);
|
if (type) {
|
contactAddress.type = type;
|
}
|
},
|
|
/**
|
* Method: read_wmc_Address
|
*/
|
read_wmc_Address: function(contactAddress, node) {
|
var address = this.getChildValue(node);
|
if (address) {
|
contactAddress.address = address;
|
}
|
},
|
|
/**
|
* Method: read_wmc_City
|
*/
|
read_wmc_City: function(contactAddress, node) {
|
var city = this.getChildValue(node);
|
if (city) {
|
contactAddress.city = city;
|
}
|
},
|
|
/**
|
* Method: read_wmc_StateOrProvince
|
*/
|
read_wmc_StateOrProvince: function(contactAddress, node) {
|
var stateOrProvince = this.getChildValue(node);
|
if (stateOrProvince) {
|
contactAddress.stateOrProvince = stateOrProvince;
|
}
|
},
|
|
/**
|
* Method: read_wmc_PostCode
|
*/
|
read_wmc_PostCode: function(contactAddress, node) {
|
var postcode = this.getChildValue(node);
|
if (postcode) {
|
contactAddress.postcode = postcode;
|
}
|
},
|
|
/**
|
* Method: read_wmc_Country
|
*/
|
read_wmc_Country: function(contactAddress, node) {
|
var country = this.getChildValue(node);
|
if (country) {
|
contactAddress.country = country;
|
}
|
},
|
|
/**
|
* Method: read_wmc_ContactVoiceTelephone
|
*/
|
read_wmc_ContactVoiceTelephone: function(contact, node) {
|
var phone = this.getChildValue(node);
|
if (phone) {
|
contact.phone = phone;
|
}
|
},
|
|
/**
|
* Method: read_wmc_ContactFacsimileTelephone
|
*/
|
read_wmc_ContactFacsimileTelephone: function(contact, node) {
|
var fax = this.getChildValue(node);
|
if (fax) {
|
contact.fax = fax;
|
}
|
},
|
|
/**
|
* Method: read_wmc_ContactElectronicMailAddress
|
*/
|
read_wmc_ContactElectronicMailAddress: function(contact, node) {
|
var email = this.getChildValue(node);
|
if (email) {
|
contact.email = email;
|
}
|
},
|
|
/**
|
* Method: read_wmc_DataURL
|
*/
|
read_wmc_DataURL: function(layerContext, node) {
|
layerContext.dataURL = this.getOnlineResource_href(node);
|
},
|
|
/**
|
* Method: read_wmc_LegendURL
|
*/
|
read_wmc_LegendURL: function(style, node) {
|
var legend = {
|
width: node.getAttribute('width'),
|
height: node.getAttribute('height'),
|
format: node.getAttribute('format'),
|
href: this.getOnlineResource_href(node)
|
};
|
style.legend = legend;
|
},
|
|
/**
|
* Method: read_wmc_DimensionList
|
*/
|
read_wmc_DimensionList: function(layerContext, node) {
|
layerContext.dimensions = {};
|
this.runChildNodes(layerContext.dimensions, node);
|
},
|
/**
|
* Method: read_wmc_Dimension
|
*/
|
read_wmc_Dimension: function(dimensions, node) {
|
var name = node.getAttribute("name").toLowerCase();
|
|
var dim = {
|
name: name,
|
units: node.getAttribute("units") || "",
|
unitSymbol: node.getAttribute("unitSymbol") || "",
|
userValue: node.getAttribute("userValue") || "",
|
nearestValue: node.getAttribute("nearestValue") === "1",
|
multipleValues: node.getAttribute("multipleValues") === "1",
|
current: node.getAttribute("current") === "1",
|
"default": node.getAttribute("default") || ""
|
};
|
var values = this.getChildValue(node);
|
dim.values = values.split(",");
|
|
dimensions[dim.name] = dim;
|
},
|
|
/**
|
* Method: write
|
*
|
* Parameters:
|
* context - {Object} An object representing the map context.
|
* options - {Object} Optional object.
|
*
|
* Returns:
|
* {String} A WMC document string.
|
*/
|
write: function(context, options) {
|
var root = this.createElementDefaultNS("ViewContext");
|
this.setAttributes(root, {
|
version: this.VERSION,
|
id: (options && typeof options.id == "string") ?
|
options.id :
|
OpenLayers.Util.createUniqueID("OpenLayers_Context_")
|
});
|
|
// add schemaLocation attribute
|
this.setAttributeNS(
|
root, this.namespaces.xsi,
|
"xsi:schemaLocation", this.schemaLocation
|
);
|
|
// required General element
|
root.appendChild(this.write_wmc_General(context));
|
|
// required LayerList element
|
root.appendChild(this.write_wmc_LayerList(context));
|
|
return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
|
},
|
|
/**
|
* Method: createElementDefaultNS
|
* Shorthand for createElementNS with namespace from <defaultPrefix>.
|
* Can optionally be used to set attributes and a text child value.
|
*
|
* Parameters:
|
* name - {String} The qualified node name.
|
* childValue - {String} Optional value for text child node.
|
* attributes - {Object} Optional object representing attributes.
|
*
|
* Returns:
|
* {Element} An element node.
|
*/
|
createElementDefaultNS: function(name, childValue, attributes) {
|
var node = this.createElementNS(
|
this.namespaces[this.defaultPrefix],
|
name
|
);
|
if(childValue) {
|
node.appendChild(this.createTextNode(childValue));
|
}
|
if(attributes) {
|
this.setAttributes(node, attributes);
|
}
|
return node;
|
},
|
|
/**
|
* Method: setAttributes
|
* Set multiple attributes given key value pairs from an object.
|
*
|
* Parameters:
|
* node - {Element} An element node.
|
* obj - {Object} An object whose properties represent attribute names and
|
* values represent attribute values.
|
*/
|
setAttributes: function(node, obj) {
|
var value;
|
for(var name in obj) {
|
value = obj[name].toString();
|
if(value.match(/[A-Z]/)) {
|
// safari lowercases attributes with setAttribute
|
this.setAttributeNS(node, null, name, value);
|
} else {
|
node.setAttribute(name, value);
|
}
|
}
|
},
|
|
/**
|
* Method: write_wmc_General
|
* Create a General node given an context object.
|
*
|
* Parameters:
|
* context - {Object} Context object.
|
*
|
* Returns:
|
* {Element} A WMC General element node.
|
*/
|
write_wmc_General: function(context) {
|
var node = this.createElementDefaultNS("General");
|
|
// optional Window element
|
if(context.size) {
|
node.appendChild(this.createElementDefaultNS(
|
"Window", null,
|
{
|
width: context.size.w,
|
height: context.size.h
|
}
|
));
|
}
|
|
// required BoundingBox element
|
var bounds = context.bounds;
|
node.appendChild(this.createElementDefaultNS(
|
"BoundingBox", null,
|
{
|
minx: bounds.left.toPrecision(18),
|
miny: bounds.bottom.toPrecision(18),
|
maxx: bounds.right.toPrecision(18),
|
maxy: bounds.top.toPrecision(18),
|
SRS: context.projection
|
}
|
));
|
|
// required Title element
|
node.appendChild(this.createElementDefaultNS(
|
"Title", context.title
|
));
|
|
// optional KeywordList element
|
if (context.keywords) {
|
node.appendChild(this.write_wmc_KeywordList(context.keywords));
|
}
|
|
// optional Abstract element
|
if (context["abstract"]) {
|
node.appendChild(this.createElementDefaultNS(
|
"Abstract", context["abstract"]
|
));
|
}
|
|
// Optional LogoURL element
|
if (context.logo) {
|
node.appendChild(this.write_wmc_URLType("LogoURL", context.logo.href, context.logo));
|
}
|
|
// Optional DescriptionURL element
|
if (context.descriptionURL) {
|
node.appendChild(this.write_wmc_URLType("DescriptionURL", context.descriptionURL));
|
}
|
|
// Optional ContactInformation element
|
if (context.contactInformation) {
|
node.appendChild(this.write_wmc_ContactInformation(context.contactInformation));
|
}
|
|
// OpenLayers specific map properties
|
node.appendChild(this.write_ol_MapExtension(context));
|
|
return node;
|
},
|
|
/**
|
* Method: write_wmc_KeywordList
|
*/
|
write_wmc_KeywordList: function(keywords) {
|
var node = this.createElementDefaultNS("KeywordList");
|
|
for (var i=0, len=keywords.length; i<len; i++) {
|
node.appendChild(this.createElementDefaultNS(
|
"Keyword", keywords[i]
|
));
|
}
|
return node;
|
},
|
/**
|
* Method: write_wmc_ContactInformation
|
*/
|
write_wmc_ContactInformation: function(contact) {
|
var node = this.createElementDefaultNS("ContactInformation");
|
|
if (contact.personPrimary) {
|
node.appendChild(this.write_wmc_ContactPersonPrimary(contact.personPrimary));
|
}
|
if (contact.position) {
|
node.appendChild(this.createElementDefaultNS(
|
"ContactPosition", contact.position
|
));
|
}
|
if (contact.contactAddress) {
|
node.appendChild(this.write_wmc_ContactAddress(contact.contactAddress));
|
}
|
if (contact.phone) {
|
node.appendChild(this.createElementDefaultNS(
|
"ContactVoiceTelephone", contact.phone
|
));
|
}
|
if (contact.fax) {
|
node.appendChild(this.createElementDefaultNS(
|
"ContactFacsimileTelephone", contact.fax
|
));
|
}
|
if (contact.email) {
|
node.appendChild(this.createElementDefaultNS(
|
"ContactElectronicMailAddress", contact.email
|
));
|
}
|
return node;
|
},
|
|
/**
|
* Method: write_wmc_ContactPersonPrimary
|
*/
|
write_wmc_ContactPersonPrimary: function(personPrimary) {
|
var node = this.createElementDefaultNS("ContactPersonPrimary");
|
if (personPrimary.person) {
|
node.appendChild(this.createElementDefaultNS(
|
"ContactPerson", personPrimary.person
|
));
|
}
|
if (personPrimary.organization) {
|
node.appendChild(this.createElementDefaultNS(
|
"ContactOrganization", personPrimary.organization
|
));
|
}
|
return node;
|
},
|
|
/**
|
* Method: write_wmc_ContactAddress
|
*/
|
write_wmc_ContactAddress: function(contactAddress) {
|
var node = this.createElementDefaultNS("ContactAddress");
|
if (contactAddress.type) {
|
node.appendChild(this.createElementDefaultNS(
|
"AddressType", contactAddress.type
|
));
|
}
|
if (contactAddress.address) {
|
node.appendChild(this.createElementDefaultNS(
|
"Address", contactAddress.address
|
));
|
}
|
if (contactAddress.city) {
|
node.appendChild(this.createElementDefaultNS(
|
"City", contactAddress.city
|
));
|
}
|
if (contactAddress.stateOrProvince) {
|
node.appendChild(this.createElementDefaultNS(
|
"StateOrProvince", contactAddress.stateOrProvince
|
));
|
}
|
if (contactAddress.postcode) {
|
node.appendChild(this.createElementDefaultNS(
|
"PostCode", contactAddress.postcode
|
));
|
}
|
if (contactAddress.country) {
|
node.appendChild(this.createElementDefaultNS(
|
"Country", contactAddress.country
|
));
|
}
|
return node;
|
},
|
|
/**
|
* Method: write_ol_MapExtension
|
*/
|
write_ol_MapExtension: function(context) {
|
var node = this.createElementDefaultNS("Extension");
|
|
var bounds = context.maxExtent;
|
if(bounds) {
|
var maxExtent = this.createElementNS(
|
this.namespaces.ol, "ol:maxExtent"
|
);
|
this.setAttributes(maxExtent, {
|
minx: bounds.left.toPrecision(18),
|
miny: bounds.bottom.toPrecision(18),
|
maxx: bounds.right.toPrecision(18),
|
maxy: bounds.top.toPrecision(18)
|
});
|
node.appendChild(maxExtent);
|
}
|
|
return node;
|
},
|
|
/**
|
* Method: write_wmc_LayerList
|
* Create a LayerList node given an context object.
|
*
|
* Parameters:
|
* context - {Object} Context object.
|
*
|
* Returns:
|
* {Element} A WMC LayerList element node.
|
*/
|
write_wmc_LayerList: function(context) {
|
var list = this.createElementDefaultNS("LayerList");
|
|
for(var i=0, len=context.layersContext.length; i<len; ++i) {
|
list.appendChild(this.write_wmc_Layer(context.layersContext[i]));
|
}
|
|
return list;
|
},
|
|
/**
|
* Method: write_wmc_Layer
|
* Create a Layer node given a layer context object.
|
*
|
* Parameters:
|
* context - {Object} A layer context object.}
|
*
|
* Returns:
|
* {Element} A WMC Layer element node.
|
*/
|
write_wmc_Layer: function(context) {
|
var node = this.createElementDefaultNS(
|
"Layer", null, {
|
queryable: context.queryable ? "1" : "0",
|
hidden: context.visibility ? "0" : "1"
|
}
|
);
|
|
// required Server element
|
node.appendChild(this.write_wmc_Server(context));
|
|
// required Name element
|
node.appendChild(this.createElementDefaultNS(
|
"Name", context.name
|
));
|
|
// required Title element
|
node.appendChild(this.createElementDefaultNS(
|
"Title", context.title
|
));
|
|
// optional Abstract element
|
if (context["abstract"]) {
|
node.appendChild(this.createElementDefaultNS(
|
"Abstract", context["abstract"]
|
));
|
}
|
|
// optional DataURL element
|
if (context.dataURL) {
|
node.appendChild(this.write_wmc_URLType("DataURL", context.dataURL));
|
}
|
|
// optional MetadataURL element
|
if (context.metadataURL) {
|
node.appendChild(this.write_wmc_URLType("MetadataURL", context.metadataURL));
|
}
|
|
return node;
|
},
|
|
/**
|
* Method: write_wmc_LayerExtension
|
* Add OpenLayers specific layer parameters to an Extension element.
|
*
|
* Parameters:
|
* context - {Object} A layer context object.
|
*
|
* Returns:
|
* {Element} A WMC Extension element (for a layer).
|
*/
|
write_wmc_LayerExtension: function(context) {
|
var node = this.createElementDefaultNS("Extension");
|
|
var bounds = context.maxExtent;
|
var maxExtent = this.createElementNS(
|
this.namespaces.ol, "ol:maxExtent"
|
);
|
this.setAttributes(maxExtent, {
|
minx: bounds.left.toPrecision(18),
|
miny: bounds.bottom.toPrecision(18),
|
maxx: bounds.right.toPrecision(18),
|
maxy: bounds.top.toPrecision(18)
|
});
|
node.appendChild(maxExtent);
|
|
if (context.tileSize && !context.singleTile) {
|
var size = this.createElementNS(
|
this.namespaces.ol, "ol:tileSize"
|
);
|
this.setAttributes(size, context.tileSize);
|
node.appendChild(size);
|
}
|
|
var properties = [
|
"transparent", "numZoomLevels", "units", "isBaseLayer",
|
"opacity", "displayInLayerSwitcher", "singleTile"
|
];
|
var child;
|
for(var i=0, len=properties.length; i<len; ++i) {
|
child = this.createOLPropertyNode(context, properties[i]);
|
if(child) {
|
node.appendChild(child);
|
}
|
}
|
|
return node;
|
},
|
|
/**
|
* Method: createOLPropertyNode
|
* Create a node representing an OpenLayers property. If the property is
|
* null or undefined, null will be returned.
|
*
|
* Parameters:
|
* obj - {Object} An object.
|
* prop - {String} A property.
|
*
|
* Returns:
|
* {Element} A property node.
|
*/
|
createOLPropertyNode: function(obj, prop) {
|
var node = null;
|
if(obj[prop] != null) {
|
node = this.createElementNS(this.namespaces.ol, "ol:" + prop);
|
node.appendChild(this.createTextNode(obj[prop].toString()));
|
}
|
return node;
|
},
|
|
/**
|
* Method: write_wmc_Server
|
* Create a Server node given a layer context object.
|
*
|
* Parameters:
|
* context - {Object} Layer context object.
|
*
|
* Returns:
|
* {Element} A WMC Server element node.
|
*/
|
write_wmc_Server: function(context) {
|
var server = context.server;
|
var node = this.createElementDefaultNS("Server");
|
var attributes = {
|
service: "OGC:WMS",
|
version: server.version
|
};
|
if (server.title) {
|
attributes.title = server.title;
|
}
|
this.setAttributes(node, attributes);
|
|
// required OnlineResource element
|
node.appendChild(this.write_wmc_OnlineResource(server.url));
|
|
return node;
|
},
|
|
/**
|
* Method: write_wmc_URLType
|
* Create a LogoURL/DescriptionURL/MetadataURL/DataURL/LegendURL node given a object and elementName.
|
*
|
* Parameters:
|
* elName - {String} Name of element (LogoURL/DescriptionURL/MetadataURL/LegendURL)
|
* url - {String} URL string value
|
* attr - {Object} Optional attributes (width, height, format)
|
*
|
* Returns:
|
* {Element} A WMC element node.
|
*/
|
write_wmc_URLType: function(elName, url, attr) {
|
var node = this.createElementDefaultNS(elName);
|
node.appendChild(this.write_wmc_OnlineResource(url));
|
if (attr) {
|
var optionalAttributes = ["width", "height", "format"];
|
for (var i=0; i<optionalAttributes.length; i++) {
|
if (optionalAttributes[i] in attr) {
|
node.setAttribute(optionalAttributes[i], attr[optionalAttributes[i]]);
|
}
|
}
|
}
|
return node;
|
},
|
|
/**
|
* Method: write_wmc_DimensionList
|
*/
|
write_wmc_DimensionList: function(context) {
|
var node = this.createElementDefaultNS("DimensionList");
|
var required_attributes = {
|
name: true,
|
units: true,
|
unitSymbol: true,
|
userValue: true
|
};
|
for (var dim in context.dimensions) {
|
var attributes = {};
|
var dimension = context.dimensions[dim];
|
for (var name in dimension) {
|
if (typeof dimension[name] == "boolean") {
|
attributes[name] = Number(dimension[name]);
|
} else {
|
attributes[name] = dimension[name];
|
}
|
}
|
var values = "";
|
if (attributes.values) {
|
values = attributes.values.join(",");
|
delete attributes.values;
|
}
|
|
node.appendChild(this.createElementDefaultNS(
|
"Dimension", values, attributes
|
));
|
}
|
return node;
|
},
|
|
/**
|
* Method: write_wmc_FormatList
|
* Create a FormatList node given a layer context.
|
*
|
* Parameters:
|
* context - {Object} Layer context object.
|
*
|
* Returns:
|
* {Element} A WMC FormatList element node.
|
*/
|
write_wmc_FormatList: function(context) {
|
var node = this.createElementDefaultNS("FormatList");
|
for (var i=0, len=context.formats.length; i<len; i++) {
|
var format = context.formats[i];
|
node.appendChild(this.createElementDefaultNS(
|
"Format",
|
format.value,
|
(format.current && format.current == true) ?
|
{current: "1"} : null
|
));
|
}
|
|
return node;
|
},
|
|
/**
|
* Method: write_wmc_StyleList
|
* Create a StyleList node given a layer context.
|
*
|
* Parameters:
|
* layer - {Object} Layer context object.
|
*
|
* Returns:
|
* {Element} A WMC StyleList element node.
|
*/
|
write_wmc_StyleList: function(layer) {
|
var node = this.createElementDefaultNS("StyleList");
|
|
var styles = layer.styles;
|
if (styles && OpenLayers.Util.isArray(styles)) {
|
var sld;
|
for (var i=0, len=styles.length; i<len; i++) {
|
var s = styles[i];
|
// three style types to consider
|
// [1] linked SLD
|
// [2] inline SLD
|
// [3] named style
|
// running child nodes always gets name, optionally gets href or body
|
var style = this.createElementDefaultNS(
|
"Style",
|
null,
|
(s.current && s.current == true) ?
|
{current: "1"} : null
|
);
|
if(s.href) { // [1]
|
sld = this.createElementDefaultNS("SLD");
|
// Name is optional.
|
if (s.name) {
|
sld.appendChild(this.createElementDefaultNS("Name", s.name));
|
}
|
// Title is optional.
|
if (s.title) {
|
sld.appendChild(this.createElementDefaultNS("Title", s.title));
|
}
|
// LegendURL is optional
|
if (s.legend) {
|
sld.appendChild(this.write_wmc_URLType("LegendURL", s.legend.href, s.legend));
|
}
|
|
var link = this.write_wmc_OnlineResource(s.href);
|
sld.appendChild(link);
|
style.appendChild(sld);
|
} else if(s.body) { // [2]
|
sld = this.createElementDefaultNS("SLD");
|
// Name is optional.
|
if (s.name) {
|
sld.appendChild(this.createElementDefaultNS("Name", s.name));
|
}
|
// Title is optional.
|
if (s.title) {
|
sld.appendChild(this.createElementDefaultNS("Title", s.title));
|
}
|
// LegendURL is optional
|
if (s.legend) {
|
sld.appendChild(this.write_wmc_URLType("LegendURL", s.legend.href, s.legend));
|
}
|
|
// read in body as xml doc - assume proper namespace declarations
|
var doc = OpenLayers.Format.XML.prototype.read.apply(this, [s.body]);
|
// append to StyledLayerDescriptor node
|
var imported = doc.documentElement;
|
if(sld.ownerDocument && sld.ownerDocument.importNode) {
|
imported = sld.ownerDocument.importNode(imported, true);
|
}
|
sld.appendChild(imported);
|
style.appendChild(sld);
|
} else { // [3]
|
// both Name and Title are required.
|
style.appendChild(this.createElementDefaultNS("Name", s.name));
|
style.appendChild(this.createElementDefaultNS("Title", s.title));
|
// Abstract is optional
|
if (s['abstract']) { // abstract is a js keyword
|
style.appendChild(this.createElementDefaultNS(
|
"Abstract", s['abstract']
|
));
|
}
|
// LegendURL is optional
|
if (s.legend) {
|
style.appendChild(this.write_wmc_URLType("LegendURL", s.legend.href, s.legend));
|
}
|
}
|
node.appendChild(style);
|
}
|
}
|
|
return node;
|
},
|
|
/**
|
* Method: write_wmc_OnlineResource
|
* Create an OnlineResource node given a URL.
|
*
|
* Parameters:
|
* href - {String} URL for the resource.
|
*
|
* Returns:
|
* {Element} A WMC OnlineResource element node.
|
*/
|
write_wmc_OnlineResource: function(href) {
|
var node = this.createElementDefaultNS("OnlineResource");
|
this.setAttributeNS(node, this.namespaces.xlink, "xlink:type", "simple");
|
this.setAttributeNS(node, this.namespaces.xlink, "xlink:href", href);
|
return node;
|
},
|
|
/**
|
* Method: getOnlineResource_href
|
*/
|
getOnlineResource_href: function(node) {
|
var object = {};
|
var links = node.getElementsByTagName("OnlineResource");
|
if(links.length > 0) {
|
this.read_wmc_OnlineResource(object, links[0]);
|
}
|
return object.href;
|
},
|
|
|
CLASS_NAME: "OpenLayers.Format.WMC.v1"
|
|
});
|
/* ======================================================================
|
OpenLayers/Control/PanPanel.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control/Panel.js
|
* @requires OpenLayers/Control/Pan.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.PanPanel
|
* The PanPanel is visible control for panning the map North, South, East or
|
* West in small steps. By default it is drawn in the top left corner of the
|
* map.
|
*
|
* Note:
|
* If you wish to use this class with the default images and you want
|
* it to look nice in ie6, you should add the following, conditionally
|
* added css stylesheet to your HTML file:
|
*
|
* (code)
|
* <!--[if lte IE 6]>
|
* <link rel="stylesheet" href="../theme/default/ie6-style.css" type="text/css" />
|
* <![endif]-->
|
* (end)
|
*
|
* Inherits from:
|
* - <OpenLayers.Control.Panel>
|
*/
|
OpenLayers.Control.PanPanel = OpenLayers.Class(OpenLayers.Control.Panel, {
|
|
/**
|
* APIProperty: slideFactor
|
* {Integer} Number of pixels by which we'll pan the map in any direction
|
* on clicking the arrow buttons, defaults to 50. If you want to pan
|
* by some ratio of the map dimensions, use <slideRatio> instead.
|
*/
|
slideFactor: 50,
|
|
/**
|
* APIProperty: slideRatio
|
* {Number} The fraction of map width/height by which we'll pan the map
|
* on clicking the arrow buttons. Default is null. If set, will
|
* override <slideFactor>. E.g. if slideRatio is .5, then Pan Up will
|
* pan up half the map height.
|
*/
|
slideRatio: null,
|
|
/**
|
* Constructor: OpenLayers.Control.PanPanel
|
* Add the four directional pan buttons.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be used
|
* to extend the control.
|
*/
|
initialize: function(options) {
|
OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);
|
var options = {
|
slideFactor: this.slideFactor,
|
slideRatio: this.slideRatio
|
};
|
this.addControls([
|
new OpenLayers.Control.Pan(OpenLayers.Control.Pan.NORTH, options),
|
new OpenLayers.Control.Pan(OpenLayers.Control.Pan.SOUTH, options),
|
new OpenLayers.Control.Pan(OpenLayers.Control.Pan.EAST, options),
|
new OpenLayers.Control.Pan(OpenLayers.Control.Pan.WEST, options)
|
]);
|
},
|
|
CLASS_NAME: "OpenLayers.Control.PanPanel"
|
});
|
/* ======================================================================
|
OpenLayers/Control/Attribution.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.Attribution
|
* The attribution control adds attribution from layers to the map display.
|
* It uses 'attribution' property of each layer.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.Attribution =
|
OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* APIProperty: separator
|
* {String} String used to separate layers.
|
*/
|
separator: ", ",
|
|
/**
|
* APIProperty: template
|
* {String} Template for the attribution. This has to include the substring
|
* "${layers}", which will be replaced by the layer specific
|
* attributions, separated by <separator>. The default is "${layers}".
|
*/
|
template: "${layers}",
|
|
/**
|
* Constructor: OpenLayers.Control.Attribution
|
*
|
* Parameters:
|
* options - {Object} Options for control.
|
*/
|
|
/**
|
* Method: destroy
|
* Destroy control.
|
*/
|
destroy: function() {
|
this.map.events.un({
|
"removelayer": this.updateAttribution,
|
"addlayer": this.updateAttribution,
|
"changelayer": this.updateAttribution,
|
"changebaselayer": this.updateAttribution,
|
scope: this
|
});
|
|
OpenLayers.Control.prototype.destroy.apply(this, arguments);
|
},
|
|
/**
|
* Method: draw
|
* Initialize control.
|
*
|
* Returns:
|
* {DOMElement} A reference to the DIV DOMElement containing the control
|
*/
|
draw: function() {
|
OpenLayers.Control.prototype.draw.apply(this, arguments);
|
|
this.map.events.on({
|
'changebaselayer': this.updateAttribution,
|
'changelayer': this.updateAttribution,
|
'addlayer': this.updateAttribution,
|
'removelayer': this.updateAttribution,
|
scope: this
|
});
|
this.updateAttribution();
|
|
return this.div;
|
},
|
|
/**
|
* Method: updateAttribution
|
* Update attribution string.
|
*/
|
updateAttribution: function() {
|
var attributions = [];
|
if (this.map && this.map.layers) {
|
for(var i=0, len=this.map.layers.length; i<len; i++) {
|
var layer = this.map.layers[i];
|
if (layer.attribution && layer.getVisibility()) {
|
// add attribution only if attribution text is unique
|
if (OpenLayers.Util.indexOf(
|
attributions, layer.attribution) === -1) {
|
attributions.push( layer.attribution );
|
}
|
}
|
}
|
this.div.innerHTML = OpenLayers.String.format(this.template, {
|
layers: attributions.join(this.separator)
|
});
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Control.Attribution"
|
});
|
/* ======================================================================
|
OpenLayers/Kinetic.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
* @requires OpenLayers/Animation.js
|
*/
|
|
OpenLayers.Kinetic = OpenLayers.Class({
|
|
/**
|
* Property: threshold
|
* In most cases changing the threshold isn't needed.
|
* In px/ms, default to 0.
|
*/
|
threshold: 0,
|
|
/**
|
* Property: deceleration
|
* {Float} the deseleration in px/ms², default to 0.0035.
|
*/
|
deceleration: 0.0035,
|
|
/**
|
* Property: nbPoints
|
* {Integer} the number of points we use to calculate the kinetic
|
* initial values.
|
*/
|
nbPoints: 100,
|
|
/**
|
* Property: delay
|
* {Float} time to consider to calculate the kinetic initial values.
|
* In ms, default to 200.
|
*/
|
delay: 200,
|
|
/**
|
* Property: points
|
* List of points use to calculate the kinetic initial values.
|
*/
|
points: undefined,
|
|
/**
|
* Property: timerId
|
* ID of the timer.
|
*/
|
timerId: undefined,
|
|
/**
|
* Constructor: OpenLayers.Kinetic
|
*
|
* Parameters:
|
* options - {Object}
|
*/
|
initialize: function(options) {
|
OpenLayers.Util.extend(this, options);
|
},
|
|
/**
|
* Method: begin
|
* Begins the dragging.
|
*/
|
begin: function() {
|
OpenLayers.Animation.stop(this.timerId);
|
this.timerId = undefined;
|
this.points = [];
|
},
|
|
/**
|
* Method: update
|
* Updates during the dragging.
|
*
|
* Parameters:
|
* xy - {<OpenLayers.Pixel>} The new position.
|
*/
|
update: function(xy) {
|
this.points.unshift({xy: xy, tick: new Date().getTime()});
|
if (this.points.length > this.nbPoints) {
|
this.points.pop();
|
}
|
},
|
|
/**
|
* Method: end
|
* Ends the dragging, start the kinetic.
|
*
|
* Parameters:
|
* xy - {<OpenLayers.Pixel>} The last position.
|
*
|
* Returns:
|
* {Object} An object with two properties: "speed", and "theta". The
|
* "speed" and "theta" values are to be passed to the move
|
* function when starting the animation.
|
*/
|
end: function(xy) {
|
var last, now = new Date().getTime();
|
for (var i = 0, l = this.points.length, point; i < l; i++) {
|
point = this.points[i];
|
if (now - point.tick > this.delay) {
|
break;
|
}
|
last = point;
|
}
|
if (!last) {
|
return;
|
}
|
var time = new Date().getTime() - last.tick;
|
var dist = Math.sqrt(Math.pow(xy.x - last.xy.x, 2) +
|
Math.pow(xy.y - last.xy.y, 2));
|
var speed = dist / time;
|
if (speed == 0 || speed < this.threshold) {
|
return;
|
}
|
var theta = Math.asin((xy.y - last.xy.y) / dist);
|
if (last.xy.x <= xy.x) {
|
theta = Math.PI - theta;
|
}
|
return {speed: speed, theta: theta};
|
},
|
|
/**
|
* Method: move
|
* Launch the kinetic move pan.
|
*
|
* Parameters:
|
* info - {Object} An object with two properties, "speed", and "theta".
|
* These values are those returned from the "end" call.
|
* callback - {Function} Function called on every step of the animation,
|
* receives x, y (values to pan), end (is the last point).
|
*/
|
move: function(info, callback) {
|
var v0 = info.speed;
|
var fx = Math.cos(info.theta);
|
var fy = -Math.sin(info.theta);
|
|
var initialTime = new Date().getTime();
|
|
var lastX = 0;
|
var lastY = 0;
|
|
var timerCallback = function() {
|
if (this.timerId == null) {
|
return;
|
}
|
|
var t = new Date().getTime() - initialTime;
|
|
var p = (-this.deceleration * Math.pow(t, 2)) / 2.0 + v0 * t;
|
var x = p * fx;
|
var y = p * fy;
|
|
var args = {};
|
args.end = false;
|
var v = -this.deceleration * t + v0;
|
|
if (v <= 0) {
|
OpenLayers.Animation.stop(this.timerId);
|
this.timerId = null;
|
args.end = true;
|
}
|
|
args.x = x - lastX;
|
args.y = y - lastY;
|
lastX = x;
|
lastY = y;
|
callback(args.x, args.y, args.end);
|
};
|
|
this.timerId = OpenLayers.Animation.start(
|
OpenLayers.Function.bind(timerCallback, this)
|
);
|
},
|
|
CLASS_NAME: "OpenLayers.Kinetic"
|
});
|
/* ======================================================================
|
OpenLayers/Format/WPSExecute.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML.js
|
* @requires OpenLayers/Format/OWSCommon/v1_1_0.js
|
* @requires OpenLayers/Format/WCSGetCoverage.js
|
* @requires OpenLayers/Format/WFST/v1_1_0.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WPSExecute version 1.0.0
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.WPSExecute = OpenLayers.Class(OpenLayers.Format.XML,
|
OpenLayers.Format.Filter.v1_1_0, {
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
ows: "http://www.opengis.net/ows/1.1",
|
gml: "http://www.opengis.net/gml",
|
wps: "http://www.opengis.net/wps/1.0.0",
|
wfs: "http://www.opengis.net/wfs",
|
ogc: "http://www.opengis.net/ogc",
|
wcs: "http://www.opengis.net/wcs",
|
xlink: "http://www.w3.org/1999/xlink",
|
xsi: "http://www.w3.org/2001/XMLSchema-instance"
|
},
|
|
/**
|
* Property: regExes
|
* Compiled regular expressions for manipulating strings.
|
*/
|
regExes: {
|
trimSpace: (/^\s*|\s*$/g),
|
removeSpace: (/\s*/g),
|
splitSpace: (/\s+/),
|
trimComma: (/\s*,\s*/g)
|
},
|
|
/**
|
* Constant: VERSION
|
* {String} 1.0.0
|
*/
|
VERSION: "1.0.0",
|
|
/**
|
* Property: schemaLocation
|
* {String} Schema location
|
*/
|
schemaLocation: "http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd",
|
|
schemaLocationAttr: function(options) {
|
return undefined;
|
},
|
|
/**
|
* Constructor: OpenLayers.Format.WPSExecute
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* Method: write
|
*
|
* Parameters:
|
* options - {Object} Optional object.
|
*
|
* Returns:
|
* {String} An WPS Execute request XML string.
|
*/
|
write: function(options) {
|
var doc;
|
if (window.ActiveXObject) {
|
doc = new ActiveXObject("Microsoft.XMLDOM");
|
this.xmldom = doc;
|
} else {
|
doc = document.implementation.createDocument("", "", null);
|
}
|
var node = this.writeNode("wps:Execute", options, doc);
|
this.setAttributeNS(
|
node, this.namespaces.xsi,
|
"xsi:schemaLocation", this.schemaLocation
|
);
|
return OpenLayers.Format.XML.prototype.write.apply(this, [node]);
|
},
|
|
/**
|
* APIMethod: read
|
* Parse a WPS Execute and return an object with its information.
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
*
|
* Returns:
|
* {Object}
|
*/
|
read: function(data) {
|
if(typeof data == "string") {
|
data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
|
}
|
if(data && data.nodeType == 9) {
|
data = data.documentElement;
|
}
|
var info = {};
|
this.readNode(data, info);
|
return info;
|
},
|
|
/**
|
* Property: writers
|
* As a compliment to the readers property, this structure contains public
|
* writing functions grouped by namespace alias and named like the
|
* node names they produce.
|
*/
|
writers: {
|
"wps": {
|
"Execute": function(options) {
|
var node = this.createElementNSPlus("wps:Execute", {
|
attributes: {
|
version: this.VERSION,
|
service: 'WPS'
|
}
|
});
|
this.writeNode("ows:Identifier", options.identifier, node);
|
this.writeNode("wps:DataInputs", options.dataInputs, node);
|
this.writeNode("wps:ResponseForm", options.responseForm, node);
|
return node;
|
},
|
"ResponseForm": function(responseForm) {
|
var node = this.createElementNSPlus("wps:ResponseForm", {});
|
if (responseForm.rawDataOutput) {
|
this.writeNode("wps:RawDataOutput", responseForm.rawDataOutput, node);
|
}
|
if (responseForm.responseDocument) {
|
this.writeNode("wps:ResponseDocument", responseForm.responseDocument, node);
|
}
|
return node;
|
},
|
"ResponseDocument": function(responseDocument) {
|
var node = this.createElementNSPlus("wps:ResponseDocument", {
|
attributes: {
|
storeExecuteResponse: responseDocument.storeExecuteResponse,
|
lineage: responseDocument.lineage,
|
status: responseDocument.status
|
}
|
});
|
if (responseDocument.outputs) {
|
for (var i = 0, len = responseDocument.outputs.length; i < len; i++) {
|
this.writeNode("wps:Output", responseDocument.outputs[i], node);
|
}
|
}
|
return node;
|
},
|
"Output": function(output) {
|
var node = this.createElementNSPlus("wps:Output", {
|
attributes: {
|
asReference: output.asReference,
|
mimeType: output.mimeType,
|
encoding: output.encoding,
|
schema: output.schema
|
}
|
});
|
this.writeNode("ows:Identifier", output.identifier, node);
|
this.writeNode("ows:Title", output.title, node);
|
this.writeNode("ows:Abstract", output["abstract"], node);
|
return node;
|
},
|
"RawDataOutput": function(rawDataOutput) {
|
var node = this.createElementNSPlus("wps:RawDataOutput", {
|
attributes: {
|
mimeType: rawDataOutput.mimeType,
|
encoding: rawDataOutput.encoding,
|
schema: rawDataOutput.schema
|
}
|
});
|
this.writeNode("ows:Identifier", rawDataOutput.identifier, node);
|
return node;
|
},
|
"DataInputs": function(dataInputs) {
|
var node = this.createElementNSPlus("wps:DataInputs", {});
|
for (var i=0, ii=dataInputs.length; i<ii; ++i) {
|
this.writeNode("wps:Input", dataInputs[i], node);
|
}
|
return node;
|
},
|
"Input": function(input) {
|
var node = this.createElementNSPlus("wps:Input", {});
|
this.writeNode("ows:Identifier", input.identifier, node);
|
if (input.title) {
|
this.writeNode("ows:Title", input.title, node);
|
}
|
if (input.data) {
|
this.writeNode("wps:Data", input.data, node);
|
}
|
if (input.reference) {
|
this.writeNode("wps:Reference", input.reference, node);
|
}
|
if (input.boundingBoxData) {
|
this.writeNode("wps:BoundingBoxData", input.boundingBoxData, node);
|
}
|
return node;
|
},
|
"Data": function(data) {
|
var node = this.createElementNSPlus("wps:Data", {});
|
if (data.literalData) {
|
this.writeNode("wps:LiteralData", data.literalData, node);
|
} else if (data.complexData) {
|
this.writeNode("wps:ComplexData", data.complexData, node);
|
} else if (data.boundingBoxData) {
|
this.writeNode("ows:BoundingBox", data.boundingBoxData, node);
|
}
|
return node;
|
},
|
"LiteralData": function(literalData) {
|
var node = this.createElementNSPlus("wps:LiteralData", {
|
attributes: {
|
uom: literalData.uom
|
},
|
value: literalData.value
|
});
|
return node;
|
},
|
"ComplexData": function(complexData) {
|
var node = this.createElementNSPlus("wps:ComplexData", {
|
attributes: {
|
mimeType: complexData.mimeType,
|
encoding: complexData.encoding,
|
schema: complexData.schema
|
}
|
});
|
var data = complexData.value;
|
if (typeof data === "string") {
|
node.appendChild(
|
this.getXMLDoc().createCDATASection(complexData.value)
|
);
|
} else {
|
node.appendChild(data);
|
}
|
return node;
|
},
|
"Reference": function(reference) {
|
var node = this.createElementNSPlus("wps:Reference", {
|
attributes: {
|
mimeType: reference.mimeType,
|
"xlink:href": reference.href,
|
method: reference.method,
|
encoding: reference.encoding,
|
schema: reference.schema
|
}
|
});
|
if (reference.body) {
|
this.writeNode("wps:Body", reference.body, node);
|
}
|
return node;
|
},
|
"BoundingBoxData": function(node, obj) {
|
this.writers['ows']['BoundingBox'].apply(this, [node, obj, "wps:BoundingBoxData"]);
|
},
|
"Body": function(body) {
|
var node = this.createElementNSPlus("wps:Body", {});
|
if (body.wcs) {
|
this.writeNode("wcs:GetCoverage", body.wcs, node);
|
}
|
else if (body.wfs) {
|
// OpenLayers.Format.WFST expects these to be on the
|
// instance and not in the options
|
this.featureType = body.wfs.featureType;
|
this.version = body.wfs.version;
|
this.writeNode("wfs:GetFeature", body.wfs, node);
|
} else {
|
this.writeNode("wps:Execute", body, node);
|
}
|
return node;
|
}
|
},
|
"wcs": OpenLayers.Format.WCSGetCoverage.prototype.writers.wcs,
|
"wfs": OpenLayers.Format.WFST.v1_1_0.prototype.writers.wfs,
|
"ogc": OpenLayers.Format.Filter.v1_1_0.prototype.writers.ogc,
|
"ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.writers.ows
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"wps": {
|
"ExecuteResponse": function(node, obj) {
|
obj.executeResponse = {
|
lang: node.getAttribute("lang"),
|
statusLocation: node.getAttribute("statusLocation"),
|
serviceInstance: node.getAttribute("serviceInstance"),
|
service: node.getAttribute("service")
|
};
|
this.readChildNodes(node, obj.executeResponse);
|
},
|
"Process":function(node,obj) {
|
obj.process = {};
|
this.readChildNodes(node, obj.process);
|
},
|
"Status":function(node,obj) {
|
obj.status = {
|
creationTime: node.getAttribute("creationTime")
|
};
|
this.readChildNodes(node, obj.status);
|
},
|
"ProcessSucceeded": function(node,obj) {
|
obj.processSucceeded = true;
|
},
|
"ProcessOutputs": function(node, processDescription) {
|
processDescription.processOutputs = [];
|
this.readChildNodes(node, processDescription.processOutputs);
|
},
|
"Output": function(node, processOutputs) {
|
var output = {};
|
this.readChildNodes(node, output);
|
processOutputs.push(output);
|
},
|
"Reference": function(node, output) {
|
output.reference = {
|
href: node.getAttribute("href"),
|
mimeType: node.getAttribute("mimeType"),
|
encoding: node.getAttribute("encoding"),
|
schema: node.getAttribute("schema")
|
};
|
},
|
"Data": function(node, output) {
|
output.data = {};
|
this.readChildNodes(node, output);
|
},
|
"LiteralData": function(node, output) {
|
output.literalData = {
|
dataType: node.getAttribute("dataType"),
|
uom: node.getAttribute("uom"),
|
value: this.getChildValue(node)
|
};
|
},
|
"ComplexData": function(node, output) {
|
output.complexData = {
|
mimeType: node.getAttribute("mimeType"),
|
schema: node.getAttribute("schema"),
|
encoding: node.getAttribute("encoding"),
|
value: ""
|
};
|
|
// try to get *some* value, ignore the empty text values
|
if (this.isSimpleContent(node)) {
|
var child;
|
for(child=node.firstChild; child; child=child.nextSibling) {
|
switch(child.nodeType) {
|
case 3: // text node
|
case 4: // cdata section
|
output.complexData.value += child.nodeValue;
|
}
|
}
|
}
|
else {
|
for(child=node.firstChild; child; child=child.nextSibling) {
|
if (child.nodeType == 1) {
|
output.complexData.value = child;
|
}
|
}
|
}
|
|
},
|
"BoundingBox": function(node, output) {
|
output.boundingBoxData = {
|
dimensions: node.getAttribute("dimensions"),
|
crs: node.getAttribute("crs")
|
};
|
this.readChildNodes(node, output.boundingBoxData);
|
}
|
},
|
|
// TODO: we should add Exception parsing here
|
"ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"]
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WPSExecute"
|
|
});
|
/* ======================================================================
|
OpenLayers/Layer/GeoRSS.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Layer/Markers.js
|
* @requires OpenLayers/Request/XMLHttpRequest.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.GeoRSS
|
* Add GeoRSS Point features to your map.
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer.Markers>
|
*/
|
OpenLayers.Layer.GeoRSS = OpenLayers.Class(OpenLayers.Layer.Markers, {
|
|
/**
|
* Property: location
|
* {String} store url of text file
|
*/
|
location: null,
|
|
/**
|
* Property: features
|
* {Array(<OpenLayers.Feature>)}
|
*/
|
features: null,
|
|
/**
|
* APIProperty: formatOptions
|
* {Object} Hash of options which should be passed to the format when it is
|
* created. Must be passed in the constructor.
|
*/
|
formatOptions: null,
|
|
/**
|
* Property: selectedFeature
|
* {<OpenLayers.Feature>}
|
*/
|
selectedFeature: null,
|
|
/**
|
* APIProperty: icon
|
* {<OpenLayers.Icon>}. This determines the Icon to be used on the map
|
* for this GeoRSS layer.
|
*/
|
icon: null,
|
|
/**
|
* APIProperty: popupSize
|
* {<OpenLayers.Size>} This determines the size of GeoRSS popups. If
|
* not provided, defaults to 250px by 120px.
|
*/
|
popupSize: null,
|
|
/**
|
* APIProperty: useFeedTitle
|
* {Boolean} Set layer.name to the first <title> element in the feed. Default is true.
|
*/
|
useFeedTitle: true,
|
|
/**
|
* Constructor: OpenLayers.Layer.GeoRSS
|
* Create a GeoRSS Layer.
|
*
|
* Parameters:
|
* name - {String}
|
* location - {String}
|
* options - {Object}
|
*/
|
initialize: function(name, location, options) {
|
OpenLayers.Layer.Markers.prototype.initialize.apply(this, [name, options]);
|
this.location = location;
|
this.features = [];
|
},
|
|
/**
|
* Method: destroy
|
*/
|
destroy: function() {
|
// Warning: Layer.Markers.destroy() must be called prior to calling
|
// clearFeatures() here, otherwise we leak memory. Indeed, if
|
// Layer.Markers.destroy() is called after clearFeatures(), it won't be
|
// able to remove the marker image elements from the layer's div since
|
// the markers will have been destroyed by clearFeatures().
|
OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);
|
this.clearFeatures();
|
this.features = null;
|
},
|
|
/**
|
* Method: loadRSS
|
* Start the load of the RSS data. Don't do this when we first add the layer,
|
* since we may not be visible at any point, and it would therefore be a waste.
|
*/
|
loadRSS: function() {
|
if (!this.loaded) {
|
this.events.triggerEvent("loadstart");
|
OpenLayers.Request.GET({
|
url: this.location,
|
success: this.parseData,
|
scope: this
|
});
|
this.loaded = true;
|
}
|
},
|
|
/**
|
* Method: moveTo
|
* If layer is visible and RSS has not been loaded, load RSS.
|
*
|
* Parameters:
|
* bounds - {Object}
|
* zoomChanged - {Object}
|
* minor - {Object}
|
*/
|
moveTo:function(bounds, zoomChanged, minor) {
|
OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);
|
if(this.visibility && !this.loaded){
|
this.loadRSS();
|
}
|
},
|
|
/**
|
* Method: parseData
|
* Parse the data returned from the Events call.
|
*
|
* Parameters:
|
* ajaxRequest - {<OpenLayers.Request.XMLHttpRequest>}
|
*/
|
parseData: function(ajaxRequest) {
|
var doc = ajaxRequest.responseXML;
|
if (!doc || !doc.documentElement) {
|
doc = OpenLayers.Format.XML.prototype.read(ajaxRequest.responseText);
|
}
|
|
if (this.useFeedTitle) {
|
var name = null;
|
try {
|
name = doc.getElementsByTagNameNS('*', 'title')[0].firstChild.nodeValue;
|
}
|
catch (e) {
|
name = doc.getElementsByTagName('title')[0].firstChild.nodeValue;
|
}
|
if (name) {
|
this.setName(name);
|
}
|
}
|
|
var options = {};
|
|
OpenLayers.Util.extend(options, this.formatOptions);
|
|
if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
|
options.externalProjection = this.projection;
|
options.internalProjection = this.map.getProjectionObject();
|
}
|
|
var format = new OpenLayers.Format.GeoRSS(options);
|
var features = format.read(doc);
|
|
for (var i=0, len=features.length; i<len; i++) {
|
var data = {};
|
var feature = features[i];
|
|
// we don't support features with no geometry in the GeoRSS
|
// layer at this time.
|
if (!feature.geometry) {
|
continue;
|
}
|
|
var title = feature.attributes.title ?
|
feature.attributes.title : "Untitled";
|
|
var description = feature.attributes.description ?
|
feature.attributes.description : "No description.";
|
|
var link = feature.attributes.link ? feature.attributes.link : "";
|
|
var location = feature.geometry.getBounds().getCenterLonLat();
|
|
|
data.icon = this.icon == null ?
|
OpenLayers.Marker.defaultIcon() :
|
this.icon.clone();
|
|
data.popupSize = this.popupSize ?
|
this.popupSize.clone() :
|
new OpenLayers.Size(250, 120);
|
|
if (title || description) {
|
// we have supplemental data, store them.
|
data.title = title;
|
data.description = description;
|
|
var contentHTML = '<div class="olLayerGeoRSSClose">[x]</div>';
|
contentHTML += '<div class="olLayerGeoRSSTitle">';
|
if (link) {
|
contentHTML += '<a class="link" href="'+link+'" target="_blank">';
|
}
|
contentHTML += title;
|
if (link) {
|
contentHTML += '</a>';
|
}
|
contentHTML += '</div>';
|
contentHTML += '<div style="" class="olLayerGeoRSSDescription">';
|
contentHTML += description;
|
contentHTML += '</div>';
|
data['popupContentHTML'] = contentHTML;
|
}
|
var feature = new OpenLayers.Feature(this, location, data);
|
this.features.push(feature);
|
var marker = feature.createMarker();
|
marker.events.register('click', feature, this.markerClick);
|
this.addMarker(marker);
|
}
|
this.events.triggerEvent("loadend");
|
},
|
|
/**
|
* Method: markerClick
|
*
|
* Parameters:
|
* evt - {Event}
|
*/
|
markerClick: function(evt) {
|
var sameMarkerClicked = (this == this.layer.selectedFeature);
|
this.layer.selectedFeature = (!sameMarkerClicked) ? this : null;
|
for(var i=0, len=this.layer.map.popups.length; i<len; i++) {
|
this.layer.map.removePopup(this.layer.map.popups[i]);
|
}
|
if (!sameMarkerClicked) {
|
var popup = this.createPopup();
|
OpenLayers.Event.observe(popup.div, "click",
|
OpenLayers.Function.bind(function() {
|
for(var i=0, len=this.layer.map.popups.length; i<len; i++) {
|
this.layer.map.removePopup(this.layer.map.popups[i]);
|
}
|
}, this)
|
);
|
this.layer.map.addPopup(popup);
|
}
|
OpenLayers.Event.stop(evt);
|
},
|
|
/**
|
* Method: clearFeatures
|
* Destroy all features in this layer.
|
*/
|
clearFeatures: function() {
|
if (this.features != null) {
|
while(this.features.length > 0) {
|
var feature = this.features[0];
|
OpenLayers.Util.removeItem(this.features, feature);
|
feature.destroy();
|
}
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.GeoRSS"
|
});
|
/* ======================================================================
|
OpenLayers/Symbolizer/Point.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Symbolizer.js
|
*/
|
|
/**
|
* Class: OpenLayers.Symbolizer.Point
|
* A symbolizer used to render point features.
|
*/
|
OpenLayers.Symbolizer.Point = OpenLayers.Class(OpenLayers.Symbolizer, {
|
|
/**
|
* APIProperty: strokeColor
|
* {String} Color for line stroke. This is a RGB hex value (e.g. "#ff0000"
|
* for red).
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* APIProperty: strokeOpacity
|
* {Number} Stroke opacity (0-1).
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* APIProperty: strokeWidth
|
* {Number} Pixel stroke width.
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* APIProperty: strokeLinecap
|
* {String} Stroke cap type ("butt", "round", or "square").
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* Property: strokeDashstyle
|
* {String} Stroke dash style according to the SLD spec. Note that the
|
* OpenLayers values for strokeDashstyle ("dot", "dash", "dashdot",
|
* "longdash", "longdashdot", or "solid") will not work in SLD, but
|
* most SLD patterns will render correctly in OpenLayers.
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* APIProperty: fillColor
|
* {String} RGB hex fill color (e.g. "#ff0000" for red).
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* APIProperty: fillOpacity
|
* {Number} Fill opacity (0-1).
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* APIProperty: pointRadius
|
* {Number} Pixel point radius.
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* APIProperty: externalGraphic
|
* {String} Url to an external graphic that will be used for rendering
|
* points.
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* APIProperty: graphicWidth
|
* {Number} Pixel width for sizing an external graphic.
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* APIProperty: graphicHeight
|
* {Number} Pixel height for sizing an external graphic.
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* APIProperty: graphicOpacity
|
* {Number} Opacity (0-1) for an external graphic.
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* APIProperty: graphicXOffset
|
* {Number} Pixel offset along the positive x axis for displacing an
|
* external graphic.
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* APIProperty: graphicYOffset
|
* {Number} Pixel offset along the positive y axis for displacing an
|
* external graphic.
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* APIProperty: rotation
|
* {Number} The rotation of a graphic in the clockwise direction about its
|
* center point (or any point off center as specified by
|
* <graphicXOffset> and <graphicYOffset>).
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* APIProperty: graphicName
|
* {String} Named graphic to use when rendering points. Supported values
|
* include "circle", "square", "star", "x", "cross", and "triangle".
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* Constructor: OpenLayers.Symbolizer.Point
|
* Create a symbolizer for rendering points.
|
*
|
* Parameters:
|
* config - {Object} An object containing properties to be set on the
|
* symbolizer. Any documented symbolizer property can be set at
|
* construction.
|
*
|
* Returns:
|
* A new point symbolizer.
|
*/
|
initialize: function(config) {
|
OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);
|
},
|
|
CLASS_NAME: "OpenLayers.Symbolizer.Point"
|
|
});
|
|
/* ======================================================================
|
OpenLayers/Symbolizer/Line.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Symbolizer.js
|
*/
|
|
/**
|
* Class: OpenLayers.Symbolizer.Line
|
* A symbolizer used to render line features.
|
*/
|
OpenLayers.Symbolizer.Line = OpenLayers.Class(OpenLayers.Symbolizer, {
|
|
/**
|
* APIProperty: strokeColor
|
* {String} Color for line stroke. This is a RGB hex value (e.g. "#ff0000"
|
* for red).
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* APIProperty: strokeOpacity
|
* {Number} Stroke opacity (0-1).
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* APIProperty: strokeWidth
|
* {Number} Pixel stroke width.
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* APIProperty: strokeLinecap
|
* {String} Stroke cap type ("butt", "round", or "square").
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* Property: strokeDashstyle
|
* {String} Stroke dash style according to the SLD spec. Note that the
|
* OpenLayers values for strokeDashstyle ("dot", "dash", "dashdot",
|
* "longdash", "longdashdot", or "solid") will not work in SLD, but
|
* most SLD patterns will render correctly in OpenLayers.
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* Constructor: OpenLayers.Symbolizer.Line
|
* Create a symbolizer for rendering lines.
|
*
|
* Parameters:
|
* config - {Object} An object containing properties to be set on the
|
* symbolizer. Any documented symbolizer property can be set at
|
* construction.
|
*
|
* Returns:
|
* A new line symbolizer.
|
*/
|
initialize: function(config) {
|
OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);
|
},
|
|
CLASS_NAME: "OpenLayers.Symbolizer.Line"
|
|
});
|
|
/* ======================================================================
|
OpenLayers/Symbolizer/Text.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Symbolizer.js
|
*/
|
|
/**
|
* Class: OpenLayers.Symbolizer.Text
|
* A symbolizer used to render text labels for features.
|
*/
|
OpenLayers.Symbolizer.Text = OpenLayers.Class(OpenLayers.Symbolizer, {
|
|
/**
|
* APIProperty: label
|
* {String} The text for the label.
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* APIProperty: fontFamily
|
* {String} The font family for the label.
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* APIProperty: fontSize
|
* {String} The font size for the label.
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* APIProperty: fontWeight
|
* {String} The font weight for the label.
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* Property: fontStyle
|
* {String} The font style for the label.
|
*
|
* No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
|
*/
|
|
/**
|
* Constructor: OpenLayers.Symbolizer.Text
|
* Create a symbolizer for rendering text labels.
|
*
|
* Parameters:
|
* config - {Object} An object containing properties to be set on the
|
* symbolizer. Any documented symbolizer property can be set at
|
* construction.
|
*
|
* Returns:
|
* A new text symbolizer.
|
*/
|
initialize: function(config) {
|
OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);
|
},
|
|
CLASS_NAME: "OpenLayers.Symbolizer.Text"
|
|
});
|
|
/* ======================================================================
|
OpenLayers/Format/SLD/v1.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Rule.js
|
* @requires OpenLayers/Format/SLD.js
|
* @requires OpenLayers/Format/Filter/v1_0_0.js
|
* @requires OpenLayers/Symbolizer/Point.js
|
* @requires OpenLayers/Symbolizer/Line.js
|
* @requires OpenLayers/Symbolizer/Polygon.js
|
* @requires OpenLayers/Symbolizer/Text.js
|
* @requires OpenLayers/Symbolizer/Raster.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.SLD.v1
|
* Superclass for SLD version 1 parsers.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.Filter.v1_0_0>
|
*/
|
OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, {
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
sld: "http://www.opengis.net/sld",
|
ogc: "http://www.opengis.net/ogc",
|
gml: "http://www.opengis.net/gml",
|
xlink: "http://www.w3.org/1999/xlink",
|
xsi: "http://www.w3.org/2001/XMLSchema-instance"
|
},
|
|
/**
|
* Property: defaultPrefix
|
*/
|
defaultPrefix: "sld",
|
|
/**
|
* Property: schemaLocation
|
* {String} Schema location for a particular minor version.
|
*/
|
schemaLocation: null,
|
|
/**
|
* APIProperty: multipleSymbolizers
|
* {Boolean} Support multiple symbolizers per rule. Default is false. if
|
* true, an OpenLayers.Style2 instance will be created to represent
|
* user styles instead of an OpenLayers.Style instace. The
|
* OpenLayers.Style2 class allows collections of rules with multiple
|
* symbolizers, but is not currently useful for client side rendering.
|
* If multiple symbolizers is true, multiple FeatureTypeStyle elements
|
* are preserved in reading/writing by setting symbolizer zIndex values.
|
* In addition, the <defaultSymbolizer> property is ignored if
|
* multiple symbolizers are supported (defaults should be applied
|
* when rendering).
|
*/
|
multipleSymbolizers: false,
|
|
/**
|
* Property: featureTypeCounter
|
* {Number} Private counter for multiple feature type styles.
|
*/
|
featureTypeCounter: null,
|
|
/**
|
* APIProperty: defaultSymbolizer.
|
* {Object} A symbolizer with the SLD defaults.
|
*/
|
defaultSymbolizer: {
|
fillColor: "#808080",
|
fillOpacity: 1,
|
strokeColor: "#000000",
|
strokeOpacity: 1,
|
strokeWidth: 1,
|
strokeDashstyle: "solid",
|
pointRadius: 3,
|
graphicName: "square"
|
},
|
|
/**
|
* Constructor: OpenLayers.Format.SLD.v1
|
* Instances of this class are not created directly. Use the
|
* <OpenLayers.Format.SLD> constructor instead.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* Method: read
|
*
|
* Parameters:
|
* data - {DOMElement} An SLD document element.
|
* options - {Object} Options for the reader.
|
*
|
* Valid options:
|
* namedLayersAsArray - {Boolean} Generate a namedLayers array. If false,
|
* the namedLayers property value will be an object keyed by layer name.
|
* Default is false.
|
*
|
* Returns:
|
* {Object} An object representing the SLD.
|
*/
|
read: function(data, options) {
|
options = OpenLayers.Util.applyDefaults(options, this.options);
|
var sld = {
|
namedLayers: options.namedLayersAsArray === true ? [] : {}
|
};
|
this.readChildNodes(data, sld);
|
return sld;
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: OpenLayers.Util.applyDefaults({
|
"sld": {
|
"StyledLayerDescriptor": function(node, sld) {
|
sld.version = node.getAttribute("version");
|
this.readChildNodes(node, sld);
|
},
|
"Name": function(node, obj) {
|
obj.name = this.getChildValue(node);
|
},
|
"Title": function(node, obj) {
|
obj.title = this.getChildValue(node);
|
},
|
"Abstract": function(node, obj) {
|
obj.description = this.getChildValue(node);
|
},
|
"NamedLayer": function(node, sld) {
|
var layer = {
|
userStyles: [],
|
namedStyles: []
|
};
|
this.readChildNodes(node, layer);
|
// give each of the user styles this layer name
|
for(var i=0, len=layer.userStyles.length; i<len; ++i) {
|
layer.userStyles[i].layerName = layer.name;
|
}
|
if(OpenLayers.Util.isArray(sld.namedLayers)) {
|
sld.namedLayers.push(layer);
|
} else {
|
sld.namedLayers[layer.name] = layer;
|
}
|
},
|
"NamedStyle": function(node, layer) {
|
layer.namedStyles.push(
|
this.getChildName(node.firstChild)
|
);
|
},
|
"UserStyle": function(node, layer) {
|
var obj = {defaultsPerSymbolizer: true, rules: []};
|
this.featureTypeCounter = -1;
|
this.readChildNodes(node, obj);
|
var style;
|
if (this.multipleSymbolizers) {
|
delete obj.defaultsPerSymbolizer;
|
style = new OpenLayers.Style2(obj);
|
} else {
|
style = new OpenLayers.Style(this.defaultSymbolizer, obj);
|
}
|
layer.userStyles.push(style);
|
},
|
"IsDefault": function(node, style) {
|
if(this.getChildValue(node) == "1") {
|
style.isDefault = true;
|
}
|
},
|
"FeatureTypeStyle": function(node, style) {
|
++this.featureTypeCounter;
|
var obj = {
|
rules: this.multipleSymbolizers ? style.rules : []
|
};
|
this.readChildNodes(node, obj);
|
if (!this.multipleSymbolizers) {
|
style.rules = obj.rules;
|
}
|
},
|
"Rule": function(node, obj) {
|
var config;
|
if (this.multipleSymbolizers) {
|
config = {symbolizers: []};
|
}
|
var rule = new OpenLayers.Rule(config);
|
this.readChildNodes(node, rule);
|
obj.rules.push(rule);
|
},
|
"ElseFilter": function(node, rule) {
|
rule.elseFilter = true;
|
},
|
"MinScaleDenominator": function(node, rule) {
|
rule.minScaleDenominator = parseFloat(this.getChildValue(node));
|
},
|
"MaxScaleDenominator": function(node, rule) {
|
rule.maxScaleDenominator = parseFloat(this.getChildValue(node));
|
},
|
"TextSymbolizer": function(node, rule) {
|
var config = {};
|
this.readChildNodes(node, config);
|
if (this.multipleSymbolizers) {
|
config.zIndex = this.featureTypeCounter;
|
rule.symbolizers.push(
|
new OpenLayers.Symbolizer.Text(config)
|
);
|
} else {
|
rule.symbolizer["Text"] = OpenLayers.Util.applyDefaults(
|
config, rule.symbolizer["Text"]
|
);
|
}
|
},
|
"LabelPlacement": function(node, symbolizer) {
|
this.readChildNodes(node, symbolizer);
|
},
|
"PointPlacement": function(node, symbolizer) {
|
var config = {};
|
this.readChildNodes(node, config);
|
config.labelRotation = config.rotation;
|
delete config.rotation;
|
var labelAlign,
|
x = symbolizer.labelAnchorPointX,
|
y = symbolizer.labelAnchorPointY;
|
if (x <= 1/3) {
|
labelAlign = 'l';
|
} else if (x > 1/3 && x < 2/3) {
|
labelAlign = 'c';
|
} else if (x >= 2/3) {
|
labelAlign = 'r';
|
}
|
if (y <= 1/3) {
|
labelAlign += 'b';
|
} else if (y > 1/3 && y < 2/3) {
|
labelAlign += 'm';
|
} else if (y >= 2/3) {
|
labelAlign += 't';
|
}
|
config.labelAlign = labelAlign;
|
OpenLayers.Util.applyDefaults(symbolizer, config);
|
},
|
"AnchorPoint": function(node, symbolizer) {
|
this.readChildNodes(node, symbolizer);
|
},
|
"AnchorPointX": function(node, symbolizer) {
|
var labelAnchorPointX = this.readers.ogc._expression.call(this, node);
|
// always string, could be empty string
|
if(labelAnchorPointX) {
|
symbolizer.labelAnchorPointX = labelAnchorPointX;
|
}
|
},
|
"AnchorPointY": function(node, symbolizer) {
|
var labelAnchorPointY = this.readers.ogc._expression.call(this, node);
|
// always string, could be empty string
|
if(labelAnchorPointY) {
|
symbolizer.labelAnchorPointY = labelAnchorPointY;
|
}
|
},
|
"Displacement": function(node, symbolizer) {
|
this.readChildNodes(node, symbolizer);
|
},
|
"DisplacementX": function(node, symbolizer) {
|
var labelXOffset = this.readers.ogc._expression.call(this, node);
|
// always string, could be empty string
|
if(labelXOffset) {
|
symbolizer.labelXOffset = labelXOffset;
|
}
|
},
|
"DisplacementY": function(node, symbolizer) {
|
var labelYOffset = this.readers.ogc._expression.call(this, node);
|
// always string, could be empty string
|
if(labelYOffset) {
|
symbolizer.labelYOffset = labelYOffset;
|
}
|
},
|
"LinePlacement": function(node, symbolizer) {
|
this.readChildNodes(node, symbolizer);
|
},
|
"PerpendicularOffset": function(node, symbolizer) {
|
var labelPerpendicularOffset = this.readers.ogc._expression.call(this, node);
|
// always string, could be empty string
|
if(labelPerpendicularOffset) {
|
symbolizer.labelPerpendicularOffset = labelPerpendicularOffset;
|
}
|
},
|
"Label": function(node, symbolizer) {
|
var value = this.readers.ogc._expression.call(this, node);
|
if (value) {
|
symbolizer.label = value;
|
}
|
},
|
"Font": function(node, symbolizer) {
|
this.readChildNodes(node, symbolizer);
|
},
|
"Halo": function(node, symbolizer) {
|
// halo has a fill, so send fresh object
|
var obj = {};
|
this.readChildNodes(node, obj);
|
symbolizer.haloRadius = obj.haloRadius;
|
symbolizer.haloColor = obj.fillColor;
|
symbolizer.haloOpacity = obj.fillOpacity;
|
},
|
"Radius": function(node, symbolizer) {
|
var radius = this.readers.ogc._expression.call(this, node);
|
if(radius != null) {
|
// radius is only used for halo
|
symbolizer.haloRadius = radius;
|
}
|
},
|
"RasterSymbolizer": function(node, rule) {
|
var config = {};
|
this.readChildNodes(node, config);
|
if (this.multipleSymbolizers) {
|
config.zIndex = this.featureTypeCounter;
|
rule.symbolizers.push(
|
new OpenLayers.Symbolizer.Raster(config)
|
);
|
} else {
|
rule.symbolizer["Raster"] = OpenLayers.Util.applyDefaults(
|
config, rule.symbolizer["Raster"]
|
);
|
}
|
},
|
"Geometry": function(node, obj) {
|
obj.geometry = {};
|
this.readChildNodes(node, obj.geometry);
|
},
|
"ColorMap": function(node, symbolizer) {
|
symbolizer.colorMap = [];
|
this.readChildNodes(node, symbolizer.colorMap);
|
},
|
"ColorMapEntry": function(node, colorMap) {
|
var q = node.getAttribute("quantity");
|
var o = node.getAttribute("opacity");
|
colorMap.push({
|
color: node.getAttribute("color"),
|
quantity: q !== null ? parseFloat(q) : undefined,
|
label: node.getAttribute("label") || undefined,
|
opacity: o !== null ? parseFloat(o) : undefined
|
});
|
},
|
"LineSymbolizer": function(node, rule) {
|
var config = {};
|
this.readChildNodes(node, config);
|
if (this.multipleSymbolizers) {
|
config.zIndex = this.featureTypeCounter;
|
rule.symbolizers.push(
|
new OpenLayers.Symbolizer.Line(config)
|
);
|
} else {
|
rule.symbolizer["Line"] = OpenLayers.Util.applyDefaults(
|
config, rule.symbolizer["Line"]
|
);
|
}
|
},
|
"PolygonSymbolizer": function(node, rule) {
|
var config = {
|
fill: false,
|
stroke: false
|
};
|
if (!this.multipleSymbolizers) {
|
config = rule.symbolizer["Polygon"] || config;
|
}
|
this.readChildNodes(node, config);
|
if (this.multipleSymbolizers) {
|
config.zIndex = this.featureTypeCounter;
|
rule.symbolizers.push(
|
new OpenLayers.Symbolizer.Polygon(config)
|
);
|
} else {
|
rule.symbolizer["Polygon"] = config;
|
}
|
},
|
"PointSymbolizer": function(node, rule) {
|
var config = {
|
fill: false,
|
stroke: false,
|
graphic: false
|
};
|
if (!this.multipleSymbolizers) {
|
config = rule.symbolizer["Point"] || config;
|
}
|
this.readChildNodes(node, config);
|
if (this.multipleSymbolizers) {
|
config.zIndex = this.featureTypeCounter;
|
rule.symbolizers.push(
|
new OpenLayers.Symbolizer.Point(config)
|
);
|
} else {
|
rule.symbolizer["Point"] = config;
|
}
|
},
|
"Stroke": function(node, symbolizer) {
|
symbolizer.stroke = true;
|
this.readChildNodes(node, symbolizer);
|
},
|
"Fill": function(node, symbolizer) {
|
symbolizer.fill = true;
|
this.readChildNodes(node, symbolizer);
|
},
|
"CssParameter": function(node, symbolizer) {
|
var cssProperty = node.getAttribute("name");
|
var symProperty = this.cssMap[cssProperty];
|
// for labels, fill should map to fontColor and fill-opacity
|
// to fontOpacity
|
if (symbolizer.label) {
|
if (cssProperty === 'fill') {
|
symProperty = "fontColor";
|
} else if (cssProperty === 'fill-opacity') {
|
symProperty = "fontOpacity";
|
}
|
}
|
if(symProperty) {
|
// Limited support for parsing of OGC expressions
|
var value = this.readers.ogc._expression.call(this, node);
|
// always string, could be an empty string
|
if(value) {
|
symbolizer[symProperty] = value;
|
}
|
}
|
},
|
"Graphic": function(node, symbolizer) {
|
symbolizer.graphic = true;
|
var graphic = {};
|
// painter's order not respected here, clobber previous with next
|
this.readChildNodes(node, graphic);
|
// directly properties with names that match symbolizer properties
|
var properties = [
|
"stroke", "strokeColor", "strokeWidth", "strokeOpacity",
|
"strokeLinecap", "fill", "fillColor", "fillOpacity",
|
"graphicName", "rotation", "graphicFormat"
|
];
|
var prop, value;
|
for(var i=0, len=properties.length; i<len; ++i) {
|
prop = properties[i];
|
value = graphic[prop];
|
if(value != undefined) {
|
symbolizer[prop] = value;
|
}
|
}
|
// set other generic properties with specific graphic property names
|
if(graphic.opacity != undefined) {
|
symbolizer.graphicOpacity = graphic.opacity;
|
}
|
if(graphic.size != undefined) {
|
var pointRadius = graphic.size / 2;
|
if (isNaN(pointRadius)) {
|
// likely a property name
|
symbolizer.graphicWidth = graphic.size;
|
} else {
|
symbolizer.pointRadius = graphic.size / 2;
|
}
|
}
|
if(graphic.href != undefined) {
|
symbolizer.externalGraphic = graphic.href;
|
}
|
if(graphic.rotation != undefined) {
|
symbolizer.rotation = graphic.rotation;
|
}
|
},
|
"ExternalGraphic": function(node, graphic) {
|
this.readChildNodes(node, graphic);
|
},
|
"Mark": function(node, graphic) {
|
this.readChildNodes(node, graphic);
|
},
|
"WellKnownName": function(node, graphic) {
|
graphic.graphicName = this.getChildValue(node);
|
},
|
"Opacity": function(node, obj) {
|
var opacity = this.readers.ogc._expression.call(this, node);
|
// always string, could be empty string
|
if(opacity) {
|
obj.opacity = opacity;
|
}
|
},
|
"Size": function(node, obj) {
|
var size = this.readers.ogc._expression.call(this, node);
|
// always string, could be empty string
|
if(size) {
|
obj.size = size;
|
}
|
},
|
"Rotation": function(node, obj) {
|
var rotation = this.readers.ogc._expression.call(this, node);
|
// always string, could be empty string
|
if(rotation) {
|
obj.rotation = rotation;
|
}
|
},
|
"OnlineResource": function(node, obj) {
|
obj.href = this.getAttributeNS(
|
node, this.namespaces.xlink, "href"
|
);
|
},
|
"Format": function(node, graphic) {
|
graphic.graphicFormat = this.getChildValue(node);
|
}
|
}
|
}, OpenLayers.Format.Filter.v1_0_0.prototype.readers),
|
|
/**
|
* Property: cssMap
|
* {Object} Object mapping supported css property names to OpenLayers
|
* symbolizer property names.
|
*/
|
cssMap: {
|
"stroke": "strokeColor",
|
"stroke-opacity": "strokeOpacity",
|
"stroke-width": "strokeWidth",
|
"stroke-linecap": "strokeLinecap",
|
"stroke-dasharray": "strokeDashstyle",
|
"fill": "fillColor",
|
"fill-opacity": "fillOpacity",
|
"font-family": "fontFamily",
|
"font-size": "fontSize",
|
"font-weight": "fontWeight",
|
"font-style": "fontStyle"
|
},
|
|
/**
|
* Method: getCssProperty
|
* Given a symbolizer property, get the corresponding CSS property
|
* from the <cssMap>.
|
*
|
* Parameters:
|
* sym - {String} A symbolizer property name.
|
*
|
* Returns:
|
* {String} A CSS property name or null if none found.
|
*/
|
getCssProperty: function(sym) {
|
var css = null;
|
for(var prop in this.cssMap) {
|
if(this.cssMap[prop] == sym) {
|
css = prop;
|
break;
|
}
|
}
|
return css;
|
},
|
|
/**
|
* Method: getGraphicFormat
|
* Given a href for an external graphic, try to determine the mime-type.
|
* This method doesn't try too hard, and will fall back to
|
* <defaultGraphicFormat> if one of the known <graphicFormats> is not
|
* the file extension of the provided href.
|
*
|
* Parameters:
|
* href - {String}
|
*
|
* Returns:
|
* {String} The graphic format.
|
*/
|
getGraphicFormat: function(href) {
|
var format, regex;
|
for(var key in this.graphicFormats) {
|
if(this.graphicFormats[key].test(href)) {
|
format = key;
|
break;
|
}
|
}
|
return format || this.defaultGraphicFormat;
|
},
|
|
/**
|
* Property: defaultGraphicFormat
|
* {String} If none other can be determined from <getGraphicFormat>, this
|
* default will be returned.
|
*/
|
defaultGraphicFormat: "image/png",
|
|
/**
|
* Property: graphicFormats
|
* {Object} Mapping of image mime-types to regular extensions matching
|
* well-known file extensions.
|
*/
|
graphicFormats: {
|
"image/jpeg": /\.jpe?g$/i,
|
"image/gif": /\.gif$/i,
|
"image/png": /\.png$/i
|
},
|
|
/**
|
* Method: write
|
*
|
* Parameters:
|
* sld - {Object} An object representing the SLD.
|
*
|
* Returns:
|
* {DOMElement} The root of an SLD document.
|
*/
|
write: function(sld) {
|
return this.writers.sld.StyledLayerDescriptor.apply(this, [sld]);
|
},
|
|
/**
|
* Property: writers
|
* As a compliment to the readers property, this structure contains public
|
* writing functions grouped by namespace alias and named like the
|
* node names they produce.
|
*/
|
writers: OpenLayers.Util.applyDefaults({
|
"sld": {
|
"_OGCExpression": function(nodeName, value) {
|
// only the simplest of ogc:expression handled
|
// {label: "some text and a ${propertyName}"}
|
var node = this.createElementNSPlus(nodeName);
|
var tokens = typeof value == "string" ?
|
value.split("${") :
|
[value];
|
node.appendChild(this.createTextNode(tokens[0]));
|
var item, last;
|
for(var i=1, len=tokens.length; i<len; i++) {
|
item = tokens[i];
|
last = item.indexOf("}");
|
if(last > 0) {
|
this.writeNode(
|
"ogc:PropertyName",
|
{property: item.substring(0, last)},
|
node
|
);
|
node.appendChild(
|
this.createTextNode(item.substring(++last))
|
);
|
} else {
|
// no ending }, so this is a literal ${
|
node.appendChild(
|
this.createTextNode("${" + item)
|
);
|
}
|
}
|
return node;
|
},
|
"StyledLayerDescriptor": function(sld) {
|
var root = this.createElementNSPlus(
|
"sld:StyledLayerDescriptor",
|
{attributes: {
|
"version": this.VERSION,
|
"xsi:schemaLocation": this.schemaLocation
|
}}
|
);
|
|
// For ArcGIS Server it is necessary to define this
|
// at the root level (see ticket:2166).
|
root.setAttribute("xmlns:ogc", this.namespaces.ogc);
|
root.setAttribute("xmlns:gml", this.namespaces.gml);
|
|
// add in optional name
|
if(sld.name) {
|
this.writeNode("Name", sld.name, root);
|
}
|
// add in optional title
|
if(sld.title) {
|
this.writeNode("Title", sld.title, root);
|
}
|
// add in optional description
|
if(sld.description) {
|
this.writeNode("Abstract", sld.description, root);
|
}
|
// add in named layers
|
// allow namedLayers to be an array
|
if(OpenLayers.Util.isArray(sld.namedLayers)) {
|
for(var i=0, len=sld.namedLayers.length; i<len; ++i) {
|
this.writeNode("NamedLayer", sld.namedLayers[i], root);
|
}
|
} else {
|
for(var name in sld.namedLayers) {
|
this.writeNode("NamedLayer", sld.namedLayers[name], root);
|
}
|
}
|
return root;
|
},
|
"Name": function(name) {
|
return this.createElementNSPlus("sld:Name", {value: name});
|
},
|
"Title": function(title) {
|
return this.createElementNSPlus("sld:Title", {value: title});
|
},
|
"Abstract": function(description) {
|
return this.createElementNSPlus(
|
"sld:Abstract", {value: description}
|
);
|
},
|
"NamedLayer": function(layer) {
|
var node = this.createElementNSPlus("sld:NamedLayer");
|
|
// add in required name
|
this.writeNode("Name", layer.name, node);
|
|
// optional sld:LayerFeatureConstraints here
|
|
// add in named styles
|
if(layer.namedStyles) {
|
for(var i=0, len=layer.namedStyles.length; i<len; ++i) {
|
this.writeNode(
|
"NamedStyle", layer.namedStyles[i], node
|
);
|
}
|
}
|
|
// add in user styles
|
if(layer.userStyles) {
|
for(var i=0, len=layer.userStyles.length; i<len; ++i) {
|
this.writeNode(
|
"UserStyle", layer.userStyles[i], node
|
);
|
}
|
}
|
|
return node;
|
},
|
"NamedStyle": function(name) {
|
var node = this.createElementNSPlus("sld:NamedStyle");
|
this.writeNode("Name", name, node);
|
return node;
|
},
|
"UserStyle": function(style) {
|
var node = this.createElementNSPlus("sld:UserStyle");
|
|
// add in optional name
|
if(style.name) {
|
this.writeNode("Name", style.name, node);
|
}
|
// add in optional title
|
if(style.title) {
|
this.writeNode("Title", style.title, node);
|
}
|
// add in optional description
|
if(style.description) {
|
this.writeNode("Abstract", style.description, node);
|
}
|
|
// add isdefault
|
if(style.isDefault) {
|
this.writeNode("IsDefault", style.isDefault, node);
|
}
|
|
// add FeatureTypeStyles
|
if (this.multipleSymbolizers && style.rules) {
|
// group style objects by symbolizer zIndex
|
var rulesByZ = {
|
0: []
|
};
|
var zValues = [0];
|
var rule, ruleMap, symbolizer, zIndex, clone;
|
for (var i=0, ii=style.rules.length; i<ii; ++i) {
|
rule = style.rules[i];
|
if (rule.symbolizers) {
|
ruleMap = {};
|
for (var j=0, jj=rule.symbolizers.length; j<jj; ++j) {
|
symbolizer = rule.symbolizers[j];
|
zIndex = symbolizer.zIndex;
|
if (!(zIndex in ruleMap)) {
|
clone = rule.clone();
|
clone.symbolizers = [];
|
ruleMap[zIndex] = clone;
|
}
|
ruleMap[zIndex].symbolizers.push(symbolizer.clone());
|
}
|
for (zIndex in ruleMap) {
|
if (!(zIndex in rulesByZ)) {
|
zValues.push(zIndex);
|
rulesByZ[zIndex] = [];
|
}
|
rulesByZ[zIndex].push(ruleMap[zIndex]);
|
}
|
} else {
|
// no symbolizers in rule
|
rulesByZ[0].push(rule.clone());
|
}
|
}
|
// write one FeatureTypeStyle per zIndex
|
zValues.sort();
|
var rules;
|
for (var i=0, ii=zValues.length; i<ii; ++i) {
|
rules = rulesByZ[zValues[i]];
|
if (rules.length > 0) {
|
clone = style.clone();
|
clone.rules = rulesByZ[zValues[i]];
|
this.writeNode("FeatureTypeStyle", clone, node);
|
}
|
}
|
} else {
|
this.writeNode("FeatureTypeStyle", style, node);
|
}
|
|
return node;
|
},
|
"IsDefault": function(bool) {
|
return this.createElementNSPlus(
|
"sld:IsDefault", {value: (bool) ? "1" : "0"}
|
);
|
},
|
"FeatureTypeStyle": function(style) {
|
var node = this.createElementNSPlus("sld:FeatureTypeStyle");
|
|
// OpenLayers currently stores no Name, Title, Abstract,
|
// FeatureTypeName, or SemanticTypeIdentifier information
|
// related to FeatureTypeStyle
|
|
// add in rules
|
for(var i=0, len=style.rules.length; i<len; ++i) {
|
this.writeNode("Rule", style.rules[i], node);
|
}
|
|
return node;
|
},
|
"Rule": function(rule) {
|
var node = this.createElementNSPlus("sld:Rule");
|
|
// add in optional name
|
if(rule.name) {
|
this.writeNode("Name", rule.name, node);
|
}
|
// add in optional title
|
if(rule.title) {
|
this.writeNode("Title", rule.title, node);
|
}
|
// add in optional description
|
if(rule.description) {
|
this.writeNode("Abstract", rule.description, node);
|
}
|
|
// add in LegendGraphic here
|
|
// add in optional filters
|
if(rule.elseFilter) {
|
this.writeNode("ElseFilter", null, node);
|
} else if(rule.filter) {
|
this.writeNode("ogc:Filter", rule.filter, node);
|
}
|
|
// add in scale limits
|
if(rule.minScaleDenominator != undefined) {
|
this.writeNode(
|
"MinScaleDenominator", rule.minScaleDenominator, node
|
);
|
}
|
if(rule.maxScaleDenominator != undefined) {
|
this.writeNode(
|
"MaxScaleDenominator", rule.maxScaleDenominator, node
|
);
|
}
|
|
var type, symbolizer;
|
if (this.multipleSymbolizers && rule.symbolizers) {
|
var symbolizer;
|
for (var i=0, ii=rule.symbolizers.length; i<ii; ++i) {
|
symbolizer = rule.symbolizers[i];
|
type = symbolizer.CLASS_NAME.split(".").pop();
|
this.writeNode(
|
type + "Symbolizer", symbolizer, node
|
);
|
}
|
} else {
|
// add in symbolizers (relies on geometry type keys)
|
var types = OpenLayers.Style.SYMBOLIZER_PREFIXES;
|
for(var i=0, len=types.length; i<len; ++i) {
|
type = types[i];
|
symbolizer = rule.symbolizer[type];
|
if(symbolizer) {
|
this.writeNode(
|
type + "Symbolizer", symbolizer, node
|
);
|
}
|
}
|
}
|
return node;
|
|
},
|
"ElseFilter": function() {
|
return this.createElementNSPlus("sld:ElseFilter");
|
},
|
"MinScaleDenominator": function(scale) {
|
return this.createElementNSPlus(
|
"sld:MinScaleDenominator", {value: scale}
|
);
|
},
|
"MaxScaleDenominator": function(scale) {
|
return this.createElementNSPlus(
|
"sld:MaxScaleDenominator", {value: scale}
|
);
|
},
|
"LineSymbolizer": function(symbolizer) {
|
var node = this.createElementNSPlus("sld:LineSymbolizer");
|
this.writeNode("Stroke", symbolizer, node);
|
return node;
|
},
|
"Stroke": function(symbolizer) {
|
var node = this.createElementNSPlus("sld:Stroke");
|
|
// GraphicFill here
|
// GraphicStroke here
|
|
// add in CssParameters
|
if(symbolizer.strokeColor != undefined) {
|
this.writeNode(
|
"CssParameter",
|
{symbolizer: symbolizer, key: "strokeColor"},
|
node
|
);
|
}
|
if(symbolizer.strokeOpacity != undefined) {
|
this.writeNode(
|
"CssParameter",
|
{symbolizer: symbolizer, key: "strokeOpacity"},
|
node
|
);
|
}
|
if(symbolizer.strokeWidth != undefined) {
|
this.writeNode(
|
"CssParameter",
|
{symbolizer: symbolizer, key: "strokeWidth"},
|
node
|
);
|
}
|
if(symbolizer.strokeDashstyle != undefined && symbolizer.strokeDashstyle !== "solid") {
|
// assumes valid stroke-dasharray value
|
this.writeNode(
|
"CssParameter",
|
{symbolizer: symbolizer, key: "strokeDashstyle"},
|
node
|
);
|
}
|
if(symbolizer.strokeLinecap != undefined) {
|
this.writeNode(
|
"CssParameter",
|
{symbolizer: symbolizer, key: "strokeLinecap"},
|
node
|
);
|
}
|
return node;
|
},
|
"CssParameter": function(obj) {
|
// not handling ogc:expressions for now
|
return this.createElementNSPlus("sld:CssParameter", {
|
attributes: {name: this.getCssProperty(obj.key)},
|
value: obj.symbolizer[obj.key]
|
});
|
},
|
"TextSymbolizer": function(symbolizer) {
|
var node = this.createElementNSPlus("sld:TextSymbolizer");
|
// add in optional Label
|
if(symbolizer.label != null) {
|
this.writeNode("Label", symbolizer.label, node);
|
}
|
// add in optional Font
|
if(symbolizer.fontFamily != null ||
|
symbolizer.fontSize != null ||
|
symbolizer.fontWeight != null ||
|
symbolizer.fontStyle != null) {
|
this.writeNode("Font", symbolizer, node);
|
}
|
// add in optional LabelPlacement
|
if (symbolizer.labelAnchorPointX != null ||
|
symbolizer.labelAnchorPointY != null ||
|
symbolizer.labelAlign != null ||
|
symbolizer.labelXOffset != null ||
|
symbolizer.labelYOffset != null ||
|
symbolizer.labelRotation != null ||
|
symbolizer.labelPerpendicularOffset != null) {
|
this.writeNode("LabelPlacement", symbolizer, node);
|
}
|
// add in optional Halo
|
if(symbolizer.haloRadius != null ||
|
symbolizer.haloColor != null ||
|
symbolizer.haloOpacity != null) {
|
this.writeNode("Halo", symbolizer, node);
|
}
|
// add in optional Fill
|
if(symbolizer.fontColor != null ||
|
symbolizer.fontOpacity != null) {
|
this.writeNode("Fill", {
|
fillColor: symbolizer.fontColor,
|
fillOpacity: symbolizer.fontOpacity
|
}, node);
|
}
|
return node;
|
},
|
"LabelPlacement": function(symbolizer) {
|
var node = this.createElementNSPlus("sld:LabelPlacement");
|
// PointPlacement and LinePlacement are choices, so don't output both
|
if ((symbolizer.labelAnchorPointX != null ||
|
symbolizer.labelAnchorPointY != null ||
|
symbolizer.labelAlign != null ||
|
symbolizer.labelXOffset != null ||
|
symbolizer.labelYOffset != null ||
|
symbolizer.labelRotation != null) &&
|
symbolizer.labelPerpendicularOffset == null) {
|
this.writeNode("PointPlacement", symbolizer, node);
|
}
|
if (symbolizer.labelPerpendicularOffset != null) {
|
this.writeNode("LinePlacement", symbolizer, node);
|
}
|
return node;
|
},
|
"LinePlacement": function(symbolizer) {
|
var node = this.createElementNSPlus("sld:LinePlacement");
|
this.writeNode("PerpendicularOffset", symbolizer.labelPerpendicularOffset, node);
|
return node;
|
},
|
"PerpendicularOffset": function(value) {
|
return this.createElementNSPlus("sld:PerpendicularOffset", {
|
value: value
|
});
|
},
|
"PointPlacement": function(symbolizer) {
|
var node = this.createElementNSPlus("sld:PointPlacement");
|
if (symbolizer.labelAnchorPointX != null ||
|
symbolizer.labelAnchorPointY != null ||
|
symbolizer.labelAlign != null) {
|
this.writeNode("AnchorPoint", symbolizer, node);
|
}
|
if (symbolizer.labelXOffset != null ||
|
symbolizer.labelYOffset != null) {
|
this.writeNode("Displacement", symbolizer, node);
|
}
|
if (symbolizer.labelRotation != null) {
|
this.writeNode("Rotation", symbolizer.labelRotation, node);
|
}
|
return node;
|
},
|
"AnchorPoint": function(symbolizer) {
|
var node = this.createElementNSPlus("sld:AnchorPoint");
|
var x = symbolizer.labelAnchorPointX,
|
y = symbolizer.labelAnchorPointY;
|
if (x != null) {
|
this.writeNode("AnchorPointX", x, node);
|
}
|
if (y != null) {
|
this.writeNode("AnchorPointY", y, node);
|
}
|
if (x == null && y == null) {
|
var xAlign = symbolizer.labelAlign.substr(0, 1),
|
yAlign = symbolizer.labelAlign.substr(1, 1);
|
if (xAlign === "l") {
|
x = 0;
|
} else if (xAlign === "c") {
|
x = 0.5;
|
} else if (xAlign === "r") {
|
x = 1;
|
}
|
if (yAlign === "b") {
|
y = 0;
|
} else if (yAlign === "m") {
|
y = 0.5;
|
} else if (yAlign === "t") {
|
y = 1;
|
}
|
this.writeNode("AnchorPointX", x, node);
|
this.writeNode("AnchorPointY", y, node);
|
}
|
return node;
|
},
|
"AnchorPointX": function(value) {
|
return this.createElementNSPlus("sld:AnchorPointX", {
|
value: value
|
});
|
},
|
"AnchorPointY": function(value) {
|
return this.createElementNSPlus("sld:AnchorPointY", {
|
value: value
|
});
|
},
|
"Displacement": function(symbolizer) {
|
var node = this.createElementNSPlus("sld:Displacement");
|
if (symbolizer.labelXOffset != null) {
|
this.writeNode("DisplacementX", symbolizer.labelXOffset, node);
|
}
|
if (symbolizer.labelYOffset != null) {
|
this.writeNode("DisplacementY", symbolizer.labelYOffset, node);
|
}
|
return node;
|
},
|
"DisplacementX": function(value) {
|
return this.createElementNSPlus("sld:DisplacementX", {
|
value: value
|
});
|
},
|
"DisplacementY": function(value) {
|
return this.createElementNSPlus("sld:DisplacementY", {
|
value: value
|
});
|
},
|
"Font": function(symbolizer) {
|
var node = this.createElementNSPlus("sld:Font");
|
// add in CssParameters
|
if(symbolizer.fontFamily) {
|
this.writeNode(
|
"CssParameter",
|
{symbolizer: symbolizer, key: "fontFamily"},
|
node
|
);
|
}
|
if(symbolizer.fontSize) {
|
this.writeNode(
|
"CssParameter",
|
{symbolizer: symbolizer, key: "fontSize"},
|
node
|
);
|
}
|
if(symbolizer.fontWeight) {
|
this.writeNode(
|
"CssParameter",
|
{symbolizer: symbolizer, key: "fontWeight"},
|
node
|
);
|
}
|
if(symbolizer.fontStyle) {
|
this.writeNode(
|
"CssParameter",
|
{symbolizer: symbolizer, key: "fontStyle"},
|
node
|
);
|
}
|
return node;
|
},
|
"Label": function(label) {
|
return this.writers.sld._OGCExpression.call(
|
this, "sld:Label", label
|
);
|
},
|
"Halo": function(symbolizer) {
|
var node = this.createElementNSPlus("sld:Halo");
|
if(symbolizer.haloRadius) {
|
this.writeNode("Radius", symbolizer.haloRadius, node);
|
}
|
if(symbolizer.haloColor || symbolizer.haloOpacity) {
|
this.writeNode("Fill", {
|
fillColor: symbolizer.haloColor,
|
fillOpacity: symbolizer.haloOpacity
|
}, node);
|
}
|
return node;
|
},
|
"Radius": function(value) {
|
return this.createElementNSPlus("sld:Radius", {
|
value: value
|
});
|
},
|
"RasterSymbolizer": function(symbolizer) {
|
var node = this.createElementNSPlus("sld:RasterSymbolizer");
|
if (symbolizer.geometry) {
|
this.writeNode("Geometry", symbolizer.geometry, node);
|
}
|
if (symbolizer.opacity) {
|
this.writeNode("Opacity", symbolizer.opacity, node);
|
}
|
if (symbolizer.colorMap) {
|
this.writeNode("ColorMap", symbolizer.colorMap, node);
|
}
|
return node;
|
},
|
"Geometry": function(geometry) {
|
var node = this.createElementNSPlus("sld:Geometry");
|
if (geometry.property) {
|
this.writeNode("ogc:PropertyName", geometry, node);
|
}
|
return node;
|
},
|
"ColorMap": function(colorMap) {
|
var node = this.createElementNSPlus("sld:ColorMap");
|
for (var i=0, len=colorMap.length; i<len; ++i) {
|
this.writeNode("ColorMapEntry", colorMap[i], node);
|
}
|
return node;
|
},
|
"ColorMapEntry": function(colorMapEntry) {
|
var node = this.createElementNSPlus("sld:ColorMapEntry");
|
var a = colorMapEntry;
|
node.setAttribute("color", a.color);
|
a.opacity !== undefined && node.setAttribute("opacity",
|
parseFloat(a.opacity));
|
a.quantity !== undefined && node.setAttribute("quantity",
|
parseFloat(a.quantity));
|
a.label !== undefined && node.setAttribute("label", a.label);
|
return node;
|
},
|
"PolygonSymbolizer": function(symbolizer) {
|
var node = this.createElementNSPlus("sld:PolygonSymbolizer");
|
if(symbolizer.fill !== false) {
|
this.writeNode("Fill", symbolizer, node);
|
}
|
if(symbolizer.stroke !== false) {
|
this.writeNode("Stroke", symbolizer, node);
|
}
|
return node;
|
},
|
"Fill": function(symbolizer) {
|
var node = this.createElementNSPlus("sld:Fill");
|
|
// GraphicFill here
|
|
// add in CssParameters
|
if(symbolizer.fillColor) {
|
this.writeNode(
|
"CssParameter",
|
{symbolizer: symbolizer, key: "fillColor"},
|
node
|
);
|
}
|
if(symbolizer.fillOpacity != null) {
|
this.writeNode(
|
"CssParameter",
|
{symbolizer: symbolizer, key: "fillOpacity"},
|
node
|
);
|
}
|
return node;
|
},
|
"PointSymbolizer": function(symbolizer) {
|
var node = this.createElementNSPlus("sld:PointSymbolizer");
|
this.writeNode("Graphic", symbolizer, node);
|
return node;
|
},
|
"Graphic": function(symbolizer) {
|
var node = this.createElementNSPlus("sld:Graphic");
|
if(symbolizer.externalGraphic != undefined) {
|
this.writeNode("ExternalGraphic", symbolizer, node);
|
} else {
|
this.writeNode("Mark", symbolizer, node);
|
}
|
|
if(symbolizer.graphicOpacity != undefined) {
|
this.writeNode("Opacity", symbolizer.graphicOpacity, node);
|
}
|
if(symbolizer.pointRadius != undefined) {
|
this.writeNode("Size", symbolizer.pointRadius * 2, node);
|
} else if (symbolizer.graphicWidth != undefined) {
|
this.writeNode("Size", symbolizer.graphicWidth, node);
|
}
|
if(symbolizer.rotation != undefined) {
|
this.writeNode("Rotation", symbolizer.rotation, node);
|
}
|
return node;
|
},
|
"ExternalGraphic": function(symbolizer) {
|
var node = this.createElementNSPlus("sld:ExternalGraphic");
|
this.writeNode(
|
"OnlineResource", symbolizer.externalGraphic, node
|
);
|
var format = symbolizer.graphicFormat ||
|
this.getGraphicFormat(symbolizer.externalGraphic);
|
this.writeNode("Format", format, node);
|
return node;
|
},
|
"Mark": function(symbolizer) {
|
var node = this.createElementNSPlus("sld:Mark");
|
if(symbolizer.graphicName) {
|
this.writeNode("WellKnownName", symbolizer.graphicName, node);
|
}
|
if (symbolizer.fill !== false) {
|
this.writeNode("Fill", symbolizer, node);
|
}
|
if (symbolizer.stroke !== false) {
|
this.writeNode("Stroke", symbolizer, node);
|
}
|
return node;
|
},
|
"WellKnownName": function(name) {
|
return this.createElementNSPlus("sld:WellKnownName", {
|
value: name
|
});
|
},
|
"Opacity": function(value) {
|
return this.createElementNSPlus("sld:Opacity", {
|
value: value
|
});
|
},
|
"Size": function(value) {
|
return this.writers.sld._OGCExpression.call(
|
this, "sld:Size", value
|
);
|
},
|
"Rotation": function(value) {
|
return this.createElementNSPlus("sld:Rotation", {
|
value: value
|
});
|
},
|
"OnlineResource": function(href) {
|
return this.createElementNSPlus("sld:OnlineResource", {
|
attributes: {
|
"xlink:type": "simple",
|
"xlink:href": href
|
}
|
});
|
},
|
"Format": function(format) {
|
return this.createElementNSPlus("sld:Format", {
|
value: format
|
});
|
}
|
}
|
}, OpenLayers.Format.Filter.v1_0_0.prototype.writers),
|
|
CLASS_NAME: "OpenLayers.Format.SLD.v1"
|
|
});
|
/* ======================================================================
|
OpenLayers/Layer/WMS.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Layer/Grid.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.WMS
|
* Instances of OpenLayers.Layer.WMS are used to display data from OGC Web
|
* Mapping Services. Create a new WMS layer with the <OpenLayers.Layer.WMS>
|
* constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer.Grid>
|
*/
|
OpenLayers.Layer.WMS = OpenLayers.Class(OpenLayers.Layer.Grid, {
|
|
/**
|
* Constant: DEFAULT_PARAMS
|
* {Object} Hashtable of default parameter key/value pairs
|
*/
|
DEFAULT_PARAMS: { service: "WMS",
|
version: "1.1.1",
|
request: "GetMap",
|
styles: "",
|
format: "image/jpeg"
|
},
|
|
/**
|
* APIProperty: isBaseLayer
|
* {Boolean} Default is true for WMS layer
|
*/
|
isBaseLayer: true,
|
|
/**
|
* APIProperty: encodeBBOX
|
* {Boolean} Should the BBOX commas be encoded? The WMS spec says 'no',
|
* but some services want it that way. Default false.
|
*/
|
encodeBBOX: false,
|
|
/**
|
* APIProperty: noMagic
|
* {Boolean} If true, the image format will not be automagicaly switched
|
* from image/jpeg to image/png or image/gif when using
|
* TRANSPARENT=TRUE. Also isBaseLayer will not changed by the
|
* constructor. Default false.
|
*/
|
noMagic: false,
|
|
/**
|
* Property: yx
|
* {Object} Keys in this object are EPSG codes for which the axis order
|
* is to be reversed (yx instead of xy, LatLon instead of LonLat), with
|
* true as value. This is only relevant for WMS versions >= 1.3.0, and
|
* only if yx is not set in <OpenLayers.Projection.defaults> for the
|
* used projection.
|
*/
|
yx: {},
|
|
/**
|
* Constructor: OpenLayers.Layer.WMS
|
* Create a new WMS layer object
|
*
|
* Examples:
|
*
|
* The code below creates a simple WMS layer using the image/jpeg format.
|
* (code)
|
* var wms = new OpenLayers.Layer.WMS("NASA Global Mosaic",
|
* "http://wms.jpl.nasa.gov/wms.cgi",
|
* {layers: "modis,global_mosaic"});
|
* (end)
|
* Note the 3rd argument (params). Properties added to this object will be
|
* added to the WMS GetMap requests used for this layer's tiles. The only
|
* mandatory parameter is "layers". Other common WMS params include
|
* "transparent", "styles" and "format". Note that the "srs" param will
|
* always be ignored. Instead, it will be derived from the baseLayer's or
|
* map's projection.
|
*
|
* The code below creates a transparent WMS layer with additional options.
|
* (code)
|
* var wms = new OpenLayers.Layer.WMS("NASA Global Mosaic",
|
* "http://wms.jpl.nasa.gov/wms.cgi",
|
* {
|
* layers: "modis,global_mosaic",
|
* transparent: true
|
* }, {
|
* opacity: 0.5,
|
* singleTile: true
|
* });
|
* (end)
|
* Note that by default, a WMS layer is configured as baseLayer. Setting
|
* the "transparent" param to true will apply some magic (see <noMagic>).
|
* The default image format changes from image/jpeg to image/png, and the
|
* layer is not configured as baseLayer.
|
*
|
* Parameters:
|
* name - {String} A name for the layer
|
* url - {String} Base url for the WMS
|
* (e.g. http://wms.jpl.nasa.gov/wms.cgi)
|
* params - {Object} An object with key/value pairs representing the
|
* GetMap query string parameters and parameter values.
|
* options - {Object} Hashtable of extra options to tag onto the layer.
|
* These options include all properties listed above, plus the ones
|
* inherited from superclasses.
|
*/
|
initialize: function(name, url, params, options) {
|
var newArguments = [];
|
//uppercase params
|
params = OpenLayers.Util.upperCaseObject(params);
|
if (parseFloat(params.VERSION) >= 1.3 && !params.EXCEPTIONS) {
|
params.EXCEPTIONS = "INIMAGE";
|
}
|
newArguments.push(name, url, params, options);
|
OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
|
OpenLayers.Util.applyDefaults(
|
this.params,
|
OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)
|
);
|
|
|
//layer is transparent
|
if (!this.noMagic && this.params.TRANSPARENT &&
|
this.params.TRANSPARENT.toString().toLowerCase() == "true") {
|
|
// unless explicitly set in options, make layer an overlay
|
if ( (options == null) || (!options.isBaseLayer) ) {
|
this.isBaseLayer = false;
|
}
|
|
// jpegs can never be transparent, so intelligently switch the
|
// format, depending on the browser's capabilities
|
if (this.params.FORMAT == "image/jpeg") {
|
this.params.FORMAT = OpenLayers.Util.alphaHack() ? "image/gif"
|
: "image/png";
|
}
|
}
|
|
},
|
|
/**
|
* Method: clone
|
* Create a clone of this layer
|
*
|
* Returns:
|
* {<OpenLayers.Layer.WMS>} An exact clone of this layer
|
*/
|
clone: function (obj) {
|
|
if (obj == null) {
|
obj = new OpenLayers.Layer.WMS(this.name,
|
this.url,
|
this.params,
|
this.getOptions());
|
}
|
|
//get all additions from superclasses
|
obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
|
|
// copy/set any non-init, non-simple values here
|
|
return obj;
|
},
|
|
/**
|
* APIMethod: reverseAxisOrder
|
* Returns true if the axis order is reversed for the WMS version and
|
* projection of the layer.
|
*
|
* Returns:
|
* {Boolean} true if the axis order is reversed, false otherwise.
|
*/
|
reverseAxisOrder: function() {
|
var projCode = this.projection.getCode();
|
return parseFloat(this.params.VERSION) >= 1.3 &&
|
!!(this.yx[projCode] || (OpenLayers.Projection.defaults[projCode] &&
|
OpenLayers.Projection.defaults[projCode].yx));
|
},
|
|
/**
|
* Method: getURL
|
* Return a GetMap query string for this layer
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the
|
* request.
|
*
|
* Returns:
|
* {String} A string with the layer's url and parameters and also the
|
* passed-in bounds and appropriate tile size specified as
|
* parameters.
|
*/
|
getURL: function (bounds) {
|
bounds = this.adjustBounds(bounds);
|
|
var imageSize = this.getImageSize();
|
var newParams = {};
|
// WMS 1.3 introduced axis order
|
var reverseAxisOrder = this.reverseAxisOrder();
|
newParams.BBOX = this.encodeBBOX ?
|
bounds.toBBOX(null, reverseAxisOrder) :
|
bounds.toArray(reverseAxisOrder);
|
newParams.WIDTH = imageSize.w;
|
newParams.HEIGHT = imageSize.h;
|
var requestString = this.getFullRequestString(newParams);
|
return requestString;
|
},
|
|
/**
|
* APIMethod: mergeNewParams
|
* Catch changeParams and uppercase the new params to be merged in
|
* before calling changeParams on the super class.
|
*
|
* Once params have been changed, the tiles will be reloaded with
|
* the new parameters.
|
*
|
* Parameters:
|
* newParams - {Object} Hashtable of new params to use
|
*/
|
mergeNewParams:function(newParams) {
|
var upperParams = OpenLayers.Util.upperCaseObject(newParams);
|
var newArguments = [upperParams];
|
return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this,
|
newArguments);
|
},
|
|
/**
|
* APIMethod: getFullRequestString
|
* Combine the layer's url with its params and these newParams.
|
*
|
* Add the SRS parameter from projection -- this is probably
|
* more eloquently done via a setProjection() method, but this
|
* works for now and always.
|
*
|
* Parameters:
|
* newParams - {Object}
|
* altUrl - {String} Use this as the url instead of the layer's url
|
*
|
* Returns:
|
* {String}
|
*/
|
getFullRequestString:function(newParams, altUrl) {
|
var mapProjection = this.map.getProjectionObject();
|
var projectionCode = this.projection && this.projection.equals(mapProjection) ?
|
this.projection.getCode() :
|
mapProjection.getCode();
|
var value = (projectionCode == "none") ? null : projectionCode;
|
if (parseFloat(this.params.VERSION) >= 1.3) {
|
this.params.CRS = value;
|
} else {
|
this.params.SRS = value;
|
}
|
|
if (typeof this.params.TRANSPARENT == "boolean") {
|
newParams.TRANSPARENT = this.params.TRANSPARENT ? "TRUE" : "FALSE";
|
}
|
|
return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(
|
this, arguments);
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.WMS"
|
});
|
/* ======================================================================
|
OpenLayers/Layer/KaMap.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Layer/Grid.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.KaMap
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer.Grid>
|
*/
|
OpenLayers.Layer.KaMap = OpenLayers.Class(OpenLayers.Layer.Grid, {
|
|
/**
|
* APIProperty: isBaseLayer
|
* {Boolean} KaMap Layer is always a base layer
|
*/
|
isBaseLayer: true,
|
|
/**
|
* Constant: DEFAULT_PARAMS
|
* {Object} parameters set by default. The default parameters set
|
* the format via the 'i' parameter to 'jpeg'.
|
*/
|
DEFAULT_PARAMS: {
|
i: 'jpeg',
|
map: ''
|
},
|
|
/**
|
* Constructor: OpenLayers.Layer.KaMap
|
*
|
* Parameters:
|
* name - {String}
|
* url - {String}
|
* params - {Object} Parameters to be sent to the HTTP server in the
|
* query string for the tile. The format can be set via the 'i'
|
* parameter (defaults to jpg) , and the map should be set via
|
* the 'map' parameter. It has been reported that ka-Map may behave
|
* inconsistently if your format parameter does not match the format
|
* parameter configured in your config.php. (See ticket #327 for more
|
* information.)
|
* options - {Object} Additional options for the layer. Any of the
|
* APIProperties listed on this layer, and any layer types it
|
* extends, can be overridden through the options parameter.
|
*/
|
initialize: function(name, url, params, options) {
|
OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments);
|
this.params = OpenLayers.Util.applyDefaults(
|
this.params, this.DEFAULT_PARAMS
|
);
|
},
|
|
/**
|
* Method: getURL
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
*
|
* Returns:
|
* {String} A string with the layer's url and parameters and also the
|
* passed-in bounds and appropriate tile size specified as
|
* parameters
|
*/
|
getURL: function (bounds) {
|
bounds = this.adjustBounds(bounds);
|
var mapRes = this.map.getResolution();
|
var scale = Math.round((this.map.getScale() * 10000)) / 10000;
|
var pX = Math.round(bounds.left / mapRes);
|
var pY = -Math.round(bounds.top / mapRes);
|
return this.getFullRequestString(
|
{ t: pY,
|
l: pX,
|
s: scale
|
});
|
},
|
|
/**
|
* Method: calculateGridLayout
|
* ka-Map uses the center point of the map as an origin for
|
* its tiles. Override calculateGridLayout to center tiles
|
* correctly for this case.
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bound>}
|
* origin - {<OpenLayers.LonLat>}
|
* resolution - {Number}
|
*
|
* Returns:
|
* {Object} Object containing properties tilelon, tilelat, startcol,
|
* startrow
|
*/
|
calculateGridLayout: function(bounds, origin, resolution) {
|
var tilelon = resolution*this.tileSize.w;
|
var tilelat = resolution*this.tileSize.h;
|
|
var offsetlon = bounds.left;
|
var tilecol = Math.floor(offsetlon/tilelon) - this.buffer;
|
|
var offsetlat = bounds.top;
|
var tilerow = Math.floor(offsetlat/tilelat) + this.buffer;
|
|
return {
|
tilelon: tilelon, tilelat: tilelat,
|
startcol: tilecol, startrow: tilerow
|
};
|
},
|
|
/**
|
* Method: getTileBoundsForGridIndex
|
*
|
* Parameters:
|
* row - {Number} The row of the grid
|
* col - {Number} The column of the grid
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} The bounds for the tile at (row, col)
|
*/
|
getTileBoundsForGridIndex: function(row, col) {
|
var origin = this.getTileOrigin();
|
var tileLayout = this.gridLayout;
|
var tilelon = tileLayout.tilelon;
|
var tilelat = tileLayout.tilelat;
|
var minX = (tileLayout.startcol + col) * tilelon;
|
var minY = (tileLayout.startrow - row) * tilelat;
|
return new OpenLayers.Bounds(
|
minX, minY,
|
minX + tilelon, minY + tilelat
|
);
|
},
|
|
/**
|
* APIMethod: clone
|
*
|
* Parameters:
|
* obj - {Object}
|
*
|
* Returns:
|
* {<OpenLayers.Layer.Kamap>} An exact clone of this OpenLayers.Layer.KaMap
|
*/
|
clone: function (obj) {
|
|
if (obj == null) {
|
obj = new OpenLayers.Layer.KaMap(this.name,
|
this.url,
|
this.params,
|
this.getOptions());
|
}
|
|
//get all additions from superclasses
|
obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
|
|
// copy/set any non-init, non-simple values here
|
if (this.tileSize != null) {
|
obj.tileSize = this.tileSize.clone();
|
}
|
|
// we do not want to copy reference to grid, so we make a new array
|
obj.grid = [];
|
|
return obj;
|
},
|
|
/**
|
* APIMethod: getTileBounds
|
* Returns The tile bounds for a layer given a pixel location.
|
*
|
* Parameters:
|
* viewPortPx - {<OpenLayers.Pixel>} The location in the viewport.
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} Bounds of the tile at the given pixel location.
|
*/
|
getTileBounds: function(viewPortPx) {
|
var resolution = this.getResolution();
|
var tileMapWidth = resolution * this.tileSize.w;
|
var tileMapHeight = resolution * this.tileSize.h;
|
var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);
|
var tileLeft = tileMapWidth * Math.floor(mapPoint.lon / tileMapWidth);
|
var tileBottom = tileMapHeight * Math.floor(mapPoint.lat / tileMapHeight);
|
return new OpenLayers.Bounds(tileLeft, tileBottom,
|
tileLeft + tileMapWidth,
|
tileBottom + tileMapHeight);
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.KaMap"
|
});
|
/* ======================================================================
|
OpenLayers/Format/WMC/v1_1_0.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/WMC/v1.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WMC.v1_1_0
|
* Read and write WMC version 1.1.0.
|
*
|
* Differences between 1.1.0 and 1.0.0:
|
* - 1.1.0 Layers have optional sld:MinScaleDenominator and
|
* sld:MaxScaleDenominator
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.WMC.v1>
|
*/
|
OpenLayers.Format.WMC.v1_1_0 = OpenLayers.Class(
|
OpenLayers.Format.WMC.v1, {
|
|
/**
|
* Constant: VERSION
|
* {String} 1.1.0
|
*/
|
VERSION: "1.1.0",
|
|
/**
|
* Property: schemaLocation
|
* {String} http://www.opengis.net/context
|
* http://schemas.opengis.net/context/1.1.0/context.xsd
|
*/
|
schemaLocation: "http://www.opengis.net/context http://schemas.opengis.net/context/1.1.0/context.xsd",
|
|
/**
|
* Constructor: OpenLayers.Format.WMC.v1_1_0
|
* Instances of this class are not created directly. Use the
|
* <OpenLayers.Format.WMC> constructor instead.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
initialize: function(options) {
|
OpenLayers.Format.WMC.v1.prototype.initialize.apply(
|
this, [options]
|
);
|
},
|
|
/**
|
* Method: read_sld_MinScaleDenominator
|
* Read a sld:MinScaleDenominator node.
|
*
|
* Parameters:
|
* layerContext - {Object} An object representing a layer.
|
* node - {Element} An element node.
|
*/
|
read_sld_MinScaleDenominator: function(layerContext, node) {
|
var minScaleDenominator = parseFloat(this.getChildValue(node));
|
if (minScaleDenominator > 0) {
|
layerContext.maxScale = minScaleDenominator;
|
}
|
},
|
|
/**
|
* Method: read_sld_MaxScaleDenominator
|
* Read a sld:MaxScaleDenominator node.
|
*
|
* Parameters:
|
* layerContext - {Object} An object representing a layer.
|
* node - {Element} An element node.
|
*/
|
read_sld_MaxScaleDenominator: function(layerContext, node) {
|
layerContext.minScale = parseFloat(this.getChildValue(node));
|
},
|
|
/**
|
* Method: read_wmc_SRS
|
*/
|
read_wmc_SRS: function(layerContext, node) {
|
if (! ("srs" in layerContext)) {
|
layerContext.srs = {};
|
}
|
layerContext.srs[this.getChildValue(node)] = true;
|
},
|
|
/**
|
* Method: write_wmc_Layer
|
* Create a Layer node given a layer context object. This method adds
|
* elements specific to version 1.1.0.
|
*
|
* Parameters:
|
* context - {Object} A layer context object.}
|
*
|
* Returns:
|
* {Element} A WMC Layer element node.
|
*/
|
write_wmc_Layer: function(context) {
|
var node = OpenLayers.Format.WMC.v1.prototype.write_wmc_Layer.apply(
|
this, [context]
|
);
|
|
// min/max scale denominator elements go before the 4th element in v1
|
if(context.maxScale) {
|
var minSD = this.createElementNS(
|
this.namespaces.sld, "sld:MinScaleDenominator"
|
);
|
minSD.appendChild(this.createTextNode(context.maxScale.toPrecision(16)));
|
node.appendChild(minSD);
|
}
|
|
if(context.minScale) {
|
var maxSD = this.createElementNS(
|
this.namespaces.sld, "sld:MaxScaleDenominator"
|
);
|
maxSD.appendChild(this.createTextNode(context.minScale.toPrecision(16)));
|
node.appendChild(maxSD);
|
}
|
|
// optional SRS element(s)
|
if (context.srs) {
|
for(var name in context.srs) {
|
node.appendChild(this.createElementDefaultNS("SRS", name));
|
}
|
}
|
|
// optional FormatList element
|
node.appendChild(this.write_wmc_FormatList(context));
|
|
// optional StyleList element
|
node.appendChild(this.write_wmc_StyleList(context));
|
|
// optional DimensionList element
|
if (context.dimensions) {
|
node.appendChild(this.write_wmc_DimensionList(context));
|
}
|
|
// OpenLayers specific properties go in an Extension element
|
node.appendChild(this.write_wmc_LayerExtension(context));
|
|
return node;
|
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WMC.v1_1_0"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/XLS.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML/VersionedOGC.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.XLS
|
* Read/Write XLS (OpenLS). Create a new instance with the <OpenLayers.Format.XLS>
|
* constructor. Currently only implemented for Location Utility Services, more
|
* specifically only for Geocoding. No support for Reverse Geocoding as yet.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML.VersionedOGC>
|
*/
|
OpenLayers.Format.XLS = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
|
|
/**
|
* APIProperty: defaultVersion
|
* {String} Version number to assume if none found. Default is "1.1.0".
|
*/
|
defaultVersion: "1.1.0",
|
|
/**
|
* APIProperty: stringifyOutput
|
* {Boolean} If true, write will return a string otherwise a DOMElement.
|
* Default is true.
|
*/
|
stringifyOutput: true,
|
|
/**
|
* Constructor: OpenLayers.Format.XLS
|
* Create a new parser for XLS.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* APIMethod: write
|
* Write out an XLS request.
|
*
|
* Parameters:
|
* request - {Object} An object representing the LUS request.
|
* options - {Object} Optional configuration object.
|
*
|
* Returns:
|
* {String} An XLS document string.
|
*/
|
|
/**
|
* APIMethod: read
|
* Read an XLS doc and return an object representing the result.
|
*
|
* Parameters:
|
* data - {String | DOMElement} Data to read.
|
* options - {Object} Options for the reader.
|
*
|
* Returns:
|
* {Object} An object representing the GeocodeResponse.
|
*/
|
|
CLASS_NAME: "OpenLayers.Format.XLS"
|
});
|
/* ======================================================================
|
OpenLayers/Format/XLS/v1.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XLS.js
|
* @requires OpenLayers/Format/GML/v3.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.XLS.v1
|
* Superclass for XLS version 1 parsers. Only supports GeocodeRequest for now.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.XLS.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
xls: "http://www.opengis.net/xls",
|
gml: "http://www.opengis.net/gml",
|
xsi: "http://www.w3.org/2001/XMLSchema-instance"
|
},
|
|
/**
|
* Property: regExes
|
* Compiled regular expressions for manipulating strings.
|
*/
|
regExes: {
|
trimSpace: (/^\s*|\s*$/g),
|
removeSpace: (/\s*/g),
|
splitSpace: (/\s+/),
|
trimComma: (/\s*,\s*/g)
|
},
|
|
/**
|
* APIProperty: xy
|
* {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
|
* Changing is not recommended, a new Format should be instantiated.
|
*/
|
xy: true,
|
|
/**
|
* Property: defaultPrefix
|
*/
|
defaultPrefix: "xls",
|
|
/**
|
* Property: schemaLocation
|
* {String} Schema location for a particular minor version.
|
*/
|
schemaLocation: null,
|
|
/**
|
* Constructor: OpenLayers.Format.XLS.v1
|
* Instances of this class are not created directly. Use the
|
* <OpenLayers.Format.XLS> constructor instead.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* Method: read
|
*
|
* Parameters:
|
* data - {DOMElement} An XLS document element.
|
* options - {Object} Options for the reader.
|
*
|
* Returns:
|
* {Object} An object representing the XLSResponse.
|
*/
|
read: function(data, options) {
|
options = OpenLayers.Util.applyDefaults(options, this.options);
|
var xls = {};
|
this.readChildNodes(data, xls);
|
return xls;
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"xls": {
|
"XLS": function(node, xls) {
|
xls.version = node.getAttribute("version");
|
this.readChildNodes(node, xls);
|
},
|
"Response": function(node, xls) {
|
this.readChildNodes(node, xls);
|
},
|
"GeocodeResponse": function(node, xls) {
|
xls.responseLists = [];
|
this.readChildNodes(node, xls);
|
},
|
"GeocodeResponseList": function(node, xls) {
|
var responseList = {
|
features: [],
|
numberOfGeocodedAddresses:
|
parseInt(node.getAttribute("numberOfGeocodedAddresses"))
|
};
|
xls.responseLists.push(responseList);
|
this.readChildNodes(node, responseList);
|
},
|
"GeocodedAddress": function(node, responseList) {
|
var feature = new OpenLayers.Feature.Vector();
|
responseList.features.push(feature);
|
this.readChildNodes(node, feature);
|
// post-process geometry
|
feature.geometry = feature.components[0];
|
},
|
"GeocodeMatchCode": function(node, feature) {
|
feature.attributes.matchCode = {
|
accuracy: parseFloat(node.getAttribute("accuracy")),
|
matchType: node.getAttribute("matchType")
|
};
|
},
|
"Address": function(node, feature) {
|
var address = {
|
countryCode: node.getAttribute("countryCode"),
|
addressee: node.getAttribute("addressee"),
|
street: [],
|
place: []
|
};
|
feature.attributes.address = address;
|
this.readChildNodes(node, address);
|
},
|
"freeFormAddress": function(node, address) {
|
address.freeFormAddress = this.getChildValue(node);
|
},
|
"StreetAddress": function(node, address) {
|
this.readChildNodes(node, address);
|
},
|
"Building": function(node, address) {
|
address.building = {
|
'number': node.getAttribute("number"),
|
subdivision: node.getAttribute("subdivision"),
|
buildingName: node.getAttribute("buildingName")
|
};
|
},
|
"Street": function(node, address) {
|
// only support the built-in primitive type for now
|
address.street.push(this.getChildValue(node));
|
},
|
"Place": function(node, address) {
|
// type is one of CountrySubdivision,
|
// CountrySecondarySubdivision, Municipality or
|
// MunicipalitySubdivision
|
address.place[node.getAttribute("type")] =
|
this.getChildValue(node);
|
},
|
"PostalCode": function(node, address) {
|
address.postalCode = this.getChildValue(node);
|
}
|
},
|
"gml": OpenLayers.Format.GML.v3.prototype.readers.gml
|
},
|
|
/**
|
* Method: write
|
*
|
* Parameters:
|
* request - {Object} An object representing the geocode request.
|
*
|
* Returns:
|
* {DOMElement} The root of an XLS document.
|
*/
|
write: function(request) {
|
return this.writers.xls.XLS.apply(this, [request]);
|
},
|
|
/**
|
* Property: writers
|
* As a compliment to the readers property, this structure contains public
|
* writing functions grouped by namespace alias and named like the
|
* node names they produce.
|
*/
|
writers: {
|
"xls": {
|
"XLS": function(request) {
|
var root = this.createElementNSPlus(
|
"xls:XLS",
|
{attributes: {
|
"version": this.VERSION,
|
"xsi:schemaLocation": this.schemaLocation
|
}}
|
);
|
this.writeNode("RequestHeader", request.header, root);
|
this.writeNode("Request", request, root);
|
return root;
|
},
|
"RequestHeader": function(header) {
|
return this.createElementNSPlus("xls:RequestHeader");
|
},
|
"Request": function(request) {
|
var node = this.createElementNSPlus("xls:Request", {
|
attributes: {
|
methodName: "GeocodeRequest",
|
requestID: request.requestID || "",
|
version: this.VERSION
|
}
|
});
|
this.writeNode("GeocodeRequest", request.addresses, node);
|
return node;
|
},
|
"GeocodeRequest": function(addresses) {
|
var node = this.createElementNSPlus("xls:GeocodeRequest");
|
for (var i=0, len=addresses.length; i<len; i++) {
|
this.writeNode("Address", addresses[i], node);
|
}
|
return node;
|
},
|
"Address": function(address) {
|
var node = this.createElementNSPlus("xls:Address", {
|
attributes: {
|
countryCode: address.countryCode
|
}
|
});
|
if (address.freeFormAddress) {
|
this.writeNode("freeFormAddress", address.freeFormAddress, node);
|
} else {
|
if (address.street) {
|
this.writeNode("StreetAddress", address, node);
|
}
|
if (address.municipality) {
|
this.writeNode("Municipality", address.municipality, node);
|
}
|
if (address.countrySubdivision) {
|
this.writeNode("CountrySubdivision", address.countrySubdivision, node);
|
}
|
if (address.postalCode) {
|
this.writeNode("PostalCode", address.postalCode, node);
|
}
|
}
|
return node;
|
},
|
"freeFormAddress": function(freeFormAddress) {
|
return this.createElementNSPlus("freeFormAddress",
|
{value: freeFormAddress});
|
},
|
"StreetAddress": function(address) {
|
var node = this.createElementNSPlus("xls:StreetAddress");
|
if (address.building) {
|
this.writeNode(node, "Building", address.building);
|
}
|
var street = address.street;
|
if (!(OpenLayers.Util.isArray(street))) {
|
street = [street];
|
}
|
for (var i=0, len=street.length; i < len; i++) {
|
this.writeNode("Street", street[i], node);
|
}
|
return node;
|
},
|
"Building": function(building) {
|
return this.createElementNSPlus("xls:Building", {
|
attributes: {
|
"number": building["number"],
|
"subdivision": building.subdivision,
|
"buildingName": building.buildingName
|
}
|
});
|
},
|
"Street": function(street) {
|
return this.createElementNSPlus("xls:Street", {value: street});
|
},
|
"Municipality": function(municipality) {
|
return this.createElementNSPlus("xls:Place", {
|
attributes: {
|
type: "Municipality"
|
},
|
value: municipality
|
});
|
},
|
"CountrySubdivision": function(countrySubdivision) {
|
return this.createElementNSPlus("xls:Place", {
|
attributes: {
|
type: "CountrySubdivision"
|
},
|
value: countrySubdivision
|
});
|
},
|
"PostalCode": function(postalCode) {
|
return this.createElementNSPlus("xls:PostalCode", {
|
value: postalCode
|
});
|
}
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Format.XLS.v1"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/XLS/v1_1_0.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XLS/v1.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.XLS.v1_1_0
|
* Read / write XLS version 1.1.0.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XLS.v1>
|
*/
|
OpenLayers.Format.XLS.v1_1_0 = OpenLayers.Class(
|
OpenLayers.Format.XLS.v1, {
|
|
/**
|
* Constant: VERSION
|
* {String} 1.1
|
*/
|
VERSION: "1.1",
|
|
/**
|
* Property: schemaLocation
|
* {String} http://www.opengis.net/xls
|
* http://schemas.opengis.net/ols/1.1.0/LocationUtilityService.xsd
|
*/
|
schemaLocation: "http://www.opengis.net/xls http://schemas.opengis.net/ols/1.1.0/LocationUtilityService.xsd",
|
|
/**
|
* Constructor: OpenLayers.Format.XLS.v1_1_0
|
* Instances of this class are not created directly. Use the
|
* <OpenLayers.Format.XLS> constructor instead.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
CLASS_NAME: "OpenLayers.Format.XLS.v1_1_0"
|
|
});
|
|
// Support non standard implementation
|
OpenLayers.Format.XLS.v1_1 = OpenLayers.Format.XLS.v1_1_0;
|
/* ======================================================================
|
OpenLayers/Renderer/SVG.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Renderer/Elements.js
|
*/
|
|
/**
|
* Class: OpenLayers.Renderer.SVG
|
*
|
* Inherits:
|
* - <OpenLayers.Renderer.Elements>
|
*/
|
OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
|
|
/**
|
* Property: xmlns
|
* {String}
|
*/
|
xmlns: "http://www.w3.org/2000/svg",
|
|
/**
|
* Property: xlinkns
|
* {String}
|
*/
|
xlinkns: "http://www.w3.org/1999/xlink",
|
|
/**
|
* Constant: MAX_PIXEL
|
* {Integer} Firefox has a limitation where values larger or smaller than
|
* about 15000 in an SVG document lock the browser up. This
|
* works around it.
|
*/
|
MAX_PIXEL: 15000,
|
|
/**
|
* Property: translationParameters
|
* {Object} Hash with "x" and "y" properties
|
*/
|
translationParameters: null,
|
|
/**
|
* Property: symbolMetrics
|
* {Object} Cache for symbol metrics according to their svg coordinate
|
* space. This is an object keyed by the symbol's id, and values are
|
* an array of [width, centerX, centerY].
|
*/
|
symbolMetrics: null,
|
|
/**
|
* Constructor: OpenLayers.Renderer.SVG
|
*
|
* Parameters:
|
* containerID - {String}
|
*/
|
initialize: function(containerID) {
|
if (!this.supported()) {
|
return;
|
}
|
OpenLayers.Renderer.Elements.prototype.initialize.apply(this,
|
arguments);
|
this.translationParameters = {x: 0, y: 0};
|
|
this.symbolMetrics = {};
|
},
|
|
/**
|
* APIMethod: supported
|
*
|
* Returns:
|
* {Boolean} Whether or not the browser supports the SVG renderer
|
*/
|
supported: function() {
|
var svgFeature = "http://www.w3.org/TR/SVG11/feature#";
|
return (document.implementation &&
|
(document.implementation.hasFeature("org.w3c.svg", "1.0") ||
|
document.implementation.hasFeature(svgFeature + "SVG", "1.1") ||
|
document.implementation.hasFeature(svgFeature + "BasicStructure", "1.1") ));
|
},
|
|
/**
|
* Method: inValidRange
|
* See #669 for more information
|
*
|
* Parameters:
|
* x - {Integer}
|
* y - {Integer}
|
* xyOnly - {Boolean} whether or not to just check for x and y, which means
|
* to not take the current translation parameters into account if true.
|
*
|
* Returns:
|
* {Boolean} Whether or not the 'x' and 'y' coordinates are in the
|
* valid range.
|
*/
|
inValidRange: function(x, y, xyOnly) {
|
var left = x + (xyOnly ? 0 : this.translationParameters.x);
|
var top = y + (xyOnly ? 0 : this.translationParameters.y);
|
return (left >= -this.MAX_PIXEL && left <= this.MAX_PIXEL &&
|
top >= -this.MAX_PIXEL && top <= this.MAX_PIXEL);
|
},
|
|
/**
|
* Method: setExtent
|
*
|
* Parameters:
|
* extent - {<OpenLayers.Bounds>}
|
* resolutionChanged - {Boolean}
|
*
|
* Returns:
|
* {Boolean} true to notify the layer that the new extent does not exceed
|
* the coordinate range, and the features will not need to be redrawn.
|
* False otherwise.
|
*/
|
setExtent: function(extent, resolutionChanged) {
|
var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments);
|
|
var resolution = this.getResolution(),
|
left = -extent.left / resolution,
|
top = extent.top / resolution;
|
|
// If the resolution has changed, start over changing the corner, because
|
// the features will redraw.
|
if (resolutionChanged) {
|
this.left = left;
|
this.top = top;
|
// Set the viewbox
|
var extentString = "0 0 " + this.size.w + " " + this.size.h;
|
|
this.rendererRoot.setAttributeNS(null, "viewBox", extentString);
|
this.translate(this.xOffset, 0);
|
return true;
|
} else {
|
var inRange = this.translate(left - this.left + this.xOffset, top - this.top);
|
if (!inRange) {
|
// recenter the coordinate system
|
this.setExtent(extent, true);
|
}
|
return coordSysUnchanged && inRange;
|
}
|
},
|
|
/**
|
* Method: translate
|
* Transforms the SVG coordinate system
|
*
|
* Parameters:
|
* x - {Float}
|
* y - {Float}
|
*
|
* Returns:
|
* {Boolean} true if the translation parameters are in the valid coordinates
|
* range, false otherwise.
|
*/
|
translate: function(x, y) {
|
if (!this.inValidRange(x, y, true)) {
|
return false;
|
} else {
|
var transformString = "";
|
if (x || y) {
|
transformString = "translate(" + x + "," + y + ")";
|
}
|
this.root.setAttributeNS(null, "transform", transformString);
|
this.translationParameters = {x: x, y: y};
|
return true;
|
}
|
},
|
|
/**
|
* Method: setSize
|
* Sets the size of the drawing surface.
|
*
|
* Parameters:
|
* size - {<OpenLayers.Size>} The size of the drawing surface
|
*/
|
setSize: function(size) {
|
OpenLayers.Renderer.prototype.setSize.apply(this, arguments);
|
|
this.rendererRoot.setAttributeNS(null, "width", this.size.w);
|
this.rendererRoot.setAttributeNS(null, "height", this.size.h);
|
},
|
|
/**
|
* Method: getNodeType
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
* style - {Object}
|
*
|
* Returns:
|
* {String} The corresponding node type for the specified geometry
|
*/
|
getNodeType: function(geometry, style) {
|
var nodeType = null;
|
switch (geometry.CLASS_NAME) {
|
case "OpenLayers.Geometry.Point":
|
if (style.externalGraphic) {
|
nodeType = "image";
|
} else if (this.isComplexSymbol(style.graphicName)) {
|
nodeType = "svg";
|
} else {
|
nodeType = "circle";
|
}
|
break;
|
case "OpenLayers.Geometry.Rectangle":
|
nodeType = "rect";
|
break;
|
case "OpenLayers.Geometry.LineString":
|
nodeType = "polyline";
|
break;
|
case "OpenLayers.Geometry.LinearRing":
|
nodeType = "polygon";
|
break;
|
case "OpenLayers.Geometry.Polygon":
|
case "OpenLayers.Geometry.Curve":
|
nodeType = "path";
|
break;
|
default:
|
break;
|
}
|
return nodeType;
|
},
|
|
/**
|
* Method: setStyle
|
* Use to set all the style attributes to a SVG node.
|
*
|
* Takes care to adjust stroke width and point radius to be
|
* resolution-relative
|
*
|
* Parameters:
|
* node - {SVGDomElement} An SVG element to decorate
|
* style - {Object}
|
* options - {Object} Currently supported options include
|
* 'isFilled' {Boolean} and
|
* 'isStroked' {Boolean}
|
*/
|
setStyle: function(node, style, options) {
|
style = style || node._style;
|
options = options || node._options;
|
|
var title = style.title || style.graphicTitle;
|
if (title) {
|
node.setAttributeNS(null, "title", title);
|
//Standards-conformant SVG
|
// Prevent duplicate nodes. See issue https://github.com/openlayers/openlayers/issues/92
|
var titleNode = node.getElementsByTagName("title");
|
if (titleNode.length > 0) {
|
titleNode[0].firstChild.textContent = title;
|
} else {
|
var label = this.nodeFactory(null, "title");
|
label.textContent = title;
|
node.appendChild(label);
|
}
|
}
|
|
var r = parseFloat(node.getAttributeNS(null, "r"));
|
var widthFactor = 1;
|
var pos;
|
if (node._geometryClass == "OpenLayers.Geometry.Point" && r) {
|
node.style.visibility = "";
|
if (style.graphic === false) {
|
node.style.visibility = "hidden";
|
} else if (style.externalGraphic) {
|
pos = this.getPosition(node);
|
if (style.graphicWidth && style.graphicHeight) {
|
node.setAttributeNS(null, "preserveAspectRatio", "none");
|
}
|
var width = style.graphicWidth || style.graphicHeight;
|
var height = style.graphicHeight || style.graphicWidth;
|
width = width ? width : style.pointRadius*2;
|
height = height ? height : style.pointRadius*2;
|
var xOffset = (style.graphicXOffset != undefined) ?
|
style.graphicXOffset : -(0.5 * width);
|
var yOffset = (style.graphicYOffset != undefined) ?
|
style.graphicYOffset : -(0.5 * height);
|
|
var opacity = style.graphicOpacity || style.fillOpacity;
|
|
node.setAttributeNS(null, "x", (pos.x + xOffset).toFixed());
|
node.setAttributeNS(null, "y", (pos.y + yOffset).toFixed());
|
node.setAttributeNS(null, "width", width);
|
node.setAttributeNS(null, "height", height);
|
node.setAttributeNS(this.xlinkns, "xlink:href", style.externalGraphic);
|
node.setAttributeNS(null, "style", "opacity: "+opacity);
|
node.onclick = OpenLayers.Event.preventDefault;
|
} else if (this.isComplexSymbol(style.graphicName)) {
|
// the symbol viewBox is three times as large as the symbol
|
var offset = style.pointRadius * 3;
|
var size = offset * 2;
|
var src = this.importSymbol(style.graphicName);
|
pos = this.getPosition(node);
|
widthFactor = this.symbolMetrics[src.id][0] * 3 / size;
|
|
// remove the node from the dom before we modify it. This
|
// prevents various rendering issues in Safari and FF
|
var parent = node.parentNode;
|
var nextSibling = node.nextSibling;
|
if(parent) {
|
parent.removeChild(node);
|
}
|
|
// The more appropriate way to implement this would be use/defs,
|
// but due to various issues in several browsers, it is safer to
|
// copy the symbols instead of referencing them.
|
// See e.g. ticket http://trac.osgeo.org/openlayers/ticket/2985
|
// and this email thread
|
// http://osgeo-org.1803224.n2.nabble.com/Select-Control-Ctrl-click-on-Feature-with-a-graphicName-opens-new-browser-window-tc5846039.html
|
node.firstChild && node.removeChild(node.firstChild);
|
node.appendChild(src.firstChild.cloneNode(true));
|
node.setAttributeNS(null, "viewBox", src.getAttributeNS(null, "viewBox"));
|
|
node.setAttributeNS(null, "width", size);
|
node.setAttributeNS(null, "height", size);
|
node.setAttributeNS(null, "x", pos.x - offset);
|
node.setAttributeNS(null, "y", pos.y - offset);
|
|
// now that the node has all its new properties, insert it
|
// back into the dom where it was
|
if(nextSibling) {
|
parent.insertBefore(node, nextSibling);
|
} else if(parent) {
|
parent.appendChild(node);
|
}
|
} else {
|
node.setAttributeNS(null, "r", style.pointRadius);
|
}
|
|
var rotation = style.rotation;
|
|
if ((rotation !== undefined || node._rotation !== undefined) && pos) {
|
node._rotation = rotation;
|
rotation |= 0;
|
if (node.nodeName !== "svg") {
|
node.setAttributeNS(null, "transform",
|
"rotate(" + rotation + " " + pos.x + " " +
|
pos.y + ")");
|
} else {
|
var metrics = this.symbolMetrics[src.id];
|
node.firstChild.setAttributeNS(null, "transform", "rotate("
|
+ rotation + " "
|
+ metrics[1] + " "
|
+ metrics[2] + ")");
|
}
|
}
|
}
|
|
if (options.isFilled) {
|
node.setAttributeNS(null, "fill", style.fillColor);
|
node.setAttributeNS(null, "fill-opacity", style.fillOpacity);
|
} else {
|
node.setAttributeNS(null, "fill", "none");
|
}
|
|
if (options.isStroked) {
|
node.setAttributeNS(null, "stroke", style.strokeColor);
|
node.setAttributeNS(null, "stroke-opacity", style.strokeOpacity);
|
node.setAttributeNS(null, "stroke-width", style.strokeWidth * widthFactor);
|
node.setAttributeNS(null, "stroke-linecap", style.strokeLinecap || "round");
|
// Hard-coded linejoin for now, to make it look the same as in VML.
|
// There is no strokeLinejoin property yet for symbolizers.
|
node.setAttributeNS(null, "stroke-linejoin", "round");
|
style.strokeDashstyle && node.setAttributeNS(null,
|
"stroke-dasharray", this.dashStyle(style, widthFactor));
|
} else {
|
node.setAttributeNS(null, "stroke", "none");
|
}
|
|
if (style.pointerEvents) {
|
node.setAttributeNS(null, "pointer-events", style.pointerEvents);
|
}
|
|
if (style.cursor != null) {
|
node.setAttributeNS(null, "cursor", style.cursor);
|
}
|
|
return node;
|
},
|
|
/**
|
* Method: dashStyle
|
*
|
* Parameters:
|
* style - {Object}
|
* widthFactor - {Number}
|
*
|
* Returns:
|
* {String} A SVG compliant 'stroke-dasharray' value
|
*/
|
dashStyle: function(style, widthFactor) {
|
var w = style.strokeWidth * widthFactor;
|
var str = style.strokeDashstyle;
|
switch (str) {
|
case 'solid':
|
return 'none';
|
case 'dot':
|
return [1, 4 * w].join();
|
case 'dash':
|
return [4 * w, 4 * w].join();
|
case 'dashdot':
|
return [4 * w, 4 * w, 1, 4 * w].join();
|
case 'longdash':
|
return [8 * w, 4 * w].join();
|
case 'longdashdot':
|
return [8 * w, 4 * w, 1, 4 * w].join();
|
default:
|
return OpenLayers.String.trim(str).replace(/\s+/g, ",");
|
}
|
},
|
|
/**
|
* Method: createNode
|
*
|
* Parameters:
|
* type - {String} Kind of node to draw
|
* id - {String} Id for node
|
*
|
* Returns:
|
* {DOMElement} A new node of the given type and id
|
*/
|
createNode: function(type, id) {
|
var node = document.createElementNS(this.xmlns, type);
|
if (id) {
|
node.setAttributeNS(null, "id", id);
|
}
|
return node;
|
},
|
|
/**
|
* Method: nodeTypeCompare
|
*
|
* Parameters:
|
* node - {SVGDomElement} An SVG element
|
* type - {String} Kind of node
|
*
|
* Returns:
|
* {Boolean} Whether or not the specified node is of the specified type
|
*/
|
nodeTypeCompare: function(node, type) {
|
return (type == node.nodeName);
|
},
|
|
/**
|
* Method: createRenderRoot
|
*
|
* Returns:
|
* {DOMElement} The specific render engine's root element
|
*/
|
createRenderRoot: function() {
|
var svg = this.nodeFactory(this.container.id + "_svgRoot", "svg");
|
svg.style.display = "block";
|
return svg;
|
},
|
|
/**
|
* Method: createRoot
|
*
|
* Parameters:
|
* suffix - {String} suffix to append to the id
|
*
|
* Returns:
|
* {DOMElement}
|
*/
|
createRoot: function(suffix) {
|
return this.nodeFactory(this.container.id + suffix, "g");
|
},
|
|
/**
|
* Method: createDefs
|
*
|
* Returns:
|
* {DOMElement} The element to which we'll add the symbol definitions
|
*/
|
createDefs: function() {
|
var defs = this.nodeFactory(this.container.id + "_defs", "defs");
|
this.rendererRoot.appendChild(defs);
|
return defs;
|
},
|
|
/**************************************
|
* *
|
* GEOMETRY DRAWING FUNCTIONS *
|
* *
|
**************************************/
|
|
/**
|
* Method: drawPoint
|
* This method is only called by the renderer itself.
|
*
|
* Parameters:
|
* node - {DOMElement}
|
* geometry - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {DOMElement} or false if the renderer could not draw the point
|
*/
|
drawPoint: function(node, geometry) {
|
return this.drawCircle(node, geometry, 1);
|
},
|
|
/**
|
* Method: drawCircle
|
* This method is only called by the renderer itself.
|
*
|
* Parameters:
|
* node - {DOMElement}
|
* geometry - {<OpenLayers.Geometry>}
|
* radius - {Float}
|
*
|
* Returns:
|
* {DOMElement} or false if the renderer could not draw the circle
|
*/
|
drawCircle: function(node, geometry, radius) {
|
var resolution = this.getResolution();
|
var x = ((geometry.x - this.featureDx) / resolution + this.left);
|
var y = (this.top - geometry.y / resolution);
|
|
if (this.inValidRange(x, y)) {
|
node.setAttributeNS(null, "cx", x);
|
node.setAttributeNS(null, "cy", y);
|
node.setAttributeNS(null, "r", radius);
|
return node;
|
} else {
|
return false;
|
}
|
|
},
|
|
/**
|
* Method: drawLineString
|
* This method is only called by the renderer itself.
|
*
|
* Parameters:
|
* node - {DOMElement}
|
* geometry - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {DOMElement} or null if the renderer could not draw all components of
|
* the linestring, or false if nothing could be drawn
|
*/
|
drawLineString: function(node, geometry) {
|
var componentsResult = this.getComponentsString(geometry.components);
|
if (componentsResult.path) {
|
node.setAttributeNS(null, "points", componentsResult.path);
|
return (componentsResult.complete ? node : null);
|
} else {
|
return false;
|
}
|
},
|
|
/**
|
* Method: drawLinearRing
|
* This method is only called by the renderer itself.
|
*
|
* Parameters:
|
* node - {DOMElement}
|
* geometry - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {DOMElement} or null if the renderer could not draw all components
|
* of the linear ring, or false if nothing could be drawn
|
*/
|
drawLinearRing: function(node, geometry) {
|
var componentsResult = this.getComponentsString(geometry.components);
|
if (componentsResult.path) {
|
node.setAttributeNS(null, "points", componentsResult.path);
|
return (componentsResult.complete ? node : null);
|
} else {
|
return false;
|
}
|
},
|
|
/**
|
* Method: drawPolygon
|
* This method is only called by the renderer itself.
|
*
|
* Parameters:
|
* node - {DOMElement}
|
* geometry - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {DOMElement} or null if the renderer could not draw all components
|
* of the polygon, or false if nothing could be drawn
|
*/
|
drawPolygon: function(node, geometry) {
|
var d = "";
|
var draw = true;
|
var complete = true;
|
var linearRingResult, path;
|
for (var j=0, len=geometry.components.length; j<len; j++) {
|
d += " M";
|
linearRingResult = this.getComponentsString(
|
geometry.components[j].components, " ");
|
path = linearRingResult.path;
|
if (path) {
|
d += " " + path;
|
complete = linearRingResult.complete && complete;
|
} else {
|
draw = false;
|
}
|
}
|
d += " z";
|
if (draw) {
|
node.setAttributeNS(null, "d", d);
|
node.setAttributeNS(null, "fill-rule", "evenodd");
|
return complete ? node : null;
|
} else {
|
return false;
|
}
|
},
|
|
/**
|
* Method: drawRectangle
|
* This method is only called by the renderer itself.
|
*
|
* Parameters:
|
* node - {DOMElement}
|
* geometry - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {DOMElement} or false if the renderer could not draw the rectangle
|
*/
|
drawRectangle: function(node, geometry) {
|
var resolution = this.getResolution();
|
var x = ((geometry.x - this.featureDx) / resolution + this.left);
|
var y = (this.top - geometry.y / resolution);
|
|
if (this.inValidRange(x, y)) {
|
node.setAttributeNS(null, "x", x);
|
node.setAttributeNS(null, "y", y);
|
node.setAttributeNS(null, "width", geometry.width / resolution);
|
node.setAttributeNS(null, "height", geometry.height / resolution);
|
return node;
|
} else {
|
return false;
|
}
|
},
|
|
/**
|
* Method: drawText
|
* This method is only called by the renderer itself.
|
*
|
* Parameters:
|
* featureId - {String}
|
* style -
|
* location - {<OpenLayers.Geometry.Point>}
|
*/
|
drawText: function(featureId, style, location) {
|
var drawOutline = (!!style.labelOutlineWidth);
|
// First draw text in halo color and size and overlay the
|
// normal text afterwards
|
if (drawOutline) {
|
var outlineStyle = OpenLayers.Util.extend({}, style);
|
outlineStyle.fontColor = outlineStyle.labelOutlineColor;
|
outlineStyle.fontStrokeColor = outlineStyle.labelOutlineColor;
|
outlineStyle.fontStrokeWidth = style.labelOutlineWidth;
|
if (style.labelOutlineOpacity) {
|
outlineStyle.fontOpacity = style.labelOutlineOpacity;
|
}
|
delete outlineStyle.labelOutlineWidth;
|
this.drawText(featureId, outlineStyle, location);
|
}
|
|
var resolution = this.getResolution();
|
|
var x = ((location.x - this.featureDx) / resolution + this.left);
|
var y = (location.y / resolution - this.top);
|
|
var suffix = (drawOutline)?this.LABEL_OUTLINE_SUFFIX:this.LABEL_ID_SUFFIX;
|
var label = this.nodeFactory(featureId + suffix, "text");
|
|
label.setAttributeNS(null, "x", x);
|
label.setAttributeNS(null, "y", -y);
|
|
if (style.fontColor) {
|
label.setAttributeNS(null, "fill", style.fontColor);
|
}
|
if (style.fontStrokeColor) {
|
label.setAttributeNS(null, "stroke", style.fontStrokeColor);
|
}
|
if (style.fontStrokeWidth) {
|
label.setAttributeNS(null, "stroke-width", style.fontStrokeWidth);
|
}
|
if (style.fontOpacity) {
|
label.setAttributeNS(null, "opacity", style.fontOpacity);
|
}
|
if (style.fontFamily) {
|
label.setAttributeNS(null, "font-family", style.fontFamily);
|
}
|
if (style.fontSize) {
|
label.setAttributeNS(null, "font-size", style.fontSize);
|
}
|
if (style.fontWeight) {
|
label.setAttributeNS(null, "font-weight", style.fontWeight);
|
}
|
if (style.fontStyle) {
|
label.setAttributeNS(null, "font-style", style.fontStyle);
|
}
|
if (style.labelSelect === true) {
|
label.setAttributeNS(null, "pointer-events", "visible");
|
label._featureId = featureId;
|
} else {
|
label.setAttributeNS(null, "pointer-events", "none");
|
}
|
var align = style.labelAlign || OpenLayers.Renderer.defaultSymbolizer.labelAlign;
|
label.setAttributeNS(null, "text-anchor",
|
OpenLayers.Renderer.SVG.LABEL_ALIGN[align[0]] || "middle");
|
|
if (OpenLayers.IS_GECKO === true) {
|
label.setAttributeNS(null, "dominant-baseline",
|
OpenLayers.Renderer.SVG.LABEL_ALIGN[align[1]] || "central");
|
}
|
|
var labelRows = style.label.split('\n');
|
var numRows = labelRows.length;
|
while (label.childNodes.length > numRows) {
|
label.removeChild(label.lastChild);
|
}
|
for (var i = 0; i < numRows; i++) {
|
var tspan = this.nodeFactory(featureId + suffix + "_tspan_" + i, "tspan");
|
if (style.labelSelect === true) {
|
tspan._featureId = featureId;
|
tspan._geometry = location;
|
tspan._geometryClass = location.CLASS_NAME;
|
}
|
if (OpenLayers.IS_GECKO === false) {
|
tspan.setAttributeNS(null, "baseline-shift",
|
OpenLayers.Renderer.SVG.LABEL_VSHIFT[align[1]] || "-35%");
|
}
|
tspan.setAttribute("x", x);
|
if (i == 0) {
|
var vfactor = OpenLayers.Renderer.SVG.LABEL_VFACTOR[align[1]];
|
if (vfactor == null) {
|
vfactor = -.5;
|
}
|
tspan.setAttribute("dy", (vfactor*(numRows-1)) + "em");
|
} else {
|
tspan.setAttribute("dy", "1em");
|
}
|
tspan.textContent = (labelRows[i] === '') ? ' ' : labelRows[i];
|
if (!tspan.parentNode) {
|
label.appendChild(tspan);
|
}
|
}
|
|
if (!label.parentNode) {
|
this.textRoot.appendChild(label);
|
}
|
},
|
|
/**
|
* Method: getComponentString
|
*
|
* Parameters:
|
* components - {Array(<OpenLayers.Geometry.Point>)} Array of points
|
* separator - {String} character between coordinate pairs. Defaults to ","
|
*
|
* Returns:
|
* {Object} hash with properties "path" (the string created from the
|
* components and "complete" (false if the renderer was unable to
|
* draw all components)
|
*/
|
getComponentsString: function(components, separator) {
|
var renderCmp = [];
|
var complete = true;
|
var len = components.length;
|
var strings = [];
|
var str, component;
|
for(var i=0; i<len; i++) {
|
component = components[i];
|
renderCmp.push(component);
|
str = this.getShortString(component);
|
if (str) {
|
strings.push(str);
|
} else {
|
// The current component is outside the valid range. Let's
|
// see if the previous or next component is inside the range.
|
// If so, add the coordinate of the intersection with the
|
// valid range bounds.
|
if (i > 0) {
|
if (this.getShortString(components[i - 1])) {
|
strings.push(this.clipLine(components[i],
|
components[i-1]));
|
}
|
}
|
if (i < len - 1) {
|
if (this.getShortString(components[i + 1])) {
|
strings.push(this.clipLine(components[i],
|
components[i+1]));
|
}
|
}
|
complete = false;
|
}
|
}
|
|
return {
|
path: strings.join(separator || ","),
|
complete: complete
|
};
|
},
|
|
/**
|
* Method: clipLine
|
* Given two points (one inside the valid range, and one outside),
|
* clips the line betweeen the two points so that the new points are both
|
* inside the valid range.
|
*
|
* Parameters:
|
* badComponent - {<OpenLayers.Geometry.Point>} original geometry of the
|
* invalid point
|
* goodComponent - {<OpenLayers.Geometry.Point>} original geometry of the
|
* valid point
|
* Returns
|
* {String} the SVG coordinate pair of the clipped point (like
|
* getShortString), or an empty string if both passed componets are at
|
* the same point.
|
*/
|
clipLine: function(badComponent, goodComponent) {
|
if (goodComponent.equals(badComponent)) {
|
return "";
|
}
|
var resolution = this.getResolution();
|
var maxX = this.MAX_PIXEL - this.translationParameters.x;
|
var maxY = this.MAX_PIXEL - this.translationParameters.y;
|
var x1 = (goodComponent.x - this.featureDx) / resolution + this.left;
|
var y1 = this.top - goodComponent.y / resolution;
|
var x2 = (badComponent.x - this.featureDx) / resolution + this.left;
|
var y2 = this.top - badComponent.y / resolution;
|
var k;
|
if (x2 < -maxX || x2 > maxX) {
|
k = (y2 - y1) / (x2 - x1);
|
x2 = x2 < 0 ? -maxX : maxX;
|
y2 = y1 + (x2 - x1) * k;
|
}
|
if (y2 < -maxY || y2 > maxY) {
|
k = (x2 - x1) / (y2 - y1);
|
y2 = y2 < 0 ? -maxY : maxY;
|
x2 = x1 + (y2 - y1) * k;
|
}
|
return x2 + "," + y2;
|
},
|
|
/**
|
* Method: getShortString
|
*
|
* Parameters:
|
* point - {<OpenLayers.Geometry.Point>}
|
*
|
* Returns:
|
* {String} or false if point is outside the valid range
|
*/
|
getShortString: function(point) {
|
var resolution = this.getResolution();
|
var x = ((point.x - this.featureDx) / resolution + this.left);
|
var y = (this.top - point.y / resolution);
|
|
if (this.inValidRange(x, y)) {
|
return x + "," + y;
|
} else {
|
return false;
|
}
|
},
|
|
/**
|
* Method: getPosition
|
* Finds the position of an svg node.
|
*
|
* Parameters:
|
* node - {DOMElement}
|
*
|
* Returns:
|
* {Object} hash with x and y properties, representing the coordinates
|
* within the svg coordinate system
|
*/
|
getPosition: function(node) {
|
return({
|
x: parseFloat(node.getAttributeNS(null, "cx")),
|
y: parseFloat(node.getAttributeNS(null, "cy"))
|
});
|
},
|
|
/**
|
* Method: importSymbol
|
* add a new symbol definition from the rendererer's symbol hash
|
*
|
* Parameters:
|
* graphicName - {String} name of the symbol to import
|
*
|
* Returns:
|
* {DOMElement} - the imported symbol
|
*/
|
importSymbol: function (graphicName) {
|
if (!this.defs) {
|
// create svg defs tag
|
this.defs = this.createDefs();
|
}
|
var id = this.container.id + "-" + graphicName;
|
|
// check if symbol already exists in the defs
|
var existing = document.getElementById(id);
|
if (existing != null) {
|
return existing;
|
}
|
|
var symbol = OpenLayers.Renderer.symbol[graphicName];
|
if (!symbol) {
|
throw new Error(graphicName + ' is not a valid symbol name');
|
}
|
|
var symbolNode = this.nodeFactory(id, "symbol");
|
var node = this.nodeFactory(null, "polygon");
|
symbolNode.appendChild(node);
|
var symbolExtent = new OpenLayers.Bounds(
|
Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
|
|
var points = [];
|
var x,y;
|
for (var i=0; i<symbol.length; i=i+2) {
|
x = symbol[i];
|
y = symbol[i+1];
|
symbolExtent.left = Math.min(symbolExtent.left, x);
|
symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
|
symbolExtent.right = Math.max(symbolExtent.right, x);
|
symbolExtent.top = Math.max(symbolExtent.top, y);
|
points.push(x, ",", y);
|
}
|
|
node.setAttributeNS(null, "points", points.join(" "));
|
|
var width = symbolExtent.getWidth();
|
var height = symbolExtent.getHeight();
|
// create a viewBox three times as large as the symbol itself,
|
// to allow for strokeWidth being displayed correctly at the corners.
|
var viewBox = [symbolExtent.left - width,
|
symbolExtent.bottom - height, width * 3, height * 3];
|
symbolNode.setAttributeNS(null, "viewBox", viewBox.join(" "));
|
this.symbolMetrics[id] = [
|
Math.max(width, height),
|
symbolExtent.getCenterLonLat().lon,
|
symbolExtent.getCenterLonLat().lat
|
];
|
|
this.defs.appendChild(symbolNode);
|
return symbolNode;
|
},
|
|
/**
|
* Method: getFeatureIdFromEvent
|
*
|
* Parameters:
|
* evt - {Object} An <OpenLayers.Event> object
|
*
|
* Returns:
|
* {String} A feature id or undefined.
|
*/
|
getFeatureIdFromEvent: function(evt) {
|
var featureId = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent.apply(this, arguments);
|
if(!featureId) {
|
var target = evt.target;
|
featureId = target.parentNode && target != this.rendererRoot ?
|
target.parentNode._featureId : undefined;
|
}
|
return featureId;
|
},
|
|
CLASS_NAME: "OpenLayers.Renderer.SVG"
|
});
|
|
/**
|
* Constant: OpenLayers.Renderer.SVG.LABEL_ALIGN
|
* {Object}
|
*/
|
OpenLayers.Renderer.SVG.LABEL_ALIGN = {
|
"l": "start",
|
"r": "end",
|
"b": "bottom",
|
"t": "hanging"
|
};
|
|
/**
|
* Constant: OpenLayers.Renderer.SVG.LABEL_VSHIFT
|
* {Object}
|
*/
|
OpenLayers.Renderer.SVG.LABEL_VSHIFT = {
|
// according to
|
// http://www.w3.org/Graphics/SVG/Test/20061213/htmlObjectHarness/full-text-align-02-b.html
|
// a baseline-shift of -70% shifts the text exactly from the
|
// bottom to the top of the baseline, so -35% moves the text to
|
// the center of the baseline.
|
"t": "-70%",
|
"b": "0"
|
};
|
|
/**
|
* Constant: OpenLayers.Renderer.SVG.LABEL_VFACTOR
|
* {Object}
|
*/
|
OpenLayers.Renderer.SVG.LABEL_VFACTOR = {
|
"t": 0,
|
"b": -1
|
};
|
|
/**
|
* Function: OpenLayers.Renderer.SVG.preventDefault
|
* *Deprecated*. Use <OpenLayers.Event.preventDefault> method instead.
|
* Used to prevent default events (especially opening images in a new tab on
|
* ctrl-click) from being executed for externalGraphic symbols
|
*/
|
OpenLayers.Renderer.SVG.preventDefault = function(e) {
|
OpenLayers.Event.preventDefault(e);
|
};
|
/* ======================================================================
|
OpenLayers/Format/SLD/v1_0_0.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/SLD/v1.js
|
* @requires OpenLayers/Format/Filter/v1_0_0.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.SLD.v1_0_0
|
* Write SLD version 1.0.0.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.SLD.v1>
|
*/
|
OpenLayers.Format.SLD.v1_0_0 = OpenLayers.Class(
|
OpenLayers.Format.SLD.v1, {
|
|
/**
|
* Constant: VERSION
|
* {String} 1.0.0
|
*/
|
VERSION: "1.0.0",
|
|
/**
|
* Property: schemaLocation
|
* {String} http://www.opengis.net/sld
|
* http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd
|
*/
|
schemaLocation: "http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd",
|
|
/**
|
* Constructor: OpenLayers.Format.SLD.v1_0_0
|
* Instances of this class are not created directly. Use the
|
* <OpenLayers.Format.SLD> constructor instead.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
CLASS_NAME: "OpenLayers.Format.SLD.v1_0_0"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/OWSContext.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/Context.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.OWSContext
|
* Read and write OWS Context documents. OWS Context documents are a
|
* preliminary OGC (Open Geospatial Consortium) standard for storing the
|
* state of a web mapping application. In a way it is the successor to
|
* Web Map Context (WMC), since it is more generic and more types of layers
|
* can be stored. Also, nesting of layers is supported since version 0.3.1.
|
* For more information see: http://www.ogcnetwork.net/context
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.Context>
|
*/
|
OpenLayers.Format.OWSContext = OpenLayers.Class(OpenLayers.Format.Context,{
|
|
/**
|
* APIProperty: defaultVersion
|
* {String} Version number to assume if none found. Default is "0.3.1".
|
*/
|
defaultVersion: "0.3.1",
|
|
/**
|
* Constructor: OpenLayers.Format.OWSContext
|
* Create a new parser for OWS Context documents.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* Method: getVersion
|
* Returns the version to use. Subclasses can override this function
|
* if a different version detection is needed.
|
*
|
* Parameters:
|
* root - {DOMElement}
|
* options - {Object} Optional configuration object.
|
*
|
* Returns:
|
* {String} The version to use.
|
*/
|
getVersion: function(root, options) {
|
var version = OpenLayers.Format.XML.VersionedOGC.prototype.getVersion.apply(
|
this, arguments);
|
// 0.3.1 is backwards compatible with 0.3.0
|
if (version === "0.3.0") {
|
version = this.defaultVersion;
|
}
|
return version;
|
},
|
|
/**
|
* Method: toContext
|
* Create a context object free from layer given a map or a
|
* context object.
|
*
|
* Parameters:
|
* obj - {<OpenLayers.Map> | Object} The map or context.
|
*
|
* Returns:
|
* {Object} A context object.
|
*/
|
toContext: function(obj) {
|
var context = {};
|
if(obj.CLASS_NAME == "OpenLayers.Map") {
|
context.bounds = obj.getExtent();
|
context.maxExtent = obj.maxExtent;
|
context.projection = obj.projection;
|
context.size = obj.getSize();
|
context.layers = obj.layers;
|
}
|
return context;
|
},
|
|
CLASS_NAME: "OpenLayers.Format.OWSContext"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/OWSContext/v0_3_1.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML.js
|
* @requires OpenLayers/Format/KML.js
|
* @requires OpenLayers/Format/GML.js
|
* @requires OpenLayers/Format/GML/v2.js
|
* @requires OpenLayers/Format/SLD/v1_0_0.js
|
* @requires OpenLayers/Format/OWSContext.js
|
* @requires OpenLayers/Format/OWSCommon/v1_0_0.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.OWSContext.v0_3_1
|
* Read and write OWSContext version 0.3.1.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.OWSContext.v0_3_1 = OpenLayers.Class(OpenLayers.Format.XML, {
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
owc: "http://www.opengis.net/ows-context",
|
gml: "http://www.opengis.net/gml",
|
kml: "http://www.opengis.net/kml/2.2",
|
ogc: "http://www.opengis.net/ogc",
|
ows: "http://www.opengis.net/ows",
|
sld: "http://www.opengis.net/sld",
|
xlink: "http://www.w3.org/1999/xlink",
|
xsi: "http://www.w3.org/2001/XMLSchema-instance"
|
},
|
|
/**
|
* Constant: VERSION
|
* {String} 0.3.1
|
*/
|
VERSION: "0.3.1",
|
|
/**
|
* Property: schemaLocation
|
* {String} Schema location
|
*/
|
schemaLocation: "http://www.opengis.net/ows-context http://www.ogcnetwork.net/schemas/owc/0.3.1/owsContext.xsd",
|
|
/**
|
* Property: defaultPrefix
|
* {String} Default namespace prefix to use.
|
*/
|
defaultPrefix: "owc",
|
|
/**
|
* APIProperty: extractAttributes
|
* {Boolean} Extract attributes from GML. Default is true.
|
*/
|
extractAttributes: true,
|
|
/**
|
* APIProperty: xy
|
* {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
|
* Changing is not recommended, a new Format should be instantiated.
|
*/
|
xy: true,
|
|
/**
|
* Property: regExes
|
* Compiled regular expressions for manipulating strings.
|
*/
|
regExes: {
|
trimSpace: (/^\s*|\s*$/g),
|
removeSpace: (/\s*/g),
|
splitSpace: (/\s+/),
|
trimComma: (/\s*,\s*/g)
|
},
|
|
/**
|
* Property: featureNS
|
* {String} The namespace uri to use for writing InlineGeometry
|
*/
|
featureNS: "http://mapserver.gis.umn.edu/mapserver",
|
|
/**
|
* Property: featureType
|
* {String} The name to use as the feature type when writing out
|
* InlineGeometry
|
*/
|
featureType: 'vector',
|
|
/**
|
* Property: geometryName
|
* {String} The name to use for the geometry attribute when writing out
|
* InlineGeometry
|
*/
|
geometryName: 'geometry',
|
|
/**
|
* Property: nestingLayerLookup
|
* {Object} Hashtable lookup for nesting layer nodes. Used while writing
|
* the OWS context document. It is necessary to keep track of the
|
* nestingPaths for which nesting layer nodes have already been
|
* created, so (nesting) layer nodes are added to those nodes.
|
*
|
* For example:
|
*
|
* If there are three layers with nestingPaths:
|
* layer1.metadata.nestingPath = "a/b/"
|
* layer2.metadata.nestingPath = "a/b/"
|
* layer2.metadata.nestingPath = "a/c"
|
*
|
* then a nesting layer node "a" should be created once and added
|
* to the resource list, a nesting layer node "b" should be created
|
* once and added under "a", and a nesting layer node "c" should be
|
* created and added under "a". The lookup paths for these nodes
|
* will be "a", "a/b", and "a/c" respectively.
|
*/
|
nestingLayerLookup: null,
|
|
/**
|
* Constructor: OpenLayers.Format.OWSContext.v0_3_1
|
* Instances of this class are not created directly. Use the
|
* <OpenLayers.Format.OWSContext> constructor instead.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
initialize: function(options) {
|
OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
|
OpenLayers.Format.GML.v2.prototype.setGeometryTypes.call(this);
|
},
|
|
/**
|
* Method: setNestingPath
|
* Set the nestingPath property of the layer depending on the position
|
* of the layer in hierarchy of layers.
|
*
|
* Parameters:
|
* l - {Object} An object that may have a layersContext array property.
|
*
|
*/
|
setNestingPath : function(l){
|
if(l.layersContext){
|
for (var i = 0, len = l.layersContext.length; i < len; i++) {
|
var layerContext = l.layersContext[i];
|
var nPath = [];
|
var nTitle = l.title || "";
|
if(l.metadata && l.metadata.nestingPath){
|
nPath = l.metadata.nestingPath.slice();
|
}
|
if (nTitle != "") {
|
nPath.push(nTitle);
|
}
|
layerContext.metadata.nestingPath = nPath;
|
if(layerContext.layersContext){
|
this.setNestingPath(layerContext);
|
}
|
}
|
}
|
},
|
|
/**
|
* Function: decomposeNestingPath
|
* Takes a nestingPath like "a/b/c" and decomposes it into subpaths:
|
* "a", "a/b", "a/b/c"
|
*
|
* Parameters:
|
* nPath - {Array} the nesting path
|
*
|
* Returns:
|
* Array({String}) Array with subpaths, or empty array if there is nothing
|
* to decompose
|
*/
|
decomposeNestingPath: function(nPath){
|
var a = [];
|
if (OpenLayers.Util.isArray(nPath)) {
|
var path = nPath.slice();
|
while (path.length > 0) {
|
a.push(path.slice());
|
path.pop();
|
}
|
a.reverse();
|
}
|
return a;
|
},
|
|
/**
|
* APIMethod: read
|
* Read OWS context data from a string or DOMElement, and return a list
|
* of layers.
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
*
|
* Returns:
|
* {Object} The context object with a flat layer list as a property named
|
* layersContext.
|
*/
|
read: function(data) {
|
if(typeof data == "string") {
|
data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
|
}
|
if(data && data.nodeType == 9) {
|
data = data.documentElement;
|
}
|
var context = {};
|
this.readNode(data, context);
|
// since an OWSContext can be nested we need to go through this
|
// structure recursively
|
this.setNestingPath({layersContext : context.layersContext});
|
// after nesting path has been set, create a flat list of layers
|
var layers = [];
|
this.processLayer(layers, context);
|
delete context.layersContext;
|
context.layersContext = layers;
|
return context;
|
},
|
|
/**
|
* Method: processLayer
|
* Recursive function to get back a flat list of layers from the hierarchic
|
* layer structure.
|
*
|
* Parameters:
|
* layerArray - {Array({Object})} Array of layerContext objects
|
* layer - {Object} layerContext object
|
*/
|
processLayer: function(layerArray, layer) {
|
if (layer.layersContext) {
|
for (var i=0, len = layer.layersContext.length; i<len; i++) {
|
var l = layer.layersContext[i];
|
layerArray.push(l);
|
if (l.layersContext) {
|
this.processLayer(layerArray, l);
|
}
|
}
|
}
|
},
|
|
/**
|
* APIMethod: write
|
*
|
* Parameters:
|
* context - {Object} An object representing the map context.
|
* options - {Object} Optional object.
|
*
|
* Returns:
|
* {String} An OWS Context document string.
|
*/
|
write: function(context, options) {
|
var name = "OWSContext";
|
this.nestingLayerLookup = {}; //start with empty lookup
|
options = options || {};
|
OpenLayers.Util.applyDefaults(options, context);
|
var root = this.writeNode(name, options);
|
this.nestingLayerLookup = null; //clear lookup
|
this.setAttributeNS(
|
root, this.namespaces["xsi"],
|
"xsi:schemaLocation", this.schemaLocation
|
);
|
return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"kml": {
|
"Document": function(node, obj) {
|
obj.features = new OpenLayers.Format.KML(
|
{kmlns: this.namespaces.kml,
|
extractStyles: true}).read(node);
|
}
|
},
|
"owc": {
|
"OWSContext": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"General": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"ResourceList": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"Layer": function(node, obj) {
|
var layerContext = {
|
metadata: {},
|
visibility: (node.getAttribute("hidden") != "1"),
|
queryable: (node.getAttribute("queryable") == "1"),
|
opacity: ((node.getAttribute("opacity") != null) ?
|
parseFloat(node.getAttribute("opacity")) : null),
|
name: node.getAttribute("name"),
|
/* A category layer is a dummy layer meant for creating
|
hierarchies. It is not a physical layer in the
|
OpenLayers sense. The assumption we make here is that
|
category layers do not have a name attribute */
|
categoryLayer: (node.getAttribute("name") == null),
|
formats: [],
|
styles: []
|
};
|
if (!obj.layersContext) {
|
obj.layersContext = [];
|
}
|
obj.layersContext.push(layerContext);
|
this.readChildNodes(node, layerContext);
|
},
|
"InlineGeometry": function(node, obj) {
|
obj.features = [];
|
var elements = this.getElementsByTagNameNS(node,
|
this.namespaces.gml, "featureMember");
|
var el;
|
if (elements.length >= 1) {
|
el = elements[0];
|
}
|
if (el && el.firstChild) {
|
var featurenode = (el.firstChild.nextSibling) ?
|
el.firstChild.nextSibling : el.firstChild;
|
this.setNamespace("feature", featurenode.namespaceURI);
|
this.featureType = featurenode.localName ||
|
featurenode.nodeName.split(":").pop();
|
this.readChildNodes(node, obj);
|
}
|
},
|
"Server": function(node, obj) {
|
// when having multiple Server types, we prefer WMS
|
if ((!obj.service && !obj.version) ||
|
(obj.service !=
|
OpenLayers.Format.Context.serviceTypes.WMS)) {
|
obj.service = node.getAttribute("service");
|
obj.version = node.getAttribute("version");
|
this.readChildNodes(node, obj);
|
}
|
},
|
"Name": function(node, obj) {
|
obj.name = this.getChildValue(node);
|
this.readChildNodes(node, obj);
|
},
|
"Title": function(node, obj) {
|
obj.title = this.getChildValue(node);
|
this.readChildNodes(node, obj);
|
},
|
"StyleList": function(node, obj) {
|
this.readChildNodes(node, obj.styles);
|
},
|
"Style": function(node, obj) {
|
var style = {};
|
obj.push(style);
|
this.readChildNodes(node, style);
|
},
|
"LegendURL": function(node, obj) {
|
var legend = {};
|
obj.legend = legend;
|
this.readChildNodes(node, legend);
|
},
|
"OnlineResource": function(node, obj) {
|
obj.url = this.getAttributeNS(node, this.namespaces.xlink,
|
"href");
|
this.readChildNodes(node, obj);
|
}
|
},
|
"ows": OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers.ows,
|
"gml": OpenLayers.Format.GML.v2.prototype.readers.gml,
|
"sld": OpenLayers.Format.SLD.v1_0_0.prototype.readers.sld,
|
"feature": OpenLayers.Format.GML.v2.prototype.readers.feature
|
},
|
|
/**
|
* Property: writers
|
* As a compliment to the readers property, this structure contains public
|
* writing functions grouped by namespace alias and named like the
|
* node names they produce.
|
*/
|
writers: {
|
"owc": {
|
"OWSContext": function(options) {
|
var node = this.createElementNSPlus("OWSContext", {
|
attributes: {
|
version: this.VERSION,
|
id: options.id || OpenLayers.Util.createUniqueID("OpenLayers_OWSContext_")
|
}
|
});
|
this.writeNode("General", options, node);
|
this.writeNode("ResourceList", options, node);
|
return node;
|
},
|
"General": function(options) {
|
var node = this.createElementNSPlus("General");
|
this.writeNode("ows:BoundingBox", options, node);
|
this.writeNode("ows:Title", options.title || 'OpenLayers OWSContext', node);
|
return node;
|
},
|
"ResourceList": function(options) {
|
var node = this.createElementNSPlus("ResourceList");
|
for (var i=0, len=options.layers.length; i<len; i++) {
|
var layer = options.layers[i];
|
var decomposedPath = this.decomposeNestingPath(layer.metadata.nestingPath);
|
this.writeNode("_Layer", {layer: layer, subPaths: decomposedPath}, node);
|
}
|
return node;
|
},
|
"Server": function(options) {
|
var node = this.createElementNSPlus("Server", {attributes: {
|
version: options.version,
|
service: options.service }
|
});
|
this.writeNode("OnlineResource", options, node);
|
return node;
|
},
|
"OnlineResource": function(options) {
|
var node = this.createElementNSPlus("OnlineResource", {attributes: {
|
"xlink:href": options.url }
|
});
|
return node;
|
},
|
"InlineGeometry": function(layer) {
|
var node = this.createElementNSPlus("InlineGeometry"),
|
dataExtent = layer.getDataExtent();
|
if (dataExtent !== null) {
|
this.writeNode("gml:boundedBy", dataExtent, node);
|
}
|
for (var i=0, len=layer.features.length; i<len; i++) {
|
this.writeNode("gml:featureMember", layer.features[i], node);
|
}
|
return node;
|
},
|
"StyleList": function(styles) {
|
var node = this.createElementNSPlus("StyleList");
|
for (var i=0, len=styles.length; i<len; i++) {
|
this.writeNode("Style", styles[i], node);
|
}
|
return node;
|
},
|
"Style": function(style) {
|
var node = this.createElementNSPlus("Style");
|
this.writeNode("Name", style, node);
|
this.writeNode("Title", style, node);
|
if (style.legend) {
|
this.writeNode("LegendURL", style, node);
|
}
|
return node;
|
},
|
"Name": function(obj) {
|
var node = this.createElementNSPlus("Name", {
|
value: obj.name });
|
return node;
|
},
|
"Title": function(obj) {
|
var node = this.createElementNSPlus("Title", {
|
value: obj.title });
|
return node;
|
},
|
"LegendURL": function(style) {
|
var node = this.createElementNSPlus("LegendURL");
|
this.writeNode("OnlineResource", style.legend, node);
|
return node;
|
},
|
"_WMS": function(layer) {
|
var node = this.createElementNSPlus("Layer", {attributes: {
|
name: layer.params.LAYERS,
|
queryable: layer.queryable ? "1" : "0",
|
hidden: layer.visibility ? "0" : "1",
|
opacity: layer.hasOwnProperty("opacity") ? layer.opacity : null}
|
});
|
this.writeNode("ows:Title", layer.name, node);
|
this.writeNode("ows:OutputFormat", layer.params.FORMAT, node);
|
this.writeNode("Server", {service:
|
OpenLayers.Format.Context.serviceTypes.WMS,
|
version: layer.params.VERSION, url: layer.url}, node);
|
if (layer.metadata.styles && layer.metadata.styles.length > 0) {
|
this.writeNode("StyleList", layer.metadata.styles, node);
|
}
|
return node;
|
},
|
"_Layer": function(options) {
|
var layer, subPaths, node, title;
|
layer = options.layer;
|
subPaths = options.subPaths;
|
node = null;
|
title = null;
|
// subPaths is an array of an array
|
// recursively calling _Layer writer eats up subPaths, until a
|
// real writer is called and nodes are returned.
|
if(subPaths.length > 0){
|
var path = subPaths[0].join("/");
|
var index = path.lastIndexOf("/");
|
node = this.nestingLayerLookup[path];
|
title = (index > 0)?path.substring(index + 1, path.length):path;
|
if(!node){
|
// category layer
|
node = this.createElementNSPlus("Layer");
|
this.writeNode("ows:Title", title, node);
|
this.nestingLayerLookup[path] = node;
|
}
|
options.subPaths.shift();//remove a path after each call
|
this.writeNode("_Layer", options, node);
|
return node;
|
} else {
|
// write out the actual layer
|
if (layer instanceof OpenLayers.Layer.WMS) {
|
node = this.writeNode("_WMS", layer);
|
} else if (layer instanceof OpenLayers.Layer.Vector) {
|
if (layer.protocol instanceof OpenLayers.Protocol.WFS.v1) {
|
node = this.writeNode("_WFS", layer);
|
} else if (layer.protocol instanceof OpenLayers.Protocol.HTTP) {
|
if (layer.protocol.format instanceof OpenLayers.Format.GML) {
|
layer.protocol.format.version = "2.1.2";
|
node = this.writeNode("_GML", layer);
|
} else if (layer.protocol.format instanceof OpenLayers.Format.KML) {
|
layer.protocol.format.version = "2.2";
|
node = this.writeNode("_KML", layer);
|
}
|
} else {
|
// write out as inline GML since we have no idea
|
// about the original Format
|
this.setNamespace("feature", this.featureNS);
|
node = this.writeNode("_InlineGeometry", layer);
|
}
|
}
|
if (layer.options.maxScale) {
|
this.writeNode("sld:MinScaleDenominator",
|
layer.options.maxScale, node);
|
}
|
if (layer.options.minScale) {
|
this.writeNode("sld:MaxScaleDenominator",
|
layer.options.minScale, node);
|
}
|
this.nestingLayerLookup[layer.name] = node;
|
return node;
|
}
|
},
|
"_WFS": function(layer) {
|
var node = this.createElementNSPlus("Layer", {attributes: {
|
name: layer.protocol.featurePrefix + ":" + layer.protocol.featureType,
|
hidden: layer.visibility ? "0" : "1" }
|
});
|
this.writeNode("ows:Title", layer.name, node);
|
this.writeNode("Server", {service:
|
OpenLayers.Format.Context.serviceTypes.WFS,
|
version: layer.protocol.version,
|
url: layer.protocol.url}, node);
|
return node;
|
},
|
"_InlineGeometry": function(layer) {
|
var node = this.createElementNSPlus("Layer", {attributes: {
|
name: this.featureType,
|
hidden: layer.visibility ? "0" : "1" }
|
});
|
this.writeNode("ows:Title", layer.name, node);
|
this.writeNode("InlineGeometry", layer, node);
|
return node;
|
},
|
"_GML": function(layer) {
|
var node = this.createElementNSPlus("Layer");
|
this.writeNode("ows:Title", layer.name, node);
|
this.writeNode("Server", {service:
|
OpenLayers.Format.Context.serviceTypes.GML,
|
url: layer.protocol.url, version:
|
layer.protocol.format.version}, node);
|
return node;
|
},
|
"_KML": function(layer) {
|
var node = this.createElementNSPlus("Layer");
|
this.writeNode("ows:Title", layer.name, node);
|
this.writeNode("Server", {service:
|
OpenLayers.Format.Context.serviceTypes.KML,
|
version: layer.protocol.format.version, url:
|
layer.protocol.url}, node);
|
return node;
|
}
|
},
|
"gml": OpenLayers.Util.applyDefaults({
|
"boundedBy": function(bounds) {
|
var node = this.createElementNSPlus("gml:boundedBy");
|
this.writeNode("gml:Box", bounds, node);
|
return node;
|
}
|
}, OpenLayers.Format.GML.v2.prototype.writers.gml),
|
"ows": OpenLayers.Format.OWSCommon.v1_0_0.prototype.writers.ows,
|
"sld": OpenLayers.Format.SLD.v1_0_0.prototype.writers.sld,
|
"feature": OpenLayers.Format.GML.v2.prototype.writers.feature
|
},
|
|
CLASS_NAME: "OpenLayers.Format.OWSContext.v0_3_1"
|
|
});
|
/* ======================================================================
|
OpenLayers/Popup.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
*/
|
|
|
/**
|
* Class: OpenLayers.Popup
|
* A popup is a small div that can opened and closed on the map.
|
* Typically opened in response to clicking on a marker.
|
* See <OpenLayers.Marker>. Popup's don't require their own
|
* layer and are added the the map using the <OpenLayers.Map.addPopup>
|
* method.
|
*
|
* Example:
|
* (code)
|
* popup = new OpenLayers.Popup("chicken",
|
* new OpenLayers.LonLat(5,40),
|
* new OpenLayers.Size(200,200),
|
* "example popup",
|
* true);
|
*
|
* map.addPopup(popup);
|
* (end)
|
*/
|
OpenLayers.Popup = OpenLayers.Class({
|
|
/**
|
* Property: events
|
* {<OpenLayers.Events>} custom event manager
|
*/
|
events: null,
|
|
/** Property: id
|
* {String} the unique identifier assigned to this popup.
|
*/
|
id: "",
|
|
/**
|
* Property: lonlat
|
* {<OpenLayers.LonLat>} the position of this popup on the map
|
*/
|
lonlat: null,
|
|
/**
|
* Property: div
|
* {DOMElement} the div that contains this popup.
|
*/
|
div: null,
|
|
/**
|
* Property: contentSize
|
* {<OpenLayers.Size>} the width and height of the content.
|
*/
|
contentSize: null,
|
|
/**
|
* Property: size
|
* {<OpenLayers.Size>} the width and height of the popup.
|
*/
|
size: null,
|
|
/**
|
* Property: contentHTML
|
* {String} An HTML string for this popup to display.
|
*/
|
contentHTML: null,
|
|
/**
|
* Property: backgroundColor
|
* {String} the background color used by the popup.
|
*/
|
backgroundColor: "",
|
|
/**
|
* Property: opacity
|
* {float} the opacity of this popup (between 0.0 and 1.0)
|
*/
|
opacity: "",
|
|
/**
|
* Property: border
|
* {String} the border size of the popup. (eg 2px)
|
*/
|
border: "",
|
|
/**
|
* Property: contentDiv
|
* {DOMElement} a reference to the element that holds the content of
|
* the div.
|
*/
|
contentDiv: null,
|
|
/**
|
* Property: groupDiv
|
* {DOMElement} First and only child of 'div'. The group Div contains the
|
* 'contentDiv' and the 'closeDiv'.
|
*/
|
groupDiv: null,
|
|
/**
|
* Property: closeDiv
|
* {DOMElement} the optional closer image
|
*/
|
closeDiv: null,
|
|
/**
|
* APIProperty: autoSize
|
* {Boolean} Resize the popup to auto-fit the contents.
|
* Default is false.
|
*/
|
autoSize: false,
|
|
/**
|
* APIProperty: minSize
|
* {<OpenLayers.Size>} Minimum size allowed for the popup's contents.
|
*/
|
minSize: null,
|
|
/**
|
* APIProperty: maxSize
|
* {<OpenLayers.Size>} Maximum size allowed for the popup's contents.
|
*/
|
maxSize: null,
|
|
/**
|
* Property: displayClass
|
* {String} The CSS class of the popup.
|
*/
|
displayClass: "olPopup",
|
|
/**
|
* Property: contentDisplayClass
|
* {String} The CSS class of the popup content div.
|
*/
|
contentDisplayClass: "olPopupContent",
|
|
/**
|
* Property: padding
|
* {int or <OpenLayers.Bounds>} An extra opportunity to specify internal
|
* padding of the content div inside the popup. This was originally
|
* confused with the css padding as specified in style.css's
|
* 'olPopupContent' class. We would like to get rid of this altogether,
|
* except that it does come in handy for the framed and anchoredbubble
|
* popups, who need to maintain yet another barrier between their
|
* content and the outer border of the popup itself.
|
*
|
* Note that in order to not break API, we must continue to support
|
* this property being set as an integer. Really, though, we'd like to
|
* have this specified as a Bounds object so that user can specify
|
* distinct left, top, right, bottom paddings. With the 3.0 release
|
* we can make this only a bounds.
|
*/
|
padding: 0,
|
|
/**
|
* Property: disableFirefoxOverflowHack
|
* {Boolean} The hack for overflow in Firefox causes all elements
|
* to be re-drawn, which causes Flash elements to be
|
* re-initialized, which is troublesome.
|
* With this property the hack can be disabled.
|
*/
|
disableFirefoxOverflowHack: false,
|
|
/**
|
* Method: fixPadding
|
* To be removed in 3.0, this function merely helps us to deal with the
|
* case where the user may have set an integer value for padding,
|
* instead of an <OpenLayers.Bounds> object.
|
*/
|
fixPadding: function() {
|
if (typeof this.padding == "number") {
|
this.padding = new OpenLayers.Bounds(
|
this.padding, this.padding, this.padding, this.padding
|
);
|
}
|
},
|
|
/**
|
* APIProperty: panMapIfOutOfView
|
* {Boolean} When drawn, pan map such that the entire popup is visible in
|
* the current viewport (if necessary).
|
* Default is false.
|
*/
|
panMapIfOutOfView: false,
|
|
/**
|
* APIProperty: keepInMap
|
* {Boolean} If panMapIfOutOfView is false, and this property is true,
|
* contrain the popup such that it always fits in the available map
|
* space. By default, this is not set on the base class. If you are
|
* creating popups that are near map edges and not allowing pannning,
|
* and especially if you have a popup which has a
|
* fixedRelativePosition, setting this to false may be a smart thing to
|
* do. Subclasses may want to override this setting.
|
*
|
* Default is false.
|
*/
|
keepInMap: false,
|
|
/**
|
* APIProperty: closeOnMove
|
* {Boolean} When map pans, close the popup.
|
* Default is false.
|
*/
|
closeOnMove: false,
|
|
/**
|
* Property: map
|
* {<OpenLayers.Map>} this gets set in Map.js when the popup is added to the map
|
*/
|
map: null,
|
|
/**
|
* Constructor: OpenLayers.Popup
|
* Create a popup.
|
*
|
* Parameters:
|
* id - {String} a unqiue identifier for this popup. If null is passed
|
* an identifier will be automatically generated.
|
* lonlat - {<OpenLayers.LonLat>} The position on the map the popup will
|
* be shown.
|
* contentSize - {<OpenLayers.Size>} The size of the content.
|
* contentHTML - {String} An HTML string to display inside the
|
* popup.
|
* closeBox - {Boolean} Whether to display a close box inside
|
* the popup.
|
* closeBoxCallback - {Function} Function to be called on closeBox click.
|
*/
|
initialize:function(id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback) {
|
if (id == null) {
|
id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
|
}
|
|
this.id = id;
|
this.lonlat = lonlat;
|
|
this.contentSize = (contentSize != null) ? contentSize
|
: new OpenLayers.Size(
|
OpenLayers.Popup.WIDTH,
|
OpenLayers.Popup.HEIGHT);
|
if (contentHTML != null) {
|
this.contentHTML = contentHTML;
|
}
|
this.backgroundColor = OpenLayers.Popup.COLOR;
|
this.opacity = OpenLayers.Popup.OPACITY;
|
this.border = OpenLayers.Popup.BORDER;
|
|
this.div = OpenLayers.Util.createDiv(this.id, null, null,
|
null, null, null, "hidden");
|
this.div.className = this.displayClass;
|
|
var groupDivId = this.id + "_GroupDiv";
|
this.groupDiv = OpenLayers.Util.createDiv(groupDivId, null, null,
|
null, "relative", null,
|
"hidden");
|
|
var id = this.div.id + "_contentDiv";
|
this.contentDiv = OpenLayers.Util.createDiv(id, null, this.contentSize.clone(),
|
null, "relative");
|
this.contentDiv.className = this.contentDisplayClass;
|
this.groupDiv.appendChild(this.contentDiv);
|
this.div.appendChild(this.groupDiv);
|
|
if (closeBox) {
|
this.addCloseBox(closeBoxCallback);
|
}
|
|
this.registerEvents();
|
},
|
|
/**
|
* Method: destroy
|
* nullify references to prevent circular references and memory leaks
|
*/
|
destroy: function() {
|
|
this.id = null;
|
this.lonlat = null;
|
this.size = null;
|
this.contentHTML = null;
|
|
this.backgroundColor = null;
|
this.opacity = null;
|
this.border = null;
|
|
if (this.closeOnMove && this.map) {
|
this.map.events.unregister("movestart", this, this.hide);
|
}
|
|
this.events.destroy();
|
this.events = null;
|
|
if (this.closeDiv) {
|
OpenLayers.Event.stopObservingElement(this.closeDiv);
|
this.groupDiv.removeChild(this.closeDiv);
|
}
|
this.closeDiv = null;
|
|
this.div.removeChild(this.groupDiv);
|
this.groupDiv = null;
|
|
if (this.map != null) {
|
this.map.removePopup(this);
|
}
|
this.map = null;
|
this.div = null;
|
|
this.autoSize = null;
|
this.minSize = null;
|
this.maxSize = null;
|
this.padding = null;
|
this.panMapIfOutOfView = null;
|
},
|
|
/**
|
* Method: draw
|
* Constructs the elements that make up the popup.
|
*
|
* Parameters:
|
* px - {<OpenLayers.Pixel>} the position the popup in pixels.
|
*
|
* Returns:
|
* {DOMElement} Reference to a div that contains the drawn popup
|
*/
|
draw: function(px) {
|
if (px == null) {
|
if ((this.lonlat != null) && (this.map != null)) {
|
px = this.map.getLayerPxFromLonLat(this.lonlat);
|
}
|
}
|
|
// this assumes that this.map already exists, which is okay because
|
// this.draw is only called once the popup has been added to the map.
|
if (this.closeOnMove) {
|
this.map.events.register("movestart", this, this.hide);
|
}
|
|
//listen to movestart, moveend to disable overflow (FF bug)
|
if (!this.disableFirefoxOverflowHack && OpenLayers.BROWSER_NAME == 'firefox') {
|
this.map.events.register("movestart", this, function() {
|
var style = document.defaultView.getComputedStyle(
|
this.contentDiv, null
|
);
|
var currentOverflow = style.getPropertyValue("overflow");
|
if (currentOverflow != "hidden") {
|
this.contentDiv._oldOverflow = currentOverflow;
|
this.contentDiv.style.overflow = "hidden";
|
}
|
});
|
this.map.events.register("moveend", this, function() {
|
var oldOverflow = this.contentDiv._oldOverflow;
|
if (oldOverflow) {
|
this.contentDiv.style.overflow = oldOverflow;
|
this.contentDiv._oldOverflow = null;
|
}
|
});
|
}
|
|
this.moveTo(px);
|
if (!this.autoSize && !this.size) {
|
this.setSize(this.contentSize);
|
}
|
this.setBackgroundColor();
|
this.setOpacity();
|
this.setBorder();
|
this.setContentHTML();
|
|
if (this.panMapIfOutOfView) {
|
this.panIntoView();
|
}
|
|
return this.div;
|
},
|
|
/**
|
* Method: updatePosition
|
* if the popup has a lonlat and its map members set,
|
* then have it move itself to its proper position
|
*/
|
updatePosition: function() {
|
if ((this.lonlat) && (this.map)) {
|
var px = this.map.getLayerPxFromLonLat(this.lonlat);
|
if (px) {
|
this.moveTo(px);
|
}
|
}
|
},
|
|
/**
|
* Method: moveTo
|
*
|
* Parameters:
|
* px - {<OpenLayers.Pixel>} the top and left position of the popup div.
|
*/
|
moveTo: function(px) {
|
if ((px != null) && (this.div != null)) {
|
this.div.style.left = px.x + "px";
|
this.div.style.top = px.y + "px";
|
}
|
},
|
|
/**
|
* Method: visible
|
*
|
* Returns:
|
* {Boolean} Boolean indicating whether or not the popup is visible
|
*/
|
visible: function() {
|
return OpenLayers.Element.visible(this.div);
|
},
|
|
/**
|
* Method: toggle
|
* Toggles visibility of the popup.
|
*/
|
toggle: function() {
|
if (this.visible()) {
|
this.hide();
|
} else {
|
this.show();
|
}
|
},
|
|
/**
|
* Method: show
|
* Makes the popup visible.
|
*/
|
show: function() {
|
this.div.style.display = '';
|
|
if (this.panMapIfOutOfView) {
|
this.panIntoView();
|
}
|
},
|
|
/**
|
* Method: hide
|
* Makes the popup invisible.
|
*/
|
hide: function() {
|
this.div.style.display = 'none';
|
},
|
|
/**
|
* Method: setSize
|
* Used to adjust the size of the popup.
|
*
|
* Parameters:
|
* contentSize - {<OpenLayers.Size>} the new size for the popup's
|
* contents div (in pixels).
|
*/
|
setSize:function(contentSize) {
|
this.size = contentSize.clone();
|
|
// if our contentDiv has a css 'padding' set on it by a stylesheet, we
|
// must add that to the desired "size".
|
var contentDivPadding = this.getContentDivPadding();
|
var wPadding = contentDivPadding.left + contentDivPadding.right;
|
var hPadding = contentDivPadding.top + contentDivPadding.bottom;
|
|
// take into account the popup's 'padding' property
|
this.fixPadding();
|
wPadding += this.padding.left + this.padding.right;
|
hPadding += this.padding.top + this.padding.bottom;
|
|
// make extra space for the close div
|
if (this.closeDiv) {
|
var closeDivWidth = parseInt(this.closeDiv.style.width);
|
wPadding += closeDivWidth + contentDivPadding.right;
|
}
|
|
//increase size of the main popup div to take into account the
|
// users's desired padding and close div.
|
this.size.w += wPadding;
|
this.size.h += hPadding;
|
|
//now if our browser is IE, we need to actually make the contents
|
// div itself bigger to take its own padding into effect. this makes
|
// me want to shoot someone, but so it goes.
|
if (OpenLayers.BROWSER_NAME == "msie") {
|
this.contentSize.w +=
|
contentDivPadding.left + contentDivPadding.right;
|
this.contentSize.h +=
|
contentDivPadding.bottom + contentDivPadding.top;
|
}
|
|
if (this.div != null) {
|
this.div.style.width = this.size.w + "px";
|
this.div.style.height = this.size.h + "px";
|
}
|
if (this.contentDiv != null){
|
this.contentDiv.style.width = contentSize.w + "px";
|
this.contentDiv.style.height = contentSize.h + "px";
|
}
|
},
|
|
/**
|
* APIMethod: updateSize
|
* Auto size the popup so that it precisely fits its contents (as
|
* determined by this.contentDiv.innerHTML). Popup size will, of
|
* course, be limited by the available space on the current map
|
*/
|
updateSize: function() {
|
|
// determine actual render dimensions of the contents by putting its
|
// contents into a fake contentDiv (for the CSS) and then measuring it
|
var preparedHTML = "<div class='" + this.contentDisplayClass+ "'>" +
|
this.contentDiv.innerHTML +
|
"</div>";
|
|
var containerElement = (this.map) ? this.map.div : document.body;
|
var realSize = OpenLayers.Util.getRenderedDimensions(
|
preparedHTML, null, {
|
displayClass: this.displayClass,
|
containerElement: containerElement
|
}
|
);
|
|
// is the "real" size of the div is safe to display in our map?
|
var safeSize = this.getSafeContentSize(realSize);
|
|
var newSize = null;
|
if (safeSize.equals(realSize)) {
|
//real size of content is small enough to fit on the map,
|
// so we use real size.
|
newSize = realSize;
|
|
} else {
|
|
// make a new 'size' object with the clipped dimensions
|
// set or null if not clipped.
|
var fixedSize = {
|
w: (safeSize.w < realSize.w) ? safeSize.w : null,
|
h: (safeSize.h < realSize.h) ? safeSize.h : null
|
};
|
|
if (fixedSize.w && fixedSize.h) {
|
//content is too big in both directions, so we will use
|
// max popup size (safeSize), knowing well that it will
|
// overflow both ways.
|
newSize = safeSize;
|
} else {
|
//content is clipped in only one direction, so we need to
|
// run getRenderedDimensions() again with a fixed dimension
|
var clippedSize = OpenLayers.Util.getRenderedDimensions(
|
preparedHTML, fixedSize, {
|
displayClass: this.contentDisplayClass,
|
containerElement: containerElement
|
}
|
);
|
|
//if the clipped size is still the same as the safeSize,
|
// that means that our content must be fixed in the
|
// offending direction. If overflow is 'auto', this means
|
// we are going to have a scrollbar for sure, so we must
|
// adjust for that.
|
//
|
var currentOverflow = OpenLayers.Element.getStyle(
|
this.contentDiv, "overflow"
|
);
|
if ( (currentOverflow != "hidden") &&
|
(clippedSize.equals(safeSize)) ) {
|
var scrollBar = OpenLayers.Util.getScrollbarWidth();
|
if (fixedSize.w) {
|
clippedSize.h += scrollBar;
|
} else {
|
clippedSize.w += scrollBar;
|
}
|
}
|
|
newSize = this.getSafeContentSize(clippedSize);
|
}
|
}
|
this.setSize(newSize);
|
},
|
|
/**
|
* Method: setBackgroundColor
|
* Sets the background color of the popup.
|
*
|
* Parameters:
|
* color - {String} the background color. eg "#FFBBBB"
|
*/
|
setBackgroundColor:function(color) {
|
if (color != undefined) {
|
this.backgroundColor = color;
|
}
|
|
if (this.div != null) {
|
this.div.style.backgroundColor = this.backgroundColor;
|
}
|
},
|
|
/**
|
* Method: setOpacity
|
* Sets the opacity of the popup.
|
*
|
* Parameters:
|
* opacity - {float} A value between 0.0 (transparent) and 1.0 (solid).
|
*/
|
setOpacity:function(opacity) {
|
if (opacity != undefined) {
|
this.opacity = opacity;
|
}
|
|
if (this.div != null) {
|
// for Mozilla and Safari
|
this.div.style.opacity = this.opacity;
|
|
// for IE
|
this.div.style.filter = 'alpha(opacity=' + this.opacity*100 + ')';
|
}
|
},
|
|
/**
|
* Method: setBorder
|
* Sets the border style of the popup.
|
*
|
* Parameters:
|
* border - {String} The border style value. eg 2px
|
*/
|
setBorder:function(border) {
|
if (border != undefined) {
|
this.border = border;
|
}
|
|
if (this.div != null) {
|
this.div.style.border = this.border;
|
}
|
},
|
|
/**
|
* Method: setContentHTML
|
* Allows the user to set the HTML content of the popup.
|
*
|
* Parameters:
|
* contentHTML - {String} HTML for the div.
|
*/
|
setContentHTML:function(contentHTML) {
|
|
if (contentHTML != null) {
|
this.contentHTML = contentHTML;
|
}
|
|
if ((this.contentDiv != null) &&
|
(this.contentHTML != null) &&
|
(this.contentHTML != this.contentDiv.innerHTML)) {
|
|
this.contentDiv.innerHTML = this.contentHTML;
|
|
if (this.autoSize) {
|
|
//if popup has images, listen for when they finish
|
// loading and resize accordingly
|
this.registerImageListeners();
|
|
//auto size the popup to its current contents
|
this.updateSize();
|
}
|
}
|
|
},
|
|
/**
|
* Method: registerImageListeners
|
* Called when an image contained by the popup loaded. this function
|
* updates the popup size, then unregisters the image load listener.
|
*/
|
registerImageListeners: function() {
|
|
// As the images load, this function will call updateSize() to
|
// resize the popup to fit the content div (which presumably is now
|
// bigger than when the image was not loaded).
|
//
|
// If the 'panMapIfOutOfView' property is set, we will pan the newly
|
// resized popup back into view.
|
//
|
// Note that this function, when called, will have 'popup' and
|
// 'img' properties in the context.
|
//
|
var onImgLoad = function() {
|
if (this.popup.id === null) { // this.popup has been destroyed!
|
return;
|
}
|
this.popup.updateSize();
|
|
if ( this.popup.visible() && this.popup.panMapIfOutOfView ) {
|
this.popup.panIntoView();
|
}
|
|
OpenLayers.Event.stopObserving(
|
this.img, "load", this.img._onImgLoad
|
);
|
|
};
|
|
//cycle through the images and if their size is 0x0, that means that
|
// they haven't been loaded yet, so we attach the listener, which
|
// will fire when the images finish loading and will resize the
|
// popup accordingly to its new size.
|
var images = this.contentDiv.getElementsByTagName("img");
|
for (var i = 0, len = images.length; i < len; i++) {
|
var img = images[i];
|
if (img.width == 0 || img.height == 0) {
|
|
var context = {
|
'popup': this,
|
'img': img
|
};
|
|
//expando this function to the image itself before registering
|
// it. This way we can easily and properly unregister it.
|
img._onImgLoad = OpenLayers.Function.bind(onImgLoad, context);
|
|
OpenLayers.Event.observe(img, 'load', img._onImgLoad);
|
}
|
}
|
},
|
|
/**
|
* APIMethod: getSafeContentSize
|
*
|
* Parameters:
|
* size - {<OpenLayers.Size>} Desired size to make the popup.
|
*
|
* Returns:
|
* {<OpenLayers.Size>} A size to make the popup which is neither smaller
|
* than the specified minimum size, nor bigger than the maximum
|
* size (which is calculated relative to the size of the viewport).
|
*/
|
getSafeContentSize: function(size) {
|
|
var safeContentSize = size.clone();
|
|
// if our contentDiv has a css 'padding' set on it by a stylesheet, we
|
// must add that to the desired "size".
|
var contentDivPadding = this.getContentDivPadding();
|
var wPadding = contentDivPadding.left + contentDivPadding.right;
|
var hPadding = contentDivPadding.top + contentDivPadding.bottom;
|
|
// take into account the popup's 'padding' property
|
this.fixPadding();
|
wPadding += this.padding.left + this.padding.right;
|
hPadding += this.padding.top + this.padding.bottom;
|
|
if (this.closeDiv) {
|
var closeDivWidth = parseInt(this.closeDiv.style.width);
|
wPadding += closeDivWidth + contentDivPadding.right;
|
}
|
|
// prevent the popup from being smaller than a specified minimal size
|
if (this.minSize) {
|
safeContentSize.w = Math.max(safeContentSize.w,
|
(this.minSize.w - wPadding));
|
safeContentSize.h = Math.max(safeContentSize.h,
|
(this.minSize.h - hPadding));
|
}
|
|
// prevent the popup from being bigger than a specified maximum size
|
if (this.maxSize) {
|
safeContentSize.w = Math.min(safeContentSize.w,
|
(this.maxSize.w - wPadding));
|
safeContentSize.h = Math.min(safeContentSize.h,
|
(this.maxSize.h - hPadding));
|
}
|
|
//make sure the desired size to set doesn't result in a popup that
|
// is bigger than the map's viewport.
|
//
|
if (this.map && this.map.size) {
|
|
var extraX = 0, extraY = 0;
|
if (this.keepInMap && !this.panMapIfOutOfView) {
|
var px = this.map.getPixelFromLonLat(this.lonlat);
|
switch (this.relativePosition) {
|
case "tr":
|
extraX = px.x;
|
extraY = this.map.size.h - px.y;
|
break;
|
case "tl":
|
extraX = this.map.size.w - px.x;
|
extraY = this.map.size.h - px.y;
|
break;
|
case "bl":
|
extraX = this.map.size.w - px.x;
|
extraY = px.y;
|
break;
|
case "br":
|
extraX = px.x;
|
extraY = px.y;
|
break;
|
default:
|
extraX = px.x;
|
extraY = this.map.size.h - px.y;
|
break;
|
}
|
}
|
|
var maxY = this.map.size.h -
|
this.map.paddingForPopups.top -
|
this.map.paddingForPopups.bottom -
|
hPadding - extraY;
|
|
var maxX = this.map.size.w -
|
this.map.paddingForPopups.left -
|
this.map.paddingForPopups.right -
|
wPadding - extraX;
|
|
safeContentSize.w = Math.min(safeContentSize.w, maxX);
|
safeContentSize.h = Math.min(safeContentSize.h, maxY);
|
}
|
|
return safeContentSize;
|
},
|
|
/**
|
* Method: getContentDivPadding
|
* Glorious, oh glorious hack in order to determine the css 'padding' of
|
* the contentDiv. IE/Opera return null here unless we actually add the
|
* popup's main 'div' element (which contains contentDiv) to the DOM.
|
* So we make it invisible and then add it to the document temporarily.
|
*
|
* Once we've taken the padding readings we need, we then remove it
|
* from the DOM (it will actually get added to the DOM in
|
* Map.js's addPopup)
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>}
|
*/
|
getContentDivPadding: function() {
|
|
//use cached value if we have it
|
var contentDivPadding = this._contentDivPadding;
|
if (!contentDivPadding) {
|
|
if (this.div.parentNode == null) {
|
//make the div invisible and add it to the page
|
this.div.style.display = "none";
|
document.body.appendChild(this.div);
|
}
|
|
//read the padding settings from css, put them in an OL.Bounds
|
contentDivPadding = new OpenLayers.Bounds(
|
OpenLayers.Element.getStyle(this.contentDiv, "padding-left"),
|
OpenLayers.Element.getStyle(this.contentDiv, "padding-bottom"),
|
OpenLayers.Element.getStyle(this.contentDiv, "padding-right"),
|
OpenLayers.Element.getStyle(this.contentDiv, "padding-top")
|
);
|
|
//cache the value
|
this._contentDivPadding = contentDivPadding;
|
|
if (this.div.parentNode == document.body) {
|
//remove the div from the page and make it visible again
|
document.body.removeChild(this.div);
|
this.div.style.display = "";
|
}
|
}
|
return contentDivPadding;
|
},
|
|
/**
|
* Method: addCloseBox
|
*
|
* Parameters:
|
* callback - {Function} The callback to be called when the close button
|
* is clicked.
|
*/
|
addCloseBox: function(callback) {
|
|
this.closeDiv = OpenLayers.Util.createDiv(
|
this.id + "_close", null, {w: 17, h: 17}
|
);
|
this.closeDiv.className = "olPopupCloseBox";
|
|
// use the content div's css padding to determine if we should
|
// padd the close div
|
var contentDivPadding = this.getContentDivPadding();
|
|
this.closeDiv.style.right = contentDivPadding.right + "px";
|
this.closeDiv.style.top = contentDivPadding.top + "px";
|
this.groupDiv.appendChild(this.closeDiv);
|
|
var closePopup = callback || function(e) {
|
this.hide();
|
OpenLayers.Event.stop(e);
|
};
|
OpenLayers.Event.observe(this.closeDiv, "touchend",
|
OpenLayers.Function.bindAsEventListener(closePopup, this));
|
OpenLayers.Event.observe(this.closeDiv, "click",
|
OpenLayers.Function.bindAsEventListener(closePopup, this));
|
},
|
|
/**
|
* Method: panIntoView
|
* Pans the map such that the popup is totaly viewable (if necessary)
|
*/
|
panIntoView: function() {
|
|
var mapSize = this.map.getSize();
|
|
//start with the top left corner of the popup, in px,
|
// relative to the viewport
|
var origTL = this.map.getViewPortPxFromLayerPx( new OpenLayers.Pixel(
|
parseInt(this.div.style.left),
|
parseInt(this.div.style.top)
|
));
|
var newTL = origTL.clone();
|
|
//new left (compare to margins, using this.size to calculate right)
|
if (origTL.x < this.map.paddingForPopups.left) {
|
newTL.x = this.map.paddingForPopups.left;
|
} else
|
if ( (origTL.x + this.size.w) > (mapSize.w - this.map.paddingForPopups.right)) {
|
newTL.x = mapSize.w - this.map.paddingForPopups.right - this.size.w;
|
}
|
|
//new top (compare to margins, using this.size to calculate bottom)
|
if (origTL.y < this.map.paddingForPopups.top) {
|
newTL.y = this.map.paddingForPopups.top;
|
} else
|
if ( (origTL.y + this.size.h) > (mapSize.h - this.map.paddingForPopups.bottom)) {
|
newTL.y = mapSize.h - this.map.paddingForPopups.bottom - this.size.h;
|
}
|
|
var dx = origTL.x - newTL.x;
|
var dy = origTL.y - newTL.y;
|
|
this.map.pan(dx, dy);
|
},
|
|
/**
|
* Method: registerEvents
|
* Registers events on the popup.
|
*
|
* Do this in a separate function so that subclasses can
|
* choose to override it if they wish to deal differently
|
* with mouse events
|
*
|
* Note in the following handler functions that some special
|
* care is needed to deal correctly with mousing and popups.
|
*
|
* Because the user might select the zoom-rectangle option and
|
* then drag it over a popup, we need a safe way to allow the
|
* mousemove and mouseup events to pass through the popup when
|
* they are initiated from outside. The same procedure is needed for
|
* touchmove and touchend events.
|
*
|
* Otherwise, we want to essentially kill the event propagation
|
* for all other events, though we have to do so carefully,
|
* without disabling basic html functionality, like clicking on
|
* hyperlinks or drag-selecting text.
|
*/
|
registerEvents:function() {
|
this.events = new OpenLayers.Events(this, this.div, null, true);
|
|
function onTouchstart(evt) {
|
OpenLayers.Event.stop(evt, true);
|
}
|
this.events.on({
|
"mousedown": this.onmousedown,
|
"mousemove": this.onmousemove,
|
"mouseup": this.onmouseup,
|
"click": this.onclick,
|
"mouseout": this.onmouseout,
|
"dblclick": this.ondblclick,
|
"touchstart": onTouchstart,
|
scope: this
|
});
|
|
},
|
|
/**
|
* Method: onmousedown
|
* When mouse goes down within the popup, make a note of
|
* it locally, and then do not propagate the mousedown
|
* (but do so safely so that user can select text inside)
|
*
|
* Parameters:
|
* evt - {Event}
|
*/
|
onmousedown: function (evt) {
|
this.mousedown = true;
|
OpenLayers.Event.stop(evt, true);
|
},
|
|
/**
|
* Method: onmousemove
|
* If the drag was started within the popup, then
|
* do not propagate the mousemove (but do so safely
|
* so that user can select text inside)
|
*
|
* Parameters:
|
* evt - {Event}
|
*/
|
onmousemove: function (evt) {
|
if (this.mousedown) {
|
OpenLayers.Event.stop(evt, true);
|
}
|
},
|
|
/**
|
* Method: onmouseup
|
* When mouse comes up within the popup, after going down
|
* in it, reset the flag, and then (once again) do not
|
* propagate the event, but do so safely so that user can
|
* select text inside
|
*
|
* Parameters:
|
* evt - {Event}
|
*/
|
onmouseup: function (evt) {
|
if (this.mousedown) {
|
this.mousedown = false;
|
OpenLayers.Event.stop(evt, true);
|
}
|
},
|
|
/**
|
* Method: onclick
|
* Ignore clicks, but allowing default browser handling
|
*
|
* Parameters:
|
* evt - {Event}
|
*/
|
onclick: function (evt) {
|
OpenLayers.Event.stop(evt, true);
|
},
|
|
/**
|
* Method: onmouseout
|
* When mouse goes out of the popup set the flag to false so that
|
* if they let go and then drag back in, we won't be confused.
|
*
|
* Parameters:
|
* evt - {Event}
|
*/
|
onmouseout: function (evt) {
|
this.mousedown = false;
|
},
|
|
/**
|
* Method: ondblclick
|
* Ignore double-clicks, but allowing default browser handling
|
*
|
* Parameters:
|
* evt - {Event}
|
*/
|
ondblclick: function (evt) {
|
OpenLayers.Event.stop(evt, true);
|
},
|
|
CLASS_NAME: "OpenLayers.Popup"
|
});
|
|
OpenLayers.Popup.WIDTH = 200;
|
OpenLayers.Popup.HEIGHT = 200;
|
OpenLayers.Popup.COLOR = "white";
|
OpenLayers.Popup.OPACITY = 1;
|
OpenLayers.Popup.BORDER = "0px";
|
/* ======================================================================
|
OpenLayers/Control/ScaleLine.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.ScaleLine
|
* The ScaleLine displays a small line indicator representing the current
|
* map scale on the map. By default it is drawn in the lower left corner of
|
* the map.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*
|
* Is a very close copy of:
|
* - <OpenLayers.Control.Scale>
|
*/
|
OpenLayers.Control.ScaleLine = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* Property: maxWidth
|
* {Integer} Maximum width of the scale line in pixels. Default is 100.
|
*/
|
maxWidth: 100,
|
|
/**
|
* Property: topOutUnits
|
* {String} Units for zoomed out on top bar. Default is km.
|
*/
|
topOutUnits: "km",
|
|
/**
|
* Property: topInUnits
|
* {String} Units for zoomed in on top bar. Default is m.
|
*/
|
topInUnits: "m",
|
|
/**
|
* Property: bottomOutUnits
|
* {String} Units for zoomed out on bottom bar. Default is mi.
|
*/
|
bottomOutUnits: "mi",
|
|
/**
|
* Property: bottomInUnits
|
* {String} Units for zoomed in on bottom bar. Default is ft.
|
*/
|
bottomInUnits: "ft",
|
|
/**
|
* Property: eTop
|
* {DOMElement}
|
*/
|
eTop: null,
|
|
/**
|
* Property: eBottom
|
* {DOMElement}
|
*/
|
eBottom:null,
|
|
/**
|
* APIProperty: geodesic
|
* {Boolean} Use geodesic measurement. Default is false. The recommended
|
* setting for maps in EPSG:4326 is false, and true EPSG:900913. If set to
|
* true, the scale will be calculated based on the horizontal size of the
|
* pixel in the center of the map viewport.
|
*/
|
geodesic: false,
|
|
/**
|
* Constructor: OpenLayers.Control.ScaleLine
|
* Create a new scale line control.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be used
|
* to extend the control.
|
*/
|
|
/**
|
* Method: draw
|
*
|
* Returns:
|
* {DOMElement}
|
*/
|
draw: function() {
|
OpenLayers.Control.prototype.draw.apply(this, arguments);
|
if (!this.eTop) {
|
// stick in the top bar
|
this.eTop = document.createElement("div");
|
this.eTop.className = this.displayClass + "Top";
|
var theLen = this.topInUnits.length;
|
this.div.appendChild(this.eTop);
|
if((this.topOutUnits == "") || (this.topInUnits == "")) {
|
this.eTop.style.visibility = "hidden";
|
} else {
|
this.eTop.style.visibility = "visible";
|
}
|
|
// and the bottom bar
|
this.eBottom = document.createElement("div");
|
this.eBottom.className = this.displayClass + "Bottom";
|
this.div.appendChild(this.eBottom);
|
if((this.bottomOutUnits == "") || (this.bottomInUnits == "")) {
|
this.eBottom.style.visibility = "hidden";
|
} else {
|
this.eBottom.style.visibility = "visible";
|
}
|
}
|
this.map.events.register('moveend', this, this.update);
|
this.update();
|
return this.div;
|
},
|
|
/**
|
* Method: getBarLen
|
* Given a number, round it down to the nearest 1,2,5 times a power of 10.
|
* That seems a fairly useful set of number groups to use.
|
*
|
* Parameters:
|
* maxLen - {float} the number we're rounding down from
|
*
|
* Returns:
|
* {Float} the rounded number (less than or equal to maxLen)
|
*/
|
getBarLen: function(maxLen) {
|
// nearest power of 10 lower than maxLen
|
var digits = parseInt(Math.log(maxLen) / Math.log(10));
|
var pow10 = Math.pow(10, digits);
|
|
// ok, find first character
|
var firstChar = parseInt(maxLen / pow10);
|
|
// right, put it into the correct bracket
|
var barLen;
|
if(firstChar > 5) {
|
barLen = 5;
|
} else if(firstChar > 2) {
|
barLen = 2;
|
} else {
|
barLen = 1;
|
}
|
|
// scale it up the correct power of 10
|
return barLen * pow10;
|
},
|
|
/**
|
* Method: update
|
* Update the size of the bars, and the labels they contain.
|
*/
|
update: function() {
|
var res = this.map.getResolution();
|
if (!res) {
|
return;
|
}
|
|
var curMapUnits = this.map.getUnits();
|
var inches = OpenLayers.INCHES_PER_UNIT;
|
|
// convert maxWidth to map units
|
var maxSizeData = this.maxWidth * res * inches[curMapUnits];
|
var geodesicRatio = 1;
|
if(this.geodesic === true) {
|
var maxSizeGeodesic = (this.map.getGeodesicPixelSize().w ||
|
0.000001) * this.maxWidth;
|
var maxSizeKilometers = maxSizeData / inches["km"];
|
geodesicRatio = maxSizeGeodesic / maxSizeKilometers;
|
maxSizeData *= geodesicRatio;
|
}
|
|
// decide whether to use large or small scale units
|
var topUnits;
|
var bottomUnits;
|
if(maxSizeData > 100000) {
|
topUnits = this.topOutUnits;
|
bottomUnits = this.bottomOutUnits;
|
} else {
|
topUnits = this.topInUnits;
|
bottomUnits = this.bottomInUnits;
|
}
|
|
// and to map units units
|
var topMax = maxSizeData / inches[topUnits];
|
var bottomMax = maxSizeData / inches[bottomUnits];
|
|
// now trim this down to useful block length
|
var topRounded = this.getBarLen(topMax);
|
var bottomRounded = this.getBarLen(bottomMax);
|
|
// and back to display units
|
topMax = topRounded / inches[curMapUnits] * inches[topUnits];
|
bottomMax = bottomRounded / inches[curMapUnits] * inches[bottomUnits];
|
|
// and to pixel units
|
var topPx = topMax / res / geodesicRatio;
|
var bottomPx = bottomMax / res / geodesicRatio;
|
|
// now set the pixel widths
|
// and the values inside them
|
|
if (this.eBottom.style.visibility == "visible"){
|
this.eBottom.style.width = Math.round(bottomPx) + "px";
|
this.eBottom.innerHTML = bottomRounded + " " + bottomUnits ;
|
}
|
|
if (this.eTop.style.visibility == "visible"){
|
this.eTop.style.width = Math.round(topPx) + "px";
|
this.eTop.innerHTML = topRounded + " " + topUnits;
|
}
|
|
},
|
|
CLASS_NAME: "OpenLayers.Control.ScaleLine"
|
});
|
|
/* ======================================================================
|
OpenLayers/Icon.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
*/
|
|
/**
|
* Class: OpenLayers.Icon
|
*
|
* The icon represents a graphical icon on the screen. Typically used in
|
* conjunction with a <OpenLayers.Marker> to represent markers on a screen.
|
*
|
* An icon has a url, size and position. It also contains an offset which
|
* allows the center point to be represented correctly. This can be
|
* provided either as a fixed offset or a function provided to calculate
|
* the desired offset.
|
*
|
*/
|
OpenLayers.Icon = OpenLayers.Class({
|
|
/**
|
* Property: url
|
* {String} image url
|
*/
|
url: null,
|
|
/**
|
* Property: size
|
* {<OpenLayers.Size>|Object} An OpenLayers.Size or
|
* an object with a 'w' and 'h' properties.
|
*/
|
size: null,
|
|
/**
|
* Property: offset
|
* {<OpenLayers.Pixel>|Object} distance in pixels to offset the
|
* image when being rendered. An OpenLayers.Pixel or an object
|
* with a 'x' and 'y' properties.
|
*/
|
offset: null,
|
|
/**
|
* Property: calculateOffset
|
* {Function} Function to calculate the offset (based on the size)
|
*/
|
calculateOffset: null,
|
|
/**
|
* Property: imageDiv
|
* {DOMElement}
|
*/
|
imageDiv: null,
|
|
/**
|
* Property: px
|
* {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object
|
* with a 'x' and 'y' properties.
|
*/
|
px: null,
|
|
/**
|
* Constructor: OpenLayers.Icon
|
* Creates an icon, which is an image tag in a div.
|
*
|
* url - {String}
|
* size - {<OpenLayers.Size>|Object} An OpenLayers.Size or an
|
* object with a 'w' and 'h'
|
* properties.
|
* offset - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an
|
* object with a 'x' and 'y'
|
* properties.
|
* calculateOffset - {Function}
|
*/
|
initialize: function(url, size, offset, calculateOffset) {
|
this.url = url;
|
this.size = size || {w: 20, h: 20};
|
this.offset = offset || {x: -(this.size.w/2), y: -(this.size.h/2)};
|
this.calculateOffset = calculateOffset;
|
|
var id = OpenLayers.Util.createUniqueID("OL_Icon_");
|
this.imageDiv = OpenLayers.Util.createAlphaImageDiv(id);
|
},
|
|
/**
|
* Method: destroy
|
* Nullify references and remove event listeners to prevent circular
|
* references and memory leaks
|
*/
|
destroy: function() {
|
// erase any drawn elements
|
this.erase();
|
|
OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild);
|
this.imageDiv.innerHTML = "";
|
this.imageDiv = null;
|
},
|
|
/**
|
* Method: clone
|
*
|
* Returns:
|
* {<OpenLayers.Icon>} A fresh copy of the icon.
|
*/
|
clone: function() {
|
return new OpenLayers.Icon(this.url,
|
this.size,
|
this.offset,
|
this.calculateOffset);
|
},
|
|
/**
|
* Method: setSize
|
*
|
* Parameters:
|
* size - {<OpenLayers.Size>|Object} An OpenLayers.Size or
|
* an object with a 'w' and 'h' properties.
|
*/
|
setSize: function(size) {
|
if (size != null) {
|
this.size = size;
|
}
|
this.draw();
|
},
|
|
/**
|
* Method: setUrl
|
*
|
* Parameters:
|
* url - {String}
|
*/
|
setUrl: function(url) {
|
if (url != null) {
|
this.url = url;
|
}
|
this.draw();
|
},
|
|
/**
|
* Method: draw
|
* Move the div to the given pixel.
|
*
|
* Parameters:
|
* px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an
|
* object with a 'x' and 'y' properties.
|
*
|
* Returns:
|
* {DOMElement} A new DOM Image of this icon set at the location passed-in
|
*/
|
draw: function(px) {
|
OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv,
|
null,
|
null,
|
this.size,
|
this.url,
|
"absolute");
|
this.moveTo(px);
|
return this.imageDiv;
|
},
|
|
/**
|
* Method: erase
|
* Erase the underlying image element.
|
*/
|
erase: function() {
|
if (this.imageDiv != null && this.imageDiv.parentNode != null) {
|
OpenLayers.Element.remove(this.imageDiv);
|
}
|
},
|
|
/**
|
* Method: setOpacity
|
* Change the icon's opacity
|
*
|
* Parameters:
|
* opacity - {float}
|
*/
|
setOpacity: function(opacity) {
|
OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, null,
|
null, null, null, null, opacity);
|
|
},
|
|
/**
|
* Method: moveTo
|
* move icon to passed in px.
|
*
|
* Parameters:
|
* px - {<OpenLayers.Pixel>|Object} the pixel position to move to.
|
* An OpenLayers.Pixel or an object with a 'x' and 'y' properties.
|
*/
|
moveTo: function (px) {
|
//if no px passed in, use stored location
|
if (px != null) {
|
this.px = px;
|
}
|
|
if (this.imageDiv != null) {
|
if (this.px == null) {
|
this.display(false);
|
} else {
|
if (this.calculateOffset) {
|
this.offset = this.calculateOffset(this.size);
|
}
|
OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, {
|
x: this.px.x + this.offset.x,
|
y: this.px.y + this.offset.y
|
});
|
}
|
}
|
},
|
|
/**
|
* Method: display
|
* Hide or show the icon
|
*
|
* Parameters:
|
* display - {Boolean}
|
*/
|
display: function(display) {
|
this.imageDiv.style.display = (display) ? "" : "none";
|
},
|
|
|
/**
|
* APIMethod: isDrawn
|
*
|
* Returns:
|
* {Boolean} Whether or not the icon is drawn.
|
*/
|
isDrawn: function() {
|
// nodeType 11 for ie, whose nodes *always* have a parentNode
|
// (of type document fragment)
|
var isDrawn = (this.imageDiv && this.imageDiv.parentNode &&
|
(this.imageDiv.parentNode.nodeType != 11));
|
|
return isDrawn;
|
},
|
|
CLASS_NAME: "OpenLayers.Icon"
|
});
|
/* ======================================================================
|
OpenLayers/Marker.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
* @requires OpenLayers/Events.js
|
* @requires OpenLayers/Icon.js
|
*/
|
|
/**
|
* Class: OpenLayers.Marker
|
* Instances of OpenLayers.Marker are a combination of a
|
* <OpenLayers.LonLat> and an <OpenLayers.Icon>.
|
*
|
* Markers are generally added to a special layer called
|
* <OpenLayers.Layer.Markers>.
|
*
|
* Example:
|
* (code)
|
* var markers = new OpenLayers.Layer.Markers( "Markers" );
|
* map.addLayer(markers);
|
*
|
* var size = new OpenLayers.Size(21,25);
|
* var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
|
* var icon = new OpenLayers.Icon('http://www.openlayers.org/dev/img/marker.png', size, offset);
|
* markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon));
|
* markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon.clone()));
|
*
|
* (end)
|
*
|
* Note that if you pass an icon into the Marker constructor, it will take
|
* that icon and use it. This means that you should not share icons between
|
* markers -- you use them once, but you should clone() for any additional
|
* markers using that same icon.
|
*/
|
OpenLayers.Marker = OpenLayers.Class({
|
|
/**
|
* Property: icon
|
* {<OpenLayers.Icon>} The icon used by this marker.
|
*/
|
icon: null,
|
|
/**
|
* Property: lonlat
|
* {<OpenLayers.LonLat>} location of object
|
*/
|
lonlat: null,
|
|
/**
|
* Property: events
|
* {<OpenLayers.Events>} the event handler.
|
*/
|
events: null,
|
|
/**
|
* Property: map
|
* {<OpenLayers.Map>} the map this marker is attached to
|
*/
|
map: null,
|
|
/**
|
* Constructor: OpenLayers.Marker
|
*
|
* Parameters:
|
* lonlat - {<OpenLayers.LonLat>} the position of this marker
|
* icon - {<OpenLayers.Icon>} the icon for this marker
|
*/
|
initialize: function(lonlat, icon) {
|
this.lonlat = lonlat;
|
|
var newIcon = (icon) ? icon : OpenLayers.Marker.defaultIcon();
|
if (this.icon == null) {
|
this.icon = newIcon;
|
} else {
|
this.icon.url = newIcon.url;
|
this.icon.size = newIcon.size;
|
this.icon.offset = newIcon.offset;
|
this.icon.calculateOffset = newIcon.calculateOffset;
|
}
|
this.events = new OpenLayers.Events(this, this.icon.imageDiv);
|
},
|
|
/**
|
* APIMethod: destroy
|
* Destroy the marker. You must first remove the marker from any
|
* layer which it has been added to, or you will get buggy behavior.
|
* (This can not be done within the marker since the marker does not
|
* know which layer it is attached to.)
|
*/
|
destroy: function() {
|
// erase any drawn features
|
this.erase();
|
|
this.map = null;
|
|
this.events.destroy();
|
this.events = null;
|
|
if (this.icon != null) {
|
this.icon.destroy();
|
this.icon = null;
|
}
|
},
|
|
/**
|
* Method: draw
|
* Calls draw on the icon, and returns that output.
|
*
|
* Parameters:
|
* px - {<OpenLayers.Pixel>}
|
*
|
* Returns:
|
* {DOMElement} A new DOM Image with this marker's icon set at the
|
* location passed-in
|
*/
|
draw: function(px) {
|
return this.icon.draw(px);
|
},
|
|
/**
|
* Method: erase
|
* Erases any drawn elements for this marker.
|
*/
|
erase: function() {
|
if (this.icon != null) {
|
this.icon.erase();
|
}
|
},
|
|
/**
|
* Method: moveTo
|
* Move the marker to the new location.
|
*
|
* Parameters:
|
* px - {<OpenLayers.Pixel>|Object} the pixel position to move to.
|
* An OpenLayers.Pixel or an object with a 'x' and 'y' properties.
|
*/
|
moveTo: function (px) {
|
if ((px != null) && (this.icon != null)) {
|
this.icon.moveTo(px);
|
}
|
this.lonlat = this.map.getLonLatFromLayerPx(px);
|
},
|
|
/**
|
* APIMethod: isDrawn
|
*
|
* Returns:
|
* {Boolean} Whether or not the marker is drawn.
|
*/
|
isDrawn: function() {
|
var isDrawn = (this.icon && this.icon.isDrawn());
|
return isDrawn;
|
},
|
|
/**
|
* Method: onScreen
|
*
|
* Returns:
|
* {Boolean} Whether or not the marker is currently visible on screen.
|
*/
|
onScreen:function() {
|
|
var onScreen = false;
|
if (this.map) {
|
var screenBounds = this.map.getExtent();
|
onScreen = screenBounds.containsLonLat(this.lonlat);
|
}
|
return onScreen;
|
},
|
|
/**
|
* Method: inflate
|
* Englarges the markers icon by the specified ratio.
|
*
|
* Parameters:
|
* inflate - {float} the ratio to enlarge the marker by (passing 2
|
* will double the size).
|
*/
|
inflate: function(inflate) {
|
if (this.icon) {
|
this.icon.setSize({
|
w: this.icon.size.w * inflate,
|
h: this.icon.size.h * inflate
|
});
|
}
|
},
|
|
/**
|
* Method: setOpacity
|
* Change the opacity of the marker by changin the opacity of
|
* its icon
|
*
|
* Parameters:
|
* opacity - {float} Specified as fraction (0.4, etc)
|
*/
|
setOpacity: function(opacity) {
|
this.icon.setOpacity(opacity);
|
},
|
|
/**
|
* Method: setUrl
|
* Change URL of the Icon Image.
|
*
|
* url - {String}
|
*/
|
setUrl: function(url) {
|
this.icon.setUrl(url);
|
},
|
|
/**
|
* Method: display
|
* Hide or show the icon
|
*
|
* display - {Boolean}
|
*/
|
display: function(display) {
|
this.icon.display(display);
|
},
|
|
CLASS_NAME: "OpenLayers.Marker"
|
});
|
|
|
/**
|
* Function: defaultIcon
|
* Creates a default <OpenLayers.Icon>.
|
*
|
* Returns:
|
* {<OpenLayers.Icon>} A default OpenLayers.Icon to use for a marker
|
*/
|
OpenLayers.Marker.defaultIcon = function() {
|
return new OpenLayers.Icon(OpenLayers.Util.getImageLocation("marker.png"),
|
{w: 21, h: 25}, {x: -10.5, y: -25});
|
};
|
|
|
/* ======================================================================
|
OpenLayers/Layer/TileCache.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Layer/Grid.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.TileCache
|
* A read only TileCache layer. Used to requests tiles cached by TileCache in
|
* a web accessible cache. This means that you have to pre-populate your
|
* cache before this layer can be used. It is meant only to read tiles
|
* created by TileCache, and not to make calls to TileCache for tile
|
* creation. Create a new instance with the
|
* <OpenLayers.Layer.TileCache> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer.Grid>
|
*/
|
OpenLayers.Layer.TileCache = OpenLayers.Class(OpenLayers.Layer.Grid, {
|
|
/**
|
* APIProperty: isBaseLayer
|
* {Boolean} Treat this layer as a base layer. Default is true.
|
*/
|
isBaseLayer: true,
|
|
/**
|
* APIProperty: format
|
* {String} Mime type of the images returned. Default is image/png.
|
*/
|
format: 'image/png',
|
|
/**
|
* APIProperty: serverResolutions
|
* {Array} A list of all resolutions available on the server. Only set this
|
* property if the map resolutions differ from the server. This
|
* property serves two purposes. (a) <serverResolutions> can include
|
* resolutions that the server supports and that you don't want to
|
* provide with this layer. (b) The map can work with resolutions
|
* that aren't supported by the server, i.e. that aren't in
|
* <serverResolutions>. When the map is displayed in such a resolution
|
* data for the closest server-supported resolution is loaded and the
|
* layer div is stretched as necessary.
|
*/
|
serverResolutions: null,
|
|
/**
|
* Constructor: OpenLayers.Layer.TileCache
|
* Create a new read only TileCache layer.
|
*
|
* Parameters:
|
* name - {String} Name of the layer displayed in the interface
|
* url - {String} Location of the web accessible cache (not the location of
|
* your tilecache script!)
|
* layername - {String} Layer name as defined in the TileCache
|
* configuration
|
* options - {Object} Optional object with properties to be set on the
|
* layer. Note that you should speficy your resolutions to match
|
* your TileCache configuration. This can be done by setting
|
* the resolutions array directly (here or on the map), by setting
|
* maxResolution and numZoomLevels, or by using scale based properties.
|
*/
|
initialize: function(name, url, layername, options) {
|
this.layername = layername;
|
OpenLayers.Layer.Grid.prototype.initialize.apply(this,
|
[name, url, {}, options]);
|
this.extension = this.format.split('/')[1].toLowerCase();
|
this.extension = (this.extension == 'jpg') ? 'jpeg' : this.extension;
|
},
|
|
/**
|
* APIMethod: clone
|
* obj - {Object}
|
*
|
* Returns:
|
* {<OpenLayers.Layer.TileCache>} An exact clone of this
|
* <OpenLayers.Layer.TileCache>
|
*/
|
clone: function (obj) {
|
|
if (obj == null) {
|
obj = new OpenLayers.Layer.TileCache(this.name,
|
this.url,
|
this.layername,
|
this.getOptions());
|
}
|
|
//get all additions from superclasses
|
obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
|
|
// copy/set any non-init, non-simple values here
|
|
return obj;
|
},
|
|
/**
|
* Method: getURL
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
*
|
* Returns:
|
* {String} A string with the layer's url and parameters and also the
|
* passed-in bounds and appropriate tile size specified as parameters.
|
*/
|
getURL: function(bounds) {
|
var res = this.getServerResolution();
|
var bbox = this.maxExtent;
|
var size = this.tileSize;
|
var tileX = Math.round((bounds.left - bbox.left) / (res * size.w));
|
var tileY = Math.round((bounds.bottom - bbox.bottom) / (res * size.h));
|
var tileZ = this.serverResolutions != null ?
|
OpenLayers.Util.indexOf(this.serverResolutions, res) :
|
this.map.getZoom();
|
|
var components = [
|
this.layername,
|
OpenLayers.Number.zeroPad(tileZ, 2),
|
OpenLayers.Number.zeroPad(parseInt(tileX / 1000000), 3),
|
OpenLayers.Number.zeroPad((parseInt(tileX / 1000) % 1000), 3),
|
OpenLayers.Number.zeroPad((parseInt(tileX) % 1000), 3),
|
OpenLayers.Number.zeroPad(parseInt(tileY / 1000000), 3),
|
OpenLayers.Number.zeroPad((parseInt(tileY / 1000) % 1000), 3),
|
OpenLayers.Number.zeroPad((parseInt(tileY) % 1000), 3) + '.' + this.extension
|
];
|
var path = components.join('/');
|
var url = this.url;
|
if (OpenLayers.Util.isArray(url)) {
|
url = this.selectUrl(path, url);
|
}
|
url = (url.charAt(url.length - 1) == '/') ? url : url + '/';
|
return url + path;
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.TileCache"
|
});
|
/* ======================================================================
|
OpenLayers/Strategy/Paging.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Strategy.js
|
*/
|
|
/**
|
* Class: OpenLayers.Strategy.Paging
|
* Strategy for vector feature paging
|
*
|
* Inherits from:
|
* - <OpenLayers.Strategy>
|
*/
|
OpenLayers.Strategy.Paging = OpenLayers.Class(OpenLayers.Strategy, {
|
|
/**
|
* Property: features
|
* {Array(<OpenLayers.Feature.Vector>)} Cached features.
|
*/
|
features: null,
|
|
/**
|
* Property: length
|
* {Integer} Number of features per page. Default is 10.
|
*/
|
length: 10,
|
|
/**
|
* Property: num
|
* {Integer} The currently displayed page number.
|
*/
|
num: null,
|
|
/**
|
* Property: paging
|
* {Boolean} The strategy is currently changing pages.
|
*/
|
paging: false,
|
|
/**
|
* Constructor: OpenLayers.Strategy.Paging
|
* Create a new paging strategy.
|
*
|
* Parameters:
|
* options - {Object} Optional object whose properties will be set on the
|
* instance.
|
*/
|
|
/**
|
* APIMethod: activate
|
* Activate the strategy. Register any listeners, do appropriate setup.
|
*
|
* Returns:
|
* {Boolean} The strategy was successfully activated.
|
*/
|
activate: function() {
|
var activated = OpenLayers.Strategy.prototype.activate.call(this);
|
if(activated) {
|
this.layer.events.on({
|
"beforefeaturesadded": this.cacheFeatures,
|
scope: this
|
});
|
}
|
return activated;
|
},
|
|
/**
|
* APIMethod: deactivate
|
* Deactivate the strategy. Unregister any listeners, do appropriate
|
* tear-down.
|
*
|
* Returns:
|
* {Boolean} The strategy was successfully deactivated.
|
*/
|
deactivate: function() {
|
var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
|
if(deactivated) {
|
this.clearCache();
|
this.layer.events.un({
|
"beforefeaturesadded": this.cacheFeatures,
|
scope: this
|
});
|
}
|
return deactivated;
|
},
|
|
/**
|
* Method: cacheFeatures
|
* Cache features before they are added to the layer.
|
*
|
* Parameters:
|
* event - {Object} The event that this was listening for. This will come
|
* with a batch of features to be paged.
|
*/
|
cacheFeatures: function(event) {
|
if(!this.paging) {
|
this.clearCache();
|
this.features = event.features;
|
this.pageNext(event);
|
}
|
},
|
|
/**
|
* Method: clearCache
|
* Clear out the cached features. This destroys features, assuming
|
* nothing else has a reference.
|
*/
|
clearCache: function() {
|
if(this.features) {
|
for(var i=0; i<this.features.length; ++i) {
|
this.features[i].destroy();
|
}
|
}
|
this.features = null;
|
this.num = null;
|
},
|
|
/**
|
* APIMethod: pageCount
|
* Get the total count of pages given the current cache of features.
|
*
|
* Returns:
|
* {Integer} The page count.
|
*/
|
pageCount: function() {
|
var numFeatures = this.features ? this.features.length : 0;
|
return Math.ceil(numFeatures / this.length);
|
},
|
|
/**
|
* APIMethod: pageNum
|
* Get the zero based page number.
|
*
|
* Returns:
|
* {Integer} The current page number being displayed.
|
*/
|
pageNum: function() {
|
return this.num;
|
},
|
|
/**
|
* APIMethod: pageLength
|
* Gets or sets page length.
|
*
|
* Parameters:
|
* newLength - {Integer} Optional length to be set.
|
*
|
* Returns:
|
* {Integer} The length of a page (number of features per page).
|
*/
|
pageLength: function(newLength) {
|
if(newLength && newLength > 0) {
|
this.length = newLength;
|
}
|
return this.length;
|
},
|
|
/**
|
* APIMethod: pageNext
|
* Display the next page of features.
|
*
|
* Returns:
|
* {Boolean} A new page was displayed.
|
*/
|
pageNext: function(event) {
|
var changed = false;
|
if(this.features) {
|
if(this.num === null) {
|
this.num = -1;
|
}
|
var start = (this.num + 1) * this.length;
|
changed = this.page(start, event);
|
}
|
return changed;
|
},
|
|
/**
|
* APIMethod: pagePrevious
|
* Display the previous page of features.
|
*
|
* Returns:
|
* {Boolean} A new page was displayed.
|
*/
|
pagePrevious: function() {
|
var changed = false;
|
if(this.features) {
|
if(this.num === null) {
|
this.num = this.pageCount();
|
}
|
var start = (this.num - 1) * this.length;
|
changed = this.page(start);
|
}
|
return changed;
|
},
|
|
/**
|
* Method: page
|
* Display the page starting at the given index from the cache.
|
*
|
* Returns:
|
* {Boolean} A new page was displayed.
|
*/
|
page: function(start, event) {
|
var changed = false;
|
if(this.features) {
|
if(start >= 0 && start < this.features.length) {
|
var num = Math.floor(start / this.length);
|
if(num != this.num) {
|
this.paging = true;
|
var features = this.features.slice(start, start + this.length);
|
this.layer.removeFeatures(this.layer.features);
|
this.num = num;
|
// modify the event if any
|
if(event && event.features) {
|
// this.was called by an event listener
|
event.features = features;
|
} else {
|
// this was called directly on the strategy
|
this.layer.addFeatures(features);
|
}
|
this.paging = false;
|
changed = true;
|
}
|
}
|
}
|
return changed;
|
},
|
|
CLASS_NAME: "OpenLayers.Strategy.Paging"
|
});
|
/* ======================================================================
|
OpenLayers/Control/DragFeature.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/Handler/Drag.js
|
* @requires OpenLayers/Handler/Feature.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.DragFeature
|
* The DragFeature control moves a feature with a drag of the mouse. Create a
|
* new control with the <OpenLayers.Control.DragFeature> constructor.
|
*
|
* Inherits From:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.DragFeature = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* APIProperty: geometryTypes
|
* {Array(String)} To restrict dragging to a limited set of geometry types,
|
* send a list of strings corresponding to the geometry class names.
|
*/
|
geometryTypes: null,
|
|
/**
|
* APIProperty: onStart
|
* {Function} Define this function if you want to know when a drag starts.
|
* The function should expect to receive two arguments: the feature
|
* that is about to be dragged and the pixel location of the mouse.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>} The feature that is about to be
|
* dragged.
|
* pixel - {<OpenLayers.Pixel>} The pixel location of the mouse.
|
*/
|
onStart: function(feature, pixel) {},
|
|
/**
|
* APIProperty: onDrag
|
* {Function} Define this function if you want to know about each move of a
|
* feature. The function should expect to receive two arguments: the
|
* feature that is being dragged and the pixel location of the mouse.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>} The feature that was dragged.
|
* pixel - {<OpenLayers.Pixel>} The pixel location of the mouse.
|
*/
|
onDrag: function(feature, pixel) {},
|
|
/**
|
* APIProperty: onComplete
|
* {Function} Define this function if you want to know when a feature is
|
* done dragging. The function should expect to receive two arguments:
|
* the feature that is being dragged and the pixel location of the
|
* mouse.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>} The feature that was dragged.
|
* pixel - {<OpenLayers.Pixel>} The pixel location of the mouse.
|
*/
|
onComplete: function(feature, pixel) {},
|
|
/**
|
* APIProperty: onEnter
|
* {Function} Define this function if you want to know when the mouse
|
* goes over a feature and thereby makes this feature a candidate
|
* for dragging.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>} The feature that is ready
|
* to be dragged.
|
*/
|
onEnter: function(feature) {},
|
|
/**
|
* APIProperty: onLeave
|
* {Function} Define this function if you want to know when the mouse
|
* goes out of the feature that was dragged.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>} The feature that was dragged.
|
*/
|
onLeave: function(feature) {},
|
|
/**
|
* APIProperty: documentDrag
|
* {Boolean} If set to true, mouse dragging will continue even if the
|
* mouse cursor leaves the map viewport. Default is false.
|
*/
|
documentDrag: false,
|
|
/**
|
* Property: layer
|
* {<OpenLayers.Layer.Vector>}
|
*/
|
layer: null,
|
|
/**
|
* Property: feature
|
* {<OpenLayers.Feature.Vector>}
|
*/
|
feature: null,
|
|
/**
|
* Property: dragCallbacks
|
* {Object} The functions that are sent to the drag handler for callback.
|
*/
|
dragCallbacks: {},
|
|
/**
|
* Property: featureCallbacks
|
* {Object} The functions that are sent to the feature handler for callback.
|
*/
|
featureCallbacks: {},
|
|
/**
|
* Property: lastPixel
|
* {<OpenLayers.Pixel>}
|
*/
|
lastPixel: null,
|
|
/**
|
* Constructor: OpenLayers.Control.DragFeature
|
* Create a new control to drag features.
|
*
|
* Parameters:
|
* layer - {<OpenLayers.Layer.Vector>} The layer containing features to be
|
* dragged.
|
* options - {Object} Optional object whose properties will be set on the
|
* control.
|
*/
|
initialize: function(layer, options) {
|
OpenLayers.Control.prototype.initialize.apply(this, [options]);
|
this.layer = layer;
|
this.handlers = {
|
drag: new OpenLayers.Handler.Drag(
|
this, OpenLayers.Util.extend({
|
down: this.downFeature,
|
move: this.moveFeature,
|
up: this.upFeature,
|
out: this.cancel,
|
done: this.doneDragging
|
}, this.dragCallbacks), {
|
documentDrag: this.documentDrag
|
}
|
),
|
feature: new OpenLayers.Handler.Feature(
|
this, this.layer, OpenLayers.Util.extend({
|
// 'click' and 'clickout' callback are for the mobile
|
// support: no 'over' or 'out' in touch based browsers.
|
click: this.clickFeature,
|
clickout: this.clickoutFeature,
|
over: this.overFeature,
|
out: this.outFeature
|
}, this.featureCallbacks),
|
{geometryTypes: this.geometryTypes}
|
)
|
};
|
},
|
|
/**
|
* Method: clickFeature
|
* Called when the feature handler detects a click-in on a feature.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
*/
|
clickFeature: function(feature) {
|
if (this.handlers.feature.touch && !this.over && this.overFeature(feature)) {
|
this.handlers.drag.dragstart(this.handlers.feature.evt);
|
// to let the events propagate to the feature handler (click callback)
|
this.handlers.drag.stopDown = false;
|
}
|
},
|
|
/**
|
* Method: clickoutFeature
|
* Called when the feature handler detects a click-out on a feature.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
*/
|
clickoutFeature: function(feature) {
|
if (this.handlers.feature.touch && this.over) {
|
this.outFeature(feature);
|
this.handlers.drag.stopDown = true;
|
}
|
},
|
|
/**
|
* APIMethod: destroy
|
* Take care of things that are not handled in superclass
|
*/
|
destroy: function() {
|
this.layer = null;
|
OpenLayers.Control.prototype.destroy.apply(this, []);
|
},
|
|
/**
|
* APIMethod: activate
|
* Activate the control and the feature handler.
|
*
|
* Returns:
|
* {Boolean} Successfully activated the control and feature handler.
|
*/
|
activate: function() {
|
return (this.handlers.feature.activate() &&
|
OpenLayers.Control.prototype.activate.apply(this, arguments));
|
},
|
|
/**
|
* APIMethod: deactivate
|
* Deactivate the control and all handlers.
|
*
|
* Returns:
|
* {Boolean} Successfully deactivated the control.
|
*/
|
deactivate: function() {
|
// the return from the handlers is unimportant in this case
|
this.handlers.drag.deactivate();
|
this.handlers.feature.deactivate();
|
this.feature = null;
|
this.dragging = false;
|
this.lastPixel = null;
|
OpenLayers.Element.removeClass(
|
this.map.viewPortDiv, this.displayClass + "Over"
|
);
|
return OpenLayers.Control.prototype.deactivate.apply(this, arguments);
|
},
|
|
/**
|
* Method: overFeature
|
* Called when the feature handler detects a mouse-over on a feature.
|
* This activates the drag handler.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>} The selected feature.
|
*
|
* Returns:
|
* {Boolean} Successfully activated the drag handler.
|
*/
|
overFeature: function(feature) {
|
var activated = false;
|
if(!this.handlers.drag.dragging) {
|
this.feature = feature;
|
this.handlers.drag.activate();
|
activated = true;
|
this.over = true;
|
OpenLayers.Element.addClass(this.map.viewPortDiv, this.displayClass + "Over");
|
this.onEnter(feature);
|
} else {
|
if(this.feature.id == feature.id) {
|
this.over = true;
|
} else {
|
this.over = false;
|
}
|
}
|
return activated;
|
},
|
|
/**
|
* Method: downFeature
|
* Called when the drag handler detects a mouse-down.
|
*
|
* Parameters:
|
* pixel - {<OpenLayers.Pixel>} Location of the mouse event.
|
*/
|
downFeature: function(pixel) {
|
this.lastPixel = pixel;
|
this.onStart(this.feature, pixel);
|
},
|
|
/**
|
* Method: moveFeature
|
* Called when the drag handler detects a mouse-move. Also calls the
|
* optional onDrag method.
|
*
|
* Parameters:
|
* pixel - {<OpenLayers.Pixel>} Location of the mouse event.
|
*/
|
moveFeature: function(pixel) {
|
var res = this.map.getResolution();
|
this.feature.geometry.move(res * (pixel.x - this.lastPixel.x),
|
res * (this.lastPixel.y - pixel.y));
|
this.layer.drawFeature(this.feature);
|
this.lastPixel = pixel;
|
this.onDrag(this.feature, pixel);
|
},
|
|
/**
|
* Method: upFeature
|
* Called when the drag handler detects a mouse-up.
|
*
|
* Parameters:
|
* pixel - {<OpenLayers.Pixel>} Location of the mouse event.
|
*/
|
upFeature: function(pixel) {
|
if(!this.over) {
|
this.handlers.drag.deactivate();
|
}
|
},
|
|
/**
|
* Method: doneDragging
|
* Called when the drag handler is done dragging.
|
*
|
* Parameters:
|
* pixel - {<OpenLayers.Pixel>} The last event pixel location. If this event
|
* came from a mouseout, this may not be in the map viewport.
|
*/
|
doneDragging: function(pixel) {
|
this.onComplete(this.feature, pixel);
|
},
|
|
/**
|
* Method: outFeature
|
* Called when the feature handler detects a mouse-out on a feature.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>} The feature that the mouse left.
|
*/
|
outFeature: function(feature) {
|
if(!this.handlers.drag.dragging) {
|
this.over = false;
|
this.handlers.drag.deactivate();
|
OpenLayers.Element.removeClass(
|
this.map.viewPortDiv, this.displayClass + "Over"
|
);
|
this.onLeave(feature);
|
this.feature = null;
|
} else {
|
if(this.feature.id == feature.id) {
|
this.over = false;
|
}
|
}
|
},
|
|
/**
|
* Method: cancel
|
* Called when the drag handler detects a mouse-out (from the map viewport).
|
*/
|
cancel: function() {
|
this.handlers.drag.deactivate();
|
this.over = false;
|
},
|
|
/**
|
* Method: setMap
|
* Set the map property for the control and all handlers.
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>} The control's map.
|
*/
|
setMap: function(map) {
|
this.handlers.drag.setMap(map);
|
this.handlers.feature.setMap(map);
|
OpenLayers.Control.prototype.setMap.apply(this, arguments);
|
},
|
|
CLASS_NAME: "OpenLayers.Control.DragFeature"
|
});
|
/* ======================================================================
|
OpenLayers/Control/TransformFeature.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/Control/DragFeature.js
|
* @requires OpenLayers/Feature/Vector.js
|
* @requires OpenLayers/Geometry/LineString.js
|
* @requires OpenLayers/Geometry/Point.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.TransformFeature
|
* Control to transform features with a standard transformation box.
|
*
|
* Inherits From:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.TransformFeature = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* APIProperty: events
|
* {<OpenLayers.Events>} Events instance for listeners and triggering
|
* control specific events.
|
*
|
* Register a listener for a particular event with the following syntax:
|
* (code)
|
* control.events.register(type, obj, listener);
|
* (end)
|
*
|
* Supported event types (in addition to those from <OpenLayers.Control.events>):
|
* beforesetfeature - Triggered before a feature is set for
|
* tranformation. The feature will not be set if a listener returns
|
* false. Listeners receive a *feature* property, with the feature
|
* that will be set for transformation. Listeners are allowed to
|
* set the control's *scale*, *ratio* and *rotation* properties,
|
* which will set the initial scale, ratio and rotation of the
|
* feature, like the <setFeature> method's initialParams argument.
|
* setfeature - Triggered when a feature is set for tranformation.
|
* Listeners receive a *feature* property, with the feature that
|
* is now set for transformation.
|
* beforetransform - Triggered while dragging, before a feature is
|
* transformed. The feature will not be transformed if a listener
|
* returns false (but the box still will). Listeners receive one or
|
* more of *center*, *scale*, *ratio* and *rotation*. The *center*
|
* property is an <OpenLayers.Geometry.Point> object with the new
|
* center of the transformed feature, the others are Floats with the
|
* scale, ratio or rotation change since the last transformation.
|
* transform - Triggered while dragging, when a feature is transformed.
|
* Listeners receive an event object with one or more of *center*,
|
* scale*, *ratio* and *rotation*. The *center* property is an
|
* <OpenLayers.Geometry.Point> object with the new center of the
|
* transformed feature, the others are Floats with the scale, ratio
|
* or rotation change of the feature since the last transformation.
|
* transformcomplete - Triggered after dragging. Listeners receive
|
* an event object with the transformed *feature*.
|
*/
|
|
/**
|
* APIProperty: geometryTypes
|
* {Array(String)} To restrict transformation to a limited set of geometry
|
* types, send a list of strings corresponding to the geometry class
|
* names.
|
*/
|
geometryTypes: null,
|
|
/**
|
* Property: layer
|
* {<OpenLayers.Layer.Vector>}
|
*/
|
layer: null,
|
|
/**
|
* APIProperty: preserveAspectRatio
|
* {Boolean} set to true to not change the feature's aspect ratio.
|
*/
|
preserveAspectRatio: false,
|
|
/**
|
* APIProperty: rotate
|
* {Boolean} set to false if rotation should be disabled. Default is true.
|
* To be passed with the constructor or set when the control is not
|
* active.
|
*/
|
rotate: true,
|
|
/**
|
* APIProperty: feature
|
* {<OpenLayers.Feature.Vector>} Feature currently available for
|
* transformation. Read-only, use <setFeature> to set it manually.
|
*/
|
feature: null,
|
|
/**
|
* APIProperty: renderIntent
|
* {String|Object} Render intent for the transformation box and
|
* handles. A symbolizer object can also be provided here.
|
*/
|
renderIntent: "temporary",
|
|
/**
|
* APIProperty: rotationHandleSymbolizer
|
* {Object|String} Optional. A custom symbolizer for the rotation handles.
|
* A render intent can also be provided here. Defaults to
|
* (code)
|
* {
|
* stroke: false,
|
* pointRadius: 10,
|
* fillOpacity: 0,
|
* cursor: "pointer"
|
* }
|
* (end)
|
*/
|
rotationHandleSymbolizer: null,
|
|
/**
|
* APIProperty: box
|
* {<OpenLayers.Feature.Vector>} The transformation box rectangle.
|
* Read-only.
|
*/
|
box: null,
|
|
/**
|
* APIProperty: center
|
* {<OpenLayers.Geometry.Point>} The center of the feature bounds.
|
* Read-only.
|
*/
|
center: null,
|
|
/**
|
* APIProperty: scale
|
* {Float} The scale of the feature, relative to the scale the time the
|
* feature was set. Read-only, except for *beforesetfeature*
|
* listeners.
|
*/
|
scale: 1,
|
|
/**
|
* APIProperty: ratio
|
* {Float} The ratio of the feature relative to the ratio the time the
|
* feature was set. Read-only, except for *beforesetfeature*
|
* listeners.
|
*/
|
ratio: 1,
|
|
/**
|
* Property: rotation
|
* {Integer} the current rotation angle of the box. Read-only, except for
|
* *beforesetfeature* listeners.
|
*/
|
rotation: 0,
|
|
/**
|
* APIProperty: handles
|
* {Array(<OpenLayers.Feature.Vector>)} The 8 handles currently available
|
* for scaling/resizing. Numbered counterclockwise, starting from the
|
* southwest corner. Read-only.
|
*/
|
handles: null,
|
|
/**
|
* APIProperty: rotationHandles
|
* {Array(<OpenLayers.Feature.Vector>)} The 4 rotation handles currently
|
* available for rotating. Numbered counterclockwise, starting from
|
* the southwest corner. Read-only.
|
*/
|
rotationHandles: null,
|
|
/**
|
* Property: dragControl
|
* {<OpenLayers.Control.DragFeature>}
|
*/
|
dragControl: null,
|
|
/**
|
* APIProperty: irregular
|
* {Boolean} Make scaling/resizing work irregularly. If true then
|
* dragging a handle causes the feature to resize in the direction
|
* of movement. If false then the feature resizes symetrically
|
* about it's center.
|
*/
|
irregular: false,
|
|
/**
|
* Constructor: OpenLayers.Control.TransformFeature
|
* Create a new transform feature control.
|
*
|
* Parameters:
|
* layer - {<OpenLayers.Layer.Vector>} Layer that contains features that
|
* will be transformed.
|
* options - {Object} Optional object whose properties will be set on the
|
* control.
|
*/
|
initialize: function(layer, options) {
|
OpenLayers.Control.prototype.initialize.apply(this, [options]);
|
|
this.layer = layer;
|
|
if(!this.rotationHandleSymbolizer) {
|
this.rotationHandleSymbolizer = {
|
stroke: false,
|
pointRadius: 10,
|
fillOpacity: 0,
|
cursor: "pointer"
|
};
|
}
|
|
this.createBox();
|
this.createControl();
|
},
|
|
/**
|
* APIMethod: activate
|
* Activates the control.
|
*/
|
activate: function() {
|
var activated = false;
|
if(OpenLayers.Control.prototype.activate.apply(this, arguments)) {
|
this.dragControl.activate();
|
this.layer.addFeatures([this.box]);
|
this.rotate && this.layer.addFeatures(this.rotationHandles);
|
this.layer.addFeatures(this.handles);
|
activated = true;
|
}
|
return activated;
|
},
|
|
/**
|
* APIMethod: deactivate
|
* Deactivates the control.
|
*/
|
deactivate: function() {
|
var deactivated = false;
|
if(OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {
|
this.layer.removeFeatures(this.handles);
|
this.rotate && this.layer.removeFeatures(this.rotationHandles);
|
this.layer.removeFeatures([this.box]);
|
this.dragControl.deactivate();
|
deactivated = true;
|
}
|
return deactivated;
|
},
|
|
/**
|
* Method: setMap
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
setMap: function(map) {
|
this.dragControl.setMap(map);
|
OpenLayers.Control.prototype.setMap.apply(this, arguments);
|
},
|
|
/**
|
* APIMethod: setFeature
|
* Place the transformation box on a feature and start transforming it.
|
* If the control is not active, it will be activated.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
* initialParams - {Object} Initial values for rotation, scale or ratio.
|
* Setting a rotation value here will cause the transformation box to
|
* start rotated. Setting a scale or ratio will not affect the
|
* transormation box, but applications may use this to keep track of
|
* scale and ratio of a feature across multiple transforms.
|
*/
|
setFeature: function(feature, initialParams) {
|
initialParams = OpenLayers.Util.applyDefaults(initialParams, {
|
rotation: 0,
|
scale: 1,
|
ratio: 1
|
});
|
|
var oldRotation = this.rotation;
|
var oldCenter = this.center;
|
OpenLayers.Util.extend(this, initialParams);
|
|
var cont = this.events.triggerEvent("beforesetfeature",
|
{feature: feature}
|
);
|
if (cont === false) {
|
return;
|
}
|
|
this.feature = feature;
|
this.activate();
|
|
this._setfeature = true;
|
|
var featureBounds = this.feature.geometry.getBounds();
|
this.box.move(featureBounds.getCenterLonLat());
|
this.box.geometry.rotate(-oldRotation, oldCenter);
|
this._angle = 0;
|
|
var ll;
|
if(this.rotation) {
|
var geom = feature.geometry.clone();
|
geom.rotate(-this.rotation, this.center);
|
var box = new OpenLayers.Feature.Vector(
|
geom.getBounds().toGeometry());
|
box.geometry.rotate(this.rotation, this.center);
|
this.box.geometry.rotate(this.rotation, this.center);
|
this.box.move(box.geometry.getBounds().getCenterLonLat());
|
var llGeom = box.geometry.components[0].components[0];
|
ll = llGeom.getBounds().getCenterLonLat();
|
} else {
|
ll = new OpenLayers.LonLat(featureBounds.left, featureBounds.bottom);
|
}
|
this.handles[0].move(ll);
|
|
delete this._setfeature;
|
|
this.events.triggerEvent("setfeature", {feature: feature});
|
},
|
|
/**
|
* APIMethod: unsetFeature
|
* Remove the transformation box off any feature.
|
* If the control is active, it will be deactivated first.
|
*/
|
unsetFeature: function() {
|
if (this.active) {
|
this.deactivate();
|
} else {
|
this.feature = null;
|
this.rotation = 0;
|
this.scale = 1;
|
this.ratio = 1;
|
}
|
},
|
|
/**
|
* Method: createBox
|
* Creates the box with all handles and transformation handles.
|
*/
|
createBox: function() {
|
var control = this;
|
|
this.center = new OpenLayers.Geometry.Point(0, 0);
|
this.box = new OpenLayers.Feature.Vector(
|
new OpenLayers.Geometry.LineString([
|
new OpenLayers.Geometry.Point(-1, -1),
|
new OpenLayers.Geometry.Point(0, -1),
|
new OpenLayers.Geometry.Point(1, -1),
|
new OpenLayers.Geometry.Point(1, 0),
|
new OpenLayers.Geometry.Point(1, 1),
|
new OpenLayers.Geometry.Point(0, 1),
|
new OpenLayers.Geometry.Point(-1, 1),
|
new OpenLayers.Geometry.Point(-1, 0),
|
new OpenLayers.Geometry.Point(-1, -1)
|
]), null,
|
typeof this.renderIntent == "string" ? null : this.renderIntent
|
);
|
|
// Override for box move - make sure that the center gets updated
|
this.box.geometry.move = function(x, y) {
|
control._moving = true;
|
OpenLayers.Geometry.LineString.prototype.move.apply(this, arguments);
|
control.center.move(x, y);
|
delete control._moving;
|
};
|
|
// Overrides for vertex move, resize and rotate - make sure that
|
// handle and rotationHandle geometries are also moved, resized and
|
// rotated.
|
var vertexMoveFn = function(x, y) {
|
OpenLayers.Geometry.Point.prototype.move.apply(this, arguments);
|
this._rotationHandle && this._rotationHandle.geometry.move(x, y);
|
this._handle.geometry.move(x, y);
|
};
|
var vertexResizeFn = function(scale, center, ratio) {
|
OpenLayers.Geometry.Point.prototype.resize.apply(this, arguments);
|
this._rotationHandle && this._rotationHandle.geometry.resize(
|
scale, center, ratio);
|
this._handle.geometry.resize(scale, center, ratio);
|
};
|
var vertexRotateFn = function(angle, center) {
|
OpenLayers.Geometry.Point.prototype.rotate.apply(this, arguments);
|
this._rotationHandle && this._rotationHandle.geometry.rotate(
|
angle, center);
|
this._handle.geometry.rotate(angle, center);
|
};
|
|
// Override for handle move - make sure that the box and other handles
|
// are updated, and finally transform the feature.
|
var handleMoveFn = function(x, y) {
|
var oldX = this.x, oldY = this.y;
|
OpenLayers.Geometry.Point.prototype.move.call(this, x, y);
|
if(control._moving) {
|
return;
|
}
|
var evt = control.dragControl.handlers.drag.evt;
|
var preserveAspectRatio = !control._setfeature &&
|
control.preserveAspectRatio;
|
var reshape = !preserveAspectRatio && !(evt && evt.shiftKey);
|
var oldGeom = new OpenLayers.Geometry.Point(oldX, oldY);
|
var centerGeometry = control.center;
|
this.rotate(-control.rotation, centerGeometry);
|
oldGeom.rotate(-control.rotation, centerGeometry);
|
var dx1 = this.x - centerGeometry.x;
|
var dy1 = this.y - centerGeometry.y;
|
var dx0 = dx1 - (this.x - oldGeom.x);
|
var dy0 = dy1 - (this.y - oldGeom.y);
|
if (control.irregular && !control._setfeature) {
|
dx1 -= (this.x - oldGeom.x) / 2;
|
dy1 -= (this.y - oldGeom.y) / 2;
|
}
|
this.x = oldX;
|
this.y = oldY;
|
var scale, ratio = 1;
|
if (reshape) {
|
scale = Math.abs(dy0) < 0.00001 ? 1 : dy1 / dy0;
|
ratio = (Math.abs(dx0) < 0.00001 ? 1 : (dx1 / dx0)) / scale;
|
} else {
|
var l0 = Math.sqrt((dx0 * dx0) + (dy0 * dy0));
|
var l1 = Math.sqrt((dx1 * dx1) + (dy1 * dy1));
|
scale = l1 / l0;
|
}
|
|
// rotate the box to 0 before resizing - saves us some
|
// calculations and is inexpensive because we don't drawFeature.
|
control._moving = true;
|
control.box.geometry.rotate(-control.rotation, centerGeometry);
|
delete control._moving;
|
|
control.box.geometry.resize(scale, centerGeometry, ratio);
|
control.box.geometry.rotate(control.rotation, centerGeometry);
|
control.transformFeature({scale: scale, ratio: ratio});
|
if (control.irregular && !control._setfeature) {
|
var newCenter = centerGeometry.clone();
|
newCenter.x += Math.abs(oldX - centerGeometry.x) < 0.00001 ? 0 : (this.x - oldX);
|
newCenter.y += Math.abs(oldY - centerGeometry.y) < 0.00001 ? 0 : (this.y - oldY);
|
control.box.geometry.move(this.x - oldX, this.y - oldY);
|
control.transformFeature({center: newCenter});
|
}
|
};
|
|
// Override for rotation handle move - make sure that the box and
|
// other handles are updated, and finally transform the feature.
|
var rotationHandleMoveFn = function(x, y){
|
var oldX = this.x, oldY = this.y;
|
OpenLayers.Geometry.Point.prototype.move.call(this, x, y);
|
if(control._moving) {
|
return;
|
}
|
var evt = control.dragControl.handlers.drag.evt;
|
var constrain = (evt && evt.shiftKey) ? 45 : 1;
|
var centerGeometry = control.center;
|
var dx1 = this.x - centerGeometry.x;
|
var dy1 = this.y - centerGeometry.y;
|
var dx0 = dx1 - x;
|
var dy0 = dy1 - y;
|
this.x = oldX;
|
this.y = oldY;
|
var a0 = Math.atan2(dy0, dx0);
|
var a1 = Math.atan2(dy1, dx1);
|
var angle = a1 - a0;
|
angle *= 180 / Math.PI;
|
control._angle = (control._angle + angle) % 360;
|
var diff = control.rotation % constrain;
|
if(Math.abs(control._angle) >= constrain || diff !== 0) {
|
angle = Math.round(control._angle / constrain) * constrain -
|
diff;
|
control._angle = 0;
|
control.box.geometry.rotate(angle, centerGeometry);
|
control.transformFeature({rotation: angle});
|
}
|
};
|
|
var handles = new Array(8);
|
var rotationHandles = new Array(4);
|
var geom, handle, rotationHandle;
|
var positions = ["sw", "s", "se", "e", "ne", "n", "nw", "w"];
|
for(var i=0; i<8; ++i) {
|
geom = this.box.geometry.components[i];
|
handle = new OpenLayers.Feature.Vector(geom.clone(), {
|
role: positions[i] + "-resize"
|
}, typeof this.renderIntent == "string" ? null :
|
this.renderIntent);
|
if(i % 2 == 0) {
|
rotationHandle = new OpenLayers.Feature.Vector(geom.clone(), {
|
role: positions[i] + "-rotate"
|
}, typeof this.rotationHandleSymbolizer == "string" ?
|
null : this.rotationHandleSymbolizer);
|
rotationHandle.geometry.move = rotationHandleMoveFn;
|
geom._rotationHandle = rotationHandle;
|
rotationHandles[i/2] = rotationHandle;
|
}
|
geom.move = vertexMoveFn;
|
geom.resize = vertexResizeFn;
|
geom.rotate = vertexRotateFn;
|
handle.geometry.move = handleMoveFn;
|
geom._handle = handle;
|
handles[i] = handle;
|
}
|
|
this.rotationHandles = rotationHandles;
|
this.handles = handles;
|
},
|
|
/**
|
* Method: createControl
|
* Creates a DragFeature control for this control.
|
*/
|
createControl: function() {
|
var control = this;
|
this.dragControl = new OpenLayers.Control.DragFeature(this.layer, {
|
documentDrag: true,
|
// avoid moving the feature itself - move the box instead
|
moveFeature: function(pixel) {
|
if(this.feature === control.feature) {
|
this.feature = control.box;
|
}
|
OpenLayers.Control.DragFeature.prototype.moveFeature.apply(this,
|
arguments);
|
},
|
// transform while dragging
|
onDrag: function(feature, pixel) {
|
if(feature === control.box) {
|
control.transformFeature({center: control.center});
|
}
|
},
|
// set a new feature
|
onStart: function(feature, pixel) {
|
var eligible = !control.geometryTypes ||
|
OpenLayers.Util.indexOf(control.geometryTypes,
|
feature.geometry.CLASS_NAME) !== -1;
|
var i = OpenLayers.Util.indexOf(control.handles, feature);
|
i += OpenLayers.Util.indexOf(control.rotationHandles,
|
feature);
|
if(feature !== control.feature && feature !== control.box &&
|
i == -2 && eligible) {
|
control.setFeature(feature);
|
}
|
},
|
onComplete: function(feature, pixel) {
|
control.events.triggerEvent("transformcomplete",
|
{feature: control.feature});
|
}
|
});
|
},
|
|
/**
|
* Method: drawHandles
|
* Draws the handles to match the box.
|
*/
|
drawHandles: function() {
|
var layer = this.layer;
|
for(var i=0; i<8; ++i) {
|
if(this.rotate && i % 2 === 0) {
|
layer.drawFeature(this.rotationHandles[i/2],
|
this.rotationHandleSymbolizer);
|
}
|
layer.drawFeature(this.handles[i], this.renderIntent);
|
}
|
},
|
|
/**
|
* Method: transformFeature
|
* Transforms the feature.
|
*
|
* Parameters:
|
* mods - {Object} An object with optional scale, ratio, rotation and
|
* center properties.
|
*/
|
transformFeature: function(mods) {
|
if(!this._setfeature) {
|
this.scale *= (mods.scale || 1);
|
this.ratio *= (mods.ratio || 1);
|
var oldRotation = this.rotation;
|
this.rotation = (this.rotation + (mods.rotation || 0)) % 360;
|
|
if(this.events.triggerEvent("beforetransform", mods) !== false) {
|
var feature = this.feature;
|
var geom = feature.geometry;
|
var center = this.center;
|
geom.rotate(-oldRotation, center);
|
if(mods.scale || mods.ratio) {
|
geom.resize(mods.scale, center, mods.ratio);
|
} else if(mods.center) {
|
feature.move(mods.center.getBounds().getCenterLonLat());
|
}
|
geom.rotate(this.rotation, center);
|
this.layer.drawFeature(feature);
|
feature.toState(OpenLayers.State.UPDATE);
|
this.events.triggerEvent("transform", mods);
|
}
|
}
|
this.layer.drawFeature(this.box, this.renderIntent);
|
this.drawHandles();
|
},
|
|
/**
|
* APIMethod: destroy
|
* Take care of things that are not handled in superclass.
|
*/
|
destroy: function() {
|
var geom;
|
for(var i=0; i<8; ++i) {
|
geom = this.box.geometry.components[i];
|
geom._handle.destroy();
|
geom._handle = null;
|
geom._rotationHandle && geom._rotationHandle.destroy();
|
geom._rotationHandle = null;
|
}
|
this.center = null;
|
this.feature = null;
|
this.handles = null;
|
this.rotationHandleSymbolizer = null;
|
this.rotationHandles = null;
|
this.box.destroy();
|
this.box = null;
|
this.layer = null;
|
this.dragControl.destroy();
|
this.dragControl = null;
|
OpenLayers.Control.prototype.destroy.apply(this, arguments);
|
},
|
|
CLASS_NAME: "OpenLayers.Control.TransformFeature"
|
});
|
/* ======================================================================
|
OpenLayers/Handler/Box.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Handler.js
|
* @requires OpenLayers/Handler/Drag.js
|
*/
|
|
/**
|
* Class: OpenLayers.Handler.Box
|
* Handler for dragging a rectangle across the map. Box is displayed
|
* on mouse down, moves on mouse move, and is finished on mouse up.
|
*
|
* Inherits from:
|
* - <OpenLayers.Handler>
|
*/
|
OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, {
|
|
/**
|
* Property: dragHandler
|
* {<OpenLayers.Handler.Drag>}
|
*/
|
dragHandler: null,
|
|
/**
|
* APIProperty: boxDivClassName
|
* {String} The CSS class to use for drawing the box. Default is
|
* olHandlerBoxZoomBox
|
*/
|
boxDivClassName: 'olHandlerBoxZoomBox',
|
|
/**
|
* Property: boxOffsets
|
* {Object} Caches box offsets from css. This is used by the getBoxOffsets
|
* method.
|
*/
|
boxOffsets: null,
|
|
/**
|
* Constructor: OpenLayers.Handler.Box
|
*
|
* Parameters:
|
* control - {<OpenLayers.Control>}
|
* callbacks - {Object} An object with a properties whose values are
|
* functions. Various callbacks described below.
|
* options - {Object}
|
*
|
* Named callbacks:
|
* start - Called when the box drag operation starts.
|
* done - Called when the box drag operation is finished.
|
* The callback should expect to receive a single argument, the box
|
* bounds or a pixel. If the box dragging didn't span more than a 5
|
* pixel distance, a pixel will be returned instead of a bounds object.
|
*/
|
initialize: function(control, callbacks, options) {
|
OpenLayers.Handler.prototype.initialize.apply(this, arguments);
|
this.dragHandler = new OpenLayers.Handler.Drag(
|
this,
|
{
|
down: this.startBox,
|
move: this.moveBox,
|
out: this.removeBox,
|
up: this.endBox
|
},
|
{keyMask: this.keyMask}
|
);
|
},
|
|
/**
|
* Method: destroy
|
*/
|
destroy: function() {
|
OpenLayers.Handler.prototype.destroy.apply(this, arguments);
|
if (this.dragHandler) {
|
this.dragHandler.destroy();
|
this.dragHandler = null;
|
}
|
},
|
|
/**
|
* Method: setMap
|
*/
|
setMap: function (map) {
|
OpenLayers.Handler.prototype.setMap.apply(this, arguments);
|
if (this.dragHandler) {
|
this.dragHandler.setMap(map);
|
}
|
},
|
|
/**
|
* Method: startBox
|
*
|
* Parameters:
|
* xy - {<OpenLayers.Pixel>}
|
*/
|
startBox: function (xy) {
|
this.callback("start", []);
|
this.zoomBox = OpenLayers.Util.createDiv('zoomBox', {
|
x: -9999, y: -9999
|
});
|
this.zoomBox.className = this.boxDivClassName;
|
this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
|
|
this.map.viewPortDiv.appendChild(this.zoomBox);
|
|
OpenLayers.Element.addClass(
|
this.map.viewPortDiv, "olDrawBox"
|
);
|
},
|
|
/**
|
* Method: moveBox
|
*/
|
moveBox: function (xy) {
|
var startX = this.dragHandler.start.x;
|
var startY = this.dragHandler.start.y;
|
var deltaX = Math.abs(startX - xy.x);
|
var deltaY = Math.abs(startY - xy.y);
|
|
var offset = this.getBoxOffsets();
|
this.zoomBox.style.width = (deltaX + offset.width + 1) + "px";
|
this.zoomBox.style.height = (deltaY + offset.height + 1) + "px";
|
this.zoomBox.style.left = (xy.x < startX ?
|
startX - deltaX - offset.left : startX - offset.left) + "px";
|
this.zoomBox.style.top = (xy.y < startY ?
|
startY - deltaY - offset.top : startY - offset.top) + "px";
|
},
|
|
/**
|
* Method: endBox
|
*/
|
endBox: function(end) {
|
var result;
|
if (Math.abs(this.dragHandler.start.x - end.x) > 5 ||
|
Math.abs(this.dragHandler.start.y - end.y) > 5) {
|
var start = this.dragHandler.start;
|
var top = Math.min(start.y, end.y);
|
var bottom = Math.max(start.y, end.y);
|
var left = Math.min(start.x, end.x);
|
var right = Math.max(start.x, end.x);
|
result = new OpenLayers.Bounds(left, bottom, right, top);
|
} else {
|
result = this.dragHandler.start.clone(); // i.e. OL.Pixel
|
}
|
this.removeBox();
|
|
this.callback("done", [result]);
|
},
|
|
/**
|
* Method: removeBox
|
* Remove the zoombox from the screen and nullify our reference to it.
|
*/
|
removeBox: function() {
|
this.map.viewPortDiv.removeChild(this.zoomBox);
|
this.zoomBox = null;
|
this.boxOffsets = null;
|
OpenLayers.Element.removeClass(
|
this.map.viewPortDiv, "olDrawBox"
|
);
|
|
},
|
|
/**
|
* Method: activate
|
*/
|
activate: function () {
|
if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
|
this.dragHandler.activate();
|
return true;
|
} else {
|
return false;
|
}
|
},
|
|
/**
|
* Method: deactivate
|
*/
|
deactivate: function () {
|
if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
|
if (this.dragHandler.deactivate()) {
|
if (this.zoomBox) {
|
this.removeBox();
|
}
|
}
|
return true;
|
} else {
|
return false;
|
}
|
},
|
|
/**
|
* Method: getBoxOffsets
|
* Determines border offsets for a box, according to the box model.
|
*
|
* Returns:
|
* {Object} an object with the following offsets:
|
* - left
|
* - right
|
* - top
|
* - bottom
|
* - width
|
* - height
|
*/
|
getBoxOffsets: function() {
|
if (!this.boxOffsets) {
|
// Determine the box model. If the testDiv's clientWidth is 3, then
|
// the borders are outside and we are dealing with the w3c box
|
// model. Otherwise, the browser uses the traditional box model and
|
// the borders are inside the box bounds, leaving us with a
|
// clientWidth of 1.
|
var testDiv = document.createElement("div");
|
//testDiv.style.visibility = "hidden";
|
testDiv.style.position = "absolute";
|
testDiv.style.border = "1px solid black";
|
testDiv.style.width = "3px";
|
document.body.appendChild(testDiv);
|
var w3cBoxModel = testDiv.clientWidth == 3;
|
document.body.removeChild(testDiv);
|
|
var left = parseInt(OpenLayers.Element.getStyle(this.zoomBox,
|
"border-left-width"));
|
var right = parseInt(OpenLayers.Element.getStyle(
|
this.zoomBox, "border-right-width"));
|
var top = parseInt(OpenLayers.Element.getStyle(this.zoomBox,
|
"border-top-width"));
|
var bottom = parseInt(OpenLayers.Element.getStyle(
|
this.zoomBox, "border-bottom-width"));
|
this.boxOffsets = {
|
left: left,
|
right: right,
|
top: top,
|
bottom: bottom,
|
width: w3cBoxModel === false ? left + right : 0,
|
height: w3cBoxModel === false ? top + bottom : 0
|
};
|
}
|
return this.boxOffsets;
|
},
|
|
CLASS_NAME: "OpenLayers.Handler.Box"
|
});
|
/* ======================================================================
|
OpenLayers/Control/ZoomBox.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/Handler/Box.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.ZoomBox
|
* The ZoomBox control enables zooming directly to a given extent, by drawing
|
* a box on the map. The box is drawn by holding down shift, whilst dragging
|
* the mouse.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, {
|
/**
|
* Property: type
|
* {OpenLayers.Control.TYPE}
|
*/
|
type: OpenLayers.Control.TYPE_TOOL,
|
|
/**
|
* Property: out
|
* {Boolean} Should the control be used for zooming out?
|
*/
|
out: false,
|
|
/**
|
* APIProperty: keyMask
|
* {Integer} Zoom only occurs if the keyMask matches the combination of
|
* keys down. Use bitwise operators and one or more of the
|
* <OpenLayers.Handler> constants to construct a keyMask. Leave null if
|
* not used mask. Default is null.
|
*/
|
keyMask: null,
|
|
/**
|
* APIProperty: alwaysZoom
|
* {Boolean} Always zoom in/out when box drawn, even if the zoom level does
|
* not change.
|
*/
|
alwaysZoom: false,
|
|
/**
|
* APIProperty: zoomOnClick
|
* {Boolean} Should we zoom when no box was dragged, i.e. the user only
|
* clicked? Default is true.
|
*/
|
zoomOnClick: true,
|
|
/**
|
* Method: draw
|
*/
|
draw: function() {
|
this.handler = new OpenLayers.Handler.Box( this,
|
{done: this.zoomBox}, {keyMask: this.keyMask} );
|
},
|
|
/**
|
* Method: zoomBox
|
*
|
* Parameters:
|
* position - {<OpenLayers.Bounds>} or {<OpenLayers.Pixel>}
|
*/
|
zoomBox: function (position) {
|
if (position instanceof OpenLayers.Bounds) {
|
var bounds,
|
targetCenterPx = position.getCenterPixel();
|
if (!this.out) {
|
var minXY = this.map.getLonLatFromPixel({
|
x: position.left,
|
y: position.bottom
|
});
|
var maxXY = this.map.getLonLatFromPixel({
|
x: position.right,
|
y: position.top
|
});
|
bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat,
|
maxXY.lon, maxXY.lat);
|
} else {
|
var pixWidth = position.right - position.left;
|
var pixHeight = position.bottom - position.top;
|
var zoomFactor = Math.min((this.map.size.h / pixHeight),
|
(this.map.size.w / pixWidth));
|
var extent = this.map.getExtent();
|
var center = this.map.getLonLatFromPixel(targetCenterPx);
|
var xmin = center.lon - (extent.getWidth()/2)*zoomFactor;
|
var xmax = center.lon + (extent.getWidth()/2)*zoomFactor;
|
var ymin = center.lat - (extent.getHeight()/2)*zoomFactor;
|
var ymax = center.lat + (extent.getHeight()/2)*zoomFactor;
|
bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax);
|
}
|
// always zoom in/out
|
var lastZoom = this.map.getZoom(),
|
size = this.map.getSize(),
|
centerPx = {x: size.w / 2, y: size.h / 2},
|
zoom = this.map.getZoomForExtent(bounds),
|
oldRes = this.map.getResolution(),
|
newRes = this.map.getResolutionForZoom(zoom);
|
if (oldRes == newRes) {
|
this.map.setCenter(this.map.getLonLatFromPixel(targetCenterPx));
|
} else {
|
var zoomOriginPx = {
|
x: (oldRes * targetCenterPx.x - newRes * centerPx.x) /
|
(oldRes - newRes),
|
y: (oldRes * targetCenterPx.y - newRes * centerPx.y) /
|
(oldRes - newRes)
|
};
|
this.map.zoomTo(zoom, zoomOriginPx);
|
}
|
if (lastZoom == this.map.getZoom() && this.alwaysZoom == true){
|
this.map.zoomTo(lastZoom + (this.out ? -1 : 1));
|
}
|
} else if (this.zoomOnClick) { // it's a pixel
|
if (!this.out) {
|
this.map.zoomTo(this.map.getZoom() + 1, position);
|
} else {
|
this.map.zoomTo(this.map.getZoom() - 1, position);
|
}
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Control.ZoomBox"
|
});
|
/* ======================================================================
|
OpenLayers/Control/DragPan.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/Handler/Drag.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.DragPan
|
* The DragPan control pans the map with a drag of the mouse.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* Property: type
|
* {OpenLayers.Control.TYPES}
|
*/
|
type: OpenLayers.Control.TYPE_TOOL,
|
|
/**
|
* Property: panned
|
* {Boolean} The map moved.
|
*/
|
panned: false,
|
|
/**
|
* Property: interval
|
* {Integer} The number of milliseconds that should ellapse before
|
* panning the map again. Defaults to 0 milliseconds, which means that
|
* no separate cycle is used for panning. In most cases you won't want
|
* to change this value. For slow machines/devices larger values can be
|
* tried out.
|
*/
|
interval: 0,
|
|
/**
|
* APIProperty: documentDrag
|
* {Boolean} If set to true, mouse dragging will continue even if the
|
* mouse cursor leaves the map viewport. Default is false.
|
*/
|
documentDrag: false,
|
|
/**
|
* Property: kinetic
|
* {<OpenLayers.Kinetic>} The OpenLayers.Kinetic object.
|
*/
|
kinetic: null,
|
|
/**
|
* APIProperty: enableKinetic
|
* {Boolean} Set this option to enable "kinetic dragging". Can be
|
* set to true or to an object. If set to an object this
|
* object will be passed to the {<OpenLayers.Kinetic>}
|
* constructor. Defaults to true.
|
* To get kinetic dragging, ensure that OpenLayers/Kinetic.js is
|
* included in your build config.
|
*/
|
enableKinetic: true,
|
|
/**
|
* APIProperty: kineticInterval
|
* {Integer} Interval in milliseconds between 2 steps in the "kinetic
|
* scrolling". Applies only if enableKinetic is set. Defaults
|
* to 10 milliseconds.
|
*/
|
kineticInterval: 10,
|
|
|
/**
|
* Method: draw
|
* Creates a Drag handler, using <panMap> and
|
* <panMapDone> as callbacks.
|
*/
|
draw: function() {
|
if (this.enableKinetic && OpenLayers.Kinetic) {
|
var config = {interval: this.kineticInterval};
|
if(typeof this.enableKinetic === "object") {
|
config = OpenLayers.Util.extend(config, this.enableKinetic);
|
}
|
this.kinetic = new OpenLayers.Kinetic(config);
|
}
|
this.handler = new OpenLayers.Handler.Drag(this, {
|
"move": this.panMap,
|
"done": this.panMapDone,
|
"down": this.panMapStart
|
}, {
|
interval: this.interval,
|
documentDrag: this.documentDrag
|
}
|
);
|
},
|
|
/**
|
* Method: panMapStart
|
*/
|
panMapStart: function() {
|
if(this.kinetic) {
|
this.kinetic.begin();
|
}
|
},
|
|
/**
|
* Method: panMap
|
*
|
* Parameters:
|
* xy - {<OpenLayers.Pixel>} Pixel of the mouse position
|
*/
|
panMap: function(xy) {
|
if(this.kinetic) {
|
this.kinetic.update(xy);
|
}
|
this.panned = true;
|
this.map.pan(
|
this.handler.last.x - xy.x,
|
this.handler.last.y - xy.y,
|
{dragging: true, animate: false}
|
);
|
},
|
|
/**
|
* Method: panMapDone
|
* Finish the panning operation. Only call setCenter (through <panMap>)
|
* if the map has actually been moved.
|
*
|
* Parameters:
|
* xy - {<OpenLayers.Pixel>} Pixel of the mouse position
|
*/
|
panMapDone: function(xy) {
|
if(this.panned) {
|
var res = null;
|
if (this.kinetic) {
|
res = this.kinetic.end(xy);
|
}
|
this.map.pan(
|
this.handler.last.x - xy.x,
|
this.handler.last.y - xy.y,
|
{dragging: !!res, animate: false}
|
);
|
if (res) {
|
var self = this;
|
this.kinetic.move(res, function(x, y, end) {
|
self.map.pan(x, y, {dragging: !end, animate: false});
|
});
|
}
|
this.panned = false;
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Control.DragPan"
|
});
|
/* ======================================================================
|
OpenLayers/Control/Navigation.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control/ZoomBox.js
|
* @requires OpenLayers/Control/DragPan.js
|
* @requires OpenLayers/Handler/MouseWheel.js
|
* @requires OpenLayers/Handler/Click.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.Navigation
|
* The navigation control handles map browsing with mouse events (dragging,
|
* double-clicking, and scrolling the wheel). Create a new navigation
|
* control with the <OpenLayers.Control.Navigation> control.
|
*
|
* Note that this control is added to the map by default (if no controls
|
* array is sent in the options object to the <OpenLayers.Map>
|
* constructor).
|
*
|
* Inherits:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* Property: dragPan
|
* {<OpenLayers.Control.DragPan>}
|
*/
|
dragPan: null,
|
|
/**
|
* APIProperty: dragPanOptions
|
* {Object} Options passed to the DragPan control.
|
*/
|
dragPanOptions: null,
|
|
/**
|
* Property: pinchZoom
|
* {<OpenLayers.Control.PinchZoom>}
|
*/
|
pinchZoom: null,
|
|
/**
|
* APIProperty: pinchZoomOptions
|
* {Object} Options passed to the PinchZoom control.
|
*/
|
pinchZoomOptions: null,
|
|
/**
|
* APIProperty: documentDrag
|
* {Boolean} Allow panning of the map by dragging outside map viewport.
|
* Default is false.
|
*/
|
documentDrag: false,
|
|
/**
|
* Property: zoomBox
|
* {<OpenLayers.Control.ZoomBox>}
|
*/
|
zoomBox: null,
|
|
/**
|
* APIProperty: zoomBoxEnabled
|
* {Boolean} Whether the user can draw a box to zoom
|
*/
|
zoomBoxEnabled: true,
|
|
/**
|
* APIProperty: zoomWheelEnabled
|
* {Boolean} Whether the mousewheel should zoom the map
|
*/
|
zoomWheelEnabled: true,
|
|
/**
|
* Property: mouseWheelOptions
|
* {Object} Options passed to the MouseWheel control (only useful if
|
* <zoomWheelEnabled> is set to true). Default is no options for maps
|
* with fractionalZoom set to true, otherwise
|
* {cumulative: false, interval: 50, maxDelta: 6}
|
*/
|
mouseWheelOptions: null,
|
|
/**
|
* APIProperty: handleRightClicks
|
* {Boolean} Whether or not to handle right clicks. Default is false.
|
*/
|
handleRightClicks: false,
|
|
/**
|
* APIProperty: zoomBoxKeyMask
|
* {Integer} <OpenLayers.Handler> key code of the key, which has to be
|
* pressed, while drawing the zoom box with the mouse on the screen.
|
* You should probably set handleRightClicks to true if you use this
|
* with MOD_CTRL, to disable the context menu for machines which use
|
* CTRL-Click as a right click.
|
* Default: <OpenLayers.Handler.MOD_SHIFT>
|
*/
|
zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT,
|
|
/**
|
* APIProperty: autoActivate
|
* {Boolean} Activate the control when it is added to a map. Default is
|
* true.
|
*/
|
autoActivate: true,
|
|
/**
|
* Constructor: OpenLayers.Control.Navigation
|
* Create a new navigation control
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* the control
|
*/
|
initialize: function(options) {
|
this.handlers = {};
|
OpenLayers.Control.prototype.initialize.apply(this, arguments);
|
},
|
|
/**
|
* Method: destroy
|
* The destroy method is used to perform any clean up before the control
|
* is dereferenced. Typically this is where event listeners are removed
|
* to prevent memory leaks.
|
*/
|
destroy: function() {
|
this.deactivate();
|
|
if (this.dragPan) {
|
this.dragPan.destroy();
|
}
|
this.dragPan = null;
|
|
if (this.zoomBox) {
|
this.zoomBox.destroy();
|
}
|
this.zoomBox = null;
|
|
if (this.pinchZoom) {
|
this.pinchZoom.destroy();
|
}
|
this.pinchZoom = null;
|
|
OpenLayers.Control.prototype.destroy.apply(this,arguments);
|
},
|
|
/**
|
* Method: activate
|
*/
|
activate: function() {
|
this.dragPan.activate();
|
if (this.zoomWheelEnabled) {
|
this.handlers.wheel.activate();
|
}
|
this.handlers.click.activate();
|
if (this.zoomBoxEnabled) {
|
this.zoomBox.activate();
|
}
|
if (this.pinchZoom) {
|
this.pinchZoom.activate();
|
}
|
return OpenLayers.Control.prototype.activate.apply(this,arguments);
|
},
|
|
/**
|
* Method: deactivate
|
*/
|
deactivate: function() {
|
if (this.pinchZoom) {
|
this.pinchZoom.deactivate();
|
}
|
this.zoomBox.deactivate();
|
this.dragPan.deactivate();
|
this.handlers.click.deactivate();
|
this.handlers.wheel.deactivate();
|
return OpenLayers.Control.prototype.deactivate.apply(this,arguments);
|
},
|
|
/**
|
* Method: draw
|
*/
|
draw: function() {
|
// disable right mouse context menu for support of right click events
|
if (this.handleRightClicks) {
|
this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False;
|
}
|
|
var clickCallbacks = {
|
'click': this.defaultClick,
|
'dblclick': this.defaultDblClick,
|
'dblrightclick': this.defaultDblRightClick
|
};
|
var clickOptions = {
|
'double': true,
|
'stopDouble': true
|
};
|
this.handlers.click = new OpenLayers.Handler.Click(
|
this, clickCallbacks, clickOptions
|
);
|
this.dragPan = new OpenLayers.Control.DragPan(
|
OpenLayers.Util.extend({
|
map: this.map,
|
documentDrag: this.documentDrag
|
}, this.dragPanOptions)
|
);
|
this.zoomBox = new OpenLayers.Control.ZoomBox(
|
{map: this.map, keyMask: this.zoomBoxKeyMask});
|
this.dragPan.draw();
|
this.zoomBox.draw();
|
var wheelOptions = this.map.fractionalZoom ? {} : {
|
cumulative: false,
|
interval: 50,
|
maxDelta: 6
|
};
|
this.handlers.wheel = new OpenLayers.Handler.MouseWheel(
|
this, {up : this.wheelUp, down: this.wheelDown},
|
OpenLayers.Util.extend(wheelOptions, this.mouseWheelOptions)
|
);
|
if (OpenLayers.Control.PinchZoom) {
|
this.pinchZoom = new OpenLayers.Control.PinchZoom(
|
OpenLayers.Util.extend(
|
{map: this.map}, this.pinchZoomOptions));
|
}
|
},
|
|
/**
|
* Method: defaultClick
|
*
|
* Parameters:
|
* evt - {Event}
|
*/
|
defaultClick: function (evt) {
|
if (evt.lastTouches && evt.lastTouches.length == 2) {
|
this.map.zoomOut();
|
}
|
},
|
|
/**
|
* Method: defaultDblClick
|
*
|
* Parameters:
|
* evt - {Event}
|
*/
|
defaultDblClick: function (evt) {
|
this.map.zoomTo(this.map.zoom + 1, evt.xy);
|
},
|
|
/**
|
* Method: defaultDblRightClick
|
*
|
* Parameters:
|
* evt - {Event}
|
*/
|
defaultDblRightClick: function (evt) {
|
this.map.zoomTo(this.map.zoom - 1, evt.xy);
|
},
|
|
/**
|
* Method: wheelChange
|
*
|
* Parameters:
|
* evt - {Event}
|
* deltaZ - {Integer}
|
*/
|
wheelChange: function(evt, deltaZ) {
|
if (!this.map.fractionalZoom) {
|
deltaZ = Math.round(deltaZ);
|
}
|
var currentZoom = this.map.getZoom(),
|
newZoom = currentZoom + deltaZ;
|
newZoom = Math.max(newZoom, 0);
|
newZoom = Math.min(newZoom, this.map.getNumZoomLevels());
|
if (newZoom === currentZoom) {
|
return;
|
}
|
this.map.zoomTo(newZoom, evt.xy);
|
},
|
|
/**
|
* Method: wheelUp
|
* User spun scroll wheel up
|
*
|
* Parameters:
|
* evt - {Event}
|
* delta - {Integer}
|
*/
|
wheelUp: function(evt, delta) {
|
this.wheelChange(evt, delta || 1);
|
},
|
|
/**
|
* Method: wheelDown
|
* User spun scroll wheel down
|
*
|
* Parameters:
|
* evt - {Event}
|
* delta - {Integer}
|
*/
|
wheelDown: function(evt, delta) {
|
this.wheelChange(evt, delta || -1);
|
},
|
|
/**
|
* Method: disableZoomBox
|
*/
|
disableZoomBox : function() {
|
this.zoomBoxEnabled = false;
|
this.zoomBox.deactivate();
|
},
|
|
/**
|
* Method: enableZoomBox
|
*/
|
enableZoomBox : function() {
|
this.zoomBoxEnabled = true;
|
if (this.active) {
|
this.zoomBox.activate();
|
}
|
},
|
|
/**
|
* Method: disableZoomWheel
|
*/
|
|
disableZoomWheel : function() {
|
this.zoomWheelEnabled = false;
|
this.handlers.wheel.deactivate();
|
},
|
|
/**
|
* Method: enableZoomWheel
|
*/
|
|
enableZoomWheel : function() {
|
this.zoomWheelEnabled = true;
|
if (this.active) {
|
this.handlers.wheel.activate();
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Control.Navigation"
|
});
|
/* ======================================================================
|
OpenLayers/Control/DrawFeature.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/Feature/Vector.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.DrawFeature
|
* The DrawFeature control draws point, line or polygon features on a vector
|
* layer when active.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.DrawFeature = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* Property: layer
|
* {<OpenLayers.Layer.Vector>}
|
*/
|
layer: null,
|
|
/**
|
* Property: callbacks
|
* {Object} The functions that are sent to the handler for callback
|
*/
|
callbacks: null,
|
|
/**
|
* APIProperty: events
|
* {<OpenLayers.Events>} Events instance for listeners and triggering
|
* control specific events.
|
*
|
* Register a listener for a particular event with the following syntax:
|
* (code)
|
* control.events.register(type, obj, listener);
|
* (end)
|
*
|
* Supported event types (in addition to those from <OpenLayers.Control.events>):
|
* featureadded - Triggered when a feature is added
|
*/
|
|
/**
|
* APIProperty: multi
|
* {Boolean} Cast features to multi-part geometries before passing to the
|
* layer. Default is false.
|
*/
|
multi: false,
|
|
/**
|
* APIProperty: featureAdded
|
* {Function} Called after each feature is added
|
*/
|
featureAdded: function() {},
|
|
/**
|
* APIProperty: handlerOptions
|
* {Object} Used to set non-default properties on the control's handler
|
*/
|
|
/**
|
* Constructor: OpenLayers.Control.DrawFeature
|
*
|
* Parameters:
|
* layer - {<OpenLayers.Layer.Vector>}
|
* handler - {<OpenLayers.Handler>}
|
* options - {Object}
|
*/
|
initialize: function(layer, handler, options) {
|
OpenLayers.Control.prototype.initialize.apply(this, [options]);
|
this.callbacks = OpenLayers.Util.extend(
|
{
|
done: this.drawFeature,
|
modify: function(vertex, feature) {
|
this.layer.events.triggerEvent(
|
"sketchmodified", {vertex: vertex, feature: feature}
|
);
|
},
|
create: function(vertex, feature) {
|
this.layer.events.triggerEvent(
|
"sketchstarted", {vertex: vertex, feature: feature}
|
);
|
}
|
},
|
this.callbacks
|
);
|
this.layer = layer;
|
this.handlerOptions = this.handlerOptions || {};
|
this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(
|
this.handlerOptions.layerOptions, {
|
renderers: layer.renderers, rendererOptions: layer.rendererOptions
|
}
|
);
|
if (!("multi" in this.handlerOptions)) {
|
this.handlerOptions.multi = this.multi;
|
}
|
var sketchStyle = this.layer.styleMap && this.layer.styleMap.styles.temporary;
|
if(sketchStyle) {
|
this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(
|
this.handlerOptions.layerOptions,
|
{styleMap: new OpenLayers.StyleMap({"default": sketchStyle})}
|
);
|
}
|
this.handler = new handler(this, this.callbacks, this.handlerOptions);
|
},
|
|
/**
|
* Method: drawFeature
|
*/
|
drawFeature: function(geometry) {
|
var feature = new OpenLayers.Feature.Vector(geometry);
|
var proceed = this.layer.events.triggerEvent(
|
"sketchcomplete", {feature: feature}
|
);
|
if(proceed !== false) {
|
feature.state = OpenLayers.State.INSERT;
|
this.layer.addFeatures([feature]);
|
this.featureAdded(feature);
|
this.events.triggerEvent("featureadded",{feature : feature});
|
}
|
},
|
|
/**
|
* APIMethod: insertXY
|
* Insert a point in the current sketch given x & y coordinates.
|
*
|
* Parameters:
|
* x - {Number} The x-coordinate of the point.
|
* y - {Number} The y-coordinate of the point.
|
*/
|
insertXY: function(x, y) {
|
if (this.handler && this.handler.line) {
|
this.handler.insertXY(x, y);
|
}
|
},
|
|
/**
|
* APIMethod: insertDeltaXY
|
* Insert a point given offsets from the previously inserted point.
|
*
|
* Parameters:
|
* dx - {Number} The x-coordinate offset of the point.
|
* dy - {Number} The y-coordinate offset of the point.
|
*/
|
insertDeltaXY: function(dx, dy) {
|
if (this.handler && this.handler.line) {
|
this.handler.insertDeltaXY(dx, dy);
|
}
|
},
|
|
/**
|
* APIMethod: insertDirectionLength
|
* Insert a point in the current sketch given a direction and a length.
|
*
|
* Parameters:
|
* direction - {Number} Degrees clockwise from the positive x-axis.
|
* length - {Number} Distance from the previously drawn point.
|
*/
|
insertDirectionLength: function(direction, length) {
|
if (this.handler && this.handler.line) {
|
this.handler.insertDirectionLength(direction, length);
|
}
|
},
|
|
/**
|
* APIMethod: insertDeflectionLength
|
* Insert a point in the current sketch given a deflection and a length.
|
* The deflection should be degrees clockwise from the previously
|
* digitized segment.
|
*
|
* Parameters:
|
* deflection - {Number} Degrees clockwise from the previous segment.
|
* length - {Number} Distance from the previously drawn point.
|
*/
|
insertDeflectionLength: function(deflection, length) {
|
if (this.handler && this.handler.line) {
|
this.handler.insertDeflectionLength(deflection, length);
|
}
|
},
|
|
/**
|
* APIMethod: undo
|
* Remove the most recently added point in the current sketch geometry.
|
*
|
* Returns:
|
* {Boolean} An edit was undone.
|
*/
|
undo: function() {
|
return this.handler.undo && this.handler.undo();
|
},
|
|
/**
|
* APIMethod: redo
|
* Reinsert the most recently removed point resulting from an <undo> call.
|
* The undo stack is deleted whenever a point is added by other means.
|
*
|
* Returns:
|
* {Boolean} An edit was redone.
|
*/
|
redo: function() {
|
return this.handler.redo && this.handler.redo();
|
},
|
|
/**
|
* APIMethod: finishSketch
|
* Finishes the sketch without including the currently drawn point.
|
* This method can be called to terminate drawing programmatically
|
* instead of waiting for the user to end the sketch.
|
*/
|
finishSketch: function() {
|
this.handler.finishGeometry();
|
},
|
|
/**
|
* APIMethod: cancel
|
* Cancel the current sketch. This removes the current sketch and keeps
|
* the drawing control active.
|
*/
|
cancel: function() {
|
this.handler.cancel();
|
},
|
|
CLASS_NAME: "OpenLayers.Control.DrawFeature"
|
});
|
/* ======================================================================
|
OpenLayers/Handler/Polygon.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Handler/Path.js
|
* @requires OpenLayers/Geometry/Polygon.js
|
*/
|
|
/**
|
* Class: OpenLayers.Handler.Polygon
|
* Handler to draw a polygon on the map. Polygon is displayed on mouse down,
|
* moves on mouse move, and is finished on mouse up.
|
*
|
* Inherits from:
|
* - <OpenLayers.Handler.Path>
|
* - <OpenLayers.Handler>
|
*/
|
OpenLayers.Handler.Polygon = OpenLayers.Class(OpenLayers.Handler.Path, {
|
|
/**
|
* APIProperty: holeModifier
|
* {String} Key modifier to trigger hole digitizing. Acceptable values are
|
* "altKey", "shiftKey", or "ctrlKey". If not set, no hole digitizing
|
* will take place. Default is null.
|
*/
|
holeModifier: null,
|
|
/**
|
* Property: drawingHole
|
* {Boolean} Currently drawing an interior ring.
|
*/
|
drawingHole: false,
|
|
/**
|
* Property: polygon
|
* {<OpenLayers.Feature.Vector>}
|
*/
|
polygon: null,
|
|
/**
|
* Constructor: OpenLayers.Handler.Polygon
|
* Create a Polygon Handler.
|
*
|
* Parameters:
|
* control - {<OpenLayers.Control>} The control that owns this handler
|
* callbacks - {Object} An object with a properties whose values are
|
* functions. Various callbacks described below.
|
* options - {Object} An optional object with properties to be set on the
|
* handler
|
*
|
* Named callbacks:
|
* create - Called when a sketch is first created. Callback called with
|
* the creation point geometry and sketch feature.
|
* modify - Called with each move of a vertex with the vertex (point)
|
* geometry and the sketch feature.
|
* point - Called as each point is added. Receives the new point geometry.
|
* done - Called when the point drawing is finished. The callback will
|
* recieve a single argument, the polygon geometry.
|
* cancel - Called when the handler is deactivated while drawing. The
|
* cancel callback will receive a geometry.
|
*/
|
|
/**
|
* Method: createFeature
|
* Add temporary geometries
|
*
|
* Parameters:
|
* pixel - {<OpenLayers.Pixel>} The initial pixel location for the new
|
* feature.
|
*/
|
createFeature: function(pixel) {
|
var lonlat = this.layer.getLonLatFromViewPortPx(pixel);
|
var geometry = new OpenLayers.Geometry.Point(
|
lonlat.lon, lonlat.lat
|
);
|
this.point = new OpenLayers.Feature.Vector(geometry);
|
this.line = new OpenLayers.Feature.Vector(
|
new OpenLayers.Geometry.LinearRing([this.point.geometry])
|
);
|
this.polygon = new OpenLayers.Feature.Vector(
|
new OpenLayers.Geometry.Polygon([this.line.geometry])
|
);
|
this.callback("create", [this.point.geometry, this.getSketch()]);
|
this.point.geometry.clearBounds();
|
this.layer.addFeatures([this.polygon, this.point], {silent: true});
|
},
|
|
/**
|
* Method: addPoint
|
* Add point to geometry.
|
*
|
* Parameters:
|
* pixel - {<OpenLayers.Pixel>} The pixel location for the new point.
|
*/
|
addPoint: function(pixel) {
|
if(!this.drawingHole && this.holeModifier &&
|
this.evt && this.evt[this.holeModifier]) {
|
var geometry = this.point.geometry;
|
var features = this.control.layer.features;
|
var candidate, polygon;
|
// look for intersections, last drawn gets priority
|
for (var i=features.length-1; i>=0; --i) {
|
candidate = features[i].geometry;
|
if ((candidate instanceof OpenLayers.Geometry.Polygon ||
|
candidate instanceof OpenLayers.Geometry.MultiPolygon) &&
|
candidate.intersects(geometry)) {
|
polygon = features[i];
|
this.control.layer.removeFeatures([polygon], {silent: true});
|
this.control.layer.events.registerPriority(
|
"sketchcomplete", this, this.finalizeInteriorRing
|
);
|
this.control.layer.events.registerPriority(
|
"sketchmodified", this, this.enforceTopology
|
);
|
polygon.geometry.addComponent(this.line.geometry);
|
this.polygon = polygon;
|
this.drawingHole = true;
|
break;
|
}
|
}
|
}
|
OpenLayers.Handler.Path.prototype.addPoint.apply(this, arguments);
|
},
|
|
/**
|
* Method: getCurrentPointIndex
|
*
|
* Returns:
|
* {Number} The index of the most recently drawn point.
|
*/
|
getCurrentPointIndex: function() {
|
return this.line.geometry.components.length - 2;
|
},
|
|
/**
|
* Method: enforceTopology
|
* Simple topology enforcement for drawing interior rings. Ensures vertices
|
* of interior rings are contained by exterior ring. Other topology
|
* rules are enforced in <finalizeInteriorRing> to allow drawing of
|
* rings that intersect only during the sketch (e.g. a "C" shaped ring
|
* that nearly encloses another ring).
|
*/
|
enforceTopology: function(event) {
|
var point = event.vertex;
|
var components = this.line.geometry.components;
|
// ensure that vertices of interior ring are contained by exterior ring
|
if (!this.polygon.geometry.intersects(point)) {
|
var last = components[components.length-3];
|
point.x = last.x;
|
point.y = last.y;
|
}
|
},
|
|
/**
|
* Method: finishGeometry
|
* Finish the geometry and send it back to the control.
|
*/
|
finishGeometry: function() {
|
var index = this.line.geometry.components.length - 2;
|
this.line.geometry.removeComponent(this.line.geometry.components[index]);
|
this.removePoint();
|
this.finalize();
|
},
|
|
/**
|
* Method: finalizeInteriorRing
|
* Enforces that new ring has some area and doesn't contain vertices of any
|
* other rings.
|
*/
|
finalizeInteriorRing: function() {
|
var ring = this.line.geometry;
|
// ensure that ring has some area
|
var modified = (ring.getArea() !== 0);
|
if (modified) {
|
// ensure that new ring doesn't intersect any other rings
|
var rings = this.polygon.geometry.components;
|
for (var i=rings.length-2; i>=0; --i) {
|
if (ring.intersects(rings[i])) {
|
modified = false;
|
break;
|
}
|
}
|
if (modified) {
|
// ensure that new ring doesn't contain any other rings
|
var target;
|
outer: for (var i=rings.length-2; i>0; --i) {
|
var points = rings[i].components;
|
for (var j=0, jj=points.length; j<jj; ++j) {
|
if (ring.containsPoint(points[j])) {
|
modified = false;
|
break outer;
|
}
|
}
|
}
|
}
|
}
|
if (modified) {
|
if (this.polygon.state !== OpenLayers.State.INSERT) {
|
this.polygon.state = OpenLayers.State.UPDATE;
|
}
|
} else {
|
this.polygon.geometry.removeComponent(ring);
|
}
|
this.restoreFeature();
|
return false;
|
},
|
|
/**
|
* APIMethod: cancel
|
* Finish the geometry and call the "cancel" callback.
|
*/
|
cancel: function() {
|
if (this.drawingHole) {
|
this.polygon.geometry.removeComponent(this.line.geometry);
|
this.restoreFeature(true);
|
}
|
return OpenLayers.Handler.Path.prototype.cancel.apply(this, arguments);
|
},
|
|
/**
|
* Method: restoreFeature
|
* Move the feature from the sketch layer to the target layer.
|
*
|
* Properties:
|
* cancel - {Boolean} Cancel drawing. If falsey, the "sketchcomplete" event
|
* will be fired.
|
*/
|
restoreFeature: function(cancel) {
|
this.control.layer.events.unregister(
|
"sketchcomplete", this, this.finalizeInteriorRing
|
);
|
this.control.layer.events.unregister(
|
"sketchmodified", this, this.enforceTopology
|
);
|
this.layer.removeFeatures([this.polygon], {silent: true});
|
this.control.layer.addFeatures([this.polygon], {silent: true});
|
this.drawingHole = false;
|
if (!cancel) {
|
// Re-trigger "sketchcomplete" so other listeners can do their
|
// business. While this is somewhat sloppy (if a listener is
|
// registered with registerPriority - not common - between the start
|
// and end of a single ring drawing - very uncommon - it will be
|
// called twice).
|
// TODO: In 3.0, collapse sketch handlers into geometry specific
|
// drawing controls.
|
this.control.layer.events.triggerEvent(
|
"sketchcomplete", {feature : this.polygon}
|
);
|
}
|
},
|
|
/**
|
* Method: destroyFeature
|
* Destroy temporary geometries
|
*
|
* Parameters:
|
* force - {Boolean} Destroy even if persist is true.
|
*/
|
destroyFeature: function(force) {
|
OpenLayers.Handler.Path.prototype.destroyFeature.call(
|
this, force);
|
this.polygon = null;
|
},
|
|
/**
|
* Method: drawFeature
|
* Render geometries on the temporary layer.
|
*/
|
drawFeature: function() {
|
this.layer.drawFeature(this.polygon, this.style);
|
this.layer.drawFeature(this.point, this.style);
|
},
|
|
/**
|
* Method: getSketch
|
* Return the sketch feature.
|
*
|
* Returns:
|
* {<OpenLayers.Feature.Vector>}
|
*/
|
getSketch: function() {
|
return this.polygon;
|
},
|
|
/**
|
* Method: getGeometry
|
* Return the sketch geometry. If <multi> is true, this will return
|
* a multi-part geometry.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry.Polygon>}
|
*/
|
getGeometry: function() {
|
var geometry = this.polygon && this.polygon.geometry;
|
if(geometry && this.multi) {
|
geometry = new OpenLayers.Geometry.MultiPolygon([geometry]);
|
}
|
return geometry;
|
},
|
|
CLASS_NAME: "OpenLayers.Handler.Polygon"
|
});
|
/* ======================================================================
|
OpenLayers/Control/EditingToolbar.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control/Panel.js
|
* @requires OpenLayers/Control/Navigation.js
|
* @requires OpenLayers/Control/DrawFeature.js
|
* @requires OpenLayers/Handler/Point.js
|
* @requires OpenLayers/Handler/Path.js
|
* @requires OpenLayers/Handler/Polygon.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.EditingToolbar
|
* The EditingToolbar is a panel of 4 controls to draw polygons, lines,
|
* points, or to navigate the map by panning. By default it appears in the
|
* upper right corner of the map.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control.Panel>
|
*/
|
OpenLayers.Control.EditingToolbar = OpenLayers.Class(
|
OpenLayers.Control.Panel, {
|
|
/**
|
* APIProperty: citeCompliant
|
* {Boolean} If set to true, coordinates of features drawn in a map extent
|
* crossing the date line won't exceed the world bounds. Default is false.
|
*/
|
citeCompliant: false,
|
|
/**
|
* Constructor: OpenLayers.Control.EditingToolbar
|
* Create an editing toolbar for a given layer.
|
*
|
* Parameters:
|
* layer - {<OpenLayers.Layer.Vector>}
|
* options - {Object}
|
*/
|
initialize: function(layer, options) {
|
OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);
|
|
this.addControls(
|
[ new OpenLayers.Control.Navigation() ]
|
);
|
var controls = [
|
new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Point, {
|
displayClass: 'olControlDrawFeaturePoint',
|
handlerOptions: {citeCompliant: this.citeCompliant}
|
}),
|
new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Path, {
|
displayClass: 'olControlDrawFeaturePath',
|
handlerOptions: {citeCompliant: this.citeCompliant}
|
}),
|
new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Polygon, {
|
displayClass: 'olControlDrawFeaturePolygon',
|
handlerOptions: {citeCompliant: this.citeCompliant}
|
})
|
];
|
this.addControls(controls);
|
},
|
|
/**
|
* Method: draw
|
* calls the default draw, and then activates mouse defaults.
|
*
|
* Returns:
|
* {DOMElement}
|
*/
|
draw: function() {
|
var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments);
|
if (this.defaultControl === null) {
|
this.defaultControl = this.controls[0];
|
}
|
return div;
|
},
|
|
CLASS_NAME: "OpenLayers.Control.EditingToolbar"
|
});
|
/* ======================================================================
|
OpenLayers/Strategy/BBOX.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Strategy.js
|
* @requires OpenLayers/Filter/Spatial.js
|
*/
|
|
/**
|
* Class: OpenLayers.Strategy.BBOX
|
* A simple strategy that reads new features when the viewport invalidates
|
* some bounds.
|
*
|
* Inherits from:
|
* - <OpenLayers.Strategy>
|
*/
|
OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, {
|
|
/**
|
* Property: bounds
|
* {<OpenLayers.Bounds>} The current data bounds (in the same projection
|
* as the layer - not always the same projection as the map).
|
*/
|
bounds: null,
|
|
/**
|
* Property: resolution
|
* {Float} The current data resolution.
|
*/
|
resolution: null,
|
|
/**
|
* APIProperty: ratio
|
* {Float} The ratio of the data bounds to the viewport bounds (in each
|
* dimension). Default is 2.
|
*/
|
ratio: 2,
|
|
/**
|
* Property: resFactor
|
* {Float} Optional factor used to determine when previously requested
|
* features are invalid. If set, the resFactor will be compared to the
|
* resolution of the previous request to the current map resolution.
|
* If resFactor > (old / new) and 1/resFactor < (old / new). If you
|
* set a resFactor of 1, data will be requested every time the
|
* resolution changes. If you set a resFactor of 3, data will be
|
* requested if the old resolution is 3 times the new, or if the new is
|
* 3 times the old. If the old bounds do not contain the new bounds
|
* new data will always be requested (with or without considering
|
* resFactor).
|
*/
|
resFactor: null,
|
|
/**
|
* Property: response
|
* {<OpenLayers.Protocol.Response>} The protocol response object returned
|
* by the layer protocol.
|
*/
|
response: null,
|
|
/**
|
* Constructor: OpenLayers.Strategy.BBOX
|
* Create a new BBOX strategy.
|
*
|
* Parameters:
|
* options - {Object} Optional object whose properties will be set on the
|
* instance.
|
*/
|
|
/**
|
* Method: activate
|
* Set up strategy with regard to reading new batches of remote data.
|
*
|
* Returns:
|
* {Boolean} The strategy was successfully activated.
|
*/
|
activate: function() {
|
var activated = OpenLayers.Strategy.prototype.activate.call(this);
|
if(activated) {
|
this.layer.events.on({
|
"moveend": this.update,
|
"refresh": this.update,
|
"visibilitychanged": this.update,
|
scope: this
|
});
|
this.update();
|
}
|
return activated;
|
},
|
|
/**
|
* Method: deactivate
|
* Tear down strategy with regard to reading new batches of remote data.
|
*
|
* Returns:
|
* {Boolean} The strategy was successfully deactivated.
|
*/
|
deactivate: function() {
|
var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
|
if(deactivated) {
|
this.layer.events.un({
|
"moveend": this.update,
|
"refresh": this.update,
|
"visibilitychanged": this.update,
|
scope: this
|
});
|
}
|
return deactivated;
|
},
|
|
/**
|
* Method: update
|
* Callback function called on "moveend" or "refresh" layer events.
|
*
|
* Parameters:
|
* options - {Object} Optional object whose properties will determine
|
* the behaviour of this Strategy
|
*
|
* Valid options include:
|
* force - {Boolean} if true, new data must be unconditionally read.
|
* noAbort - {Boolean} if true, do not abort previous requests.
|
*/
|
update: function(options) {
|
var mapBounds = this.getMapBounds();
|
if (mapBounds !== null && ((options && options.force) ||
|
(this.layer.visibility && this.layer.calculateInRange() && this.invalidBounds(mapBounds)))) {
|
this.calculateBounds(mapBounds);
|
this.resolution = this.layer.map.getResolution();
|
this.triggerRead(options);
|
}
|
},
|
|
/**
|
* Method: getMapBounds
|
* Get the map bounds expressed in the same projection as this layer.
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} Map bounds in the projection of the layer.
|
*/
|
getMapBounds: function() {
|
if (this.layer.map === null) {
|
return null;
|
}
|
var bounds = this.layer.map.getExtent();
|
if(bounds && !this.layer.projection.equals(
|
this.layer.map.getProjectionObject())) {
|
bounds = bounds.clone().transform(
|
this.layer.map.getProjectionObject(), this.layer.projection
|
);
|
}
|
return bounds;
|
},
|
|
/**
|
* Method: invalidBounds
|
* Determine whether the previously requested set of features is invalid.
|
* This occurs when the new map bounds do not contain the previously
|
* requested bounds. In addition, if <resFactor> is set, it will be
|
* considered.
|
*
|
* Parameters:
|
* mapBounds - {<OpenLayers.Bounds>} the current map extent, will be
|
* retrieved from the map object if not provided
|
*
|
* Returns:
|
* {Boolean}
|
*/
|
invalidBounds: function(mapBounds) {
|
if(!mapBounds) {
|
mapBounds = this.getMapBounds();
|
}
|
var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds);
|
if(!invalid && this.resFactor) {
|
var ratio = this.resolution / this.layer.map.getResolution();
|
invalid = (ratio >= this.resFactor || ratio <= (1 / this.resFactor));
|
}
|
return invalid;
|
},
|
|
/**
|
* Method: calculateBounds
|
*
|
* Parameters:
|
* mapBounds - {<OpenLayers.Bounds>} the current map extent, will be
|
* retrieved from the map object if not provided
|
*/
|
calculateBounds: function(mapBounds) {
|
if(!mapBounds) {
|
mapBounds = this.getMapBounds();
|
}
|
var center = mapBounds.getCenterLonLat();
|
var dataWidth = mapBounds.getWidth() * this.ratio;
|
var dataHeight = mapBounds.getHeight() * this.ratio;
|
this.bounds = new OpenLayers.Bounds(
|
center.lon - (dataWidth / 2),
|
center.lat - (dataHeight / 2),
|
center.lon + (dataWidth / 2),
|
center.lat + (dataHeight / 2)
|
);
|
},
|
|
/**
|
* Method: triggerRead
|
*
|
* Parameters:
|
* options - {Object} Additional options for the protocol's read method
|
* (optional)
|
*
|
* Returns:
|
* {<OpenLayers.Protocol.Response>} The protocol response object
|
* returned by the layer protocol.
|
*/
|
triggerRead: function(options) {
|
if (this.response && !(options && options.noAbort === true)) {
|
this.layer.protocol.abort(this.response);
|
this.layer.events.triggerEvent("loadend");
|
}
|
var evt = {filter: this.createFilter()};
|
this.layer.events.triggerEvent("loadstart", evt);
|
this.response = this.layer.protocol.read(
|
OpenLayers.Util.applyDefaults({
|
filter: evt.filter,
|
callback: this.merge,
|
scope: this
|
}, options));
|
},
|
|
/**
|
* Method: createFilter
|
* Creates a spatial BBOX filter. If the layer that this strategy belongs
|
* to has a filter property, this filter will be combined with the BBOX
|
* filter.
|
*
|
* Returns
|
* {<OpenLayers.Filter>} The filter object.
|
*/
|
createFilter: function() {
|
var filter = new OpenLayers.Filter.Spatial({
|
type: OpenLayers.Filter.Spatial.BBOX,
|
value: this.bounds,
|
projection: this.layer.projection
|
});
|
if (this.layer.filter) {
|
filter = new OpenLayers.Filter.Logical({
|
type: OpenLayers.Filter.Logical.AND,
|
filters: [this.layer.filter, filter]
|
});
|
}
|
return filter;
|
},
|
|
/**
|
* Method: merge
|
* Given a list of features, determine which ones to add to the layer.
|
* If the layer projection differs from the map projection, features
|
* will be transformed from the layer projection to the map projection.
|
*
|
* Parameters:
|
* resp - {<OpenLayers.Protocol.Response>} The response object passed
|
* by the protocol.
|
*/
|
merge: function(resp) {
|
this.layer.destroyFeatures();
|
if (resp.success()) {
|
var features = resp.features;
|
if(features && features.length > 0) {
|
var remote = this.layer.projection;
|
var local = this.layer.map.getProjectionObject();
|
if(!local.equals(remote)) {
|
var geom;
|
for(var i=0, len=features.length; i<len; ++i) {
|
geom = features[i].geometry;
|
if(geom) {
|
geom.transform(remote, local);
|
}
|
}
|
}
|
this.layer.addFeatures(features);
|
}
|
} else {
|
this.bounds = null;
|
}
|
this.response = null;
|
this.layer.events.triggerEvent("loadend", {response: resp});
|
},
|
|
CLASS_NAME: "OpenLayers.Strategy.BBOX"
|
});
|
/* ======================================================================
|
OpenLayers/Layer/WorldWind.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Layer/Grid.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.WorldWind
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer.Grid>
|
*/
|
OpenLayers.Layer.WorldWind = OpenLayers.Class(OpenLayers.Layer.Grid, {
|
|
DEFAULT_PARAMS: {
|
},
|
|
/**
|
* APIProperty: isBaseLayer
|
* {Boolean} WorldWind layer is a base layer by default.
|
*/
|
isBaseLayer: true,
|
|
/**
|
* APIProperty: lzd
|
* {Float} LevelZeroTileSizeDegrees
|
*/
|
lzd: null,
|
|
/**
|
* APIProperty: zoomLevels
|
* {Integer} Number of zoom levels.
|
*/
|
zoomLevels: null,
|
|
/**
|
* Constructor: OpenLayers.Layer.WorldWind
|
*
|
* Parameters:
|
* name - {String} Name of Layer
|
* url - {String} Base URL
|
* lzd - {Float} Level zero tile size degrees
|
* zoomLevels - {Integer} number of zoom levels
|
* params - {Object} additional parameters
|
* options - {Object} additional options
|
*/
|
initialize: function(name, url, lzd, zoomLevels, params, options) {
|
this.lzd = lzd;
|
this.zoomLevels = zoomLevels;
|
var newArguments = [];
|
newArguments.push(name, url, params, options);
|
OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
|
this.params = OpenLayers.Util.applyDefaults(
|
this.params, this.DEFAULT_PARAMS
|
);
|
},
|
|
/**
|
* Method: getZoom
|
* Convert map zoom to WW zoom.
|
*/
|
getZoom: function () {
|
var zoom = this.map.getZoom();
|
var extent = this.map.getMaxExtent();
|
zoom = zoom - Math.log(this.maxResolution / (this.lzd/512))/Math.log(2);
|
return zoom;
|
},
|
|
/**
|
* Method: getURL
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
*
|
* Returns:
|
* {String} A string with the layer's url and parameters and also the
|
* passed-in bounds and appropriate tile size specified as
|
* parameters
|
*/
|
getURL: function (bounds) {
|
bounds = this.adjustBounds(bounds);
|
var zoom = this.getZoom();
|
var extent = this.map.getMaxExtent();
|
var deg = this.lzd/Math.pow(2,this.getZoom());
|
var x = Math.floor((bounds.left - extent.left)/deg);
|
var y = Math.floor((bounds.bottom - extent.bottom)/deg);
|
if (this.map.getResolution() <= (this.lzd/512)
|
&& this.getZoom() <= this.zoomLevels) {
|
return this.getFullRequestString(
|
{ L: zoom,
|
X: x,
|
Y: y
|
});
|
} else {
|
return OpenLayers.Util.getImageLocation("blank.gif");
|
}
|
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.WorldWind"
|
});
|
/* ======================================================================
|
OpenLayers/Protocol/CSW.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Protocol.js
|
*/
|
|
/**
|
* Class: OpenLayers.Protocol.CSW
|
* Used to create a versioned CSW protocol. Default version is 2.0.2.
|
*/
|
OpenLayers.Protocol.CSW = function(options) {
|
options = OpenLayers.Util.applyDefaults(
|
options, OpenLayers.Protocol.CSW.DEFAULTS
|
);
|
var cls = OpenLayers.Protocol.CSW["v"+options.version.replace(/\./g, "_")];
|
if(!cls) {
|
throw "Unsupported CSW version: " + options.version;
|
}
|
return new cls(options);
|
};
|
|
/**
|
* Constant: OpenLayers.Protocol.CSW.DEFAULTS
|
*/
|
OpenLayers.Protocol.CSW.DEFAULTS = {
|
"version": "2.0.2"
|
};
|
/* ======================================================================
|
OpenLayers/Format/WMTSCapabilities.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML/VersionedOGC.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WMTSCapabilities
|
* Read WMTS Capabilities.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML.VersionedOGC>
|
*/
|
OpenLayers.Format.WMTSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
|
|
/**
|
* APIProperty: defaultVersion
|
* {String} Version number to assume if none found. Default is "1.0.0".
|
*/
|
defaultVersion: "1.0.0",
|
|
/**
|
* APIProperty: yx
|
* {Object} Members in the yx object are used to determine if a CRS URN
|
* corresponds to a CRS with y,x axis order. Member names are CRS URNs
|
* and values are boolean. By default, the following CRS URN are
|
* assumed to correspond to a CRS with y,x axis order:
|
*
|
* * urn:ogc:def:crs:EPSG::4326
|
*/
|
yx: {
|
"urn:ogc:def:crs:EPSG::4326": true
|
},
|
|
/**
|
* Constructor: OpenLayers.Format.WMTSCapabilities
|
* Create a new parser for WMTS capabilities.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* APIMethod: read
|
* Read capabilities data from a string, and return information about
|
* the service (offering and observedProperty mostly).
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
*
|
* Returns:
|
* {Object} Info about the WMTS Capabilities
|
*/
|
|
/**
|
* APIMethod: createLayer
|
* Create a WMTS layer given a capabilities object.
|
*
|
* Parameters:
|
* capabilities - {Object} The object returned from a <read> call to this
|
* format.
|
* config - {Object} Configuration properties for the layer. Defaults for
|
* the layer will apply if not provided.
|
*
|
* Required config properties:
|
* layer - {String} The layer identifier.
|
*
|
* Optional config properties:
|
* matrixSet - {String} The matrix set identifier, required if there is
|
* more than one matrix set in the layer capabilities.
|
* style - {String} The name of the style
|
* format - {String} Image format for the layer. Default is the first
|
* format returned in the GetCapabilities response.
|
* param - {Object} The dimensions values eg: {"Year": "2012"}
|
*
|
* Returns:
|
* {<OpenLayers.Layer.WMTS>} A properly configured WMTS layer. Throws an
|
* error if an incomplete config is provided. Returns undefined if no
|
* layer could be created with the provided config.
|
*/
|
createLayer: function(capabilities, config) {
|
var layer;
|
|
// confirm required properties are supplied in config
|
if (!('layer' in config)) {
|
throw new Error("Missing property 'layer' in configuration.");
|
}
|
|
var contents = capabilities.contents;
|
|
// find the layer definition with the given identifier
|
var layers = contents.layers;
|
var layerDef;
|
for (var i=0, ii=contents.layers.length; i<ii; ++i) {
|
if (contents.layers[i].identifier === config.layer) {
|
layerDef = contents.layers[i];
|
break;
|
}
|
}
|
if (!layerDef) {
|
throw new Error("Layer not found");
|
}
|
|
var format = config.format;
|
if (!format && layerDef.formats && layerDef.formats.length) {
|
format = layerDef.formats[0];
|
}
|
|
// find the matrixSet definition
|
var matrixSet;
|
if (config.matrixSet) {
|
matrixSet = contents.tileMatrixSets[config.matrixSet];
|
} else if (layerDef.tileMatrixSetLinks.length >= 1) {
|
matrixSet = contents.tileMatrixSets[
|
layerDef.tileMatrixSetLinks[0].tileMatrixSet];
|
}
|
if (!matrixSet) {
|
throw new Error("matrixSet not found");
|
}
|
|
// get the default style for the layer
|
var style;
|
for (var i=0, ii=layerDef.styles.length; i<ii; ++i) {
|
style = layerDef.styles[i];
|
if (style.isDefault) {
|
break;
|
}
|
}
|
|
var requestEncoding = config.requestEncoding;
|
if (!requestEncoding) {
|
requestEncoding = "KVP";
|
if (capabilities.operationsMetadata.GetTile.dcp.http) {
|
var http = capabilities.operationsMetadata.GetTile.dcp.http;
|
// Get first get method
|
if (http.get[0].constraints) {
|
var constraints = http.get[0].constraints;
|
var allowedValues = constraints.GetEncoding.allowedValues;
|
|
// The OGC documentation is not clear if we should use
|
// REST or RESTful, ArcGis use RESTful,
|
// and OpenLayers use REST.
|
if (!allowedValues.KVP &&
|
(allowedValues.REST || allowedValues.RESTful)) {
|
requestEncoding = "REST";
|
}
|
}
|
}
|
}
|
|
var dimensions = [];
|
var params = config.params || {};
|
// to don't overwrite the changes in the applyDefaults
|
delete config.params;
|
for (var id = 0, ld = layerDef.dimensions.length ; id < ld ; id++) {
|
var dimension = layerDef.dimensions[id];
|
dimensions.push(dimension.identifier);
|
if (!params.hasOwnProperty(dimension.identifier)) {
|
params[dimension.identifier] = dimension['default'];
|
}
|
}
|
|
var projection = config.projection || matrixSet.supportedCRS.replace(
|
/urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/, "$1:$3");
|
var units = config.units ||
|
(projection === "EPSG:4326" ? "degrees" : "m");
|
|
var resolutions = [];
|
for (var mid in matrixSet.matrixIds) {
|
if (matrixSet.matrixIds.hasOwnProperty(mid)) {
|
resolutions.push(
|
matrixSet.matrixIds[mid].scaleDenominator * 0.28E-3 /
|
OpenLayers.METERS_PER_INCH /
|
OpenLayers.INCHES_PER_UNIT[units]);
|
}
|
}
|
|
var url;
|
if (requestEncoding === "REST" && layerDef.resourceUrls) {
|
url = [];
|
var resourceUrls = layerDef.resourceUrls,
|
resourceUrl;
|
for (var t = 0, tt = layerDef.resourceUrls.length; t < tt; ++t) {
|
resourceUrl = layerDef.resourceUrls[t];
|
if (resourceUrl.format === format && resourceUrl.resourceType === "tile") {
|
url.push(resourceUrl.template);
|
}
|
}
|
}
|
else {
|
var httpGet = capabilities.operationsMetadata.GetTile.dcp.http.get;
|
url = [];
|
var constraint;
|
for (var i = 0, ii = httpGet.length; i < ii; i++) {
|
constraint = httpGet[i].constraints;
|
if (!constraint || (constraint && constraint.
|
GetEncoding.allowedValues[requestEncoding])) {
|
url.push(httpGet[i].url);
|
}
|
}
|
}
|
|
return new OpenLayers.Layer.WMTS(
|
OpenLayers.Util.applyDefaults(config, {
|
url: url,
|
requestEncoding: requestEncoding,
|
name: layerDef.title,
|
style: style.identifier,
|
format: format,
|
matrixIds: matrixSet.matrixIds,
|
matrixSet: matrixSet.identifier,
|
projection: projection,
|
units: units,
|
resolutions: config.isBaseLayer === false ? undefined :
|
resolutions,
|
serverResolutions: resolutions,
|
tileFullExtent: matrixSet.bounds,
|
dimensions: dimensions,
|
params: params
|
})
|
);
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WMTSCapabilities"
|
|
});
|
/* ======================================================================
|
OpenLayers/Layer/Google/v3.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Layer/Google.js
|
*/
|
|
/**
|
* Constant: OpenLayers.Layer.Google.v3
|
*
|
* Mixin providing functionality specific to the Google Maps API v3.
|
*
|
* To use this layer, you must include the GMaps v3 API in your html.
|
*
|
* Note that this layer configures the google.maps.map object with the
|
* "disableDefaultUI" option set to true. Using UI controls that the Google
|
* Maps API provides is not supported by the OpenLayers API.
|
*/
|
OpenLayers.Layer.Google.v3 = {
|
|
/**
|
* Constant: DEFAULTS
|
* {Object} It is not recommended to change the properties set here. Note
|
* that Google.v3 layers only work when sphericalMercator is set to true.
|
*
|
* (code)
|
* {
|
* sphericalMercator: true,
|
* projection: "EPSG:900913"
|
* }
|
* (end)
|
*/
|
DEFAULTS: {
|
sphericalMercator: true,
|
projection: "EPSG:900913"
|
},
|
|
/**
|
* APIProperty: animationEnabled
|
* {Boolean} If set to true, the transition between zoom levels will be
|
* animated (if supported by the GMaps API for the device used). Set to
|
* false to match the zooming experience of other layer types. Default
|
* is true. Note that the GMaps API does not give us control over zoom
|
* animation, so if set to false, when zooming, this will make the
|
* layer temporarily invisible, wait until GMaps reports the map being
|
* idle, and make it visible again. The result will be a blank layer
|
* for a few moments while zooming.
|
*/
|
animationEnabled: true,
|
|
/**
|
* Method: loadMapObject
|
* Load the GMap and register appropriate event listeners.
|
*/
|
loadMapObject: function() {
|
if (!this.type) {
|
this.type = google.maps.MapTypeId.ROADMAP;
|
}
|
var mapObject;
|
var cache = OpenLayers.Layer.Google.cache[this.map.id];
|
if (cache) {
|
// there are already Google layers added to this map
|
mapObject = cache.mapObject;
|
// increment the layer count
|
++cache.count;
|
} else {
|
// this is the first Google layer for this map
|
// create GMap
|
var center = this.map.getCenter();
|
var container = document.createElement('div');
|
container.className = "olForeignContainer";
|
container.style.width = '100%';
|
container.style.height = '100%';
|
mapObject = new google.maps.Map(container, {
|
center: center ?
|
new google.maps.LatLng(center.lat, center.lon) :
|
new google.maps.LatLng(0, 0),
|
zoom: this.map.getZoom() || 0,
|
mapTypeId: this.type,
|
disableDefaultUI: true,
|
keyboardShortcuts: false,
|
draggable: false,
|
disableDoubleClickZoom: true,
|
scrollwheel: false,
|
streetViewControl: false
|
});
|
var googleControl = document.createElement('div');
|
googleControl.style.width = '100%';
|
googleControl.style.height = '100%';
|
mapObject.controls[google.maps.ControlPosition.TOP_LEFT].push(googleControl);
|
|
// cache elements for use by any other google layers added to
|
// this same map
|
cache = {
|
googleControl: googleControl,
|
mapObject: mapObject,
|
count: 1
|
};
|
OpenLayers.Layer.Google.cache[this.map.id] = cache;
|
}
|
this.mapObject = mapObject;
|
this.setGMapVisibility(this.visibility);
|
},
|
|
/**
|
* APIMethod: onMapResize
|
*/
|
onMapResize: function() {
|
if (this.visibility) {
|
google.maps.event.trigger(this.mapObject, "resize");
|
}
|
},
|
|
/**
|
* Method: setGMapVisibility
|
* Display the GMap container and associated elements.
|
*
|
* Parameters:
|
* visible - {Boolean} Display the GMap elements.
|
*/
|
setGMapVisibility: function(visible) {
|
var cache = OpenLayers.Layer.Google.cache[this.map.id];
|
var map = this.map;
|
if (cache) {
|
var type = this.type;
|
var layers = map.layers;
|
var layer;
|
for (var i=layers.length-1; i>=0; --i) {
|
layer = layers[i];
|
if (layer instanceof OpenLayers.Layer.Google &&
|
layer.visibility === true && layer.inRange === true) {
|
type = layer.type;
|
visible = true;
|
break;
|
}
|
}
|
var container = this.mapObject.getDiv();
|
if (visible === true) {
|
if (container.parentNode !== map.div) {
|
if (!cache.rendered) {
|
var me = this;
|
google.maps.event.addListenerOnce(this.mapObject, 'tilesloaded', function() {
|
cache.rendered = true;
|
me.setGMapVisibility(me.getVisibility());
|
me.moveTo(me.map.getCenter());
|
});
|
} else {
|
map.div.appendChild(container);
|
cache.googleControl.appendChild(map.viewPortDiv);
|
google.maps.event.trigger(this.mapObject, 'resize');
|
}
|
}
|
this.mapObject.setMapTypeId(type);
|
} else if (cache.googleControl.hasChildNodes()) {
|
map.div.appendChild(map.viewPortDiv);
|
map.div.removeChild(container);
|
}
|
}
|
},
|
|
/**
|
* Method: getMapContainer
|
*
|
* Returns:
|
* {DOMElement} the GMap container's div
|
*/
|
getMapContainer: function() {
|
return this.mapObject.getDiv();
|
},
|
|
//
|
// TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds
|
//
|
|
/**
|
* APIMethod: getMapObjectBoundsFromOLBounds
|
*
|
* Parameters:
|
* olBounds - {<OpenLayers.Bounds>}
|
*
|
* Returns:
|
* {Object} A MapObject Bounds, translated from olBounds
|
* Returns null if null value is passed in
|
*/
|
getMapObjectBoundsFromOLBounds: function(olBounds) {
|
var moBounds = null;
|
if (olBounds != null) {
|
var sw = this.sphericalMercator ?
|
this.inverseMercator(olBounds.bottom, olBounds.left) :
|
new OpenLayers.LonLat(olBounds.bottom, olBounds.left);
|
var ne = this.sphericalMercator ?
|
this.inverseMercator(olBounds.top, olBounds.right) :
|
new OpenLayers.LonLat(olBounds.top, olBounds.right);
|
moBounds = new google.maps.LatLngBounds(
|
new google.maps.LatLng(sw.lat, sw.lon),
|
new google.maps.LatLng(ne.lat, ne.lon)
|
);
|
}
|
return moBounds;
|
},
|
|
|
/************************************
|
* *
|
* MapObject Interface Controls *
|
* *
|
************************************/
|
|
|
// LonLat - Pixel Translation
|
|
/**
|
* APIMethod: getMapObjectLonLatFromMapObjectPixel
|
*
|
* Parameters:
|
* moPixel - {Object} MapObject Pixel format
|
*
|
* Returns:
|
* {Object} MapObject LonLat translated from MapObject Pixel
|
*/
|
getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
|
var size = this.map.getSize();
|
var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center);
|
var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center);
|
var res = this.map.getResolution();
|
|
var delta_x = moPixel.x - (size.w / 2);
|
var delta_y = moPixel.y - (size.h / 2);
|
|
var lonlat = new OpenLayers.LonLat(
|
lon + delta_x * res,
|
lat - delta_y * res
|
);
|
|
if (this.wrapDateLine) {
|
lonlat = lonlat.wrapDateLine(this.maxExtent);
|
}
|
return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat);
|
},
|
|
/**
|
* APIMethod: getMapObjectPixelFromMapObjectLonLat
|
*
|
* Parameters:
|
* moLonLat - {Object} MapObject LonLat format
|
*
|
* Returns:
|
* {Object} MapObject Pixel transtlated from MapObject LonLat
|
*/
|
getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
|
var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);
|
var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);
|
var res = this.map.getResolution();
|
var extent = this.map.getExtent();
|
return this.getMapObjectPixelFromXY((1/res * (lon - extent.left)),
|
(1/res * (extent.top - lat)));
|
},
|
|
|
/**
|
* APIMethod: setMapObjectCenter
|
* Set the mapObject to the specified center and zoom
|
*
|
* Parameters:
|
* center - {Object} MapObject LonLat format
|
* zoom - {int} MapObject zoom format
|
*/
|
setMapObjectCenter: function(center, zoom) {
|
if (this.animationEnabled === false && zoom != this.mapObject.zoom) {
|
var mapContainer = this.getMapContainer();
|
google.maps.event.addListenerOnce(
|
this.mapObject,
|
"idle",
|
function() {
|
mapContainer.style.visibility = "";
|
}
|
);
|
mapContainer.style.visibility = "hidden";
|
}
|
this.mapObject.setOptions({
|
center: center,
|
zoom: zoom
|
});
|
},
|
|
|
// Bounds
|
|
/**
|
* APIMethod: getMapObjectZoomFromMapObjectBounds
|
*
|
* Parameters:
|
* moBounds - {Object} MapObject Bounds format
|
*
|
* Returns:
|
* {Object} MapObject Zoom for specified MapObject Bounds
|
*/
|
getMapObjectZoomFromMapObjectBounds: function(moBounds) {
|
return this.mapObject.getBoundsZoomLevel(moBounds);
|
},
|
|
/************************************
|
* *
|
* MapObject Primitives *
|
* *
|
************************************/
|
|
|
// LonLat
|
|
/**
|
* APIMethod: getMapObjectLonLatFromLonLat
|
*
|
* Parameters:
|
* lon - {Float}
|
* lat - {Float}
|
*
|
* Returns:
|
* {Object} MapObject LonLat built from lon and lat params
|
*/
|
getMapObjectLonLatFromLonLat: function(lon, lat) {
|
var gLatLng;
|
if(this.sphericalMercator) {
|
var lonlat = this.inverseMercator(lon, lat);
|
gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon);
|
} else {
|
gLatLng = new google.maps.LatLng(lat, lon);
|
}
|
return gLatLng;
|
},
|
|
// Pixel
|
|
/**
|
* APIMethod: getMapObjectPixelFromXY
|
*
|
* Parameters:
|
* x - {Integer}
|
* y - {Integer}
|
*
|
* Returns:
|
* {Object} MapObject Pixel from x and y parameters
|
*/
|
getMapObjectPixelFromXY: function(x, y) {
|
return new google.maps.Point(x, y);
|
}
|
|
};
|
/* ======================================================================
|
OpenLayers/Format/WPSDescribeProcess.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML.js
|
* @requires OpenLayers/Format/OWSCommon/v1_1_0.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WPSDescribeProcess
|
* Read WPS DescribeProcess responses.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.WPSDescribeProcess = OpenLayers.Class(
|
OpenLayers.Format.XML, {
|
|
/**
|
* Constant: VERSION
|
* {String} 1.0.0
|
*/
|
VERSION: "1.0.0",
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
wps: "http://www.opengis.net/wps/1.0.0",
|
ows: "http://www.opengis.net/ows/1.1",
|
xsi: "http://www.w3.org/2001/XMLSchema-instance"
|
},
|
|
/**
|
* Property: schemaLocation
|
* {String} Schema location
|
*/
|
schemaLocation: "http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd",
|
|
/**
|
* Property: defaultPrefix
|
*/
|
defaultPrefix: "wps",
|
|
/**
|
* Property: regExes
|
* Compiled regular expressions for manipulating strings.
|
*/
|
regExes: {
|
trimSpace: (/^\s*|\s*$/g),
|
removeSpace: (/\s*/g),
|
splitSpace: (/\s+/),
|
trimComma: (/\s*,\s*/g)
|
},
|
|
/**
|
* Constructor: OpenLayers.Format.WPSDescribeProcess
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* APIMethod: read
|
* Parse a WPS DescribeProcess and return an object with its information.
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
*
|
* Returns:
|
* {Object}
|
*/
|
read: function(data) {
|
if(typeof data == "string") {
|
data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
|
}
|
if(data && data.nodeType == 9) {
|
data = data.documentElement;
|
}
|
var info = {};
|
this.readNode(data, info);
|
return info;
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"wps": {
|
"ProcessDescriptions": function(node, obj) {
|
obj.processDescriptions = {};
|
this.readChildNodes(node, obj.processDescriptions);
|
},
|
"ProcessDescription": function(node, processDescriptions) {
|
var processVersion = this.getAttributeNS(node, this.namespaces.wps, "processVersion");
|
var processDescription = {
|
processVersion: processVersion,
|
statusSupported: (node.getAttribute("statusSupported") === "true"),
|
storeSupported: (node.getAttribute("storeSupported") === "true")
|
};
|
this.readChildNodes(node, processDescription);
|
processDescriptions[processDescription.identifier] = processDescription;
|
},
|
"DataInputs": function(node, processDescription) {
|
processDescription.dataInputs = [];
|
this.readChildNodes(node, processDescription.dataInputs);
|
},
|
"ProcessOutputs": function(node, processDescription) {
|
processDescription.processOutputs = [];
|
this.readChildNodes(node, processDescription.processOutputs);
|
},
|
"Output": function(node, processOutputs) {
|
var output = {};
|
this.readChildNodes(node, output);
|
processOutputs.push(output);
|
},
|
"ComplexOutput": function(node, output) {
|
output.complexOutput = {};
|
this.readChildNodes(node, output.complexOutput);
|
},
|
"LiteralOutput": function(node, output) {
|
output.literalOutput = {};
|
this.readChildNodes(node, output.literalOutput);
|
},
|
"Input": function(node, dataInputs) {
|
var input = {
|
maxOccurs: parseInt(node.getAttribute("maxOccurs")),
|
minOccurs: parseInt(node.getAttribute("minOccurs"))
|
};
|
this.readChildNodes(node, input);
|
dataInputs.push(input);
|
},
|
"BoundingBoxData": function(node, input) {
|
input.boundingBoxData = {};
|
this.readChildNodes(node, input.boundingBoxData);
|
},
|
"CRS": function(node, obj) {
|
if (!obj.CRSs) {
|
obj.CRSs = {};
|
}
|
obj.CRSs[this.getChildValue(node)] = true;
|
},
|
"LiteralData": function(node, input) {
|
input.literalData = {};
|
this.readChildNodes(node, input.literalData);
|
},
|
"ComplexData": function(node, input) {
|
input.complexData = {};
|
this.readChildNodes(node, input.complexData);
|
},
|
"Default": function(node, complexData) {
|
complexData["default"] = {};
|
this.readChildNodes(node, complexData["default"]);
|
},
|
"Supported": function(node, complexData) {
|
complexData["supported"] = {};
|
this.readChildNodes(node, complexData["supported"]);
|
},
|
"Format": function(node, obj) {
|
var format = {};
|
this.readChildNodes(node, format);
|
if (!obj.formats) {
|
obj.formats = {};
|
}
|
obj.formats[format.mimeType] = true;
|
},
|
"MimeType": function(node, format) {
|
format.mimeType = this.getChildValue(node);
|
}
|
},
|
"ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"]
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WPSDescribeProcess"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/WKT.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format.js
|
* @requires OpenLayers/Feature/Vector.js
|
* @requires OpenLayers/Geometry/Point.js
|
* @requires OpenLayers/Geometry/MultiPoint.js
|
* @requires OpenLayers/Geometry/LineString.js
|
* @requires OpenLayers/Geometry/MultiLineString.js
|
* @requires OpenLayers/Geometry/Polygon.js
|
* @requires OpenLayers/Geometry/MultiPolygon.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WKT
|
* Class for reading and writing Well-Known Text. Create a new instance
|
* with the <OpenLayers.Format.WKT> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format>
|
*/
|
OpenLayers.Format.WKT = OpenLayers.Class(OpenLayers.Format, {
|
|
/**
|
* Constructor: OpenLayers.Format.WKT
|
* Create a new parser for WKT
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance
|
*
|
* Returns:
|
* {<OpenLayers.Format.WKT>} A new WKT parser.
|
*/
|
initialize: function(options) {
|
this.regExes = {
|
'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/,
|
'spaces': /\s+/,
|
'parenComma': /\)\s*,\s*\(/,
|
'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/, // can't use {2} here
|
'trimParens': /^\s*\(?(.*?)\)?\s*$/
|
};
|
OpenLayers.Format.prototype.initialize.apply(this, [options]);
|
},
|
|
/**
|
* APIMethod: read
|
* Deserialize a WKT string and return a vector feature or an
|
* array of vector features. Supports WKT for POINT, MULTIPOINT,
|
* LINESTRING, MULTILINESTRING, POLYGON, MULTIPOLYGON, and
|
* GEOMETRYCOLLECTION.
|
*
|
* Parameters:
|
* wkt - {String} A WKT string
|
*
|
* Returns:
|
* {<OpenLayers.Feature.Vector>|Array} A feature or array of features for
|
* GEOMETRYCOLLECTION WKT.
|
*/
|
read: function(wkt) {
|
var features, type, str;
|
wkt = wkt.replace(/[\n\r]/g, " ");
|
var matches = this.regExes.typeStr.exec(wkt);
|
if(matches) {
|
type = matches[1].toLowerCase();
|
str = matches[2];
|
if(this.parse[type]) {
|
features = this.parse[type].apply(this, [str]);
|
}
|
if (this.internalProjection && this.externalProjection) {
|
if (features &&
|
features.CLASS_NAME == "OpenLayers.Feature.Vector") {
|
features.geometry.transform(this.externalProjection,
|
this.internalProjection);
|
} else if (features &&
|
type != "geometrycollection" &&
|
typeof features == "object") {
|
for (var i=0, len=features.length; i<len; i++) {
|
var component = features[i];
|
component.geometry.transform(this.externalProjection,
|
this.internalProjection);
|
}
|
}
|
}
|
}
|
return features;
|
},
|
|
/**
|
* APIMethod: write
|
* Serialize a feature or array of features into a WKT string.
|
*
|
* Parameters:
|
* features - {<OpenLayers.Feature.Vector>|Array} A feature or array of
|
* features
|
*
|
* Returns:
|
* {String} The WKT string representation of the input geometries
|
*/
|
write: function(features) {
|
var collection, geometry, isCollection;
|
if (features.constructor == Array) {
|
collection = features;
|
isCollection = true;
|
} else {
|
collection = [features];
|
isCollection = false;
|
}
|
var pieces = [];
|
if (isCollection) {
|
pieces.push('GEOMETRYCOLLECTION(');
|
}
|
for (var i=0, len=collection.length; i<len; ++i) {
|
if (isCollection && i>0) {
|
pieces.push(',');
|
}
|
geometry = collection[i].geometry;
|
pieces.push(this.extractGeometry(geometry));
|
}
|
if (isCollection) {
|
pieces.push(')');
|
}
|
return pieces.join('');
|
},
|
|
/**
|
* Method: extractGeometry
|
* Entry point to construct the WKT for a single Geometry object.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry.Geometry>}
|
*
|
* Returns:
|
* {String} A WKT string of representing the geometry
|
*/
|
extractGeometry: function(geometry) {
|
var type = geometry.CLASS_NAME.split('.')[2].toLowerCase();
|
if (!this.extract[type]) {
|
return null;
|
}
|
if (this.internalProjection && this.externalProjection) {
|
geometry = geometry.clone();
|
geometry.transform(this.internalProjection, this.externalProjection);
|
}
|
var wktType = type == 'collection' ? 'GEOMETRYCOLLECTION' : type.toUpperCase();
|
var data = wktType + '(' + this.extract[type].apply(this, [geometry]) + ')';
|
return data;
|
},
|
|
/**
|
* Object with properties corresponding to the geometry types.
|
* Property values are functions that do the actual data extraction.
|
*/
|
extract: {
|
/**
|
* Return a space delimited string of point coordinates.
|
* @param {OpenLayers.Geometry.Point} point
|
* @returns {String} A string of coordinates representing the point
|
*/
|
'point': function(point) {
|
return point.x + ' ' + point.y;
|
},
|
|
/**
|
* Return a comma delimited string of point coordinates from a multipoint.
|
* @param {OpenLayers.Geometry.MultiPoint} multipoint
|
* @returns {String} A string of point coordinate strings representing
|
* the multipoint
|
*/
|
'multipoint': function(multipoint) {
|
var array = [];
|
for(var i=0, len=multipoint.components.length; i<len; ++i) {
|
array.push('(' +
|
this.extract.point.apply(this, [multipoint.components[i]]) +
|
')');
|
}
|
return array.join(',');
|
},
|
|
/**
|
* Return a comma delimited string of point coordinates from a line.
|
* @param {OpenLayers.Geometry.LineString} linestring
|
* @returns {String} A string of point coordinate strings representing
|
* the linestring
|
*/
|
'linestring': function(linestring) {
|
var array = [];
|
for(var i=0, len=linestring.components.length; i<len; ++i) {
|
array.push(this.extract.point.apply(this, [linestring.components[i]]));
|
}
|
return array.join(',');
|
},
|
|
/**
|
* Return a comma delimited string of linestring strings from a multilinestring.
|
* @param {OpenLayers.Geometry.MultiLineString} multilinestring
|
* @returns {String} A string of of linestring strings representing
|
* the multilinestring
|
*/
|
'multilinestring': function(multilinestring) {
|
var array = [];
|
for(var i=0, len=multilinestring.components.length; i<len; ++i) {
|
array.push('(' +
|
this.extract.linestring.apply(this, [multilinestring.components[i]]) +
|
')');
|
}
|
return array.join(',');
|
},
|
|
/**
|
* Return a comma delimited string of linear ring arrays from a polygon.
|
* @param {OpenLayers.Geometry.Polygon} polygon
|
* @returns {String} An array of linear ring arrays representing the polygon
|
*/
|
'polygon': function(polygon) {
|
var array = [];
|
for(var i=0, len=polygon.components.length; i<len; ++i) {
|
array.push('(' +
|
this.extract.linestring.apply(this, [polygon.components[i]]) +
|
')');
|
}
|
return array.join(',');
|
},
|
|
/**
|
* Return an array of polygon arrays from a multipolygon.
|
* @param {OpenLayers.Geometry.MultiPolygon} multipolygon
|
* @returns {String} An array of polygon arrays representing
|
* the multipolygon
|
*/
|
'multipolygon': function(multipolygon) {
|
var array = [];
|
for(var i=0, len=multipolygon.components.length; i<len; ++i) {
|
array.push('(' +
|
this.extract.polygon.apply(this, [multipolygon.components[i]]) +
|
')');
|
}
|
return array.join(',');
|
},
|
|
/**
|
* Return the WKT portion between 'GEOMETRYCOLLECTION(' and ')' for an <OpenLayers.Geometry.Collection>
|
* @param {OpenLayers.Geometry.Collection} collection
|
* @returns {String} internal WKT representation of the collection
|
*/
|
'collection': function(collection) {
|
var array = [];
|
for(var i=0, len=collection.components.length; i<len; ++i) {
|
array.push(this.extractGeometry.apply(this, [collection.components[i]]));
|
}
|
return array.join(',');
|
}
|
|
},
|
|
/**
|
* Object with properties corresponding to the geometry types.
|
* Property values are functions that do the actual parsing.
|
*/
|
parse: {
|
/**
|
* Return point feature given a point WKT fragment.
|
* @param {String} str A WKT fragment representing the point
|
* @returns {OpenLayers.Feature.Vector} A point feature
|
* @private
|
*/
|
'point': function(str) {
|
var coords = OpenLayers.String.trim(str).split(this.regExes.spaces);
|
return new OpenLayers.Feature.Vector(
|
new OpenLayers.Geometry.Point(coords[0], coords[1])
|
);
|
},
|
|
/**
|
* Return a multipoint feature given a multipoint WKT fragment.
|
* @param {String} str A WKT fragment representing the multipoint
|
* @returns {OpenLayers.Feature.Vector} A multipoint feature
|
* @private
|
*/
|
'multipoint': function(str) {
|
var point;
|
var points = OpenLayers.String.trim(str).split(',');
|
var components = [];
|
for(var i=0, len=points.length; i<len; ++i) {
|
point = points[i].replace(this.regExes.trimParens, '$1');
|
components.push(this.parse.point.apply(this, [point]).geometry);
|
}
|
return new OpenLayers.Feature.Vector(
|
new OpenLayers.Geometry.MultiPoint(components)
|
);
|
},
|
|
/**
|
* Return a linestring feature given a linestring WKT fragment.
|
* @param {String} str A WKT fragment representing the linestring
|
* @returns {OpenLayers.Feature.Vector} A linestring feature
|
* @private
|
*/
|
'linestring': function(str) {
|
var points = OpenLayers.String.trim(str).split(',');
|
var components = [];
|
for(var i=0, len=points.length; i<len; ++i) {
|
components.push(this.parse.point.apply(this, [points[i]]).geometry);
|
}
|
return new OpenLayers.Feature.Vector(
|
new OpenLayers.Geometry.LineString(components)
|
);
|
},
|
|
/**
|
* Return a multilinestring feature given a multilinestring WKT fragment.
|
* @param {String} str A WKT fragment representing the multilinestring
|
* @returns {OpenLayers.Feature.Vector} A multilinestring feature
|
* @private
|
*/
|
'multilinestring': function(str) {
|
var line;
|
var lines = OpenLayers.String.trim(str).split(this.regExes.parenComma);
|
var components = [];
|
for(var i=0, len=lines.length; i<len; ++i) {
|
line = lines[i].replace(this.regExes.trimParens, '$1');
|
components.push(this.parse.linestring.apply(this, [line]).geometry);
|
}
|
return new OpenLayers.Feature.Vector(
|
new OpenLayers.Geometry.MultiLineString(components)
|
);
|
},
|
|
/**
|
* Return a polygon feature given a polygon WKT fragment.
|
* @param {String} str A WKT fragment representing the polygon
|
* @returns {OpenLayers.Feature.Vector} A polygon feature
|
* @private
|
*/
|
'polygon': function(str) {
|
var ring, linestring, linearring;
|
var rings = OpenLayers.String.trim(str).split(this.regExes.parenComma);
|
var components = [];
|
for(var i=0, len=rings.length; i<len; ++i) {
|
ring = rings[i].replace(this.regExes.trimParens, '$1');
|
linestring = this.parse.linestring.apply(this, [ring]).geometry;
|
linearring = new OpenLayers.Geometry.LinearRing(linestring.components);
|
components.push(linearring);
|
}
|
return new OpenLayers.Feature.Vector(
|
new OpenLayers.Geometry.Polygon(components)
|
);
|
},
|
|
/**
|
* Return a multipolygon feature given a multipolygon WKT fragment.
|
* @param {String} str A WKT fragment representing the multipolygon
|
* @returns {OpenLayers.Feature.Vector} A multipolygon feature
|
* @private
|
*/
|
'multipolygon': function(str) {
|
var polygon;
|
var polygons = OpenLayers.String.trim(str).split(this.regExes.doubleParenComma);
|
var components = [];
|
for(var i=0, len=polygons.length; i<len; ++i) {
|
polygon = polygons[i].replace(this.regExes.trimParens, '$1');
|
components.push(this.parse.polygon.apply(this, [polygon]).geometry);
|
}
|
return new OpenLayers.Feature.Vector(
|
new OpenLayers.Geometry.MultiPolygon(components)
|
);
|
},
|
|
/**
|
* Return an array of features given a geometrycollection WKT fragment.
|
* @param {String} str A WKT fragment representing the geometrycollection
|
* @returns {Array} An array of OpenLayers.Feature.Vector
|
* @private
|
*/
|
'geometrycollection': function(str) {
|
// separate components of the collection with |
|
str = str.replace(/,\s*([A-Za-z])/g, '|$1');
|
var wktArray = OpenLayers.String.trim(str).split('|');
|
var components = [];
|
for(var i=0, len=wktArray.length; i<len; ++i) {
|
components.push(OpenLayers.Format.WKT.prototype.read.apply(this,[wktArray[i]]));
|
}
|
return components;
|
}
|
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WKT"
|
});
|
/* ======================================================================
|
OpenLayers/WPSProcess.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/SingleFile.js
|
*/
|
|
/**
|
* @requires OpenLayers/Geometry.js
|
* @requires OpenLayers/Feature/Vector.js
|
* @requires OpenLayers/Format/WKT.js
|
* @requires OpenLayers/Format/GeoJSON.js
|
* @requires OpenLayers/Format/WPSExecute.js
|
* @requires OpenLayers/Request.js
|
*/
|
|
/**
|
* Class: OpenLayers.WPSProcess
|
* Representation of a WPS process. Usually instances of
|
* <OpenLayers.WPSProcess> are created by calling 'getProcess' on an
|
* <OpenLayers.WPSClient> instance.
|
*
|
* Currently <OpenLayers.WPSProcess> supports processes that have geometries
|
* or features as output, using WKT or GeoJSON as output format. It also
|
* supports chaining of processes by using the <output> method to create a
|
* handle that is used as process input instead of a static value.
|
*/
|
OpenLayers.WPSProcess = OpenLayers.Class({
|
|
/**
|
* Property: client
|
* {<OpenLayers.WPSClient>} The client that manages this process.
|
*/
|
client: null,
|
|
/**
|
* Property: server
|
* {String} Local client identifier for this process's server.
|
*/
|
server: null,
|
|
/**
|
* Property: identifier
|
* {String} Process identifier known to the server.
|
*/
|
identifier: null,
|
|
/**
|
* Property: description
|
* {Object} DescribeProcess response for this process.
|
*/
|
description: null,
|
|
/**
|
* APIProperty: localWPS
|
* {String} Service endpoint for locally chained WPS processes. Default is
|
* 'http://geoserver/wps'.
|
*/
|
localWPS: 'http://geoserver/wps',
|
|
/**
|
* Property: formats
|
* {Object} OpenLayers.Format instances keyed by mimetype.
|
*/
|
formats: null,
|
|
/**
|
* Property: chained
|
* {Integer} Number of chained processes for pending execute requests that
|
* don't have a full configuration yet.
|
*/
|
chained: 0,
|
|
/**
|
* Property: executeCallbacks
|
* {Array} Callbacks waiting to be executed until all chained processes
|
* are configured;
|
*/
|
executeCallbacks: null,
|
|
/**
|
* Constructor: OpenLayers.WPSProcess
|
*
|
* Parameters:
|
* options - {Object} Object whose properties will be set on the instance.
|
*
|
* Avaliable options:
|
* client - {<OpenLayers.WPSClient>} Mandatory. Client that manages this
|
* process.
|
* server - {String} Mandatory. Local client identifier of this process's
|
* server.
|
* identifier - {String} Mandatory. Process identifier known to the server.
|
*/
|
initialize: function(options) {
|
OpenLayers.Util.extend(this, options);
|
this.executeCallbacks = [];
|
this.formats = {
|
'application/wkt': new OpenLayers.Format.WKT(),
|
'application/json': new OpenLayers.Format.GeoJSON()
|
};
|
},
|
|
/**
|
* Method: describe
|
* Makes the client issue a DescribeProcess request asynchronously.
|
*
|
* Parameters:
|
* options - {Object} Configuration for the method call
|
*
|
* Available options:
|
* callback - {Function} Callback to execute when the description is
|
* available. Will be called with the parsed description as argument.
|
* Optional.
|
* scope - {Object} The scope in which the callback will be executed.
|
* Default is the global object.
|
*/
|
describe: function(options) {
|
options = options || {};
|
if (!this.description) {
|
this.client.describeProcess(this.server, this.identifier, function(description) {
|
if (!this.description) {
|
this.parseDescription(description);
|
}
|
if (options.callback) {
|
options.callback.call(options.scope, this.description);
|
}
|
}, this);
|
} else if (options.callback) {
|
var description = this.description;
|
window.setTimeout(function() {
|
options.callback.call(options.scope, description);
|
}, 0);
|
}
|
},
|
|
/**
|
* APIMethod: configure
|
* Configure the process, but do not execute it. Use this for processes
|
* that are chained as input of a different process by means of the
|
* <output> method.
|
*
|
* Parameters:
|
* options - {Object}
|
*
|
* Returns:
|
* {<OpenLayers.WPSProcess>} this process.
|
*
|
* Available options:
|
* inputs - {Object} The inputs for the process, keyed by input identifier.
|
* For spatial data inputs, the value of an input is usually an
|
* <OpenLayers.Geometry>, an <OpenLayers.Feature.Vector> or an array of
|
* geometries or features.
|
* callback - {Function} Callback to call when the configuration is
|
* complete. Optional.
|
* scope - {Object} Optional scope for the callback.
|
*/
|
configure: function(options) {
|
this.describe({
|
callback: function() {
|
var description = this.description,
|
inputs = options.inputs,
|
input, i, ii;
|
for (i=0, ii=description.dataInputs.length; i<ii; ++i) {
|
input = description.dataInputs[i];
|
this.setInputData(input, inputs[input.identifier]);
|
}
|
if (options.callback) {
|
options.callback.call(options.scope);
|
}
|
},
|
scope: this
|
});
|
return this;
|
},
|
|
/**
|
* APIMethod: execute
|
* Configures and executes the process
|
*
|
* Parameters:
|
* options - {Object}
|
*
|
* Available options:
|
* inputs - {Object} The inputs for the process, keyed by input identifier.
|
* For spatial data inputs, the value of an input is usually an
|
* <OpenLayers.Geometry>, an <OpenLayers.Feature.Vector> or an array of
|
* geometries or features.
|
* output - {String} The identifier of the output to request and parse.
|
* Optional. If not provided, the first output will be requested.
|
* success - {Function} Callback to call when the process is complete.
|
* This function is called with an outputs object as argument, which
|
* will have a property with the identifier of the requested output
|
* (or 'result' if output was not configured). For processes that
|
* generate spatial output, the value will be an array of
|
* <OpenLayers.Feature.Vector> instances.
|
* scope - {Object} Optional scope for the success callback.
|
*/
|
execute: function(options) {
|
this.configure({
|
inputs: options.inputs,
|
callback: function() {
|
var me = this;
|
//TODO For now we only deal with a single output
|
var outputIndex = this.getOutputIndex(
|
me.description.processOutputs, options.output
|
);
|
me.setResponseForm({outputIndex: outputIndex});
|
(function callback() {
|
OpenLayers.Util.removeItem(me.executeCallbacks, callback);
|
if (me.chained !== 0) {
|
// need to wait until chained processes have a
|
// description and configuration - see chainProcess
|
me.executeCallbacks.push(callback);
|
return;
|
}
|
// all chained processes are added as references now, so
|
// let's proceed.
|
OpenLayers.Request.POST({
|
url: me.client.servers[me.server].url,
|
data: new OpenLayers.Format.WPSExecute().write(me.description),
|
success: function(response) {
|
var output = me.description.processOutputs[outputIndex];
|
var mimeType = me.findMimeType(
|
output.complexOutput.supported.formats
|
);
|
//TODO For now we assume a spatial output
|
var features = me.formats[mimeType].read(response.responseText);
|
if (features instanceof OpenLayers.Feature.Vector) {
|
features = [features];
|
}
|
if (options.success) {
|
var outputs = {};
|
outputs[options.output || 'result'] = features;
|
options.success.call(options.scope, outputs);
|
}
|
},
|
scope: me
|
});
|
})();
|
},
|
scope: this
|
});
|
},
|
|
/**
|
* APIMethod: output
|
* Chain an output of a configured process (see <configure>) as input to
|
* another process.
|
*
|
* (code)
|
* intersect = client.getProcess('opengeo', 'JTS:intersection');
|
* intersect.configure({
|
* // ...
|
* });
|
* buffer = client.getProcess('opengeo', 'JTS:buffer');
|
* buffer.execute({
|
* inputs: {
|
* geom: intersect.output('result'), // <-- here we're chaining
|
* distance: 1
|
* },
|
* // ...
|
* });
|
* (end)
|
*
|
* Parameters:
|
* identifier - {String} Identifier of the output that we're chaining. If
|
* not provided, the first output will be used.
|
*/
|
output: function(identifier) {
|
return new OpenLayers.WPSProcess.ChainLink({
|
process: this,
|
output: identifier
|
});
|
},
|
|
/**
|
* Method: parseDescription
|
* Parses the DescribeProcess response
|
*
|
* Parameters:
|
* description - {Object}
|
*/
|
parseDescription: function(description) {
|
var server = this.client.servers[this.server];
|
this.description = new OpenLayers.Format.WPSDescribeProcess()
|
.read(server.processDescription[this.identifier])
|
.processDescriptions[this.identifier];
|
},
|
|
/**
|
* Method: setInputData
|
* Sets the data for a single input
|
*
|
* Parameters:
|
* input - {Object} An entry from the dataInputs array of the process
|
* description.
|
* data - {Mixed} For spatial data inputs, this is usually an
|
* <OpenLayers.Geometry>, an <OpenLayers.Feature.Vector> or an array of
|
* geometries or features.
|
*/
|
setInputData: function(input, data) {
|
// clear any previous data
|
delete input.data;
|
delete input.reference;
|
if (data instanceof OpenLayers.WPSProcess.ChainLink) {
|
++this.chained;
|
input.reference = {
|
method: 'POST',
|
href: data.process.server === this.server ?
|
this.localWPS : this.client.servers[data.process.server].url
|
};
|
data.process.describe({
|
callback: function() {
|
--this.chained;
|
this.chainProcess(input, data);
|
},
|
scope: this
|
});
|
} else {
|
input.data = {};
|
var complexData = input.complexData;
|
if (complexData) {
|
var format = this.findMimeType(complexData.supported.formats);
|
input.data.complexData = {
|
mimeType: format,
|
value: this.formats[format].write(this.toFeatures(data))
|
};
|
} else {
|
input.data.literalData = {
|
value: data
|
};
|
}
|
}
|
},
|
|
/**
|
* Method: setResponseForm
|
* Sets the responseForm property of the <execute> payload.
|
*
|
* Parameters:
|
* options - {Object} See below.
|
*
|
* Available options:
|
* outputIndex - {Integer} The index of the output to use. Optional.
|
* supportedFormats - {Object} Object with supported mime types as key,
|
* and true as value for supported types. Optional.
|
*/
|
setResponseForm: function(options) {
|
options = options || {};
|
var output = this.description.processOutputs[options.outputIndex || 0];
|
this.description.responseForm = {
|
rawDataOutput: {
|
identifier: output.identifier,
|
mimeType: this.findMimeType(output.complexOutput.supported.formats, options.supportedFormats)
|
}
|
};
|
},
|
|
/**
|
* Method: getOutputIndex
|
* Gets the index of a processOutput by its identifier
|
*
|
* Parameters:
|
* outputs - {Array} The processOutputs array to look at
|
* identifier - {String} The identifier of the output
|
*
|
* Returns
|
* {Integer} The index of the processOutput with the provided identifier
|
* in the outputs array.
|
*/
|
getOutputIndex: function(outputs, identifier) {
|
var output;
|
if (identifier) {
|
for (var i=outputs.length-1; i>=0; --i) {
|
if (outputs[i].identifier === identifier) {
|
output = i;
|
break;
|
}
|
}
|
} else {
|
output = 0;
|
}
|
return output;
|
},
|
|
/**
|
* Method: chainProcess
|
* Sets a fully configured chained process as input for this process.
|
*
|
* Parameters:
|
* input - {Object} The dataInput that the chained process provides.
|
* chainLink - {<OpenLayers.WPSProcess.ChainLink>} The process to chain.
|
*/
|
chainProcess: function(input, chainLink) {
|
var output = this.getOutputIndex(
|
chainLink.process.description.processOutputs, chainLink.output
|
);
|
input.reference.mimeType = this.findMimeType(
|
input.complexData.supported.formats,
|
chainLink.process.description.processOutputs[output].complexOutput.supported.formats
|
);
|
var formats = {};
|
formats[input.reference.mimeType] = true;
|
chainLink.process.setResponseForm({
|
outputIndex: output,
|
supportedFormats: formats
|
});
|
input.reference.body = chainLink.process.description;
|
while (this.executeCallbacks.length > 0) {
|
this.executeCallbacks[0]();
|
}
|
},
|
|
/**
|
* Method: toFeatures
|
* Converts spatial input into features so it can be processed by
|
* <OpenLayers.Format> instances.
|
*
|
* Parameters:
|
* source - {Mixed} An <OpenLayers.Geometry>, an
|
* <OpenLayers.Feature.Vector>, or an array of geometries or features
|
*
|
* Returns:
|
* {Array(<OpenLayers.Feature.Vector>)}
|
*/
|
toFeatures: function(source) {
|
var isArray = OpenLayers.Util.isArray(source);
|
if (!isArray) {
|
source = [source];
|
}
|
var target = new Array(source.length),
|
current;
|
for (var i=0, ii=source.length; i<ii; ++i) {
|
current = source[i];
|
target[i] = current instanceof OpenLayers.Feature.Vector ?
|
current : new OpenLayers.Feature.Vector(current);
|
}
|
return isArray ? target : target[0];
|
},
|
|
/**
|
* Method: findMimeType
|
* Finds a supported mime type.
|
*
|
* Parameters:
|
* sourceFormats - {Object} An object literal with mime types as key and
|
* true as value for supported formats.
|
* targetFormats - {Object} Like <sourceFormats>, but optional to check for
|
* supported mime types on a different target than this process.
|
* Default is to check against this process's supported formats.
|
*
|
* Returns:
|
* {String} A supported mime type.
|
*/
|
findMimeType: function(sourceFormats, targetFormats) {
|
targetFormats = targetFormats || this.formats;
|
for (var f in sourceFormats) {
|
if (f in targetFormats) {
|
return f;
|
}
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.WPSProcess"
|
|
});
|
|
/**
|
* Class: OpenLayers.WPSProcess.ChainLink
|
* Type for chaining processes.
|
*/
|
OpenLayers.WPSProcess.ChainLink = OpenLayers.Class({
|
|
/**
|
* Property: process
|
* {<OpenLayers.WPSProcess>} The process to chain
|
*/
|
process: null,
|
|
/**
|
* Property: output
|
* {String} The output identifier of the output we are going to use as
|
* input for another process.
|
*/
|
output: null,
|
|
/**
|
* Constructor: OpenLayers.WPSProcess.ChainLink
|
*
|
* Parameters:
|
* options - {Object} Properties to set on the instance.
|
*/
|
initialize: function(options) {
|
OpenLayers.Util.extend(this, options);
|
},
|
|
CLASS_NAME: "OpenLayers.WPSProcess.ChainLink"
|
|
});
|
/* ======================================================================
|
OpenLayers/WPSClient.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/SingleFile.js
|
*/
|
|
/**
|
* @requires OpenLayers/Events.js
|
* @requires OpenLayers/WPSProcess.js
|
* @requires OpenLayers/Format/WPSDescribeProcess.js
|
* @requires OpenLayers/Request.js
|
*/
|
|
/**
|
* Class: OpenLayers.WPSClient
|
* High level API for interaction with Web Processing Services (WPS).
|
* An <OpenLayers.WPSClient> instance is used to create <OpenLayers.WPSProcess>
|
* instances for servers known to the WPSClient. The WPSClient also caches
|
* DescribeProcess responses to reduce the number of requests sent to servers
|
* when processes are created.
|
*/
|
OpenLayers.WPSClient = OpenLayers.Class({
|
|
/**
|
* Property: servers
|
* {Object} Service metadata, keyed by a local identifier.
|
*
|
* Properties:
|
* url - {String} the url of the server
|
* version - {String} WPS version of the server
|
* processDescription - {Object} Cache of raw DescribeProcess
|
* responses, keyed by process identifier.
|
*/
|
servers: null,
|
|
/**
|
* Property: version
|
* {String} The default WPS version to use if none is configured. Default
|
* is '1.0.0'.
|
*/
|
version: '1.0.0',
|
|
/**
|
* Property: lazy
|
* {Boolean} Should the DescribeProcess be deferred until a process is
|
* fully configured? Default is false.
|
*/
|
lazy: false,
|
|
/**
|
* Property: events
|
* {<OpenLayers.Events>}
|
*
|
* Supported event types:
|
* describeprocess - Fires when the process description is available.
|
* Listeners receive an object with a 'raw' property holding the raw
|
* DescribeProcess response, and an 'identifier' property holding the
|
* process identifier of the described process.
|
*/
|
events: null,
|
|
/**
|
* Constructor: OpenLayers.WPSClient
|
*
|
* Parameters:
|
* options - {Object} Object whose properties will be set on the instance.
|
*
|
* Avaliable options:
|
* servers - {Object} Mandatory. Service metadata, keyed by a local
|
* identifier. Can either be a string with the service url or an
|
* object literal with additional metadata:
|
*
|
* (code)
|
* servers: {
|
* local: '/geoserver/wps'
|
* }, {
|
* opengeo: {
|
* url: 'http://demo.opengeo.org/geoserver/wps',
|
* version: '1.0.0'
|
* }
|
* }
|
* (end)
|
*
|
* lazy - {Boolean} Optional. Set to true if DescribeProcess should not be
|
* requested until a process is fully configured. Default is false.
|
*/
|
initialize: function(options) {
|
OpenLayers.Util.extend(this, options);
|
this.events = new OpenLayers.Events(this);
|
this.servers = {};
|
for (var s in options.servers) {
|
this.servers[s] = typeof options.servers[s] == 'string' ? {
|
url: options.servers[s],
|
version: this.version,
|
processDescription: {}
|
} : options.servers[s];
|
}
|
},
|
|
/**
|
* APIMethod: execute
|
* Shortcut to execute a process with a single function call. This is
|
* equivalent to using <getProcess> and then calling execute on the
|
* process.
|
*
|
* Parameters:
|
* options - {Object} Options for the execute operation.
|
*
|
* Available options:
|
* server - {String} Mandatory. One of the local identifiers of the
|
* configured servers.
|
* process - {String} Mandatory. A process identifier known to the
|
* server.
|
* inputs - {Object} The inputs for the process, keyed by input identifier.
|
* For spatial data inputs, the value of an input is usually an
|
* <OpenLayers.Geometry>, an <OpenLayers.Feature.Vector> or an array of
|
* geometries or features.
|
* output - {String} The identifier of an output to parse. Optional. If not
|
* provided, the first output will be parsed.
|
* success - {Function} Callback to call when the process is complete.
|
* This function is called with an outputs object as argument, which
|
* will have a property with the identifier of the requested output
|
* (e.g. 'result'). For processes that generate spatial output, the
|
* value will either be a single <OpenLayers.Feature.Vector> or an
|
* array of features.
|
* scope - {Object} Optional scope for the success callback.
|
*/
|
execute: function(options) {
|
var process = this.getProcess(options.server, options.process);
|
process.execute({
|
inputs: options.inputs,
|
success: options.success,
|
scope: options.scope
|
});
|
},
|
|
/**
|
* APIMethod: getProcess
|
* Creates an <OpenLayers.WPSProcess>.
|
*
|
* Parameters:
|
* serverID - {String} Local identifier from the servers that this instance
|
* was constructed with.
|
* processID - {String} Process identifier known to the server.
|
*
|
* Returns:
|
* {<OpenLayers.WPSProcess>}
|
*/
|
getProcess: function(serverID, processID) {
|
var process = new OpenLayers.WPSProcess({
|
client: this,
|
server: serverID,
|
identifier: processID
|
});
|
if (!this.lazy) {
|
process.describe();
|
}
|
return process;
|
},
|
|
/**
|
* Method: describeProcess
|
*
|
* Parameters:
|
* serverID - {String} Identifier of the server
|
* processID - {String} Identifier of the requested process
|
* callback - {Function} Callback to call when the description is available
|
* scope - {Object} Optional execution scope for the callback function
|
*/
|
describeProcess: function(serverID, processID, callback, scope) {
|
var server = this.servers[serverID];
|
if (!server.processDescription[processID]) {
|
if (!(processID in server.processDescription)) {
|
// set to null so we know a describeFeature request is pending
|
server.processDescription[processID] = null;
|
OpenLayers.Request.GET({
|
url: server.url,
|
params: {
|
SERVICE: 'WPS',
|
VERSION: server.version,
|
REQUEST: 'DescribeProcess',
|
IDENTIFIER: processID
|
},
|
success: function(response) {
|
server.processDescription[processID] = response.responseText;
|
this.events.triggerEvent('describeprocess', {
|
identifier: processID,
|
raw: response.responseText
|
});
|
},
|
scope: this
|
});
|
} else {
|
// pending request
|
this.events.register('describeprocess', this, function describe(evt) {
|
if (evt.identifier === processID) {
|
this.events.unregister('describeprocess', this, describe);
|
callback.call(scope, evt.raw);
|
}
|
});
|
}
|
} else {
|
window.setTimeout(function() {
|
callback.call(scope, server.processDescription[processID]);
|
}, 0);
|
}
|
},
|
|
/**
|
* Method: destroy
|
*/
|
destroy: function() {
|
this.events.destroy();
|
this.events = null;
|
this.servers = null;
|
},
|
|
CLASS_NAME: 'OpenLayers.WPSClient'
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/CSWGetRecords/v2_0_2.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML.js
|
* @requires OpenLayers/Format/CSWGetRecords.js
|
* @requires OpenLayers/Format/Filter/v1_0_0.js
|
* @requires OpenLayers/Format/Filter/v1_1_0.js
|
* @requires OpenLayers/Format/OWSCommon/v1_0_0.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.CSWGetRecords.v2_0_2
|
* A format for creating CSWGetRecords v2.0.2 transactions.
|
* Create a new instance with the
|
* <OpenLayers.Format.CSWGetRecords.v2_0_2> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.CSWGetRecords.v2_0_2 = OpenLayers.Class(OpenLayers.Format.XML, {
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
csw: "http://www.opengis.net/cat/csw/2.0.2",
|
dc: "http://purl.org/dc/elements/1.1/",
|
dct: "http://purl.org/dc/terms/",
|
gmd: "http://www.isotc211.org/2005/gmd",
|
geonet: "http://www.fao.org/geonetwork",
|
ogc: "http://www.opengis.net/ogc",
|
ows: "http://www.opengis.net/ows",
|
xlink: "http://www.w3.org/1999/xlink",
|
xsi: "http://www.w3.org/2001/XMLSchema-instance"
|
},
|
|
/**
|
* Property: defaultPrefix
|
* {String} The default prefix (used by Format.XML).
|
*/
|
defaultPrefix: "csw",
|
|
/**
|
* Property: version
|
* {String} CSW version number.
|
*/
|
version: "2.0.2",
|
|
/**
|
* Property: schemaLocation
|
* {String} http://www.opengis.net/cat/csw/2.0.2
|
* http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd
|
*/
|
schemaLocation: "http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd",
|
|
/**
|
* APIProperty: requestId
|
* {String} Value of the requestId attribute of the GetRecords element.
|
*/
|
requestId: null,
|
|
/**
|
* APIProperty: resultType
|
* {String} Value of the resultType attribute of the GetRecords element,
|
* specifies the result type in the GetRecords response, "hits" is
|
* the default.
|
*/
|
resultType: null,
|
|
/**
|
* APIProperty: outputFormat
|
* {String} Value of the outputFormat attribute of the GetRecords element,
|
* specifies the format of the GetRecords response,
|
* "application/xml" is the default.
|
*/
|
outputFormat: null,
|
|
/**
|
* APIProperty: outputSchema
|
* {String} Value of the outputSchema attribute of the GetRecords element,
|
* specifies the schema of the GetRecords response.
|
*/
|
outputSchema: null,
|
|
/**
|
* APIProperty: startPosition
|
* {String} Value of the startPosition attribute of the GetRecords element,
|
* specifies the start position (offset+1) for the GetRecords response,
|
* 1 is the default.
|
*/
|
startPosition: null,
|
|
/**
|
* APIProperty: maxRecords
|
* {String} Value of the maxRecords attribute of the GetRecords element,
|
* specifies the maximum number of records in the GetRecords response,
|
* 10 is the default.
|
*/
|
maxRecords: null,
|
|
/**
|
* APIProperty: DistributedSearch
|
* {String} Value of the csw:DistributedSearch element, used when writing
|
* a csw:GetRecords document.
|
*/
|
DistributedSearch: null,
|
|
/**
|
* APIProperty: ResponseHandler
|
* {Array({String})} Values of the csw:ResponseHandler elements, used when
|
* writting a csw:GetRecords document.
|
*/
|
ResponseHandler: null,
|
|
/**
|
* APIProperty: Query
|
* {String} Value of the csw:Query element, used when writing a csw:GetRecords
|
* document.
|
*/
|
Query: null,
|
|
/**
|
* Property: regExes
|
* Compiled regular expressions for manipulating strings.
|
*/
|
regExes: {
|
trimSpace: (/^\s*|\s*$/g),
|
removeSpace: (/\s*/g),
|
splitSpace: (/\s+/),
|
trimComma: (/\s*,\s*/g)
|
},
|
|
/**
|
* Constructor: OpenLayers.Format.CSWGetRecords.v2_0_2
|
* A class for parsing and generating CSWGetRecords v2.0.2 transactions.
|
*
|
* Parameters:
|
* options - {Object} Optional object whose properties will be set on the
|
* instance.
|
*
|
* Valid options properties (documented as class properties):
|
* - requestId
|
* - resultType
|
* - outputFormat
|
* - outputSchema
|
* - startPosition
|
* - maxRecords
|
* - DistributedSearch
|
* - ResponseHandler
|
* - Query
|
*/
|
initialize: function(options) {
|
OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
|
},
|
|
/**
|
* APIMethod: read
|
* Parse the response from a GetRecords request.
|
*/
|
read: function(data) {
|
if(typeof data == "string") {
|
data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
|
}
|
if(data && data.nodeType == 9) {
|
data = data.documentElement;
|
}
|
var obj = {};
|
this.readNode(data, obj);
|
return obj;
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"csw": {
|
"GetRecordsResponse": function(node, obj) {
|
obj.records = [];
|
this.readChildNodes(node, obj);
|
var version = this.getAttributeNS(node, "", 'version');
|
if (version != "") {
|
obj.version = version;
|
}
|
},
|
"RequestId": function(node, obj) {
|
obj.RequestId = this.getChildValue(node);
|
},
|
"SearchStatus": function(node, obj) {
|
obj.SearchStatus = {};
|
var timestamp = this.getAttributeNS(node, "", 'timestamp');
|
if (timestamp != "") {
|
obj.SearchStatus.timestamp = timestamp;
|
}
|
},
|
"SearchResults": function(node, obj) {
|
this.readChildNodes(node, obj);
|
var attrs = node.attributes;
|
var SearchResults = {};
|
for(var i=0, len=attrs.length; i<len; ++i) {
|
if ((attrs[i].name == "numberOfRecordsMatched") ||
|
(attrs[i].name == "numberOfRecordsReturned") ||
|
(attrs[i].name == "nextRecord")) {
|
SearchResults[attrs[i].name] = parseInt(attrs[i].nodeValue);
|
} else {
|
SearchResults[attrs[i].name] = attrs[i].nodeValue;
|
}
|
}
|
obj.SearchResults = SearchResults;
|
},
|
"SummaryRecord": function(node, obj) {
|
var record = {type: "SummaryRecord"};
|
this.readChildNodes(node, record);
|
obj.records.push(record);
|
},
|
"BriefRecord": function(node, obj) {
|
var record = {type: "BriefRecord"};
|
this.readChildNodes(node, record);
|
obj.records.push(record);
|
},
|
"DCMIRecord": function(node, obj) {
|
var record = {type: "DCMIRecord"};
|
this.readChildNodes(node, record);
|
obj.records.push(record);
|
},
|
"Record": function(node, obj) {
|
var record = {type: "Record"};
|
this.readChildNodes(node, record);
|
obj.records.push(record);
|
},
|
"*": function(node, obj) {
|
var name = node.localName || node.nodeName.split(":").pop();
|
obj[name] = this.getChildValue(node);
|
}
|
},
|
"geonet": {
|
"info": function(node, obj) {
|
var gninfo = {};
|
this.readChildNodes(node, gninfo);
|
obj.gninfo = gninfo;
|
}
|
},
|
"dc": {
|
// audience, contributor, coverage, creator, date, description, format,
|
// identifier, language, provenance, publisher, relation, rights,
|
// rightsHolder, source, subject, title, type, URI
|
"*": function(node, obj) {
|
var name = node.localName || node.nodeName.split(":").pop();
|
if (!(OpenLayers.Util.isArray(obj[name]))) {
|
obj[name] = [];
|
}
|
var dc_element = {};
|
var attrs = node.attributes;
|
for(var i=0, len=attrs.length; i<len; ++i) {
|
dc_element[attrs[i].name] = attrs[i].nodeValue;
|
}
|
dc_element.value = this.getChildValue(node);
|
if (dc_element.value != "") {
|
obj[name].push(dc_element);
|
}
|
}
|
},
|
"dct": {
|
// abstract, modified, spatial
|
"*": function(node, obj) {
|
var name = node.localName || node.nodeName.split(":").pop();
|
if (!(OpenLayers.Util.isArray(obj[name]))) {
|
obj[name] = [];
|
}
|
obj[name].push(this.getChildValue(node));
|
}
|
},
|
"ows": OpenLayers.Util.applyDefaults({
|
"BoundingBox": function(node, obj) {
|
if (obj.bounds) {
|
obj.BoundingBox = [{crs: obj.projection, value:
|
[
|
obj.bounds.left,
|
obj.bounds.bottom,
|
obj.bounds.right,
|
obj.bounds.top
|
]
|
}];
|
delete obj.projection;
|
delete obj.bounds;
|
}
|
OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers["ows"]["BoundingBox"].apply(
|
this, arguments);
|
}
|
}, OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers["ows"])
|
},
|
|
/**
|
* Method: write
|
* Given an configuration js object, write a CSWGetRecords request.
|
*
|
* Parameters:
|
* options - {Object} A object mapping the request.
|
*
|
* Returns:
|
* {String} A serialized CSWGetRecords request.
|
*/
|
write: function(options) {
|
var node = this.writeNode("csw:GetRecords", options);
|
node.setAttribute("xmlns:gmd", this.namespaces.gmd);
|
return OpenLayers.Format.XML.prototype.write.apply(this, [node]);
|
},
|
|
/**
|
* Property: writers
|
* As a compliment to the readers property, this structure contains public
|
* writing functions grouped by namespace alias and named like the
|
* node names they produce.
|
*/
|
writers: {
|
"csw": {
|
"GetRecords": function(options) {
|
if (!options) {
|
options = {};
|
}
|
var node = this.createElementNSPlus("csw:GetRecords", {
|
attributes: {
|
service: "CSW",
|
version: this.version,
|
requestId: options.requestId || this.requestId,
|
resultType: options.resultType || this.resultType,
|
outputFormat: options.outputFormat || this.outputFormat,
|
outputSchema: options.outputSchema || this.outputSchema,
|
startPosition: options.startPosition || this.startPosition,
|
maxRecords: options.maxRecords || this.maxRecords
|
}
|
});
|
if (options.DistributedSearch || this.DistributedSearch) {
|
this.writeNode(
|
"csw:DistributedSearch",
|
options.DistributedSearch || this.DistributedSearch,
|
node
|
);
|
}
|
var ResponseHandler = options.ResponseHandler || this.ResponseHandler;
|
if (OpenLayers.Util.isArray(ResponseHandler) && ResponseHandler.length > 0) {
|
// ResponseHandler must be a non-empty array
|
for(var i=0, len=ResponseHandler.length; i<len; i++) {
|
this.writeNode(
|
"csw:ResponseHandler",
|
ResponseHandler[i],
|
node
|
);
|
}
|
}
|
this.writeNode("Query", options.Query || this.Query, node);
|
return node;
|
},
|
"DistributedSearch": function(options) {
|
var node = this.createElementNSPlus("csw:DistributedSearch", {
|
attributes: {
|
hopCount: options.hopCount
|
}
|
});
|
return node;
|
},
|
"ResponseHandler": function(options) {
|
var node = this.createElementNSPlus("csw:ResponseHandler", {
|
value: options.value
|
});
|
return node;
|
},
|
"Query": function(options) {
|
if (!options) {
|
options = {};
|
}
|
var node = this.createElementNSPlus("csw:Query", {
|
attributes: {
|
typeNames: options.typeNames || "csw:Record"
|
}
|
});
|
var ElementName = options.ElementName;
|
if (OpenLayers.Util.isArray(ElementName) && ElementName.length > 0) {
|
// ElementName must be a non-empty array
|
for(var i=0, len=ElementName.length; i<len; i++) {
|
this.writeNode(
|
"csw:ElementName",
|
ElementName[i],
|
node
|
);
|
}
|
} else {
|
this.writeNode(
|
"csw:ElementSetName",
|
options.ElementSetName || {value: 'summary'},
|
node
|
);
|
}
|
if (options.Constraint) {
|
this.writeNode(
|
"csw:Constraint",
|
options.Constraint,
|
node
|
);
|
}
|
if (options.SortBy) {
|
this.writeNode(
|
"ogc:SortBy",
|
options.SortBy,
|
node
|
);
|
}
|
return node;
|
},
|
"ElementName": function(options) {
|
var node = this.createElementNSPlus("csw:ElementName", {
|
value: options.value
|
});
|
return node;
|
},
|
"ElementSetName": function(options) {
|
var node = this.createElementNSPlus("csw:ElementSetName", {
|
attributes: {
|
typeNames: options.typeNames
|
},
|
value: options.value
|
});
|
return node;
|
},
|
"Constraint": function(options) {
|
var node = this.createElementNSPlus("csw:Constraint", {
|
attributes: {
|
version: options.version
|
}
|
});
|
if (options.Filter) {
|
var format = new OpenLayers.Format.Filter({
|
version: options.version
|
});
|
node.appendChild(format.write(options.Filter));
|
} else if (options.CqlText) {
|
var child = this.createElementNSPlus("CqlText", {
|
value: options.CqlText.value
|
});
|
node.appendChild(child);
|
}
|
return node;
|
}
|
},
|
"ogc": OpenLayers.Format.Filter.v1_1_0.prototype.writers["ogc"]
|
},
|
|
CLASS_NAME: "OpenLayers.Format.CSWGetRecords.v2_0_2"
|
});
|
/* ======================================================================
|
Rico/license.js
|
====================================================================== */
|
|
/**
|
* @license Apache 2
|
*
|
* Contains portions of Rico <http://openrico.org/>
|
*
|
* Copyright 2005 Sabre Airline Solutions
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License"); you
|
* may not use this file except in compliance with the License. You
|
* may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
* implied. See the License for the specific language governing
|
* permissions and limitations under the License.
|
*/
|
/* ======================================================================
|
OpenLayers/Marker/Box.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Marker.js
|
*/
|
|
/**
|
* Class: OpenLayers.Marker.Box
|
*
|
* Inherits from:
|
* - <OpenLayers.Marker>
|
*/
|
OpenLayers.Marker.Box = OpenLayers.Class(OpenLayers.Marker, {
|
|
/**
|
* Property: bounds
|
* {<OpenLayers.Bounds>}
|
*/
|
bounds: null,
|
|
/**
|
* Property: div
|
* {DOMElement}
|
*/
|
div: null,
|
|
/**
|
* Constructor: OpenLayers.Marker.Box
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
* borderColor - {String}
|
* borderWidth - {int}
|
*/
|
initialize: function(bounds, borderColor, borderWidth) {
|
this.bounds = bounds;
|
this.div = OpenLayers.Util.createDiv();
|
this.div.style.overflow = 'hidden';
|
this.events = new OpenLayers.Events(this, this.div);
|
this.setBorder(borderColor, borderWidth);
|
},
|
|
/**
|
* Method: destroy
|
*/
|
destroy: function() {
|
|
this.bounds = null;
|
this.div = null;
|
|
OpenLayers.Marker.prototype.destroy.apply(this, arguments);
|
},
|
|
/**
|
* Method: setBorder
|
* Allow the user to change the box's color and border width
|
*
|
* Parameters:
|
* color - {String} Default is "red"
|
* width - {int} Default is 2
|
*/
|
setBorder: function (color, width) {
|
if (!color) {
|
color = "red";
|
}
|
if (!width) {
|
width = 2;
|
}
|
this.div.style.border = width + "px solid " + color;
|
},
|
|
/**
|
* Method: draw
|
*
|
* Parameters:
|
* px - {<OpenLayers.Pixel>}
|
* sz - {<OpenLayers.Size>}
|
*
|
* Returns:
|
* {DOMElement} A new DOM Image with this marker's icon set at the
|
* location passed-in
|
*/
|
draw: function(px, sz) {
|
OpenLayers.Util.modifyDOMElement(this.div, null, px, sz);
|
return this.div;
|
},
|
|
/**
|
* Method: onScreen
|
*
|
* Rreturn:
|
* {Boolean} Whether or not the marker is currently visible on screen.
|
*/
|
onScreen:function() {
|
var onScreen = false;
|
if (this.map) {
|
var screenBounds = this.map.getExtent();
|
onScreen = screenBounds.containsBounds(this.bounds, true, true);
|
}
|
return onScreen;
|
},
|
|
/**
|
* Method: display
|
* Hide or show the icon
|
*
|
* Parameters:
|
* display - {Boolean}
|
*/
|
display: function(display) {
|
this.div.style.display = (display) ? "" : "none";
|
},
|
|
CLASS_NAME: "OpenLayers.Marker.Box"
|
});
|
|
/* ======================================================================
|
OpenLayers/Format/Text.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Feature/Vector.js
|
* @requires OpenLayers/Geometry/Point.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.Text
|
* Read Text format. Create a new instance with the <OpenLayers.Format.Text>
|
* constructor. This reads text which is formatted like CSV text, using
|
* tabs as the seperator by default. It provides parsing of data originally
|
* used in the MapViewerService, described on the wiki. This Format is used
|
* by the <OpenLayers.Layer.Text> class.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format>
|
*/
|
OpenLayers.Format.Text = OpenLayers.Class(OpenLayers.Format, {
|
|
/**
|
* APIProperty: defaultStyle
|
* defaultStyle allows one to control the default styling of the features.
|
* It should be a symbolizer hash. By default, this is set to match the
|
* Layer.Text behavior, which is to use the default OpenLayers Icon.
|
*/
|
defaultStyle: null,
|
|
/**
|
* APIProperty: extractStyles
|
* set to true to extract styles from the TSV files, using information
|
* from the image or icon, iconSize and iconOffset fields. This will result
|
* in features with a symbolizer (style) property set, using the
|
* default symbolizer specified in <defaultStyle>. Set to false if you
|
* wish to use a styleMap or OpenLayers.Style options to style your
|
* layer instead.
|
*/
|
extractStyles: true,
|
|
/**
|
* Constructor: OpenLayers.Format.Text
|
* Create a new parser for TSV Text.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
initialize: function(options) {
|
options = options || {};
|
|
if(options.extractStyles !== false) {
|
options.defaultStyle = {
|
'externalGraphic': OpenLayers.Util.getImageLocation("marker.png"),
|
'graphicWidth': 21,
|
'graphicHeight': 25,
|
'graphicXOffset': -10.5,
|
'graphicYOffset': -12.5
|
};
|
}
|
|
OpenLayers.Format.prototype.initialize.apply(this, [options]);
|
},
|
|
/**
|
* APIMethod: read
|
* Return a list of features from a Tab Seperated Values text string.
|
*
|
* Parameters:
|
* text - {String}
|
*
|
* Returns:
|
* Array({<OpenLayers.Feature.Vector>})
|
*/
|
read: function(text) {
|
var lines = text.split('\n');
|
var columns;
|
var features = [];
|
// length - 1 to allow for trailing new line
|
for (var lcv = 0; lcv < (lines.length - 1); lcv++) {
|
var currLine = lines[lcv].replace(/^\s*/,'').replace(/\s*$/,'');
|
|
if (currLine.charAt(0) != '#') { /* not a comment */
|
|
if (!columns) {
|
//First line is columns
|
columns = currLine.split('\t');
|
} else {
|
var vals = currLine.split('\t');
|
var geometry = new OpenLayers.Geometry.Point(0,0);
|
var attributes = {};
|
var style = this.defaultStyle ?
|
OpenLayers.Util.applyDefaults({}, this.defaultStyle) :
|
null;
|
var icon, iconSize, iconOffset, overflow;
|
var set = false;
|
for (var valIndex = 0; valIndex < vals.length; valIndex++) {
|
if (vals[valIndex]) {
|
if (columns[valIndex] == 'point') {
|
var coords = vals[valIndex].split(',');
|
geometry.y = parseFloat(coords[0]);
|
geometry.x = parseFloat(coords[1]);
|
set = true;
|
} else if (columns[valIndex] == 'lat') {
|
geometry.y = parseFloat(vals[valIndex]);
|
set = true;
|
} else if (columns[valIndex] == 'lon') {
|
geometry.x = parseFloat(vals[valIndex]);
|
set = true;
|
} else if (columns[valIndex] == 'title')
|
attributes['title'] = vals[valIndex];
|
else if (columns[valIndex] == 'image' ||
|
columns[valIndex] == 'icon' && style) {
|
style['externalGraphic'] = vals[valIndex];
|
} else if (columns[valIndex] == 'iconSize' && style) {
|
var size = vals[valIndex].split(',');
|
style['graphicWidth'] = parseFloat(size[0]);
|
style['graphicHeight'] = parseFloat(size[1]);
|
} else if (columns[valIndex] == 'iconOffset' && style) {
|
var offset = vals[valIndex].split(',');
|
style['graphicXOffset'] = parseFloat(offset[0]);
|
style['graphicYOffset'] = parseFloat(offset[1]);
|
} else if (columns[valIndex] == 'description') {
|
attributes['description'] = vals[valIndex];
|
} else if (columns[valIndex] == 'overflow') {
|
attributes['overflow'] = vals[valIndex];
|
} else {
|
// For StyleMap filtering, allow additional
|
// columns to be stored as attributes.
|
attributes[columns[valIndex]] = vals[valIndex];
|
}
|
}
|
}
|
if (set) {
|
if (this.internalProjection && this.externalProjection) {
|
geometry.transform(this.externalProjection,
|
this.internalProjection);
|
}
|
var feature = new OpenLayers.Feature.Vector(geometry, attributes, style);
|
features.push(feature);
|
}
|
}
|
}
|
}
|
return features;
|
},
|
|
CLASS_NAME: "OpenLayers.Format.Text"
|
});
|
/* ======================================================================
|
OpenLayers/Layer/Text.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Layer/Markers.js
|
* @requires OpenLayers/Format/Text.js
|
* @requires OpenLayers/Request/XMLHttpRequest.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.Text
|
* This layer creates markers given data in a text file. The <location>
|
* property of the layer (specified as a property of the options argument
|
* in the <OpenLayers.Layer.Text> constructor) points to a tab delimited
|
* file with data used to create markers.
|
*
|
* The first row of the data file should be a header line with the column names
|
* of the data. Each column should be delimited by a tab space. The
|
* possible columns are:
|
* - *point* lat,lon of the point where a marker is to be placed
|
* - *lat* Latitude of the point where a marker is to be placed
|
* - *lon* Longitude of the point where a marker is to be placed
|
* - *icon* or *image* URL of marker icon to use.
|
* - *iconSize* Size of Icon to use.
|
* - *iconOffset* Where the top-left corner of the icon is to be placed
|
* relative to the latitude and longitude of the point.
|
* - *title* The text of the 'title' is placed inside an 'h2' marker
|
* inside a popup, which opens when the marker is clicked.
|
* - *description* The text of the 'description' is placed below the h2
|
* in the popup. this can be plain text or HTML.
|
*
|
* Example text file:
|
* (code)
|
* lat lon title description iconSize iconOffset icon
|
* 10 20 title description 21,25 -10,-25 http://www.openlayers.org/dev/img/marker.png
|
* (end)
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer.Markers>
|
*/
|
OpenLayers.Layer.Text = OpenLayers.Class(OpenLayers.Layer.Markers, {
|
|
/**
|
* APIProperty: location
|
* {String} URL of text file. Must be specified in the "options" argument
|
* of the constructor. Can not be changed once passed in.
|
*/
|
location:null,
|
|
/**
|
* Property: features
|
* {Array(<OpenLayers.Feature>)}
|
*/
|
features: null,
|
|
/**
|
* APIProperty: formatOptions
|
* {Object} Hash of options which should be passed to the format when it is
|
* created. Must be passed in the constructor.
|
*/
|
formatOptions: null,
|
|
/**
|
* Property: selectedFeature
|
* {<OpenLayers.Feature>}
|
*/
|
selectedFeature: null,
|
|
/**
|
* Constructor: OpenLayers.Layer.Text
|
* Create a text layer.
|
*
|
* Parameters:
|
* name - {String}
|
* options - {Object} Object with properties to be set on the layer.
|
* Must include <location> property.
|
*/
|
initialize: function(name, options) {
|
OpenLayers.Layer.Markers.prototype.initialize.apply(this, arguments);
|
this.features = [];
|
},
|
|
/**
|
* APIMethod: destroy
|
*/
|
destroy: function() {
|
// Warning: Layer.Markers.destroy() must be called prior to calling
|
// clearFeatures() here, otherwise we leak memory. Indeed, if
|
// Layer.Markers.destroy() is called after clearFeatures(), it won't be
|
// able to remove the marker image elements from the layer's div since
|
// the markers will have been destroyed by clearFeatures().
|
OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);
|
this.clearFeatures();
|
this.features = null;
|
},
|
|
/**
|
* Method: loadText
|
* Start the load of the Text data. Don't do this when we first add the layer,
|
* since we may not be visible at any point, and it would therefore be a waste.
|
*/
|
loadText: function() {
|
if (!this.loaded) {
|
if (this.location != null) {
|
|
var onFail = function(e) {
|
this.events.triggerEvent("loadend");
|
};
|
|
this.events.triggerEvent("loadstart");
|
OpenLayers.Request.GET({
|
url: this.location,
|
success: this.parseData,
|
failure: onFail,
|
scope: this
|
});
|
this.loaded = true;
|
}
|
}
|
},
|
|
/**
|
* Method: moveTo
|
* If layer is visible and Text has not been loaded, load Text.
|
*
|
* Parameters:
|
* bounds - {Object}
|
* zoomChanged - {Object}
|
* minor - {Object}
|
*/
|
moveTo:function(bounds, zoomChanged, minor) {
|
OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);
|
if(this.visibility && !this.loaded){
|
this.loadText();
|
}
|
},
|
|
/**
|
* Method: parseData
|
*
|
* Parameters:
|
* ajaxRequest - {<OpenLayers.Request.XMLHttpRequest>}
|
*/
|
parseData: function(ajaxRequest) {
|
var text = ajaxRequest.responseText;
|
|
var options = {};
|
|
OpenLayers.Util.extend(options, this.formatOptions);
|
|
if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
|
options.externalProjection = this.projection;
|
options.internalProjection = this.map.getProjectionObject();
|
}
|
|
var parser = new OpenLayers.Format.Text(options);
|
var features = parser.read(text);
|
for (var i=0, len=features.length; i<len; i++) {
|
var data = {};
|
var feature = features[i];
|
var location;
|
var iconSize, iconOffset;
|
|
location = new OpenLayers.LonLat(feature.geometry.x,
|
feature.geometry.y);
|
|
if (feature.style.graphicWidth
|
&& feature.style.graphicHeight) {
|
iconSize = new OpenLayers.Size(
|
feature.style.graphicWidth,
|
feature.style.graphicHeight);
|
}
|
|
// FIXME: At the moment, we only use this if we have an
|
// externalGraphic, because icon has no setOffset API Method.
|
/**
|
* FIXME FIRST!!
|
* The Text format does all sorts of parseFloating
|
* The result of a parseFloat for a bogus string is NaN. That
|
* means the three possible values here are undefined, NaN, or a
|
* number. The previous check was an identity check for null. This
|
* means it was failing for all undefined or NaN. A slightly better
|
* check is for undefined. An even better check is to see if the
|
* value is a number (see #1441).
|
*/
|
if (feature.style.graphicXOffset !== undefined
|
&& feature.style.graphicYOffset !== undefined) {
|
iconOffset = new OpenLayers.Pixel(
|
feature.style.graphicXOffset,
|
feature.style.graphicYOffset);
|
}
|
|
if (feature.style.externalGraphic != null) {
|
data.icon = new OpenLayers.Icon(feature.style.externalGraphic,
|
iconSize,
|
iconOffset);
|
} else {
|
data.icon = OpenLayers.Marker.defaultIcon();
|
|
//allows for the case where the image url is not
|
// specified but the size is. use a default icon
|
// but change the size
|
if (iconSize != null) {
|
data.icon.setSize(iconSize);
|
}
|
}
|
|
if ((feature.attributes.title != null)
|
&& (feature.attributes.description != null)) {
|
data['popupContentHTML'] =
|
'<h2>'+feature.attributes.title+'</h2>' +
|
'<p>'+feature.attributes.description+'</p>';
|
}
|
|
data['overflow'] = feature.attributes.overflow || "auto";
|
|
var markerFeature = new OpenLayers.Feature(this, location, data);
|
this.features.push(markerFeature);
|
var marker = markerFeature.createMarker();
|
if ((feature.attributes.title != null)
|
&& (feature.attributes.description != null)) {
|
marker.events.register('click', markerFeature, this.markerClick);
|
}
|
this.addMarker(marker);
|
}
|
this.events.triggerEvent("loadend");
|
},
|
|
/**
|
* Property: markerClick
|
*
|
* Parameters:
|
* evt - {Event}
|
*
|
* Context:
|
* - {<OpenLayers.Feature>}
|
*/
|
markerClick: function(evt) {
|
var sameMarkerClicked = (this == this.layer.selectedFeature);
|
this.layer.selectedFeature = (!sameMarkerClicked) ? this : null;
|
for(var i=0, len=this.layer.map.popups.length; i<len; i++) {
|
this.layer.map.removePopup(this.layer.map.popups[i]);
|
}
|
if (!sameMarkerClicked) {
|
this.layer.map.addPopup(this.createPopup());
|
}
|
OpenLayers.Event.stop(evt);
|
},
|
|
/**
|
* Method: clearFeatures
|
*/
|
clearFeatures: function() {
|
if (this.features != null) {
|
while(this.features.length > 0) {
|
var feature = this.features[0];
|
OpenLayers.Util.removeItem(this.features, feature);
|
feature.destroy();
|
}
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.Text"
|
});
|
/* ======================================================================
|
OpenLayers/Handler/RegularPolygon.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Handler/Drag.js
|
*/
|
|
/**
|
* Class: OpenLayers.Handler.RegularPolygon
|
* Handler to draw a regular polygon on the map. Polygon is displayed on mouse
|
* down, moves or is modified on mouse move, and is finished on mouse up.
|
* The handler triggers callbacks for 'done' and 'cancel'. Create a new
|
* instance with the <OpenLayers.Handler.RegularPolygon> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Handler.Drag>
|
*/
|
OpenLayers.Handler.RegularPolygon = OpenLayers.Class(OpenLayers.Handler.Drag, {
|
|
/**
|
* APIProperty: sides
|
* {Integer} Number of sides for the regular polygon. Needs to be greater
|
* than 2. Defaults to 4.
|
*/
|
sides: 4,
|
|
/**
|
* APIProperty: radius
|
* {Float} Optional radius in map units of the regular polygon. If this is
|
* set to some non-zero value, a polygon with a fixed radius will be
|
* drawn and dragged with mose movements. If this property is not
|
* set, dragging changes the radius of the polygon. Set to null by
|
* default.
|
*/
|
radius: null,
|
|
/**
|
* APIProperty: snapAngle
|
* {Float} If set to a non-zero value, the handler will snap the polygon
|
* rotation to multiples of the snapAngle. Value is an angle measured
|
* in degrees counterclockwise from the positive x-axis.
|
*/
|
snapAngle: null,
|
|
/**
|
* APIProperty: snapToggle
|
* {String} If set, snapToggle is checked on mouse events and will set
|
* the snap mode to the opposite of what it currently is. To disallow
|
* toggling between snap and non-snap mode, set freehandToggle to
|
* null. Acceptable toggle values are 'shiftKey', 'ctrlKey', and
|
* 'altKey'. Snap mode is only possible if this.snapAngle is set to a
|
* non-zero value.
|
*/
|
snapToggle: 'shiftKey',
|
|
/**
|
* Property: layerOptions
|
* {Object} Any optional properties to be set on the sketch layer.
|
*/
|
layerOptions: null,
|
|
/**
|
* APIProperty: persist
|
* {Boolean} Leave the feature rendered until clear is called. Default
|
* is false. If set to true, the feature remains rendered until
|
* clear is called, typically by deactivating the handler or starting
|
* another drawing.
|
*/
|
persist: false,
|
|
/**
|
* APIProperty: irregular
|
* {Boolean} Draw an irregular polygon instead of a regular polygon.
|
* Default is false. If true, the initial mouse down will represent
|
* one corner of the polygon bounds and with each mouse movement, the
|
* polygon will be stretched so the opposite corner of its bounds
|
* follows the mouse position. This property takes precedence over
|
* the radius property. If set to true, the radius property will
|
* be ignored.
|
*/
|
irregular: false,
|
|
/**
|
* APIProperty: citeCompliant
|
* {Boolean} If set to true, coordinates of features drawn in a map extent
|
* crossing the date line won't exceed the world bounds. Default is false.
|
*/
|
citeCompliant: false,
|
|
/**
|
* Property: angle
|
* {Float} The angle from the origin (mouse down) to the current mouse
|
* position, in radians. This is measured counterclockwise from the
|
* positive x-axis.
|
*/
|
angle: null,
|
|
/**
|
* Property: fixedRadius
|
* {Boolean} The polygon has a fixed radius. True if a radius is set before
|
* drawing begins. False otherwise.
|
*/
|
fixedRadius: false,
|
|
/**
|
* Property: feature
|
* {<OpenLayers.Feature.Vector>} The currently drawn polygon feature
|
*/
|
feature: null,
|
|
/**
|
* Property: layer
|
* {<OpenLayers.Layer.Vector>} The temporary drawing layer
|
*/
|
layer: null,
|
|
/**
|
* Property: origin
|
* {<OpenLayers.Geometry.Point>} Location of the first mouse down
|
*/
|
origin: null,
|
|
/**
|
* Constructor: OpenLayers.Handler.RegularPolygon
|
* Create a new regular polygon handler.
|
*
|
* Parameters:
|
* control - {<OpenLayers.Control>} The control that owns this handler
|
* callbacks - {Object} An object with a properties whose values are
|
* functions. Various callbacks described below.
|
* options - {Object} An object with properties to be set on the handler.
|
* If the options.sides property is not specified, the number of sides
|
* will default to 4.
|
*
|
* Named callbacks:
|
* create - Called when a sketch is first created. Callback called with
|
* the creation point geometry and sketch feature.
|
* done - Called when the sketch drawing is finished. The callback will
|
* recieve a single argument, the sketch geometry.
|
* cancel - Called when the handler is deactivated while drawing. The
|
* cancel callback will receive a geometry.
|
*/
|
initialize: function(control, callbacks, options) {
|
if(!(options && options.layerOptions && options.layerOptions.styleMap)) {
|
this.style = OpenLayers.Util.extend(OpenLayers.Feature.Vector.style['default'], {});
|
}
|
|
OpenLayers.Handler.Drag.prototype.initialize.apply(this,
|
[control, callbacks, options]);
|
this.options = (options) ? options : {};
|
},
|
|
/**
|
* APIMethod: setOptions
|
*
|
* Parameters:
|
* newOptions - {Object}
|
*/
|
setOptions: function (newOptions) {
|
OpenLayers.Util.extend(this.options, newOptions);
|
OpenLayers.Util.extend(this, newOptions);
|
},
|
|
/**
|
* APIMethod: activate
|
* Turn on the handler.
|
*
|
* Returns:
|
* {Boolean} The handler was successfully activated
|
*/
|
activate: function() {
|
var activated = false;
|
if(OpenLayers.Handler.Drag.prototype.activate.apply(this, arguments)) {
|
// create temporary vector layer for rendering geometry sketch
|
var options = OpenLayers.Util.extend({
|
displayInLayerSwitcher: false,
|
// indicate that the temp vector layer will never be out of range
|
// without this, resolution properties must be specified at the
|
// map-level for this temporary layer to init its resolutions
|
// correctly
|
calculateInRange: OpenLayers.Function.True,
|
wrapDateLine: this.citeCompliant
|
}, this.layerOptions);
|
this.layer = new OpenLayers.Layer.Vector(this.CLASS_NAME, options);
|
this.map.addLayer(this.layer);
|
activated = true;
|
}
|
return activated;
|
},
|
|
/**
|
* APIMethod: deactivate
|
* Turn off the handler.
|
*
|
* Returns:
|
* {Boolean} The handler was successfully deactivated
|
*/
|
deactivate: function() {
|
var deactivated = false;
|
if(OpenLayers.Handler.Drag.prototype.deactivate.apply(this, arguments)) {
|
// call the cancel callback if mid-drawing
|
if(this.dragging) {
|
this.cancel();
|
}
|
// If a layer's map property is set to null, it means that that
|
// layer isn't added to the map. Since we ourself added the layer
|
// to the map in activate(), we can assume that if this.layer.map
|
// is null it means that the layer has been destroyed (as a result
|
// of map.destroy() for example.
|
if (this.layer.map != null) {
|
this.layer.destroy(false);
|
if (this.feature) {
|
this.feature.destroy();
|
}
|
}
|
this.layer = null;
|
this.feature = null;
|
deactivated = true;
|
}
|
return deactivated;
|
},
|
|
/**
|
* Method: down
|
* Start drawing a new feature
|
*
|
* Parameters:
|
* evt - {Event} The drag start event
|
*/
|
down: function(evt) {
|
this.fixedRadius = !!(this.radius);
|
var maploc = this.layer.getLonLatFromViewPortPx(evt.xy);
|
this.origin = new OpenLayers.Geometry.Point(maploc.lon, maploc.lat);
|
// create the new polygon
|
if(!this.fixedRadius || this.irregular) {
|
// smallest radius should not be less one pixel in map units
|
// VML doesn't behave well with smaller
|
this.radius = this.map.getResolution();
|
}
|
if(this.persist) {
|
this.clear();
|
}
|
this.feature = new OpenLayers.Feature.Vector();
|
this.createGeometry();
|
this.callback("create", [this.origin, this.feature]);
|
this.layer.addFeatures([this.feature], {silent: true});
|
this.layer.drawFeature(this.feature, this.style);
|
},
|
|
/**
|
* Method: move
|
* Respond to drag move events
|
*
|
* Parameters:
|
* evt - {Evt} The move event
|
*/
|
move: function(evt) {
|
var maploc = this.layer.getLonLatFromViewPortPx(evt.xy);
|
var point = new OpenLayers.Geometry.Point(maploc.lon, maploc.lat);
|
if(this.irregular) {
|
var ry = Math.sqrt(2) * Math.abs(point.y - this.origin.y) / 2;
|
this.radius = Math.max(this.map.getResolution() / 2, ry);
|
} else if(this.fixedRadius) {
|
this.origin = point;
|
} else {
|
this.calculateAngle(point, evt);
|
this.radius = Math.max(this.map.getResolution() / 2,
|
point.distanceTo(this.origin));
|
}
|
this.modifyGeometry();
|
if(this.irregular) {
|
var dx = point.x - this.origin.x;
|
var dy = point.y - this.origin.y;
|
var ratio;
|
if(dy == 0) {
|
ratio = dx / (this.radius * Math.sqrt(2));
|
} else {
|
ratio = dx / dy;
|
}
|
this.feature.geometry.resize(1, this.origin, ratio);
|
this.feature.geometry.move(dx / 2, dy / 2);
|
}
|
this.layer.drawFeature(this.feature, this.style);
|
},
|
|
/**
|
* Method: up
|
* Finish drawing the feature
|
*
|
* Parameters:
|
* evt - {Event} The mouse up event
|
*/
|
up: function(evt) {
|
this.finalize();
|
// the mouseup method of superclass doesn't call the
|
// "done" callback if there's been no move between
|
// down and up
|
if (this.start == this.last) {
|
this.callback("done", [evt.xy]);
|
}
|
},
|
|
/**
|
* Method: out
|
* Finish drawing the feature.
|
*
|
* Parameters:
|
* evt - {Event} The mouse out event
|
*/
|
out: function(evt) {
|
this.finalize();
|
},
|
|
/**
|
* Method: createGeometry
|
* Create the new polygon geometry. This is called at the start of the
|
* drag and at any point during the drag if the number of sides
|
* changes.
|
*/
|
createGeometry: function() {
|
this.angle = Math.PI * ((1/this.sides) - (1/2));
|
if(this.snapAngle) {
|
this.angle += this.snapAngle * (Math.PI / 180);
|
}
|
this.feature.geometry = OpenLayers.Geometry.Polygon.createRegularPolygon(
|
this.origin, this.radius, this.sides, this.snapAngle
|
);
|
},
|
|
/**
|
* Method: modifyGeometry
|
* Modify the polygon geometry in place.
|
*/
|
modifyGeometry: function() {
|
var angle, point;
|
var ring = this.feature.geometry.components[0];
|
// if the number of sides ever changes, create a new geometry
|
if(ring.components.length != (this.sides + 1)) {
|
this.createGeometry();
|
ring = this.feature.geometry.components[0];
|
}
|
for(var i=0; i<this.sides; ++i) {
|
point = ring.components[i];
|
angle = this.angle + (i * 2 * Math.PI / this.sides);
|
point.x = this.origin.x + (this.radius * Math.cos(angle));
|
point.y = this.origin.y + (this.radius * Math.sin(angle));
|
point.clearBounds();
|
}
|
},
|
|
/**
|
* Method: calculateAngle
|
* Calculate the angle based on settings.
|
*
|
* Parameters:
|
* point - {<OpenLayers.Geometry.Point>}
|
* evt - {Event}
|
*/
|
calculateAngle: function(point, evt) {
|
var alpha = Math.atan2(point.y - this.origin.y,
|
point.x - this.origin.x);
|
if(this.snapAngle && (this.snapToggle && !evt[this.snapToggle])) {
|
var snapAngleRad = (Math.PI / 180) * this.snapAngle;
|
this.angle = Math.round(alpha / snapAngleRad) * snapAngleRad;
|
} else {
|
this.angle = alpha;
|
}
|
},
|
|
/**
|
* APIMethod: cancel
|
* Finish the geometry and call the "cancel" callback.
|
*/
|
cancel: function() {
|
// the polygon geometry gets cloned in the callback method
|
this.callback("cancel", null);
|
this.finalize();
|
},
|
|
/**
|
* Method: finalize
|
* Finish the geometry and call the "done" callback.
|
*/
|
finalize: function() {
|
this.origin = null;
|
this.radius = this.options.radius;
|
},
|
|
/**
|
* APIMethod: clear
|
* Clear any rendered features on the temporary layer. This is called
|
* when the handler is deactivated, canceled, or done (unless persist
|
* is true).
|
*/
|
clear: function() {
|
if (this.layer) {
|
this.layer.renderer.clear();
|
this.layer.destroyFeatures();
|
}
|
},
|
|
/**
|
* Method: callback
|
* Trigger the control's named callback with the given arguments
|
*
|
* Parameters:
|
* name - {String} The key for the callback that is one of the properties
|
* of the handler's callbacks object.
|
* args - {Array} An array of arguments with which to call the callback
|
* (defined by the control).
|
*/
|
callback: function (name, args) {
|
// override the callback method to always send the polygon geometry
|
if (this.callbacks[name]) {
|
this.callbacks[name].apply(this.control,
|
[this.feature.geometry.clone()]);
|
}
|
// since sketch features are added to the temporary layer
|
// they must be cleared here if done or cancel
|
if(!this.persist && (name == "done" || name == "cancel")) {
|
this.clear();
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Handler.RegularPolygon"
|
});
|
/* ======================================================================
|
OpenLayers/Control/SLDSelect.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/Layer/WMS.js
|
* @requires OpenLayers/Handler/RegularPolygon.js
|
* @requires OpenLayers/Handler/Polygon.js
|
* @requires OpenLayers/Handler/Path.js
|
* @requires OpenLayers/Handler/Click.js
|
* @requires OpenLayers/Filter/Spatial.js
|
* @requires OpenLayers/Format/SLD/v1_0_0.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.SLDSelect
|
* Perform selections on WMS layers using Styled Layer Descriptor (SLD)
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.SLDSelect = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* APIProperty: events
|
* {<OpenLayers.Events>} Events instance for listeners and triggering
|
* control specific events.
|
*
|
* Register a listener for a particular event with the following syntax:
|
* (code)
|
* control.events.register(type, obj, listener);
|
* (end)
|
*
|
* Supported event types (in addition to those from <OpenLayers.Control.events>):
|
* selected - Triggered when a selection occurs. Listeners receive an
|
* event with *filters* and *layer* properties. Filters will be an
|
* array of OpenLayers.Filter objects created in order to perform
|
* the particular selection.
|
*/
|
|
/**
|
* APIProperty: clearOnDeactivate
|
* {Boolean} Should the selection be cleared when the control is
|
* deactivated. Default value is false.
|
*/
|
clearOnDeactivate: false,
|
|
/**
|
* APIProperty: layers
|
* {Array(<OpenLayers.Layer.WMS>)} The WMS layers this control will work
|
* on.
|
*/
|
layers: null,
|
|
/**
|
* Property: callbacks
|
* {Object} The functions that are sent to the handler for callback
|
*/
|
callbacks: null,
|
|
/**
|
* APIProperty: selectionSymbolizer
|
* {Object} Determines the styling of the selected objects. Default is
|
* a selection in red.
|
*/
|
selectionSymbolizer: {
|
'Polygon': {fillColor: '#FF0000', stroke: false},
|
'Line': {strokeColor: '#FF0000', strokeWidth: 2},
|
'Point': {graphicName: 'square', fillColor: '#FF0000', pointRadius: 5}
|
},
|
|
/**
|
* APIProperty: layerOptions
|
* {Object} The options to apply to the selection layer, by default the
|
* selection layer will be kept out of the layer switcher.
|
*/
|
layerOptions: null,
|
|
/**
|
* APIProperty: handlerOptions
|
* {Object} Used to set non-default properties on the control's handler
|
*/
|
|
/**
|
* APIProperty: sketchStyle
|
* {<OpenLayers.Style>|Object} Style or symbolizer to use for the sketch
|
* handler. The recommended way of styling the sketch layer, however, is
|
* to configure an <OpenLayers.StyleMap> in the layerOptions of the
|
* <handlerOptions>:
|
*
|
* (code)
|
* new OpenLayers.Control.SLDSelect(OpenLayers.Handler.Path, {
|
* handlerOptions: {
|
* layerOptions: {
|
* styleMap: new OpenLayers.StyleMap({
|
* "default": {strokeColor: "yellow"}
|
* })
|
* }
|
* }
|
* });
|
* (end)
|
*/
|
sketchStyle: null,
|
|
/**
|
* APIProperty: wfsCache
|
* {Object} Cache to use for storing parsed results from
|
* <OpenLayers.Format.WFSDescribeFeatureType.read>. If not provided,
|
* these will be cached on the prototype.
|
*/
|
wfsCache: {},
|
|
/**
|
* APIProperty: layerCache
|
* {Object} Cache to use for storing references to the selection layers.
|
* Normally each source layer will have exactly 1 selection layer of
|
* type OpenLayers.Layer.WMS. If not provided, layers will
|
* be cached on the prototype. Note that if <clearOnDeactivate> is
|
* true, the layer will no longer be cached after deactivating the
|
* control.
|
*/
|
layerCache: {},
|
|
/**
|
* Constructor: OpenLayers.Control.SLDSelect
|
* Create a new control for selecting features in WMS layers using
|
* Styled Layer Descriptor (SLD).
|
*
|
* Parameters:
|
* handler - {<OpenLayers.Class>} A sketch handler class. This determines
|
* the type of selection, e.g. box (<OpenLayers.Handler.Box>), point
|
* (<OpenLayers.Handler.Point>), path (<OpenLayers.Handler.Path>) or
|
* polygon (<OpenLayers.Handler.Polygon>) selection. To use circle
|
* type selection, use <OpenLayers.Handler.RegularPolygon> and pass
|
* the number of desired sides (e.g. 40) as "sides" property to the
|
* <handlerOptions>.
|
* options - {Object} An object containing all configuration properties for
|
* the control.
|
*
|
* Valid options:
|
* layers - Array({<OpenLayers.Layer.WMS>}) The layers to perform the
|
* selection on.
|
*/
|
initialize: function(handler, options) {
|
OpenLayers.Control.prototype.initialize.apply(this, [options]);
|
|
this.callbacks = OpenLayers.Util.extend({done: this.select,
|
click: this.select}, this.callbacks);
|
this.handlerOptions = this.handlerOptions || {};
|
this.layerOptions = OpenLayers.Util.applyDefaults(this.layerOptions, {
|
displayInLayerSwitcher: false,
|
tileOptions: {maxGetUrlLength: 2048}
|
});
|
if (this.sketchStyle) {
|
this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(
|
this.handlerOptions.layerOptions,
|
{styleMap: new OpenLayers.StyleMap({"default": this.sketchStyle})}
|
);
|
}
|
this.handler = new handler(this, this.callbacks, this.handlerOptions);
|
},
|
|
/**
|
* APIMethod: destroy
|
* Take care of things that are not handled in superclass.
|
*/
|
destroy: function() {
|
for (var key in this.layerCache) {
|
delete this.layerCache[key];
|
}
|
for (var key in this.wfsCache) {
|
delete this.wfsCache[key];
|
}
|
OpenLayers.Control.prototype.destroy.apply(this, arguments);
|
},
|
|
/**
|
* Method: coupleLayerVisiblity
|
* Couple the selection layer and the source layer with respect to
|
* layer visibility. So if the source layer is turned off, the
|
* selection layer is also turned off.
|
*
|
* Context:
|
* - {<OpenLayers.Layer>}
|
*
|
* Parameters:
|
* evt - {Object}
|
*/
|
coupleLayerVisiblity: function(evt) {
|
this.setVisibility(evt.object.getVisibility());
|
},
|
|
/**
|
* Method: createSelectionLayer
|
* Creates a "clone" from the source layer in which the selection can
|
* be drawn. This ensures both the source layer and the selection are
|
* visible and not only the selection.
|
*
|
* Parameters:
|
* source - {<OpenLayers.Layer.WMS>} The source layer on which the selection
|
* is performed.
|
*
|
* Returns:
|
* {<OpenLayers.Layer.WMS>} A WMS layer with maxGetUrlLength configured to 2048
|
* since SLD selections can easily get quite long.
|
*/
|
createSelectionLayer: function(source) {
|
// check if we already have a selection layer for the source layer
|
var selectionLayer;
|
if (!this.layerCache[source.id]) {
|
selectionLayer = new OpenLayers.Layer.WMS(source.name,
|
source.url, source.params,
|
OpenLayers.Util.applyDefaults(
|
this.layerOptions,
|
source.getOptions())
|
);
|
this.layerCache[source.id] = selectionLayer;
|
// make sure the layers are coupled wrt visibility, but only
|
// if they are not displayed in the layer switcher, because in
|
// that case the user cannot control visibility.
|
if (this.layerOptions.displayInLayerSwitcher === false) {
|
source.events.on({
|
"visibilitychanged": this.coupleLayerVisiblity,
|
scope: selectionLayer});
|
}
|
this.map.addLayer(selectionLayer);
|
} else {
|
selectionLayer = this.layerCache[source.id];
|
}
|
return selectionLayer;
|
},
|
|
/**
|
* Method: createSLD
|
* Create the SLD document for the layer using the supplied filters.
|
*
|
* Parameters:
|
* layer - {<OpenLayers.Layer.WMS>}
|
* filters - Array({<OpenLayers.Filter>}) The filters to be applied.
|
* geometryAttributes - Array({Object}) The geometry attributes of the
|
* layer.
|
*
|
* Returns:
|
* {String} The SLD document generated as a string.
|
*/
|
createSLD: function(layer, filters, geometryAttributes) {
|
var sld = {version: "1.0.0", namedLayers: {}};
|
var layerNames = [layer.params.LAYERS].join(",").split(",");
|
for (var i=0, len=layerNames.length; i<len; i++) {
|
var name = layerNames[i];
|
sld.namedLayers[name] = {name: name, userStyles: []};
|
var symbolizer = this.selectionSymbolizer;
|
var geometryAttribute = geometryAttributes[i];
|
if (geometryAttribute.type.indexOf('Polygon') >= 0) {
|
symbolizer = {Polygon: this.selectionSymbolizer['Polygon']};
|
} else if (geometryAttribute.type.indexOf('LineString') >= 0) {
|
symbolizer = {Line: this.selectionSymbolizer['Line']};
|
} else if (geometryAttribute.type.indexOf('Point') >= 0) {
|
symbolizer = {Point: this.selectionSymbolizer['Point']};
|
}
|
var filter = filters[i];
|
sld.namedLayers[name].userStyles.push({name: 'default', rules: [
|
new OpenLayers.Rule({symbolizer: symbolizer,
|
filter: filter,
|
maxScaleDenominator: layer.options.minScale})
|
]});
|
}
|
return new OpenLayers.Format.SLD({srsName: this.map.getProjection()}).write(sld);
|
},
|
|
/**
|
* Method: parseDescribeLayer
|
* Parse the SLD WMS DescribeLayer response and issue the corresponding
|
* WFS DescribeFeatureType request
|
*
|
* request - {XMLHttpRequest} The request object.
|
*/
|
parseDescribeLayer: function(request) {
|
var format = new OpenLayers.Format.WMSDescribeLayer();
|
var doc = request.responseXML;
|
if(!doc || !doc.documentElement) {
|
doc = request.responseText;
|
}
|
var describeLayer = format.read(doc);
|
var typeNames = [];
|
var url = null;
|
for (var i=0, len=describeLayer.length; i<len; i++) {
|
// perform a WFS DescribeFeatureType request
|
if (describeLayer[i].owsType == "WFS") {
|
typeNames.push(describeLayer[i].typeName);
|
url = describeLayer[i].owsURL;
|
}
|
}
|
var options = {
|
url: url,
|
params: {
|
SERVICE: "WFS",
|
TYPENAME: typeNames.toString(),
|
REQUEST: "DescribeFeatureType",
|
VERSION: "1.0.0"
|
},
|
callback: function(request) {
|
var format = new OpenLayers.Format.WFSDescribeFeatureType();
|
var doc = request.responseXML;
|
if(!doc || !doc.documentElement) {
|
doc = request.responseText;
|
}
|
var describeFeatureType = format.read(doc);
|
this.control.wfsCache[this.layer.id] = describeFeatureType;
|
this.control._queue && this.control.applySelection();
|
},
|
scope: this
|
};
|
OpenLayers.Request.GET(options);
|
},
|
|
/**
|
* Method: getGeometryAttributes
|
* Look up the geometry attributes from the WFS DescribeFeatureType response
|
*
|
* Parameters:
|
* layer - {<OpenLayers.Layer.WMS>} The layer for which to look up the
|
* geometry attributes.
|
*
|
* Returns:
|
* Array({Object}) Array of geometry attributes
|
*/
|
getGeometryAttributes: function(layer) {
|
var result = [];
|
var cache = this.wfsCache[layer.id];
|
for (var i=0, len=cache.featureTypes.length; i<len; i++) {
|
var typeName = cache.featureTypes[i];
|
var properties = typeName.properties;
|
for (var j=0, lenj=properties.length; j < lenj; j++) {
|
var property = properties[j];
|
var type = property.type;
|
if ((type.indexOf('LineString') >= 0) ||
|
(type.indexOf('GeometryAssociationType') >=0) ||
|
(type.indexOf('GeometryPropertyType') >= 0) ||
|
(type.indexOf('Point') >= 0) ||
|
(type.indexOf('Polygon') >= 0) ) {
|
result.push(property);
|
}
|
}
|
}
|
return result;
|
},
|
|
/**
|
* APIMethod: activate
|
* Activate the control. Activating the control will perform a SLD WMS
|
* DescribeLayer request followed by a WFS DescribeFeatureType request
|
* so that the proper symbolizers can be chosen based on the geometry
|
* type.
|
*/
|
activate: function() {
|
var activated = OpenLayers.Control.prototype.activate.call(this);
|
if(activated) {
|
for (var i=0, len=this.layers.length; i<len; i++) {
|
var layer = this.layers[i];
|
if (layer && !this.wfsCache[layer.id]) {
|
var options = {
|
url: layer.url,
|
params: {
|
SERVICE: "WMS",
|
VERSION: layer.params.VERSION,
|
LAYERS: layer.params.LAYERS,
|
REQUEST: "DescribeLayer"
|
},
|
callback: this.parseDescribeLayer,
|
scope: {layer: layer, control: this}
|
};
|
OpenLayers.Request.GET(options);
|
}
|
}
|
}
|
return activated;
|
},
|
|
/**
|
* APIMethod: deactivate
|
* Deactivate the control. If clearOnDeactivate is true, remove the
|
* selection layer(s).
|
*/
|
deactivate: function() {
|
var deactivated = OpenLayers.Control.prototype.deactivate.call(this);
|
if(deactivated) {
|
for (var i=0, len=this.layers.length; i<len; i++) {
|
var layer = this.layers[i];
|
if (layer && this.clearOnDeactivate === true) {
|
var layerCache = this.layerCache;
|
var selectionLayer = layerCache[layer.id];
|
if (selectionLayer) {
|
layer.events.un({
|
"visibilitychanged": this.coupleLayerVisiblity,
|
scope: selectionLayer});
|
selectionLayer.destroy();
|
delete layerCache[layer.id];
|
}
|
}
|
}
|
}
|
return deactivated;
|
},
|
|
/**
|
* APIMethod: setLayers
|
* Set the layers on which the selection should be performed. Call the
|
* setLayers method if the layer(s) to be used change and the same
|
* control should be used on a new set of layers.
|
* If the control is already active, it will be active after the new
|
* set of layers is set.
|
*
|
* Parameters:
|
* layers - {Array(<OpenLayers.Layer.WMS>)} The new set of layers on which
|
* the selection should be performed.
|
*/
|
setLayers: function(layers) {
|
if(this.active) {
|
this.deactivate();
|
this.layers = layers;
|
this.activate();
|
} else {
|
this.layers = layers;
|
}
|
},
|
|
/**
|
* Function: createFilter
|
* Create the filter to be used in the SLD.
|
*
|
* Parameters:
|
* geometryAttribute - {Object} Used to get the name of the geometry
|
* attribute which is needed for constructing the spatial filter.
|
* geometry - {<OpenLayers.Geometry>} The geometry to use.
|
*
|
* Returns:
|
* {<OpenLayers.Filter.Spatial>} The spatial filter created.
|
*/
|
createFilter: function(geometryAttribute, geometry) {
|
var filter = null;
|
if (this.handler instanceof OpenLayers.Handler.RegularPolygon) {
|
// box
|
if (this.handler.irregular === true) {
|
filter = new OpenLayers.Filter.Spatial({
|
type: OpenLayers.Filter.Spatial.BBOX,
|
property: geometryAttribute.name,
|
value: geometry.getBounds()}
|
);
|
} else {
|
filter = new OpenLayers.Filter.Spatial({
|
type: OpenLayers.Filter.Spatial.INTERSECTS,
|
property: geometryAttribute.name,
|
value: geometry}
|
);
|
}
|
} else if (this.handler instanceof OpenLayers.Handler.Polygon) {
|
filter = new OpenLayers.Filter.Spatial({
|
type: OpenLayers.Filter.Spatial.INTERSECTS,
|
property: geometryAttribute.name,
|
value: geometry}
|
);
|
} else if (this.handler instanceof OpenLayers.Handler.Path) {
|
// if source layer is point based, use DWITHIN instead
|
if (geometryAttribute.type.indexOf('Point') >= 0) {
|
filter = new OpenLayers.Filter.Spatial({
|
type: OpenLayers.Filter.Spatial.DWITHIN,
|
property: geometryAttribute.name,
|
distance: this.map.getExtent().getWidth()*0.01 ,
|
distanceUnits: this.map.getUnits(),
|
value: geometry}
|
);
|
} else {
|
filter = new OpenLayers.Filter.Spatial({
|
type: OpenLayers.Filter.Spatial.INTERSECTS,
|
property: geometryAttribute.name,
|
value: geometry}
|
);
|
}
|
} else if (this.handler instanceof OpenLayers.Handler.Click) {
|
if (geometryAttribute.type.indexOf('Polygon') >= 0) {
|
filter = new OpenLayers.Filter.Spatial({
|
type: OpenLayers.Filter.Spatial.INTERSECTS,
|
property: geometryAttribute.name,
|
value: geometry}
|
);
|
} else {
|
filter = new OpenLayers.Filter.Spatial({
|
type: OpenLayers.Filter.Spatial.DWITHIN,
|
property: geometryAttribute.name,
|
distance: this.map.getExtent().getWidth()*0.01 ,
|
distanceUnits: this.map.getUnits(),
|
value: geometry}
|
);
|
}
|
}
|
return filter;
|
},
|
|
/**
|
* Method: select
|
* When the handler is done, use SLD_BODY on the selection layer to
|
* display the selection in the map.
|
*
|
* Parameters:
|
* geometry - {Object} or {<OpenLayers.Geometry>}
|
*/
|
select: function(geometry) {
|
this._queue = function() {
|
for (var i=0, len=this.layers.length; i<len; i++) {
|
var layer = this.layers[i];
|
var geometryAttributes = this.getGeometryAttributes(layer);
|
var filters = [];
|
for (var j=0, lenj=geometryAttributes.length; j<lenj; j++) {
|
var geometryAttribute = geometryAttributes[j];
|
if (geometryAttribute !== null) {
|
// from the click handler we will not get an actual
|
// geometry so transform
|
if (!(geometry instanceof OpenLayers.Geometry)) {
|
var point = this.map.getLonLatFromPixel(
|
geometry.xy);
|
geometry = new OpenLayers.Geometry.Point(
|
point.lon, point.lat);
|
}
|
var filter = this.createFilter(geometryAttribute,
|
geometry);
|
if (filter !== null) {
|
filters.push(filter);
|
}
|
}
|
}
|
|
var selectionLayer = this.createSelectionLayer(layer);
|
|
this.events.triggerEvent("selected", {
|
layer: layer,
|
filters: filters
|
});
|
|
var sld = this.createSLD(layer, filters, geometryAttributes);
|
|
selectionLayer.mergeNewParams({SLD_BODY: sld});
|
delete this._queue;
|
}
|
};
|
this.applySelection();
|
},
|
|
/**
|
* Method: applySelection
|
* Checks if all required wfs data is cached, and applies the selection
|
*/
|
applySelection: function() {
|
var canApply = true;
|
for (var i=0, len=this.layers.length; i<len; i++) {
|
if(!this.wfsCache[this.layers[i].id]) {
|
canApply = false;
|
break;
|
}
|
}
|
canApply && this._queue.call(this);
|
},
|
|
CLASS_NAME: "OpenLayers.Control.SLDSelect"
|
});
|
/* ======================================================================
|
OpenLayers/Control/Scale.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/Lang.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.Scale
|
* The Scale control displays the current map scale as a ratio (e.g. Scale =
|
* 1:1M). By default it is displayed in the lower right corner of the map.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.Scale = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* Property: element
|
* {DOMElement}
|
*/
|
element: null,
|
|
/**
|
* APIProperty: geodesic
|
* {Boolean} Use geodesic measurement. Default is false. The recommended
|
* setting for maps in EPSG:4326 is false, and true EPSG:900913. If set to
|
* true, the scale will be calculated based on the horizontal size of the
|
* pixel in the center of the map viewport.
|
*/
|
geodesic: false,
|
|
/**
|
* Constructor: OpenLayers.Control.Scale
|
*
|
* Parameters:
|
* element - {DOMElement}
|
* options - {Object}
|
*/
|
initialize: function(element, options) {
|
OpenLayers.Control.prototype.initialize.apply(this, [options]);
|
this.element = OpenLayers.Util.getElement(element);
|
},
|
|
/**
|
* Method: draw
|
*
|
* Returns:
|
* {DOMElement}
|
*/
|
draw: function() {
|
OpenLayers.Control.prototype.draw.apply(this, arguments);
|
if (!this.element) {
|
this.element = document.createElement("div");
|
this.div.appendChild(this.element);
|
}
|
this.map.events.register( 'moveend', this, this.updateScale);
|
this.updateScale();
|
return this.div;
|
},
|
|
/**
|
* Method: updateScale
|
*/
|
updateScale: function() {
|
var scale;
|
if(this.geodesic === true) {
|
var units = this.map.getUnits();
|
if(!units) {
|
return;
|
}
|
var inches = OpenLayers.INCHES_PER_UNIT;
|
scale = (this.map.getGeodesicPixelSize().w || 0.000001) *
|
inches["km"] * OpenLayers.DOTS_PER_INCH;
|
} else {
|
scale = this.map.getScale();
|
}
|
|
if (!scale) {
|
return;
|
}
|
|
if (scale >= 9500 && scale <= 950000) {
|
scale = Math.round(scale / 1000) + "K";
|
} else if (scale >= 950000) {
|
scale = Math.round(scale / 1000000) + "M";
|
} else {
|
scale = Math.round(scale);
|
}
|
|
this.element.innerHTML = OpenLayers.i18n("Scale = 1 : ${scaleDenom}", {'scaleDenom':scale});
|
},
|
|
CLASS_NAME: "OpenLayers.Control.Scale"
|
});
|
|
/* ======================================================================
|
OpenLayers/Layer/MapGuide.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Request/XMLHttpRequest.js
|
* @requires OpenLayers/Layer/Grid.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.MapGuide
|
* Instances of OpenLayers.Layer.MapGuide are used to display
|
* data from a MapGuide OS instance.
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer.Grid>
|
*/
|
OpenLayers.Layer.MapGuide = OpenLayers.Class(OpenLayers.Layer.Grid, {
|
|
/**
|
* APIProperty: isBaseLayer
|
* {Boolean} Treat this layer as a base layer. Default is true.
|
**/
|
isBaseLayer: true,
|
|
/**
|
* APIProperty: useHttpTile
|
* {Boolean} use a tile cache exposed directly via a webserver rather than the
|
* via mapguide server. This does require extra configuration on the Mapguide Server,
|
* and will only work when singleTile is false. The url for the layer must be set to the
|
* webserver path rather than the Mapguide mapagent.
|
* See http://trac.osgeo.org/mapguide/wiki/CodeSamples/Tiles/ServingTilesViaHttp
|
**/
|
useHttpTile: false,
|
|
/**
|
* APIProperty: singleTile
|
* {Boolean} use tile server or request single tile image.
|
**/
|
singleTile: false,
|
|
/**
|
* APIProperty: useOverlay
|
* {Boolean} flag to indicate if the layer should be retrieved using
|
* GETMAPIMAGE (default) or using GETDYNAMICOVERLAY requests.
|
**/
|
useOverlay: false,
|
|
/**
|
* APIProperty: useAsyncOverlay
|
* {Boolean} indicates if the MapGuide site supports the asynchronous
|
* GETDYNAMICOVERLAY requests which is available in MapGuide Enterprise 2010
|
* and MapGuide Open Source v2.0.3 or higher. The newer versions of MG
|
* is called asynchronously, allows selections to be drawn separately from
|
* the map and offers styling options.
|
*
|
* With older versions of MapGuide, set useAsyncOverlay=false. Note that in
|
* this case a synchronous AJAX call is issued and the mapname and session
|
* parameters must be used to initialize the layer, not the mapdefinition
|
* parameter. Also note that this will issue a synchronous AJAX request
|
* before the image request can be issued so the users browser may lock
|
* up if the MG Web tier does not respond in a timely fashion.
|
**/
|
useAsyncOverlay: true,
|
|
/**
|
* Constant: TILE_PARAMS
|
* {Object} Hashtable of default parameter key/value pairs for tiled layer
|
*/
|
TILE_PARAMS: {
|
operation: 'GETTILEIMAGE',
|
version: '1.2.0'
|
},
|
|
/**
|
* Constant: SINGLE_TILE_PARAMS
|
* {Object} Hashtable of default parameter key/value pairs for untiled layer
|
*/
|
SINGLE_TILE_PARAMS: {
|
operation: 'GETMAPIMAGE',
|
format: 'PNG',
|
locale: 'en',
|
clip: '1',
|
version: '1.0.0'
|
},
|
|
/**
|
* Constant: OVERLAY_PARAMS
|
* {Object} Hashtable of default parameter key/value pairs for untiled layer
|
*/
|
OVERLAY_PARAMS: {
|
operation: 'GETDYNAMICMAPOVERLAYIMAGE',
|
format: 'PNG',
|
locale: 'en',
|
clip: '1',
|
version: '2.0.0'
|
},
|
|
/**
|
* Constant: FOLDER_PARAMS
|
* {Object} Hashtable of parameter key/value pairs which describe
|
* the folder structure for tiles as configured in the mapguide
|
* serverconfig.ini section [TileServiceProperties]
|
*/
|
FOLDER_PARAMS: {
|
tileColumnsPerFolder: 30,
|
tileRowsPerFolder: 30,
|
format: 'png',
|
querystring: null
|
},
|
|
/**
|
* Property: defaultSize
|
* {<OpenLayers.Size>} Tile size as produced by MapGuide server
|
**/
|
defaultSize: new OpenLayers.Size(300,300),
|
|
/**
|
* Property: tileOriginCorner
|
* {String} MapGuide tile server uses top-left as tile origin
|
**/
|
tileOriginCorner: "tl",
|
|
/**
|
* Constructor: OpenLayers.Layer.MapGuide
|
* Create a new Mapguide layer, either tiled or untiled.
|
*
|
* For tiled layers, the 'groupName' and 'mapDefinition' values
|
* must be specified as parameters in the constructor.
|
*
|
* For untiled base layers, specify either combination of 'mapName' and
|
* 'session', or 'mapDefinition' and 'locale'.
|
*
|
* For older versions of MapGuide and overlay layers, set useAsyncOverlay
|
* to false and in this case mapName and session are required parameters
|
* for the constructor.
|
*
|
* NOTE: MapGuide OS uses a DPI value and degrees to meters conversion
|
* factor that are different than the defaults used in OpenLayers,
|
* so these must be adjusted accordingly in your application.
|
* See the MapGuide example for how to set these values for MGOS.
|
*
|
* Parameters:
|
* name - {String} Name of the layer displayed in the interface
|
* url - {String} Location of the MapGuide mapagent executable
|
* (e.g. http://localhost:8008/mapguide/mapagent/mapagent.fcgi)
|
* params - {Object} hashtable of additional parameters to use. Some
|
* parameters may require additional code on the server. The ones that
|
* you may want to use are:
|
* - mapDefinition - {String} The MapGuide resource definition
|
* (e.g. Library://Samples/Gmap/Maps/gmapTiled.MapDefinition)
|
* - locale - Locale setting
|
* (for untiled overlays layers only)
|
* - mapName - {String} Name of the map as stored in the MapGuide session.
|
* (for untiled layers with a session parameter only)
|
* - session - { String} MapGuide session ID
|
* (for untiled overlays layers only)
|
* - basemaplayergroupname - {String} GroupName for tiled MapGuide layers only
|
* - format - Image format to be returned (for untiled overlay layers only)
|
* - showLayers - {String} A comma separated list of GUID's for the
|
* layers to display eg: 'cvc-xcv34,453-345-345sdf'.
|
* - hideLayers - {String} A comma separated list of GUID's for the
|
* layers to hide eg: 'cvc-xcv34,453-345-345sdf'.
|
* - showGroups - {String} A comma separated list of GUID's for the
|
* groups to display eg: 'cvc-xcv34,453-345-345sdf'.
|
* - hideGroups - {String} A comma separated list of GUID's for the
|
* groups to hide eg: 'cvc-xcv34,453-345-345sdf'
|
* - selectionXml - {String} A selection xml string Some server plumbing
|
* is required to read such a value.
|
* options - {Object} Hashtable of extra options to tag onto the layer;
|
* will vary depending if tiled or untiled maps are being requested
|
*/
|
initialize: function(name, url, params, options) {
|
|
OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments);
|
|
// unless explicitly set in options, if the layer is transparent,
|
// it will be an overlay
|
if (options == null || options.isBaseLayer == null) {
|
this.isBaseLayer = ((this.transparent != "true") &&
|
(this.transparent != true));
|
}
|
|
if (options && options.useOverlay!=null) {
|
this.useOverlay = options.useOverlay;
|
}
|
|
//initialize for untiled layers
|
if (this.singleTile) {
|
if (this.useOverlay) {
|
OpenLayers.Util.applyDefaults(
|
this.params,
|
this.OVERLAY_PARAMS
|
);
|
if (!this.useAsyncOverlay) {
|
this.params.version = "1.0.0";
|
}
|
} else {
|
OpenLayers.Util.applyDefaults(
|
this.params,
|
this.SINGLE_TILE_PARAMS
|
);
|
}
|
} else {
|
//initialize for tiled layers
|
if (this.useHttpTile) {
|
OpenLayers.Util.applyDefaults(
|
this.params,
|
this.FOLDER_PARAMS
|
);
|
} else {
|
OpenLayers.Util.applyDefaults(
|
this.params,
|
this.TILE_PARAMS
|
);
|
}
|
this.setTileSize(this.defaultSize);
|
}
|
},
|
|
/**
|
* Method: clone
|
* Create a clone of this layer
|
*
|
* Returns:
|
* {<OpenLayers.Layer.MapGuide>} An exact clone of this layer
|
*/
|
clone: function (obj) {
|
if (obj == null) {
|
obj = new OpenLayers.Layer.MapGuide(this.name,
|
this.url,
|
this.params,
|
this.getOptions());
|
}
|
//get all additions from superclasses
|
obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
|
|
return obj;
|
},
|
|
/**
|
* Method: getURL
|
* Return a query string for this layer
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>} A bounds representing the bbox
|
* for the request
|
*
|
* Returns:
|
* {String} A string with the layer's url and parameters and also
|
* the passed-in bounds and appropriate tile size specified
|
* as parameters.
|
*/
|
getURL: function (bounds) {
|
var url;
|
var center = bounds.getCenterLonLat();
|
var mapSize = this.map.getSize();
|
|
if (this.singleTile) {
|
//set up the call for GETMAPIMAGE or GETDYNAMICMAPOVERLAY with
|
//dynamic map parameters
|
var params = {
|
setdisplaydpi: OpenLayers.DOTS_PER_INCH,
|
setdisplayheight: mapSize.h*this.ratio,
|
setdisplaywidth: mapSize.w*this.ratio,
|
setviewcenterx: center.lon,
|
setviewcentery: center.lat,
|
setviewscale: this.map.getScale()
|
};
|
|
if (this.useOverlay && !this.useAsyncOverlay) {
|
//first we need to call GETVISIBLEMAPEXTENT to set the extent
|
var getVisParams = {};
|
getVisParams = OpenLayers.Util.extend(getVisParams, params);
|
getVisParams.operation = "GETVISIBLEMAPEXTENT";
|
getVisParams.version = "1.0.0";
|
getVisParams.session = this.params.session;
|
getVisParams.mapName = this.params.mapName;
|
getVisParams.format = 'text/xml';
|
url = this.getFullRequestString( getVisParams );
|
|
OpenLayers.Request.GET({url: url, async: false});
|
}
|
//construct the full URL
|
url = this.getFullRequestString( params );
|
} else {
|
|
//tiled version
|
var currentRes = this.map.getResolution();
|
var colidx = Math.floor((bounds.left-this.maxExtent.left)/currentRes);
|
colidx = Math.round(colidx/this.tileSize.w);
|
var rowidx = Math.floor((this.maxExtent.top-bounds.top)/currentRes);
|
rowidx = Math.round(rowidx/this.tileSize.h);
|
|
if (this.useHttpTile){
|
url = this.getImageFilePath(
|
{
|
tilecol: colidx,
|
tilerow: rowidx,
|
scaleindex: this.resolutions.length - this.map.zoom - 1
|
});
|
|
} else {
|
url = this.getFullRequestString(
|
{
|
tilecol: colidx,
|
tilerow: rowidx,
|
scaleindex: this.resolutions.length - this.map.zoom - 1
|
});
|
}
|
}
|
return url;
|
},
|
|
/**
|
* Method: getFullRequestString
|
* getFullRequestString on MapGuide layers is special, because we
|
* do a regular expression replace on ',' in parameters to '+'.
|
* This is why it is subclassed here.
|
*
|
* Parameters:
|
* altUrl - {String} Alternative base URL to use.
|
*
|
* Returns:
|
* {String} A string with the layer's url appropriately encoded for MapGuide
|
*/
|
getFullRequestString:function(newParams, altUrl) {
|
// use layer's url unless altUrl passed in
|
var url = (altUrl == null) ? this.url : altUrl;
|
|
// if url is not a string, it should be an array of strings,
|
// in which case we will randomly select one of them in order
|
// to evenly distribute requests to different urls.
|
if (typeof url == "object") {
|
url = url[Math.floor(Math.random()*url.length)];
|
}
|
// requestString always starts with url
|
var requestString = url;
|
|
// create a new params hashtable with all the layer params and the
|
// new params together. then convert to string
|
var allParams = OpenLayers.Util.extend({}, this.params);
|
allParams = OpenLayers.Util.extend(allParams, newParams);
|
// ignore parameters that are already in the url search string
|
var urlParams = OpenLayers.Util.upperCaseObject(
|
OpenLayers.Util.getParameters(url));
|
for(var key in allParams) {
|
if(key.toUpperCase() in urlParams) {
|
delete allParams[key];
|
}
|
}
|
var paramsString = OpenLayers.Util.getParameterString(allParams);
|
|
/* MapGuide needs '+' seperating things like bounds/height/width.
|
Since typically this is URL encoded, we use a slight hack: we
|
depend on the list-like functionality of getParameterString to
|
leave ',' only in the case of list items (since otherwise it is
|
encoded) then do a regular expression replace on the , characters
|
to '+' */
|
paramsString = paramsString.replace(/,/g, "+");
|
|
if (paramsString != "") {
|
var lastServerChar = url.charAt(url.length - 1);
|
if ((lastServerChar == "&") || (lastServerChar == "?")) {
|
requestString += paramsString;
|
} else {
|
if (url.indexOf('?') == -1) {
|
//serverPath has no ? -- add one
|
requestString += '?' + paramsString;
|
} else {
|
//serverPath contains ?, so must already have paramsString at the end
|
requestString += '&' + paramsString;
|
}
|
}
|
}
|
return requestString;
|
},
|
|
/**
|
* Method: getImageFilePath
|
* special handler to request mapguide tiles from an http exposed tilecache
|
*
|
* Parameters:
|
* altUrl - {String} Alternative base URL to use.
|
*
|
* Returns:
|
* {String} A string with the url for the tile image
|
*/
|
getImageFilePath:function(newParams, altUrl) {
|
// use layer's url unless altUrl passed in
|
var url = (altUrl == null) ? this.url : altUrl;
|
|
// if url is not a string, it should be an array of strings,
|
// in which case we will randomly select one of them in order
|
// to evenly distribute requests to different urls.
|
if (typeof url == "object") {
|
url = url[Math.floor(Math.random()*url.length)];
|
}
|
// requestString always starts with url
|
var requestString = url;
|
|
var tileRowGroup = "";
|
var tileColGroup = "";
|
|
if (newParams.tilerow < 0) {
|
tileRowGroup = '-';
|
}
|
|
if (newParams.tilerow == 0 ) {
|
tileRowGroup += '0';
|
} else {
|
tileRowGroup += Math.floor(Math.abs(newParams.tilerow/this.params.tileRowsPerFolder)) * this.params.tileRowsPerFolder;
|
}
|
|
if (newParams.tilecol < 0) {
|
tileColGroup = '-';
|
}
|
|
if (newParams.tilecol == 0) {
|
tileColGroup += '0';
|
} else {
|
tileColGroup += Math.floor(Math.abs(newParams.tilecol/this.params.tileColumnsPerFolder)) * this.params.tileColumnsPerFolder;
|
}
|
|
var tilePath = '/S' + Math.floor(newParams.scaleindex)
|
+ '/' + this.params.basemaplayergroupname
|
+ '/R' + tileRowGroup
|
+ '/C' + tileColGroup
|
+ '/' + (newParams.tilerow % this.params.tileRowsPerFolder)
|
+ '_' + (newParams.tilecol % this.params.tileColumnsPerFolder)
|
+ '.' + this.params.format;
|
|
if (this.params.querystring) {
|
tilePath += "?" + this.params.querystring;
|
}
|
|
requestString += tilePath;
|
return requestString;
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.MapGuide"
|
});
|
/* ======================================================================
|
OpenLayers/Control/Measure.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/Feature/Vector.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.Measure
|
* Allows for drawing of features for measurements.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.Measure = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* APIProperty: events
|
* {<OpenLayers.Events>} Events instance for listeners and triggering
|
* control specific events.
|
*
|
* Register a listener for a particular event with the following syntax:
|
* (code)
|
* control.events.register(type, obj, listener);
|
* (end)
|
*
|
* Supported event types (in addition to those from <OpenLayers.Control.events>):
|
* measure - Triggered when a measurement sketch is complete. Listeners
|
* will receive an event with measure, units, order, and geometry
|
* properties.
|
* measurepartial - Triggered when a new point is added to the
|
* measurement sketch or if the <immediate> property is true and the
|
* measurement sketch is modified. Listeners receive an event with measure,
|
* units, order, and geometry.
|
*/
|
|
/**
|
* APIProperty: handlerOptions
|
* {Object} Used to set non-default properties on the control's handler
|
*/
|
|
/**
|
* Property: callbacks
|
* {Object} The functions that are sent to the handler for callback
|
*/
|
callbacks: null,
|
|
/**
|
* APIProperty: displaySystem
|
* {String} Display system for output measurements. Supported values
|
* are 'english', 'metric', and 'geographic'. Default is 'metric'.
|
*/
|
displaySystem: 'metric',
|
|
/**
|
* APIProperty: geodesic
|
* {Boolean} Calculate geodesic metrics instead of planar metrics. This
|
* requires that geometries can be transformed into Geographic/WGS84
|
* (if that is not already the map projection). Default is false.
|
*/
|
geodesic: false,
|
|
/**
|
* Property: displaySystemUnits
|
* {Object} Units for various measurement systems. Values are arrays
|
* of unit abbreviations (from OpenLayers.INCHES_PER_UNIT) in decreasing
|
* order of length.
|
*/
|
displaySystemUnits: {
|
geographic: ['dd'],
|
english: ['mi', 'ft', 'in'],
|
metric: ['km', 'm']
|
},
|
|
/**
|
* Property: delay
|
* {Number} Number of milliseconds between clicks before the event is
|
* considered a double-click. The "measurepartial" event will not
|
* be triggered if the sketch is completed within this time. This
|
* is required for IE where creating a browser reflow (if a listener
|
* is modifying the DOM by displaying the measurement values) messes
|
* with the dblclick listener in the sketch handler.
|
*/
|
partialDelay: 300,
|
|
/**
|
* Property: delayedTrigger
|
* {Number} Timeout id of trigger for measurepartial.
|
*/
|
delayedTrigger: null,
|
|
/**
|
* APIProperty: persist
|
* {Boolean} Keep the temporary measurement sketch drawn after the
|
* measurement is complete. The geometry will persist until a new
|
* measurement is started, the control is deactivated, or <cancel> is
|
* called.
|
*/
|
persist: false,
|
|
/**
|
* APIProperty: immediate
|
* {Boolean} Activates the immediate measurement so that the "measurepartial"
|
* event is also fired once the measurement sketch is modified.
|
* Default is false.
|
*/
|
immediate : false,
|
|
/**
|
* Constructor: OpenLayers.Control.Measure
|
*
|
* Parameters:
|
* handler - {<OpenLayers.Handler>}
|
* options - {Object}
|
*/
|
initialize: function(handler, options) {
|
OpenLayers.Control.prototype.initialize.apply(this, [options]);
|
var callbacks = {done: this.measureComplete,
|
point: this.measurePartial};
|
if (this.immediate){
|
callbacks.modify = this.measureImmediate;
|
}
|
this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);
|
|
// let the handler options override, so old code that passes 'persist'
|
// directly to the handler does not need an update
|
this.handlerOptions = OpenLayers.Util.extend(
|
{persist: this.persist}, this.handlerOptions
|
);
|
this.handler = new handler(this, this.callbacks, this.handlerOptions);
|
},
|
|
/**
|
* APIMethod: deactivate
|
*/
|
deactivate: function() {
|
this.cancelDelay();
|
return OpenLayers.Control.prototype.deactivate.apply(this, arguments);
|
},
|
|
/**
|
* APIMethod: cancel
|
* Stop the control from measuring. If <persist> is true, the temporary
|
* sketch will be erased.
|
*/
|
cancel: function() {
|
this.cancelDelay();
|
this.handler.cancel();
|
},
|
|
/**
|
* APIMethod: setImmediate
|
* Sets the <immediate> property. Changes the activity of immediate
|
* measurement.
|
*/
|
setImmediate: function(immediate) {
|
this.immediate = immediate;
|
if (this.immediate){
|
this.callbacks.modify = this.measureImmediate;
|
} else {
|
delete this.callbacks.modify;
|
}
|
},
|
|
/**
|
* Method: updateHandler
|
*
|
* Parameters:
|
* handler - {Function} One of the sketch handler constructors.
|
* options - {Object} Options for the handler.
|
*/
|
updateHandler: function(handler, options) {
|
var active = this.active;
|
if(active) {
|
this.deactivate();
|
}
|
this.handler = new handler(this, this.callbacks, options);
|
if(active) {
|
this.activate();
|
}
|
},
|
|
/**
|
* Method: measureComplete
|
* Called when the measurement sketch is done.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
*/
|
measureComplete: function(geometry) {
|
this.cancelDelay();
|
this.measure(geometry, "measure");
|
},
|
|
/**
|
* Method: measurePartial
|
* Called each time a new point is added to the measurement sketch.
|
*
|
* Parameters:
|
* point - {<OpenLayers.Geometry.Point>} The last point added.
|
* geometry - {<OpenLayers.Geometry>} The sketch geometry.
|
*/
|
measurePartial: function(point, geometry) {
|
this.cancelDelay();
|
geometry = geometry.clone();
|
// when we're wating for a dblclick, we have to trigger measurepartial
|
// after some delay to deal with reflow issues in IE
|
if (this.handler.freehandMode(this.handler.evt)) {
|
// no dblclick in freehand mode
|
this.measure(geometry, "measurepartial");
|
} else {
|
this.delayedTrigger = window.setTimeout(
|
OpenLayers.Function.bind(function() {
|
this.delayedTrigger = null;
|
this.measure(geometry, "measurepartial");
|
}, this),
|
this.partialDelay
|
);
|
}
|
},
|
|
/**
|
* Method: measureImmediate
|
* Called each time the measurement sketch is modified.
|
*
|
* Parameters:
|
* point - {<OpenLayers.Geometry.Point>} The point at the mouse position.
|
* feature - {<OpenLayers.Feature.Vector>} The sketch feature.
|
* drawing - {Boolean} Indicates whether we're currently drawing.
|
*/
|
measureImmediate : function(point, feature, drawing) {
|
if (drawing && !this.handler.freehandMode(this.handler.evt)) {
|
this.cancelDelay();
|
this.measure(feature.geometry, "measurepartial");
|
}
|
},
|
|
/**
|
* Method: cancelDelay
|
* Cancels the delay measurement that measurePartial began.
|
*/
|
cancelDelay: function() {
|
if (this.delayedTrigger !== null) {
|
window.clearTimeout(this.delayedTrigger);
|
this.delayedTrigger = null;
|
}
|
},
|
|
/**
|
* Method: measure
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
* eventType - {String}
|
*/
|
measure: function(geometry, eventType) {
|
var stat, order;
|
if(geometry.CLASS_NAME.indexOf('LineString') > -1) {
|
stat = this.getBestLength(geometry);
|
order = 1;
|
} else {
|
stat = this.getBestArea(geometry);
|
order = 2;
|
}
|
this.events.triggerEvent(eventType, {
|
measure: stat[0],
|
units: stat[1],
|
order: order,
|
geometry: geometry
|
});
|
},
|
|
/**
|
* Method: getBestArea
|
* Based on the <displaySystem> returns the area of a geometry.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {Array([Float, String])} Returns a two item array containing the
|
* area and the units abbreviation.
|
*/
|
getBestArea: function(geometry) {
|
var units = this.displaySystemUnits[this.displaySystem];
|
var unit, area;
|
for(var i=0, len=units.length; i<len; ++i) {
|
unit = units[i];
|
area = this.getArea(geometry, unit);
|
if(area > 1) {
|
break;
|
}
|
}
|
return [area, unit];
|
},
|
|
/**
|
* Method: getArea
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
* units - {String} Unit abbreviation
|
*
|
* Returns:
|
* {Float} The geometry area in the given units.
|
*/
|
getArea: function(geometry, units) {
|
var area, geomUnits;
|
if(this.geodesic) {
|
area = geometry.getGeodesicArea(this.map.getProjectionObject());
|
geomUnits = "m";
|
} else {
|
area = geometry.getArea();
|
geomUnits = this.map.getUnits();
|
}
|
var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];
|
if(inPerDisplayUnit) {
|
var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits];
|
area *= Math.pow((inPerMapUnit / inPerDisplayUnit), 2);
|
}
|
return area;
|
},
|
|
/**
|
* Method: getBestLength
|
* Based on the <displaySystem> returns the length of a geometry.
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {Array([Float, String])} Returns a two item array containing the
|
* length and the units abbreviation.
|
*/
|
getBestLength: function(geometry) {
|
var units = this.displaySystemUnits[this.displaySystem];
|
var unit, length;
|
for(var i=0, len=units.length; i<len; ++i) {
|
unit = units[i];
|
length = this.getLength(geometry, unit);
|
if(length > 1) {
|
break;
|
}
|
}
|
return [length, unit];
|
},
|
|
/**
|
* Method: getLength
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
* units - {String} Unit abbreviation
|
*
|
* Returns:
|
* {Float} The geometry length in the given units.
|
*/
|
getLength: function(geometry, units) {
|
var length, geomUnits;
|
if(this.geodesic) {
|
length = geometry.getGeodesicLength(this.map.getProjectionObject());
|
geomUnits = "m";
|
} else {
|
length = geometry.getLength();
|
geomUnits = this.map.getUnits();
|
}
|
var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];
|
if(inPerDisplayUnit) {
|
var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits];
|
length *= (inPerMapUnit / inPerDisplayUnit);
|
}
|
return length;
|
},
|
|
CLASS_NAME: "OpenLayers.Control.Measure"
|
});
|
/* ======================================================================
|
OpenLayers/Format/WMC/v1_0_0.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/WMC/v1.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WMC.v1_0_0
|
* Read and write WMC version 1.0.0.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.WMC.v1>
|
*/
|
OpenLayers.Format.WMC.v1_0_0 = OpenLayers.Class(
|
OpenLayers.Format.WMC.v1, {
|
|
/**
|
* Constant: VERSION
|
* {String} 1.0.0
|
*/
|
VERSION: "1.0.0",
|
|
/**
|
* Property: schemaLocation
|
* {String} http://www.opengis.net/context
|
* http://schemas.opengis.net/context/1.0.0/context.xsd
|
*/
|
schemaLocation: "http://www.opengis.net/context http://schemas.opengis.net/context/1.0.0/context.xsd",
|
|
/**
|
* Constructor: OpenLayers.Format.WMC.v1_0_0
|
* Instances of this class are not created directly. Use the
|
* <OpenLayers.Format.WMC> constructor instead.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
initialize: function(options) {
|
OpenLayers.Format.WMC.v1.prototype.initialize.apply(
|
this, [options]
|
);
|
},
|
|
/**
|
* Method: read_wmc_SRS
|
*/
|
read_wmc_SRS: function(layerContext, node) {
|
var srs = this.getChildValue(node);
|
if (typeof layerContext.projections != "object") {
|
layerContext.projections = {};
|
}
|
var values = srs.split(/ +/);
|
for (var i=0, len=values.length; i<len; i++) {
|
layerContext.projections[values[i]] = true;
|
}
|
},
|
|
/**
|
* Method: write_wmc_Layer
|
* Create a Layer node given a layer context object. This method adds
|
* elements specific to version 1.0.0.
|
*
|
* Parameters:
|
* context - {Object} A layer context object.}
|
*
|
* Returns:
|
* {Element} A WMC Layer element node.
|
*/
|
write_wmc_Layer: function(context) {
|
var node = OpenLayers.Format.WMC.v1.prototype.write_wmc_Layer.apply(
|
this, [context]
|
);
|
|
// optional SRS element(s)
|
if (context.srs) {
|
var projections = [];
|
for(var name in context.srs) {
|
projections.push(name);
|
}
|
node.appendChild(this.createElementDefaultNS("SRS", projections.join(" ")));
|
}
|
|
// optional FormatList element
|
node.appendChild(this.write_wmc_FormatList(context));
|
|
// optional StyleList element
|
node.appendChild(this.write_wmc_StyleList(context));
|
|
// optional DimensionList element
|
if (context.dimensions) {
|
node.appendChild(this.write_wmc_DimensionList(context));
|
}
|
|
// OpenLayers specific properties go in an Extension element
|
node.appendChild(this.write_wmc_LayerExtension(context));
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WMC.v1_0_0"
|
|
});
|
/* ======================================================================
|
OpenLayers/Popup/Anchored.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Popup.js
|
*/
|
|
/**
|
* Class: OpenLayers.Popup.Anchored
|
*
|
* Inherits from:
|
* - <OpenLayers.Popup>
|
*/
|
OpenLayers.Popup.Anchored =
|
OpenLayers.Class(OpenLayers.Popup, {
|
|
/**
|
* Property: relativePosition
|
* {String} Relative position of the popup ("br", "tr", "tl" or "bl").
|
*/
|
relativePosition: null,
|
|
/**
|
* APIProperty: keepInMap
|
* {Boolean} If panMapIfOutOfView is false, and this property is true,
|
* contrain the popup such that it always fits in the available map
|
* space. By default, this is set. If you are creating popups that are
|
* near map edges and not allowing pannning, and especially if you have
|
* a popup which has a fixedRelativePosition, setting this to false may
|
* be a smart thing to do.
|
*
|
* For anchored popups, default is true, since subclasses will
|
* usually want this functionality.
|
*/
|
keepInMap: true,
|
|
/**
|
* Property: anchor
|
* {Object} Object to which we'll anchor the popup. Must expose a
|
* 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>).
|
*/
|
anchor: null,
|
|
/**
|
* Constructor: OpenLayers.Popup.Anchored
|
*
|
* Parameters:
|
* id - {String}
|
* lonlat - {<OpenLayers.LonLat>}
|
* contentSize - {<OpenLayers.Size>}
|
* contentHTML - {String}
|
* anchor - {Object} Object which must expose a 'size' <OpenLayers.Size>
|
* and 'offset' <OpenLayers.Pixel> (generally an <OpenLayers.Icon>).
|
* closeBox - {Boolean}
|
* closeBoxCallback - {Function} Function to be called on closeBox click.
|
*/
|
initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
|
closeBoxCallback) {
|
var newArguments = [
|
id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback
|
];
|
OpenLayers.Popup.prototype.initialize.apply(this, newArguments);
|
|
this.anchor = (anchor != null) ? anchor
|
: { size: new OpenLayers.Size(0,0),
|
offset: new OpenLayers.Pixel(0,0)};
|
},
|
|
/**
|
* APIMethod: destroy
|
*/
|
destroy: function() {
|
this.anchor = null;
|
this.relativePosition = null;
|
|
OpenLayers.Popup.prototype.destroy.apply(this, arguments);
|
},
|
|
/**
|
* APIMethod: show
|
* Overridden from Popup since user might hide popup and then show() it
|
* in a new location (meaning we might want to update the relative
|
* position on the show)
|
*/
|
show: function() {
|
this.updatePosition();
|
OpenLayers.Popup.prototype.show.apply(this, arguments);
|
},
|
|
/**
|
* Method: moveTo
|
* Since the popup is moving to a new px, it might need also to be moved
|
* relative to where the marker is. We first calculate the new
|
* relativePosition, and then we calculate the new px where we will
|
* put the popup, based on the new relative position.
|
*
|
* If the relativePosition has changed, we must also call
|
* updateRelativePosition() to make any visual changes to the popup
|
* which are associated with putting it in a new relativePosition.
|
*
|
* Parameters:
|
* px - {<OpenLayers.Pixel>}
|
*/
|
moveTo: function(px) {
|
var oldRelativePosition = this.relativePosition;
|
this.relativePosition = this.calculateRelativePosition(px);
|
|
OpenLayers.Popup.prototype.moveTo.call(this, this.calculateNewPx(px));
|
|
//if this move has caused the popup to change its relative position,
|
// we need to make the appropriate cosmetic changes.
|
if (this.relativePosition != oldRelativePosition) {
|
this.updateRelativePosition();
|
}
|
},
|
|
/**
|
* APIMethod: setSize
|
*
|
* Parameters:
|
* contentSize - {<OpenLayers.Size>} the new size for the popup's
|
* contents div (in pixels).
|
*/
|
setSize:function(contentSize) {
|
OpenLayers.Popup.prototype.setSize.apply(this, arguments);
|
|
if ((this.lonlat) && (this.map)) {
|
var px = this.map.getLayerPxFromLonLat(this.lonlat);
|
this.moveTo(px);
|
}
|
},
|
|
/**
|
* Method: calculateRelativePosition
|
*
|
* Parameters:
|
* px - {<OpenLayers.Pixel>}
|
*
|
* Returns:
|
* {String} The relative position ("br" "tr" "tl" "bl") at which the popup
|
* should be placed.
|
*/
|
calculateRelativePosition:function(px) {
|
var lonlat = this.map.getLonLatFromLayerPx(px);
|
|
var extent = this.map.getExtent();
|
var quadrant = extent.determineQuadrant(lonlat);
|
|
return OpenLayers.Bounds.oppositeQuadrant(quadrant);
|
},
|
|
/**
|
* Method: updateRelativePosition
|
* The popup has been moved to a new relative location, so we may want to
|
* make some cosmetic adjustments to it.
|
*
|
* Note that in the classic Anchored popup, there is nothing to do
|
* here, since the popup looks exactly the same in all four positions.
|
* Subclasses such as Framed, however, will want to do something
|
* special here.
|
*/
|
updateRelativePosition: function() {
|
//to be overridden by subclasses
|
},
|
|
/**
|
* Method: calculateNewPx
|
*
|
* Parameters:
|
* px - {<OpenLayers.Pixel>}
|
*
|
* Returns:
|
* {<OpenLayers.Pixel>} The the new px position of the popup on the screen
|
* relative to the passed-in px.
|
*/
|
calculateNewPx:function(px) {
|
var newPx = px.offset(this.anchor.offset);
|
|
//use contentSize if size is not already set
|
var size = this.size || this.contentSize;
|
|
var top = (this.relativePosition.charAt(0) == 't');
|
newPx.y += (top) ? -size.h : this.anchor.size.h;
|
|
var left = (this.relativePosition.charAt(1) == 'l');
|
newPx.x += (left) ? -size.w : this.anchor.size.w;
|
|
return newPx;
|
},
|
|
CLASS_NAME: "OpenLayers.Popup.Anchored"
|
});
|
/* ======================================================================
|
OpenLayers/Popup/Framed.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Popup/Anchored.js
|
*/
|
|
/**
|
* Class: OpenLayers.Popup.Framed
|
*
|
* Inherits from:
|
* - <OpenLayers.Popup.Anchored>
|
*/
|
OpenLayers.Popup.Framed =
|
OpenLayers.Class(OpenLayers.Popup.Anchored, {
|
|
/**
|
* Property: imageSrc
|
* {String} location of the image to be used as the popup frame
|
*/
|
imageSrc: null,
|
|
/**
|
* Property: imageSize
|
* {<OpenLayers.Size>} Size (measured in pixels) of the image located
|
* by the 'imageSrc' property.
|
*/
|
imageSize: null,
|
|
/**
|
* APIProperty: isAlphaImage
|
* {Boolean} The image has some alpha and thus needs to use the alpha
|
* image hack. Note that setting this to true will have no noticeable
|
* effect in FF or IE7 browsers, but will all but crush the ie6
|
* browser.
|
* Default is false.
|
*/
|
isAlphaImage: false,
|
|
/**
|
* Property: positionBlocks
|
* {Object} Hash of different position blocks (Object/Hashs). Each block
|
* will be keyed by a two-character 'relativePosition'
|
* code string (ie "tl", "tr", "bl", "br"). Block properties are
|
* 'offset', 'padding' (self-explanatory), and finally the 'blocks'
|
* parameter, which is an array of the block objects.
|
*
|
* Each block object must have 'size', 'anchor', and 'position'
|
* properties.
|
*
|
* Note that positionBlocks should never be modified at runtime.
|
*/
|
positionBlocks: null,
|
|
/**
|
* Property: blocks
|
* {Array[Object]} Array of objects, each of which is one "block" of the
|
* popup. Each block has a 'div' and an 'image' property, both of
|
* which are DOMElements, and the latter of which is appended to the
|
* former. These are reused as the popup goes changing positions for
|
* great economy and elegance.
|
*/
|
blocks: null,
|
|
/**
|
* APIProperty: fixedRelativePosition
|
* {Boolean} We want the framed popup to work dynamically placed relative
|
* to its anchor but also in just one fixed position. A well designed
|
* framed popup will have the pixels and logic to display itself in
|
* any of the four relative positions, but (understandably), this will
|
* not be the case for all of them. By setting this property to 'true',
|
* framed popup will not recalculate for the best placement each time
|
* it's open, but will always open the same way.
|
* Note that if this is set to true, it is generally advisable to also
|
* set the 'panIntoView' property to true so that the popup can be
|
* scrolled into view (since it will often be offscreen on open)
|
* Default is false.
|
*/
|
fixedRelativePosition: false,
|
|
/**
|
* Constructor: OpenLayers.Popup.Framed
|
*
|
* Parameters:
|
* id - {String}
|
* lonlat - {<OpenLayers.LonLat>}
|
* contentSize - {<OpenLayers.Size>}
|
* contentHTML - {String}
|
* anchor - {Object} Object to which we'll anchor the popup. Must expose
|
* a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>)
|
* (Note that this is generally an <OpenLayers.Icon>).
|
* closeBox - {Boolean}
|
* closeBoxCallback - {Function} Function to be called on closeBox click.
|
*/
|
initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
|
closeBoxCallback) {
|
|
OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments);
|
|
if (this.fixedRelativePosition) {
|
//based on our decided relativePostion, set the current padding
|
// this keeps us from getting into trouble
|
this.updateRelativePosition();
|
|
//make calculateRelativePosition always return the specified
|
// fixed position.
|
this.calculateRelativePosition = function(px) {
|
return this.relativePosition;
|
};
|
}
|
|
this.contentDiv.style.position = "absolute";
|
this.contentDiv.style.zIndex = 1;
|
|
if (closeBox) {
|
this.closeDiv.style.zIndex = 1;
|
}
|
|
this.groupDiv.style.position = "absolute";
|
this.groupDiv.style.top = "0px";
|
this.groupDiv.style.left = "0px";
|
this.groupDiv.style.height = "100%";
|
this.groupDiv.style.width = "100%";
|
},
|
|
/**
|
* APIMethod: destroy
|
*/
|
destroy: function() {
|
this.imageSrc = null;
|
this.imageSize = null;
|
this.isAlphaImage = null;
|
|
this.fixedRelativePosition = false;
|
this.positionBlocks = null;
|
|
//remove our blocks
|
for(var i = 0; i < this.blocks.length; i++) {
|
var block = this.blocks[i];
|
|
if (block.image) {
|
block.div.removeChild(block.image);
|
}
|
block.image = null;
|
|
if (block.div) {
|
this.groupDiv.removeChild(block.div);
|
}
|
block.div = null;
|
}
|
this.blocks = null;
|
|
OpenLayers.Popup.Anchored.prototype.destroy.apply(this, arguments);
|
},
|
|
/**
|
* APIMethod: setBackgroundColor
|
*/
|
setBackgroundColor:function(color) {
|
//does nothing since the framed popup's entire scheme is based on a
|
// an image -- changing the background color makes no sense.
|
},
|
|
/**
|
* APIMethod: setBorder
|
*/
|
setBorder:function() {
|
//does nothing since the framed popup's entire scheme is based on a
|
// an image -- changing the popup's border makes no sense.
|
},
|
|
/**
|
* Method: setOpacity
|
* Sets the opacity of the popup.
|
*
|
* Parameters:
|
* opacity - {float} A value between 0.0 (transparent) and 1.0 (solid).
|
*/
|
setOpacity:function(opacity) {
|
//does nothing since we suppose that we'll never apply an opacity
|
// to a framed popup
|
},
|
|
/**
|
* APIMethod: setSize
|
* Overridden here, because we need to update the blocks whenever the size
|
* of the popup has changed.
|
*
|
* Parameters:
|
* contentSize - {<OpenLayers.Size>} the new size for the popup's
|
* contents div (in pixels).
|
*/
|
setSize:function(contentSize) {
|
OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments);
|
|
this.updateBlocks();
|
},
|
|
/**
|
* Method: updateRelativePosition
|
* When the relative position changes, we need to set the new padding
|
* BBOX on the popup, reposition the close div, and update the blocks.
|
*/
|
updateRelativePosition: function() {
|
|
//update the padding
|
this.padding = this.positionBlocks[this.relativePosition].padding;
|
|
//update the position of our close box to new padding
|
if (this.closeDiv) {
|
// use the content div's css padding to determine if we should
|
// padd the close div
|
var contentDivPadding = this.getContentDivPadding();
|
|
this.closeDiv.style.right = contentDivPadding.right +
|
this.padding.right + "px";
|
this.closeDiv.style.top = contentDivPadding.top +
|
this.padding.top + "px";
|
}
|
|
this.updateBlocks();
|
},
|
|
/**
|
* Method: calculateNewPx
|
* Besides the standard offset as determined by the Anchored class, our
|
* Framed popups have a special 'offset' property for each of their
|
* positions, which is used to offset the popup relative to its anchor.
|
*
|
* Parameters:
|
* px - {<OpenLayers.Pixel>}
|
*
|
* Returns:
|
* {<OpenLayers.Pixel>} The the new px position of the popup on the screen
|
* relative to the passed-in px.
|
*/
|
calculateNewPx:function(px) {
|
var newPx = OpenLayers.Popup.Anchored.prototype.calculateNewPx.apply(
|
this, arguments
|
);
|
|
newPx = newPx.offset(this.positionBlocks[this.relativePosition].offset);
|
|
return newPx;
|
},
|
|
/**
|
* Method: createBlocks
|
*/
|
createBlocks: function() {
|
this.blocks = [];
|
|
//since all positions contain the same number of blocks, we can
|
// just pick the first position and use its blocks array to create
|
// our blocks array
|
var firstPosition = null;
|
for(var key in this.positionBlocks) {
|
firstPosition = key;
|
break;
|
}
|
|
var position = this.positionBlocks[firstPosition];
|
for (var i = 0; i < position.blocks.length; i++) {
|
|
var block = {};
|
this.blocks.push(block);
|
|
var divId = this.id + '_FrameDecorationDiv_' + i;
|
block.div = OpenLayers.Util.createDiv(divId,
|
null, null, null, "absolute", null, "hidden", null
|
);
|
|
var imgId = this.id + '_FrameDecorationImg_' + i;
|
var imageCreator =
|
(this.isAlphaImage) ? OpenLayers.Util.createAlphaImageDiv
|
: OpenLayers.Util.createImage;
|
|
block.image = imageCreator(imgId,
|
null, this.imageSize, this.imageSrc,
|
"absolute", null, null, null
|
);
|
|
block.div.appendChild(block.image);
|
this.groupDiv.appendChild(block.div);
|
}
|
},
|
|
/**
|
* Method: updateBlocks
|
* Internal method, called on initialize and when the popup's relative
|
* position has changed. This function takes care of re-positioning
|
* the popup's blocks in their appropropriate places.
|
*/
|
updateBlocks: function() {
|
if (!this.blocks) {
|
this.createBlocks();
|
}
|
|
if (this.size && this.relativePosition) {
|
var position = this.positionBlocks[this.relativePosition];
|
for (var i = 0; i < position.blocks.length; i++) {
|
|
var positionBlock = position.blocks[i];
|
var block = this.blocks[i];
|
|
// adjust sizes
|
var l = positionBlock.anchor.left;
|
var b = positionBlock.anchor.bottom;
|
var r = positionBlock.anchor.right;
|
var t = positionBlock.anchor.top;
|
|
//note that we use the isNaN() test here because if the
|
// size object is initialized with a "auto" parameter, the
|
// size constructor calls parseFloat() on the string,
|
// which will turn it into NaN
|
//
|
var w = (isNaN(positionBlock.size.w)) ? this.size.w - (r + l)
|
: positionBlock.size.w;
|
|
var h = (isNaN(positionBlock.size.h)) ? this.size.h - (b + t)
|
: positionBlock.size.h;
|
|
block.div.style.width = (w < 0 ? 0 : w) + 'px';
|
block.div.style.height = (h < 0 ? 0 : h) + 'px';
|
|
block.div.style.left = (l != null) ? l + 'px' : '';
|
block.div.style.bottom = (b != null) ? b + 'px' : '';
|
block.div.style.right = (r != null) ? r + 'px' : '';
|
block.div.style.top = (t != null) ? t + 'px' : '';
|
|
block.image.style.left = positionBlock.position.x + 'px';
|
block.image.style.top = positionBlock.position.y + 'px';
|
}
|
|
this.contentDiv.style.left = this.padding.left + "px";
|
this.contentDiv.style.top = this.padding.top + "px";
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Popup.Framed"
|
});
|
/* ======================================================================
|
OpenLayers/Popup/FramedCloud.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Popup/Framed.js
|
* @requires OpenLayers/Util.js
|
* @requires OpenLayers/BaseTypes/Bounds.js
|
* @requires OpenLayers/BaseTypes/Pixel.js
|
* @requires OpenLayers/BaseTypes/Size.js
|
*/
|
|
/**
|
* Class: OpenLayers.Popup.FramedCloud
|
*
|
* Inherits from:
|
* - <OpenLayers.Popup.Framed>
|
*/
|
OpenLayers.Popup.FramedCloud =
|
OpenLayers.Class(OpenLayers.Popup.Framed, {
|
|
/**
|
* Property: contentDisplayClass
|
* {String} The CSS class of the popup content div.
|
*/
|
contentDisplayClass: "olFramedCloudPopupContent",
|
|
/**
|
* APIProperty: autoSize
|
* {Boolean} Framed Cloud is autosizing by default.
|
*/
|
autoSize: true,
|
|
/**
|
* APIProperty: panMapIfOutOfView
|
* {Boolean} Framed Cloud does pan into view by default.
|
*/
|
panMapIfOutOfView: true,
|
|
/**
|
* APIProperty: imageSize
|
* {<OpenLayers.Size>}
|
*/
|
imageSize: new OpenLayers.Size(1276, 736),
|
|
/**
|
* APIProperty: isAlphaImage
|
* {Boolean} The FramedCloud does not use an alpha image (in honor of the
|
* good ie6 folk out there)
|
*/
|
isAlphaImage: false,
|
|
/**
|
* APIProperty: fixedRelativePosition
|
* {Boolean} The Framed Cloud popup works in just one fixed position.
|
*/
|
fixedRelativePosition: false,
|
|
/**
|
* Property: positionBlocks
|
* {Object} Hash of differen position blocks, keyed by relativePosition
|
* two-character code string (ie "tl", "tr", "bl", "br")
|
*/
|
positionBlocks: {
|
"tl": {
|
'offset': new OpenLayers.Pixel(44, 0),
|
'padding': new OpenLayers.Bounds(8, 40, 8, 9),
|
'blocks': [
|
{ // top-left
|
size: new OpenLayers.Size('auto', 'auto'),
|
anchor: new OpenLayers.Bounds(0, 51, 22, 0),
|
position: new OpenLayers.Pixel(0, 0)
|
},
|
{ //top-right
|
size: new OpenLayers.Size(22, 'auto'),
|
anchor: new OpenLayers.Bounds(null, 50, 0, 0),
|
position: new OpenLayers.Pixel(-1238, 0)
|
},
|
{ //bottom-left
|
size: new OpenLayers.Size('auto', 19),
|
anchor: new OpenLayers.Bounds(0, 32, 22, null),
|
position: new OpenLayers.Pixel(0, -631)
|
},
|
{ //bottom-right
|
size: new OpenLayers.Size(22, 18),
|
anchor: new OpenLayers.Bounds(null, 32, 0, null),
|
position: new OpenLayers.Pixel(-1238, -632)
|
},
|
{ // stem
|
size: new OpenLayers.Size(81, 35),
|
anchor: new OpenLayers.Bounds(null, 0, 0, null),
|
position: new OpenLayers.Pixel(0, -688)
|
}
|
]
|
},
|
"tr": {
|
'offset': new OpenLayers.Pixel(-45, 0),
|
'padding': new OpenLayers.Bounds(8, 40, 8, 9),
|
'blocks': [
|
{ // top-left
|
size: new OpenLayers.Size('auto', 'auto'),
|
anchor: new OpenLayers.Bounds(0, 51, 22, 0),
|
position: new OpenLayers.Pixel(0, 0)
|
},
|
{ //top-right
|
size: new OpenLayers.Size(22, 'auto'),
|
anchor: new OpenLayers.Bounds(null, 50, 0, 0),
|
position: new OpenLayers.Pixel(-1238, 0)
|
},
|
{ //bottom-left
|
size: new OpenLayers.Size('auto', 19),
|
anchor: new OpenLayers.Bounds(0, 32, 22, null),
|
position: new OpenLayers.Pixel(0, -631)
|
},
|
{ //bottom-right
|
size: new OpenLayers.Size(22, 19),
|
anchor: new OpenLayers.Bounds(null, 32, 0, null),
|
position: new OpenLayers.Pixel(-1238, -631)
|
},
|
{ // stem
|
size: new OpenLayers.Size(81, 35),
|
anchor: new OpenLayers.Bounds(0, 0, null, null),
|
position: new OpenLayers.Pixel(-215, -687)
|
}
|
]
|
},
|
"bl": {
|
'offset': new OpenLayers.Pixel(45, 0),
|
'padding': new OpenLayers.Bounds(8, 9, 8, 40),
|
'blocks': [
|
{ // top-left
|
size: new OpenLayers.Size('auto', 'auto'),
|
anchor: new OpenLayers.Bounds(0, 21, 22, 32),
|
position: new OpenLayers.Pixel(0, 0)
|
},
|
{ //top-right
|
size: new OpenLayers.Size(22, 'auto'),
|
anchor: new OpenLayers.Bounds(null, 21, 0, 32),
|
position: new OpenLayers.Pixel(-1238, 0)
|
},
|
{ //bottom-left
|
size: new OpenLayers.Size('auto', 21),
|
anchor: new OpenLayers.Bounds(0, 0, 22, null),
|
position: new OpenLayers.Pixel(0, -629)
|
},
|
{ //bottom-right
|
size: new OpenLayers.Size(22, 21),
|
anchor: new OpenLayers.Bounds(null, 0, 0, null),
|
position: new OpenLayers.Pixel(-1238, -629)
|
},
|
{ // stem
|
size: new OpenLayers.Size(81, 33),
|
anchor: new OpenLayers.Bounds(null, null, 0, 0),
|
position: new OpenLayers.Pixel(-101, -674)
|
}
|
]
|
},
|
"br": {
|
'offset': new OpenLayers.Pixel(-44, 0),
|
'padding': new OpenLayers.Bounds(8, 9, 8, 40),
|
'blocks': [
|
{ // top-left
|
size: new OpenLayers.Size('auto', 'auto'),
|
anchor: new OpenLayers.Bounds(0, 21, 22, 32),
|
position: new OpenLayers.Pixel(0, 0)
|
},
|
{ //top-right
|
size: new OpenLayers.Size(22, 'auto'),
|
anchor: new OpenLayers.Bounds(null, 21, 0, 32),
|
position: new OpenLayers.Pixel(-1238, 0)
|
},
|
{ //bottom-left
|
size: new OpenLayers.Size('auto', 21),
|
anchor: new OpenLayers.Bounds(0, 0, 22, null),
|
position: new OpenLayers.Pixel(0, -629)
|
},
|
{ //bottom-right
|
size: new OpenLayers.Size(22, 21),
|
anchor: new OpenLayers.Bounds(null, 0, 0, null),
|
position: new OpenLayers.Pixel(-1238, -629)
|
},
|
{ // stem
|
size: new OpenLayers.Size(81, 33),
|
anchor: new OpenLayers.Bounds(0, null, null, 0),
|
position: new OpenLayers.Pixel(-311, -674)
|
}
|
]
|
}
|
},
|
|
/**
|
* APIProperty: minSize
|
* {<OpenLayers.Size>}
|
*/
|
minSize: new OpenLayers.Size(105, 10),
|
|
/**
|
* APIProperty: maxSize
|
* {<OpenLayers.Size>}
|
*/
|
maxSize: new OpenLayers.Size(1200, 660),
|
|
/**
|
* Constructor: OpenLayers.Popup.FramedCloud
|
*
|
* Parameters:
|
* id - {String}
|
* lonlat - {<OpenLayers.LonLat>}
|
* contentSize - {<OpenLayers.Size>}
|
* contentHTML - {String}
|
* anchor - {Object} Object to which we'll anchor the popup. Must expose
|
* a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>)
|
* (Note that this is generally an <OpenLayers.Icon>).
|
* closeBox - {Boolean}
|
* closeBoxCallback - {Function} Function to be called on closeBox click.
|
*/
|
initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
|
closeBoxCallback) {
|
|
this.imageSrc = OpenLayers.Util.getImageLocation('cloud-popup-relative.png');
|
OpenLayers.Popup.Framed.prototype.initialize.apply(this, arguments);
|
this.contentDiv.className = this.contentDisplayClass;
|
},
|
|
CLASS_NAME: "OpenLayers.Popup.FramedCloud"
|
});
|
/* ======================================================================
|
OpenLayers/Tile/Image/IFrame.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Tile/Image.js
|
*/
|
|
/**
|
* Constant: OpenLayers.Tile.Image.IFrame
|
* Mixin for tiles that use form-encoded POST requests to get images from
|
* remote services. Images will be loaded using HTTP-POST into an IFrame.
|
*
|
* This mixin will be applied to <OpenLayers.Tile.Image> instances
|
* configured with <OpenLayers.Tile.Image.maxGetUrlLength> set.
|
*/
|
OpenLayers.Tile.Image.IFrame = {
|
|
/**
|
* Property: useIFrame
|
* {Boolean} true if we are currently using an IFrame to render POST
|
* responses, false if we are using an img element to render GET responses.
|
*/
|
useIFrame: null,
|
|
/**
|
* Property: blankImageUrl
|
* {String} Using a data scheme url is not supported by all browsers, but
|
* we don't care because we either set it as css backgroundImage, or the
|
* image's display style is set to "none" when we use it.
|
*/
|
blankImageUrl: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAQAIBRAA7",
|
|
/**
|
* Method: draw
|
* Set useIFrame in the instance, and operate the image/iframe switch.
|
* Then call Tile.Image.draw.
|
*
|
* Returns:
|
* {Boolean}
|
*/
|
draw: function() {
|
var draw = OpenLayers.Tile.Image.prototype.shouldDraw.call(this);
|
if(draw) {
|
|
// this.url isn't set to the currect value yet, so we call getURL
|
// on the layer and store the result in a local variable
|
var url = this.layer.getURL(this.bounds);
|
|
var usedIFrame = this.useIFrame;
|
this.useIFrame = this.maxGetUrlLength !== null &&
|
!this.layer.async &&
|
url.length > this.maxGetUrlLength;
|
|
var fromIFrame = usedIFrame && !this.useIFrame;
|
var toIFrame = !usedIFrame && this.useIFrame;
|
|
if(fromIFrame || toIFrame) {
|
|
// Switching between GET (image) and POST (iframe).
|
|
// We remove the imgDiv (really either an image or an iframe)
|
// from the frame and set it to null to make sure initImage
|
// will call getImage.
|
|
if(this.imgDiv && this.imgDiv.parentNode === this.frame) {
|
this.frame.removeChild(this.imgDiv);
|
}
|
this.imgDiv = null;
|
|
// And if we had an iframe we also remove the event pane.
|
|
if(fromIFrame) {
|
this.frame.removeChild(this.frame.firstChild);
|
}
|
}
|
}
|
return OpenLayers.Tile.Image.prototype.draw.apply(this, arguments);
|
},
|
|
/**
|
* Method: getImage
|
* Creates the content for the frame on the tile.
|
*/
|
getImage: function() {
|
if (this.useIFrame === true) {
|
if (!this.frame.childNodes.length) {
|
var eventPane = document.createElement("div"),
|
style = eventPane.style;
|
style.position = "absolute";
|
style.width = "100%";
|
style.height = "100%";
|
style.zIndex = 1;
|
style.backgroundImage = "url(" + this.blankImageUrl + ")";
|
this.frame.appendChild(eventPane);
|
}
|
|
var id = this.id + '_iFrame', iframe;
|
if (parseFloat(navigator.appVersion.split("MSIE")[1]) < 9) {
|
// Older IE versions do not set the name attribute of an iFrame
|
// properly via DOM manipulation, so we need to do it on our own with
|
// this hack.
|
iframe = document.createElement('<iframe name="'+id+'">');
|
|
// IFrames in older IE versions are not transparent, if you set
|
// the backgroundColor transparent. This is a workaround to get
|
// transparent iframes.
|
iframe.style.backgroundColor = '#FFFFFF';
|
iframe.style.filter = 'chroma(color=#FFFFFF)';
|
}
|
else {
|
iframe = document.createElement('iframe');
|
iframe.style.backgroundColor = 'transparent';
|
|
// iframe.name needs to be an unique id, otherwise it
|
// could happen that other iframes are overwritten.
|
iframe.name = id;
|
}
|
|
// some special properties to avoid scaling the images and scrollbars
|
// in the iframe
|
iframe.scrolling = 'no';
|
iframe.marginWidth = '0px';
|
iframe.marginHeight = '0px';
|
iframe.frameBorder = '0';
|
|
iframe.style.position = "absolute";
|
iframe.style.width = "100%";
|
iframe.style.height = "100%";
|
|
if (this.layer.opacity < 1) {
|
OpenLayers.Util.modifyDOMElement(iframe, null, null, null,
|
null, null, null, this.layer.opacity);
|
}
|
this.frame.appendChild(iframe);
|
this.imgDiv = iframe;
|
return iframe;
|
} else {
|
return OpenLayers.Tile.Image.prototype.getImage.apply(this, arguments);
|
}
|
},
|
|
/**
|
* Method: createRequestForm
|
* Create the html <form> element with width, height, bbox and all
|
* parameters specified in the layer params.
|
*
|
* Returns:
|
* {DOMElement} The form element which sends the HTTP-POST request to the
|
* WMS.
|
*/
|
createRequestForm: function() {
|
// creation of the form element
|
var form = document.createElement('form');
|
form.method = 'POST';
|
var cacheId = this.layer.params["_OLSALT"];
|
cacheId = (cacheId ? cacheId + "_" : "") + this.bounds.toBBOX();
|
form.action = OpenLayers.Util.urlAppend(this.layer.url, cacheId);
|
form.target = this.id + '_iFrame';
|
|
// adding all parameters in layer params as hidden fields to the html
|
// form element
|
var imageSize = this.layer.getImageSize(),
|
params = OpenLayers.Util.getParameters(this.url),
|
field;
|
|
for(var par in params) {
|
field = document.createElement('input');
|
field.type = 'hidden';
|
field.name = par;
|
field.value = params[par];
|
form.appendChild(field);
|
}
|
|
return form;
|
},
|
|
/**
|
* Method: setImgSrc
|
* Sets the source for the tile image
|
*
|
* Parameters:
|
* url - {String}
|
*/
|
setImgSrc: function(url) {
|
if (this.useIFrame === true) {
|
if (url) {
|
var form = this.createRequestForm();
|
this.frame.appendChild(form);
|
form.submit();
|
this.frame.removeChild(form);
|
} else if (this.imgDiv.parentNode === this.frame) {
|
// we don't reuse iframes to avoid caching issues
|
this.frame.removeChild(this.imgDiv);
|
this.imgDiv = null;
|
}
|
} else {
|
OpenLayers.Tile.Image.prototype.setImgSrc.apply(this, arguments);
|
}
|
},
|
|
/**
|
* Method: onImageLoad
|
* Handler for the image onload event
|
*/
|
onImageLoad: function() {
|
//TODO de-uglify opacity handling
|
OpenLayers.Tile.Image.prototype.onImageLoad.apply(this, arguments);
|
if (this.useIFrame === true) {
|
this.imgDiv.style.opacity = 1;
|
this.frame.style.opacity = this.layer.opacity;
|
}
|
},
|
|
/**
|
* Method: createBackBuffer
|
* Override createBackBuffer to do nothing when we use an iframe. Moving an
|
* iframe from one element to another makes it necessary to reload the iframe
|
* because its content is lost. So we just give up.
|
*
|
* Returns:
|
* {DOMElement}
|
*/
|
createBackBuffer: function() {
|
var backBuffer;
|
if(this.useIFrame === false) {
|
backBuffer = OpenLayers.Tile.Image.prototype.createBackBuffer.call(this);
|
}
|
return backBuffer;
|
}
|
};
|
/* ======================================================================
|
OpenLayers/Format/SOSCapabilities.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML/VersionedOGC.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.SOSCapabilities
|
* Read SOS Capabilities.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML.VersionedOGC>
|
*/
|
OpenLayers.Format.SOSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
|
|
/**
|
* APIProperty: defaultVersion
|
* {String} Version number to assume if none found. Default is "1.0.0".
|
*/
|
defaultVersion: "1.0.0",
|
|
/**
|
* Constructor: OpenLayers.Format.SOSCapabilities
|
* Create a new parser for SOS Capabilities.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* APIMethod: read
|
* Read capabilities data from a string, and return information about
|
* the service (offering and observedProperty mostly).
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
*
|
* Returns:
|
* {Object} Info about the SOS
|
*/
|
|
CLASS_NAME: "OpenLayers.Format.SOSCapabilities"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/SOSCapabilities/v1_0_0.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/SOSCapabilities.js
|
* @requires OpenLayers/Format/OWSCommon/v1_1_0.js
|
* @requires OpenLayers/Format/GML/v3.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.SOSCapabilities.v1_0_0
|
* Read SOS Capabilities version 1.0.0.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.SOSCapabilities>
|
*/
|
OpenLayers.Format.SOSCapabilities.v1_0_0 = OpenLayers.Class(
|
OpenLayers.Format.SOSCapabilities, {
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
ows: "http://www.opengis.net/ows/1.1",
|
sos: "http://www.opengis.net/sos/1.0",
|
gml: "http://www.opengis.net/gml",
|
xlink: "http://www.w3.org/1999/xlink"
|
},
|
|
/**
|
* Property: regExes
|
* Compiled regular expressions for manipulating strings.
|
*/
|
regExes: {
|
trimSpace: (/^\s*|\s*$/g),
|
removeSpace: (/\s*/g),
|
splitSpace: (/\s+/),
|
trimComma: (/\s*,\s*/g)
|
},
|
|
/**
|
* Constructor: OpenLayers.Format.SOSCapabilities.v1_0_0
|
* Create a new parser for SOS capabilities version 1.0.0.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
initialize: function(options) {
|
OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
|
this.options = options;
|
},
|
|
/**
|
* APIMethod: read
|
* Read capabilities data from a string, and return info about the SOS.
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
*
|
* Returns:
|
* {Object} Information about the SOS service.
|
*/
|
read: function(data) {
|
if(typeof data == "string") {
|
data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
|
}
|
if(data && data.nodeType == 9) {
|
data = data.documentElement;
|
}
|
var capabilities = {};
|
this.readNode(data, capabilities);
|
return capabilities;
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"gml": OpenLayers.Util.applyDefaults({
|
"name": function(node, obj) {
|
obj.name = this.getChildValue(node);
|
},
|
"TimePeriod": function(node, obj) {
|
obj.timePeriod = {};
|
this.readChildNodes(node, obj.timePeriod);
|
},
|
"beginPosition": function(node, timePeriod) {
|
timePeriod.beginPosition = this.getChildValue(node);
|
},
|
"endPosition": function(node, timePeriod) {
|
timePeriod.endPosition = this.getChildValue(node);
|
}
|
}, OpenLayers.Format.GML.v3.prototype.readers["gml"]),
|
"sos": {
|
"Capabilities": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"Contents": function(node, obj) {
|
obj.contents = {};
|
this.readChildNodes(node, obj.contents);
|
},
|
"ObservationOfferingList": function(node, contents) {
|
contents.offeringList = {};
|
this.readChildNodes(node, contents.offeringList);
|
},
|
"ObservationOffering": function(node, offeringList) {
|
var id = this.getAttributeNS(node, this.namespaces.gml, "id");
|
offeringList[id] = {
|
procedures: [],
|
observedProperties: [],
|
featureOfInterestIds: [],
|
responseFormats: [],
|
resultModels: [],
|
responseModes: []
|
};
|
this.readChildNodes(node, offeringList[id]);
|
},
|
"time": function(node, offering) {
|
offering.time = {};
|
this.readChildNodes(node, offering.time);
|
},
|
"procedure": function(node, offering) {
|
offering.procedures.push(this.getAttributeNS(node,
|
this.namespaces.xlink, "href"));
|
},
|
"observedProperty": function(node, offering) {
|
offering.observedProperties.push(this.getAttributeNS(node,
|
this.namespaces.xlink, "href"));
|
},
|
"featureOfInterest": function(node, offering) {
|
offering.featureOfInterestIds.push(this.getAttributeNS(node,
|
this.namespaces.xlink, "href"));
|
},
|
"responseFormat": function(node, offering) {
|
offering.responseFormats.push(this.getChildValue(node));
|
},
|
"resultModel": function(node, offering) {
|
offering.resultModels.push(this.getChildValue(node));
|
},
|
"responseMode": function(node, offering) {
|
offering.responseModes.push(this.getChildValue(node));
|
}
|
},
|
"ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"]
|
},
|
|
CLASS_NAME: "OpenLayers.Format.SOSCapabilities.v1_0_0"
|
|
});
|
/* ======================================================================
|
OpenLayers/Handler/Pinch.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Handler.js
|
*/
|
|
/**
|
* Class: OpenLayers.Handler.Pinch
|
* The pinch handler is used to deal with sequences of browser events related
|
* to pinch gestures. The handler is used by controls that want to know
|
* when a pinch sequence begins, when a pinch is happening, and when it has
|
* finished.
|
*
|
* Controls that use the pinch handler typically construct it with callbacks
|
* for 'start', 'move', and 'done'. Callbacks for these keys are
|
* called when the pinch begins, with each change, and when the pinch is
|
* done.
|
*
|
* Create a new pinch handler with the <OpenLayers.Handler.Pinch> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Handler>
|
*/
|
OpenLayers.Handler.Pinch = OpenLayers.Class(OpenLayers.Handler, {
|
|
/**
|
* Property: started
|
* {Boolean} When a touchstart event is received, we want to record it,
|
* but not set 'pinching' until the touchmove get started after
|
* starting.
|
*/
|
started: false,
|
|
/**
|
* Property: stopDown
|
* {Boolean} Stop propagation of touchstart events from getting to
|
* listeners on the same element. Default is false.
|
*/
|
stopDown: false,
|
|
/**
|
* Property: pinching
|
* {Boolean}
|
*/
|
pinching: false,
|
|
/**
|
* Property: last
|
* {Object} Object that store informations related to pinch last touch.
|
*/
|
last: null,
|
|
/**
|
* Property: start
|
* {Object} Object that store informations related to pinch touchstart.
|
*/
|
start: null,
|
|
/**
|
* Constructor: OpenLayers.Handler.Pinch
|
* Returns OpenLayers.Handler.Pinch
|
*
|
* Parameters:
|
* control - {<OpenLayers.Control>} The control that is making use of
|
* this handler. If a handler is being used without a control, the
|
* handlers setMap method must be overridden to deal properly with
|
* the map.
|
* callbacks - {Object} An object containing functions to be called when
|
* the pinch operation start, change, or is finished. The callbacks
|
* should expect to receive an object argument, which contains
|
* information about scale, distance, and position of touch points.
|
* options - {Object}
|
*/
|
|
/**
|
* Method: touchstart
|
* Handle touchstart events
|
*
|
* Parameters:
|
* evt - {Event}
|
*
|
* Returns:
|
* {Boolean} Let the event propagate.
|
*/
|
touchstart: function(evt) {
|
var propagate = true;
|
this.pinching = false;
|
if (OpenLayers.Event.isMultiTouch(evt)) {
|
this.started = true;
|
this.last = this.start = {
|
distance: this.getDistance(evt.touches),
|
delta: 0,
|
scale: 1
|
};
|
this.callback("start", [evt, this.start]);
|
propagate = !this.stopDown;
|
} else if (this.started) {
|
// Some webkit versions send fake single-touch events during
|
// multitouch, which cause the drag handler to trigger
|
return false;
|
} else {
|
this.started = false;
|
this.start = null;
|
this.last = null;
|
}
|
// prevent document dragging
|
OpenLayers.Event.preventDefault(evt);
|
return propagate;
|
},
|
|
/**
|
* Method: touchmove
|
* Handle touchmove events
|
*
|
* Parameters:
|
* evt - {Event}
|
*
|
* Returns:
|
* {Boolean} Let the event propagate.
|
*/
|
touchmove: function(evt) {
|
if (this.started && OpenLayers.Event.isMultiTouch(evt)) {
|
this.pinching = true;
|
var current = this.getPinchData(evt);
|
this.callback("move", [evt, current]);
|
this.last = current;
|
// prevent document dragging
|
OpenLayers.Event.stop(evt);
|
} else if (this.started) {
|
// Some webkit versions send fake single-touch events during
|
// multitouch, which cause the drag handler to trigger
|
return false;
|
}
|
return true;
|
},
|
|
/**
|
* Method: touchend
|
* Handle touchend events
|
*
|
* Parameters:
|
* evt - {Event}
|
*
|
* Returns:
|
* {Boolean} Let the event propagate.
|
*/
|
touchend: function(evt) {
|
if (this.started && !OpenLayers.Event.isMultiTouch(evt)) {
|
this.started = false;
|
this.pinching = false;
|
this.callback("done", [evt, this.start, this.last]);
|
this.start = null;
|
this.last = null;
|
return false;
|
}
|
return true;
|
},
|
|
/**
|
* Method: activate
|
* Activate the handler.
|
*
|
* Returns:
|
* {Boolean} The handler was successfully activated.
|
*/
|
activate: function() {
|
var activated = false;
|
if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
|
this.pinching = false;
|
activated = true;
|
}
|
return activated;
|
},
|
|
/**
|
* Method: deactivate
|
* Deactivate the handler.
|
*
|
* Returns:
|
* {Boolean} The handler was successfully deactivated.
|
*/
|
deactivate: function() {
|
var deactivated = false;
|
if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
|
this.started = false;
|
this.pinching = false;
|
this.start = null;
|
this.last = null;
|
deactivated = true;
|
}
|
return deactivated;
|
},
|
|
/**
|
* Method: getDistance
|
* Get the distance in pixels between two touches.
|
*
|
* Parameters:
|
* touches - {Array(Object)}
|
*
|
* Returns:
|
* {Number} The distance in pixels.
|
*/
|
getDistance: function(touches) {
|
var t0 = touches[0];
|
var t1 = touches[1];
|
return Math.sqrt(
|
Math.pow(t0.olClientX - t1.olClientX, 2) +
|
Math.pow(t0.olClientY - t1.olClientY, 2)
|
);
|
},
|
|
|
/**
|
* Method: getPinchData
|
* Get informations about the pinch event.
|
*
|
* Parameters:
|
* evt - {Event}
|
*
|
* Returns:
|
* {Object} Object that contains data about the current pinch.
|
*/
|
getPinchData: function(evt) {
|
var distance = this.getDistance(evt.touches);
|
var scale = distance / this.start.distance;
|
return {
|
distance: distance,
|
delta: this.last.distance - distance,
|
scale: scale
|
};
|
},
|
|
CLASS_NAME: "OpenLayers.Handler.Pinch"
|
});
|
|
/* ======================================================================
|
OpenLayers/Control/NavToolbar.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control/Panel.js
|
* @requires OpenLayers/Control/Navigation.js
|
* @requires OpenLayers/Control/ZoomBox.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.NavToolbar
|
* This Toolbar is an alternative to the Navigation control that displays
|
* the state of the control, and provides a UI for changing state to
|
* use the zoomBox via a Panel control.
|
*
|
* If you wish to change the properties of the Navigation control used
|
* in the NavToolbar, see:
|
* http://trac.openlayers.org/wiki/Toolbars#SubclassingNavToolbar
|
*
|
*
|
* Inherits from:
|
* - <OpenLayers.Control.Panel>
|
*/
|
OpenLayers.Control.NavToolbar = OpenLayers.Class(OpenLayers.Control.Panel, {
|
|
/**
|
* Constructor: OpenLayers.Control.NavToolbar
|
* Add our two mousedefaults controls.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be used
|
* to extend the control.
|
*/
|
initialize: function(options) {
|
OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);
|
this.addControls([
|
new OpenLayers.Control.Navigation(),
|
new OpenLayers.Control.ZoomBox()
|
]);
|
},
|
|
/**
|
* Method: draw
|
* calls the default draw, and then activates mouse defaults.
|
*/
|
draw: function() {
|
var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments);
|
if (this.defaultControl === null) {
|
this.defaultControl = this.controls[0];
|
}
|
return div;
|
},
|
|
CLASS_NAME: "OpenLayers.Control.NavToolbar"
|
});
|
/* ======================================================================
|
OpenLayers/Strategy/Refresh.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Strategy.js
|
*/
|
|
/**
|
* Class: OpenLayers.Strategy.Refresh
|
* A strategy that refreshes the layer. By default the strategy waits for a
|
* call to <refresh> before refreshing. By configuring the strategy with
|
* the <interval> option, refreshing can take place automatically.
|
*
|
* Inherits from:
|
* - <OpenLayers.Strategy>
|
*/
|
OpenLayers.Strategy.Refresh = OpenLayers.Class(OpenLayers.Strategy, {
|
|
/**
|
* Property: force
|
* {Boolean} Force a refresh on the layer. Default is false.
|
*/
|
force: false,
|
|
/**
|
* Property: interval
|
* {Number} Auto-refresh. Default is 0. If > 0, layer will be refreshed
|
* every N milliseconds.
|
*/
|
interval: 0,
|
|
/**
|
* Property: timer
|
* {Number} The id of the timer.
|
*/
|
timer: null,
|
|
/**
|
* Constructor: OpenLayers.Strategy.Refresh
|
* Create a new Refresh strategy.
|
*
|
* Parameters:
|
* options - {Object} Optional object whose properties will be set on the
|
* instance.
|
*/
|
|
/**
|
* APIMethod: activate
|
* Activate the strategy. Register any listeners, do appropriate setup.
|
*
|
* Returns:
|
* {Boolean} True if the strategy was successfully activated.
|
*/
|
activate: function() {
|
var activated = OpenLayers.Strategy.prototype.activate.call(this);
|
if(activated) {
|
if(this.layer.visibility === true) {
|
this.start();
|
}
|
this.layer.events.on({
|
"visibilitychanged": this.reset,
|
scope: this
|
});
|
}
|
return activated;
|
},
|
|
/**
|
* APIMethod: deactivate
|
* Deactivate the strategy. Unregister any listeners, do appropriate
|
* tear-down.
|
*
|
* Returns:
|
* {Boolean} True if the strategy was successfully deactivated.
|
*/
|
deactivate: function() {
|
var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
|
if(deactivated) {
|
this.stop();
|
this.layer.events.un({
|
"visibilitychanged": this.reset,
|
scope: this
|
});
|
}
|
return deactivated;
|
},
|
|
/**
|
* Method: reset
|
* Start or cancel the refresh interval depending on the visibility of
|
* the layer.
|
*/
|
reset: function() {
|
if(this.layer.visibility === true) {
|
this.start();
|
} else {
|
this.stop();
|
}
|
},
|
|
/**
|
* Method: start
|
* Start the refresh interval.
|
*/
|
start: function() {
|
if(this.interval && typeof this.interval === "number" &&
|
this.interval > 0) {
|
|
this.timer = window.setInterval(
|
OpenLayers.Function.bind(this.refresh, this),
|
this.interval);
|
}
|
},
|
|
/**
|
* APIMethod: refresh
|
* Tell the strategy to refresh which will refresh the layer.
|
*/
|
refresh: function() {
|
if (this.layer && this.layer.refresh &&
|
typeof this.layer.refresh == "function") {
|
|
this.layer.refresh({force: this.force});
|
}
|
},
|
|
/**
|
* Method: stop
|
* Cancels the refresh interval.
|
*/
|
stop: function() {
|
if(this.timer !== null) {
|
window.clearInterval(this.timer);
|
this.timer = null;
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Strategy.Refresh"
|
});
|
/* ======================================================================
|
OpenLayers/Layer/ArcGIS93Rest.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Layer/Grid.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.ArcGIS93Rest
|
* Instances of OpenLayers.Layer.ArcGIS93Rest are used to display data from
|
* ESRI ArcGIS Server 9.3 (and up?) Mapping Services using the REST API.
|
* Create a new ArcGIS93Rest layer with the <OpenLayers.Layer.ArcGIS93Rest>
|
* constructor. More detail on the REST API is available at
|
* http://sampleserver1.arcgisonline.com/ArcGIS/SDK/REST/index.html ;
|
* specifically, the URL provided to this layer should be an export service
|
* URL: http://sampleserver1.arcgisonline.com/ArcGIS/SDK/REST/export.html
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer.Grid>
|
*/
|
OpenLayers.Layer.ArcGIS93Rest = OpenLayers.Class(OpenLayers.Layer.Grid, {
|
|
/**
|
* Constant: DEFAULT_PARAMS
|
* {Object} Hashtable of default parameter key/value pairs
|
*/
|
DEFAULT_PARAMS: {
|
format: "png"
|
},
|
|
/**
|
* APIProperty: isBaseLayer
|
* {Boolean} Default is true for ArcGIS93Rest layer
|
*/
|
isBaseLayer: true,
|
|
|
/**
|
* Constructor: OpenLayers.Layer.ArcGIS93Rest
|
* Create a new ArcGIS93Rest layer object.
|
*
|
* Example:
|
* (code)
|
* var arcims = new OpenLayers.Layer.ArcGIS93Rest("MyName",
|
* "http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StateCityHighway_USA/MapServer/export",
|
* {
|
* layers: "0,1,2"
|
* });
|
* (end)
|
*
|
* Parameters:
|
* name - {String} A name for the layer
|
* url - {String} Base url for the ArcGIS server REST service
|
* options - {Object} An object with key/value pairs representing the
|
* options and option values.
|
*
|
* Valid Options:
|
* format - {String} MIME type of desired image type.
|
* layers - {String} Comma-separated list of layers to display.
|
* srs - {String} Projection ID.
|
*/
|
initialize: function(name, url, params, options) {
|
var newArguments = [];
|
//uppercase params
|
params = OpenLayers.Util.upperCaseObject(params);
|
newArguments.push(name, url, params, options);
|
OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
|
OpenLayers.Util.applyDefaults(
|
this.params,
|
OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)
|
);
|
|
//layer is transparent
|
if (this.params.TRANSPARENT &&
|
this.params.TRANSPARENT.toString().toLowerCase() == "true") {
|
|
// unless explicitly set in options, make layer an overlay
|
if ( (options == null) || (!options.isBaseLayer) ) {
|
this.isBaseLayer = false;
|
}
|
|
// jpegs can never be transparent, so intelligently switch the
|
// format, depending on the browser's capabilities
|
if (this.params.FORMAT == "jpg") {
|
this.params.FORMAT = OpenLayers.Util.alphaHack() ? "gif"
|
: "png";
|
}
|
}
|
},
|
|
/**
|
* Method: clone
|
* Create a clone of this layer
|
*
|
* Returns:
|
* {<OpenLayers.Layer.ArcGIS93Rest>} An exact clone of this layer
|
*/
|
clone: function (obj) {
|
|
if (obj == null) {
|
obj = new OpenLayers.Layer.ArcGIS93Rest(this.name,
|
this.url,
|
this.params,
|
this.getOptions());
|
}
|
|
//get all additions from superclasses
|
obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
|
|
// copy/set any non-init, non-simple values here
|
|
return obj;
|
},
|
|
|
/**
|
* Method: getURL
|
* Return an image url this layer.
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the
|
* request.
|
*
|
* Returns:
|
* {String} A string with the map image's url.
|
*/
|
getURL: function (bounds) {
|
bounds = this.adjustBounds(bounds);
|
|
// ArcGIS Server only wants the numeric portion of the projection ID.
|
var projWords = this.projection.getCode().split(":");
|
var srid = projWords[projWords.length - 1];
|
|
var imageSize = this.getImageSize();
|
var newParams = {
|
'BBOX': bounds.toBBOX(),
|
'SIZE': imageSize.w + "," + imageSize.h,
|
// We always want image, the other options were json, image with a whole lotta html around it, etc.
|
'F': "image",
|
'BBOXSR': srid,
|
'IMAGESR': srid
|
};
|
|
// Now add the filter parameters.
|
if (this.layerDefs) {
|
var layerDefStrList = [];
|
var layerID;
|
for(layerID in this.layerDefs) {
|
if (this.layerDefs.hasOwnProperty(layerID)) {
|
if (this.layerDefs[layerID]) {
|
layerDefStrList.push(layerID);
|
layerDefStrList.push(":");
|
layerDefStrList.push(this.layerDefs[layerID]);
|
layerDefStrList.push(";");
|
}
|
}
|
}
|
if (layerDefStrList.length > 0) {
|
newParams['LAYERDEFS'] = layerDefStrList.join("");
|
}
|
}
|
var requestString = this.getFullRequestString(newParams);
|
return requestString;
|
},
|
|
/**
|
* Method: setLayerFilter
|
* addTile creates a tile, initializes it, and adds it to the layer div.
|
*
|
* Parameters:
|
* id - {String} The id of the layer to which the filter applies.
|
* queryDef - {String} A sql-ish query filter, for more detail see the ESRI
|
* documentation at http://sampleserver1.arcgisonline.com/ArcGIS/SDK/REST/export.html
|
*/
|
setLayerFilter: function ( id, queryDef ) {
|
if (!this.layerDefs) {
|
this.layerDefs = {};
|
}
|
if (queryDef) {
|
this.layerDefs[id] = queryDef;
|
} else {
|
delete this.layerDefs[id];
|
}
|
},
|
|
/**
|
* Method: clearLayerFilter
|
* Clears layer filters, either from a specific layer,
|
* or all of them.
|
*
|
* Parameters:
|
* id - {String} The id of the layer from which to remove any
|
* filter. If unspecified/blank, all filters
|
* will be removed.
|
*/
|
clearLayerFilter: function ( id ) {
|
if (id) {
|
delete this.layerDefs[id];
|
} else {
|
delete this.layerDefs;
|
}
|
},
|
|
/**
|
* APIMethod: mergeNewParams
|
* Catch changeParams and uppercase the new params to be merged in
|
* before calling changeParams on the super class.
|
*
|
* Once params have been changed, the tiles will be reloaded with
|
* the new parameters.
|
*
|
* Parameters:
|
* newParams - {Object} Hashtable of new params to use
|
*/
|
mergeNewParams:function(newParams) {
|
var upperParams = OpenLayers.Util.upperCaseObject(newParams);
|
var newArguments = [upperParams];
|
return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this,
|
newArguments);
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.ArcGIS93Rest"
|
});
|
/* ======================================================================
|
OpenLayers/Handler/Hover.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Handler.js
|
*/
|
|
/**
|
* Class: OpenLayers.Handler.Hover
|
* The hover handler is to be used to emulate mouseovers on objects
|
* on the map that aren't DOM elements. For example one can use
|
* this handler to send WMS/GetFeatureInfo requests as the user
|
* moves the mouve over the map.
|
*
|
* Inherits from:
|
* - <OpenLayers.Handler>
|
*/
|
OpenLayers.Handler.Hover = OpenLayers.Class(OpenLayers.Handler, {
|
|
/**
|
* APIProperty: delay
|
* {Integer} - Number of milliseconds between mousemoves before
|
* the event is considered a hover. Default is 500.
|
*/
|
delay: 500,
|
|
/**
|
* APIProperty: pixelTolerance
|
* {Integer} - Maximum number of pixels between mousemoves for
|
* an event to be considered a hover. Default is null.
|
*/
|
pixelTolerance: null,
|
|
/**
|
* APIProperty: stopMove
|
* {Boolean} - Stop other listeners from being notified on mousemoves.
|
* Default is false.
|
*/
|
stopMove: false,
|
|
/**
|
* Property: px
|
* {<OpenLayers.Pixel>} - The location of the last mousemove, expressed
|
* in pixels.
|
*/
|
px: null,
|
|
/**
|
* Property: timerId
|
* {Number} - The id of the timer.
|
*/
|
timerId: null,
|
|
/**
|
* Constructor: OpenLayers.Handler.Hover
|
* Construct a hover handler.
|
*
|
* Parameters:
|
* control - {<OpenLayers.Control>} The control that initialized this
|
* handler. The control is assumed to have a valid map property; that
|
* map is used in the handler's own setMap method.
|
* callbacks - {Object} An object with keys corresponding to callbacks
|
* that will be called by the handler. The callbacks should
|
* expect to receive a single argument, the event. Callbacks for
|
* 'move', the mouse is moving, and 'pause', the mouse is pausing,
|
* are supported.
|
* options - {Object} An optional object whose properties will be set on
|
* the handler.
|
*/
|
|
/**
|
* Method: mousemove
|
* Called when the mouse moves on the map.
|
*
|
* Parameters:
|
* evt - {<OpenLayers.Event>}
|
*
|
* Returns:
|
* {Boolean} Continue propagating this event.
|
*/
|
mousemove: function(evt) {
|
if(this.passesTolerance(evt.xy)) {
|
this.clearTimer();
|
this.callback('move', [evt]);
|
this.px = evt.xy;
|
// clone the evt so original properties can be accessed even
|
// if the browser deletes them during the delay
|
evt = OpenLayers.Util.extend({}, evt);
|
this.timerId = window.setTimeout(
|
OpenLayers.Function.bind(this.delayedCall, this, evt),
|
this.delay
|
);
|
}
|
return !this.stopMove;
|
},
|
|
/**
|
* Method: mouseout
|
* Called when the mouse goes out of the map.
|
*
|
* Parameters:
|
* evt - {<OpenLayers.Event>}
|
*
|
* Returns:
|
* {Boolean} Continue propagating this event.
|
*/
|
mouseout: function(evt) {
|
if (OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {
|
this.clearTimer();
|
this.callback('move', [evt]);
|
}
|
return true;
|
},
|
|
/**
|
* Method: passesTolerance
|
* Determine whether the mouse move is within the optional pixel tolerance.
|
*
|
* Parameters:
|
* px - {<OpenLayers.Pixel>}
|
*
|
* Returns:
|
* {Boolean} The mouse move is within the pixel tolerance.
|
*/
|
passesTolerance: function(px) {
|
var passes = true;
|
if(this.pixelTolerance && this.px) {
|
var dpx = Math.sqrt(
|
Math.pow(this.px.x - px.x, 2) +
|
Math.pow(this.px.y - px.y, 2)
|
);
|
if(dpx < this.pixelTolerance) {
|
passes = false;
|
}
|
}
|
return passes;
|
},
|
|
/**
|
* Method: clearTimer
|
* Clear the timer and set <timerId> to null.
|
*/
|
clearTimer: function() {
|
if(this.timerId != null) {
|
window.clearTimeout(this.timerId);
|
this.timerId = null;
|
}
|
},
|
|
/**
|
* Method: delayedCall
|
* Triggers pause callback.
|
*
|
* Parameters:
|
* evt - {<OpenLayers.Event>}
|
*/
|
delayedCall: function(evt) {
|
this.callback('pause', [evt]);
|
},
|
|
/**
|
* APIMethod: deactivate
|
* Deactivate the handler.
|
*
|
* Returns:
|
* {Boolean} The handler was successfully deactivated.
|
*/
|
deactivate: function() {
|
var deactivated = false;
|
if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
|
this.clearTimer();
|
deactivated = true;
|
}
|
return deactivated;
|
},
|
|
CLASS_NAME: "OpenLayers.Handler.Hover"
|
});
|
/* ======================================================================
|
OpenLayers/Control/GetFeature.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/Handler/Click.js
|
* @requires OpenLayers/Handler/Box.js
|
* @requires OpenLayers/Handler/Hover.js
|
* @requires OpenLayers/Filter/Spatial.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.GetFeature
|
* Gets vector features for locations underneath the mouse cursor. Can be
|
* configured to act on click, hover or dragged boxes. Uses an
|
* <OpenLayers.Protocol> that supports spatial filters to retrieve
|
* features from a server and fires events that notify applications of the
|
* selected features.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.GetFeature = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* APIProperty: protocol
|
* {<OpenLayers.Protocol>} Required. The protocol used for fetching
|
* features.
|
*/
|
protocol: null,
|
|
/**
|
* APIProperty: multipleKey
|
* {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets
|
* the <multiple> property to true. Default is null.
|
*/
|
multipleKey: null,
|
|
/**
|
* APIProperty: toggleKey
|
* {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets
|
* the <toggle> property to true. Default is null.
|
*/
|
toggleKey: null,
|
|
/**
|
* Property: modifiers
|
* {Object} The event modifiers to use, according to the current event
|
* being handled by this control's handlers
|
*/
|
modifiers: null,
|
|
/**
|
* APIProperty: multiple
|
* {Boolean} Allow selection of multiple geometries. Default is false.
|
*/
|
multiple: false,
|
|
/**
|
* APIProperty: click
|
* {Boolean} Use a click handler for selecting/unselecting features. If
|
* both <click> and <box> are set to true, the click handler takes
|
* precedence over the box handler if a box with zero extent was
|
* selected. Default is true.
|
*/
|
click: true,
|
|
/**
|
* APIProperty: single
|
* {Boolean} Tells whether select by click should select a single
|
* feature. If set to false, all matching features are selected.
|
* If set to true, only the best matching feature is selected.
|
* This option has an effect only of the <click> option is set
|
* to true. Default is true.
|
*/
|
single: true,
|
|
/**
|
* APIProperty: clickout
|
* {Boolean} Unselect features when clicking outside any feature.
|
* Applies only if <click> is true. Default is true.
|
*/
|
clickout: true,
|
|
/**
|
* APIProperty: toggle
|
* {Boolean} Unselect a selected feature on click. Applies only if
|
* <click> is true. Default is false.
|
*/
|
toggle: false,
|
|
/**
|
* APIProperty: clickTolerance
|
* {Integer} Tolerance for the filter query in pixels. This has the
|
* same effect as the tolerance parameter on WMS GetFeatureInfo
|
* requests. Will be ignored for box selections. Applies only if
|
* <click> or <hover> is true. Default is 5. Note that this not
|
* only affects requests on click, but also on hover.
|
*/
|
clickTolerance: 5,
|
|
/**
|
* APIProperty: hover
|
* {Boolean} Send feature requests on mouse moves. Default is false.
|
*/
|
hover: false,
|
|
/**
|
* APIProperty: box
|
* {Boolean} Allow feature selection by drawing a box. If set to
|
* true set <click> to false to disable the click handler and
|
* rely on the box handler only, even for "zero extent" boxes.
|
* See the description of the <click> option for additional
|
* information. Default is false.
|
*/
|
box: false,
|
|
/**
|
* APIProperty: maxFeatures
|
* {Integer} Maximum number of features to return from a query in single mode
|
* if supported by the <protocol>. This set of features is then used to
|
* determine the best match client-side. Default is 10.
|
*/
|
maxFeatures: 10,
|
|
/**
|
* Property: features
|
* {Object} Hash of {<OpenLayers.Feature.Vector>}, keyed by fid, holding
|
* the currently selected features
|
*/
|
features: null,
|
|
/**
|
* Proeprty: hoverFeature
|
* {<OpenLayers.Feature.Vector>} The feature currently selected by the
|
* hover handler
|
*/
|
hoverFeature: null,
|
|
/**
|
* APIProperty: handlerOptions
|
* {Object} Additional options for the handlers used by this control. This
|
* is a hash with the keys "click", "box" and "hover".
|
*/
|
|
/**
|
* Property: handlers
|
* {Object} Object with references to multiple <OpenLayers.Handler>
|
* instances.
|
*/
|
handlers: null,
|
|
/**
|
* Property: hoverResponse
|
* {<OpenLayers.Protocol.Response>} The response object associated with
|
* the currently running hover request (if any).
|
*/
|
hoverResponse: null,
|
|
/**
|
* Property: filterType
|
* {<String>} The type of filter to use when sending off a request.
|
* Possible values:
|
* OpenLayers.Filter.Spatial.<BBOX|INTERSECTS|WITHIN|CONTAINS>
|
* Defaults to: OpenLayers.Filter.Spatial.BBOX
|
*/
|
filterType: OpenLayers.Filter.Spatial.BBOX,
|
|
/**
|
* APIProperty: events
|
* {<OpenLayers.Events>} Events instance for listeners and triggering
|
* control specific events.
|
*
|
* Register a listener for a particular event with the following syntax:
|
* (code)
|
* control.events.register(type, obj, listener);
|
* (end)
|
*
|
* Supported event types (in addition to those from <OpenLayers.Control.events>):
|
* beforefeatureselected - Triggered when <click> is true before a
|
* feature is selected. The event object has a feature property with
|
* the feature about to select
|
* featureselected - Triggered when <click> is true and a feature is
|
* selected. The event object has a feature property with the
|
* selected feature
|
* beforefeaturesselected - Triggered when <click> is true before a
|
* set of features is selected. The event object is an array of
|
* feature properties with the features about to be selected.
|
* Return false after receiving this event to discontinue processing
|
* of all featureselected events and the featuresselected event.
|
* featuresselected - Triggered when <click> is true and a set of
|
* features is selected. The event object is an array of feature
|
* properties of the selected features
|
* featureunselected - Triggered when <click> is true and a feature is
|
* unselected. The event object has a feature property with the
|
* unselected feature
|
* clickout - Triggered when when <click> is true and no feature was
|
* selected.
|
* hoverfeature - Triggered when <hover> is true and the mouse has
|
* stopped over a feature
|
* outfeature - Triggered when <hover> is true and the mouse moves
|
* moved away from a hover-selected feature
|
*/
|
|
/**
|
* Constructor: OpenLayers.Control.GetFeature
|
* Create a new control for fetching remote features.
|
*
|
* Parameters:
|
* options - {Object} A configuration object which at least has to contain
|
* a <protocol> property (if not, it has to be set before a request is
|
* made)
|
*/
|
initialize: function(options) {
|
options.handlerOptions = options.handlerOptions || {};
|
|
OpenLayers.Control.prototype.initialize.apply(this, [options]);
|
|
this.features = {};
|
|
this.handlers = {};
|
|
if(this.click) {
|
this.handlers.click = new OpenLayers.Handler.Click(this,
|
{click: this.selectClick}, this.handlerOptions.click || {});
|
}
|
|
if(this.box) {
|
this.handlers.box = new OpenLayers.Handler.Box(
|
this, {done: this.selectBox},
|
OpenLayers.Util.extend(this.handlerOptions.box, {
|
boxDivClassName: "olHandlerBoxSelectFeature"
|
})
|
);
|
}
|
|
if(this.hover) {
|
this.handlers.hover = new OpenLayers.Handler.Hover(
|
this, {'move': this.cancelHover, 'pause': this.selectHover},
|
OpenLayers.Util.extend(this.handlerOptions.hover, {
|
'delay': 250,
|
'pixelTolerance': 2
|
})
|
);
|
}
|
},
|
|
/**
|
* Method: activate
|
* Activates the control.
|
*
|
* Returns:
|
* {Boolean} The control was effectively activated.
|
*/
|
activate: function () {
|
if (!this.active) {
|
for(var i in this.handlers) {
|
this.handlers[i].activate();
|
}
|
}
|
return OpenLayers.Control.prototype.activate.apply(
|
this, arguments
|
);
|
},
|
|
/**
|
* Method: deactivate
|
* Deactivates the control.
|
*
|
* Returns:
|
* {Boolean} The control was effectively deactivated.
|
*/
|
deactivate: function () {
|
if (this.active) {
|
for(var i in this.handlers) {
|
this.handlers[i].deactivate();
|
}
|
}
|
return OpenLayers.Control.prototype.deactivate.apply(
|
this, arguments
|
);
|
},
|
|
/**
|
* Method: selectClick
|
* Called on click
|
*
|
* Parameters:
|
* evt - {<OpenLayers.Event>}
|
*/
|
selectClick: function(evt) {
|
var bounds = this.pixelToBounds(evt.xy);
|
|
this.setModifiers(evt);
|
this.request(bounds, {single: this.single});
|
},
|
|
/**
|
* Method: selectBox
|
* Callback from the handlers.box set up when <box> selection is on
|
*
|
* Parameters:
|
* position - {<OpenLayers.Bounds>|Object} An OpenLayers.Bounds or
|
* an object with a 'left', 'bottom', 'right' and 'top' properties.
|
*/
|
selectBox: function(position) {
|
var bounds;
|
if (position instanceof OpenLayers.Bounds) {
|
var minXY = this.map.getLonLatFromPixel({
|
x: position.left,
|
y: position.bottom
|
});
|
var maxXY = this.map.getLonLatFromPixel({
|
x: position.right,
|
y: position.top
|
});
|
bounds = new OpenLayers.Bounds(
|
minXY.lon, minXY.lat, maxXY.lon, maxXY.lat
|
);
|
|
} else {
|
if(this.click) {
|
// box without extent - let the click handler take care of it
|
return;
|
}
|
bounds = this.pixelToBounds(position);
|
}
|
this.setModifiers(this.handlers.box.dragHandler.evt);
|
this.request(bounds);
|
},
|
|
/**
|
* Method: selectHover
|
* Callback from the handlers.hover set up when <hover> selection is on
|
*
|
* Parameters:
|
* evt - {Object} event object with an xy property
|
*/
|
selectHover: function(evt) {
|
var bounds = this.pixelToBounds(evt.xy);
|
this.request(bounds, {single: true, hover: true});
|
},
|
|
/**
|
* Method: cancelHover
|
* Callback from the handlers.hover set up when <hover> selection is on
|
*/
|
cancelHover: function() {
|
if (this.hoverResponse) {
|
this.protocol.abort(this.hoverResponse);
|
this.hoverResponse = null;
|
|
OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait");
|
}
|
},
|
|
/**
|
* Method: request
|
* Sends a GetFeature request to the WFS
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>} bounds for the request's BBOX filter
|
* options - {Object} additional options for this method.
|
*
|
* Supported options include:
|
* single - {Boolean} A single feature should be returned.
|
* Note that this will be ignored if the protocol does not
|
* return the geometries of the features.
|
* hover - {Boolean} Do the request for the hover handler.
|
*/
|
request: function(bounds, options) {
|
options = options || {};
|
var filter = new OpenLayers.Filter.Spatial({
|
type: this.filterType,
|
value: bounds
|
});
|
|
// Set the cursor to "wait" to tell the user we're working.
|
OpenLayers.Element.addClass(this.map.viewPortDiv, "olCursorWait");
|
|
var response = this.protocol.read({
|
maxFeatures: options.single == true ? this.maxFeatures : undefined,
|
filter: filter,
|
callback: function(result) {
|
if(result.success()) {
|
if(result.features.length) {
|
if(options.single == true) {
|
this.selectBestFeature(result.features,
|
bounds.getCenterLonLat(), options);
|
} else {
|
this.select(result.features);
|
}
|
} else if(options.hover) {
|
this.hoverSelect();
|
} else {
|
this.events.triggerEvent("clickout");
|
if(this.clickout) {
|
this.unselectAll();
|
}
|
}
|
}
|
// Reset the cursor.
|
OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait");
|
},
|
scope: this
|
});
|
if(options.hover == true) {
|
this.hoverResponse = response;
|
}
|
},
|
|
/**
|
* Method: selectBestFeature
|
* Selects the feature from an array of features that is the best match
|
* for the click position.
|
*
|
* Parameters:
|
* features - {Array(<OpenLayers.Feature.Vector>)}
|
* clickPosition - {<OpenLayers.LonLat>}
|
* options - {Object} additional options for this method
|
*
|
* Supported options include:
|
* hover - {Boolean} Do the selection for the hover handler.
|
*/
|
selectBestFeature: function(features, clickPosition, options) {
|
options = options || {};
|
if(features.length) {
|
var point = new OpenLayers.Geometry.Point(clickPosition.lon,
|
clickPosition.lat);
|
var feature, resultFeature, dist;
|
var minDist = Number.MAX_VALUE;
|
for(var i=0; i<features.length; ++i) {
|
feature = features[i];
|
if(feature.geometry) {
|
dist = point.distanceTo(feature.geometry, {edge: false});
|
if(dist < minDist) {
|
minDist = dist;
|
resultFeature = feature;
|
if(minDist == 0) {
|
break;
|
}
|
}
|
}
|
}
|
|
if(options.hover == true) {
|
this.hoverSelect(resultFeature);
|
} else {
|
this.select(resultFeature || features);
|
}
|
}
|
},
|
|
/**
|
* Method: setModifiers
|
* Sets the multiple and toggle modifiers according to the current event
|
*
|
* Parameters:
|
* evt - {<OpenLayers.Event>}
|
*/
|
setModifiers: function(evt) {
|
this.modifiers = {
|
multiple: this.multiple || (this.multipleKey && evt[this.multipleKey]),
|
toggle: this.toggle || (this.toggleKey && evt[this.toggleKey])
|
};
|
},
|
|
/**
|
* Method: select
|
* Add feature to the hash of selected features and trigger the
|
* featureselected and featuresselected events.
|
*
|
* Parameters:
|
* features - {<OpenLayers.Feature.Vector>} or an array of features
|
*/
|
select: function(features) {
|
if(!this.modifiers.multiple && !this.modifiers.toggle) {
|
this.unselectAll();
|
}
|
if(!(OpenLayers.Util.isArray(features))) {
|
features = [features];
|
}
|
|
var cont = this.events.triggerEvent("beforefeaturesselected", {
|
features: features
|
});
|
if(cont !== false) {
|
var selectedFeatures = [];
|
var feature;
|
for(var i=0, len=features.length; i<len; ++i) {
|
feature = features[i];
|
if(this.features[feature.fid || feature.id]) {
|
if(this.modifiers.toggle) {
|
this.unselect(this.features[feature.fid || feature.id]);
|
}
|
} else {
|
cont = this.events.triggerEvent("beforefeatureselected", {
|
feature: feature
|
});
|
if(cont !== false) {
|
this.features[feature.fid || feature.id] = feature;
|
selectedFeatures.push(feature);
|
|
this.events.triggerEvent("featureselected",
|
{feature: feature});
|
}
|
}
|
}
|
this.events.triggerEvent("featuresselected", {
|
features: selectedFeatures
|
});
|
}
|
},
|
|
/**
|
* Method: hoverSelect
|
* Sets/unsets the <hoverFeature>
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>} the feature to hover-select.
|
* If none is provided, the current <hoverFeature> will be nulled and
|
* the outfeature event will be triggered.
|
*/
|
hoverSelect: function(feature) {
|
var fid = feature ? feature.fid || feature.id : null;
|
var hfid = this.hoverFeature ?
|
this.hoverFeature.fid || this.hoverFeature.id : null;
|
|
if(hfid && hfid != fid) {
|
this.events.triggerEvent("outfeature",
|
{feature: this.hoverFeature});
|
this.hoverFeature = null;
|
}
|
if(fid && fid != hfid) {
|
this.events.triggerEvent("hoverfeature", {feature: feature});
|
this.hoverFeature = feature;
|
}
|
},
|
|
/**
|
* Method: unselect
|
* Remove feature from the hash of selected features and trigger the
|
* featureunselected event.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
*/
|
unselect: function(feature) {
|
delete this.features[feature.fid || feature.id];
|
this.events.triggerEvent("featureunselected", {feature: feature});
|
},
|
|
/**
|
* Method: unselectAll
|
* Unselect all selected features.
|
*/
|
unselectAll: function() {
|
// we'll want an option to supress notification here
|
for(var fid in this.features) {
|
this.unselect(this.features[fid]);
|
}
|
},
|
|
/**
|
* Method: setMap
|
* Set the map property for the control.
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
setMap: function(map) {
|
for(var i in this.handlers) {
|
this.handlers[i].setMap(map);
|
}
|
OpenLayers.Control.prototype.setMap.apply(this, arguments);
|
},
|
|
/**
|
* Method: pixelToBounds
|
* Takes a pixel as argument and creates bounds after adding the
|
* <clickTolerance>.
|
*
|
* Parameters:
|
* pixel - {<OpenLayers.Pixel>}
|
*/
|
pixelToBounds: function(pixel) {
|
var llPx = pixel.add(-this.clickTolerance/2, this.clickTolerance/2);
|
var urPx = pixel.add(this.clickTolerance/2, -this.clickTolerance/2);
|
var ll = this.map.getLonLatFromPixel(llPx);
|
var ur = this.map.getLonLatFromPixel(urPx);
|
return new OpenLayers.Bounds(ll.lon, ll.lat, ur.lon, ur.lat);
|
},
|
|
CLASS_NAME: "OpenLayers.Control.GetFeature"
|
});
|
/* ======================================================================
|
OpenLayers/Format/QueryStringFilter.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Console.js
|
* @requires OpenLayers/Format.js
|
* @requires OpenLayers/Filter/Spatial.js
|
* @requires OpenLayers/Filter/Comparison.js
|
* @requires OpenLayers/Filter/Logical.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.QueryStringFilter
|
* Parser for reading a query string and creating a simple filter.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format>
|
*/
|
OpenLayers.Format.QueryStringFilter = (function() {
|
|
/**
|
* Map the OpenLayers.Filter.Comparison types to the operation strings of
|
* the protocol.
|
*/
|
var cmpToStr = {};
|
cmpToStr[OpenLayers.Filter.Comparison.EQUAL_TO] = "eq";
|
cmpToStr[OpenLayers.Filter.Comparison.NOT_EQUAL_TO] = "ne";
|
cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN] = "lt";
|
cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO] = "lte";
|
cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN] = "gt";
|
cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO] = "gte";
|
cmpToStr[OpenLayers.Filter.Comparison.LIKE] = "ilike";
|
|
/**
|
* Function: regex2value
|
* Convert the value from a regular expression string to a LIKE/ILIKE
|
* string known to the web service.
|
*
|
* Parameters:
|
* value - {String} The regex string.
|
*
|
* Returns:
|
* {String} The converted string.
|
*/
|
function regex2value(value) {
|
|
// highly sensitive!! Do not change this without running the
|
// Protocol/HTTP.html unit tests
|
|
// convert % to \%
|
value = value.replace(/%/g, "\\%");
|
|
// convert \\. to \\_ (\\.* occurences converted later)
|
value = value.replace(/\\\\\.(\*)?/g, function($0, $1) {
|
return $1 ? $0 : "\\\\_";
|
});
|
|
// convert \\.* to \\%
|
value = value.replace(/\\\\\.\*/g, "\\\\%");
|
|
// convert . to _ (\. and .* occurences converted later)
|
value = value.replace(/(\\)?\.(\*)?/g, function($0, $1, $2) {
|
return $1 || $2 ? $0 : "_";
|
});
|
|
// convert .* to % (\.* occurnces converted later)
|
value = value.replace(/(\\)?\.\*/g, function($0, $1) {
|
return $1 ? $0 : "%";
|
});
|
|
// convert \. to .
|
value = value.replace(/\\\./g, ".");
|
|
// replace \* with * (watching out for \\*)
|
value = value.replace(/(\\)?\\\*/g, function($0, $1) {
|
return $1 ? $0 : "*";
|
});
|
|
return value;
|
}
|
|
return OpenLayers.Class(OpenLayers.Format, {
|
|
/**
|
* Property: wildcarded.
|
* {Boolean} If true percent signs are added around values
|
* read from LIKE filters, for example if the protocol
|
* read method is passed a LIKE filter whose property
|
* is "foo" and whose value is "bar" the string
|
* "foo__ilike=%bar%" will be sent in the query string;
|
* defaults to false.
|
*/
|
wildcarded: false,
|
|
/**
|
* APIProperty: srsInBBOX
|
* {Boolean} Include the SRS identifier in BBOX query string parameter.
|
* Default is false. If true and the layer has a projection object set,
|
* any BBOX filter will be serialized with a fifth item identifying the
|
* projection. E.g. bbox=-1000,-1000,1000,1000,EPSG:900913
|
*/
|
srsInBBOX: false,
|
|
/**
|
* APIMethod: write
|
* Serialize an <OpenLayers.Filter> objects using the "simple" filter syntax for
|
* query string parameters. This function must be called as a method of
|
* a protocol instance.
|
*
|
* Parameters:
|
* filter - {<OpenLayers.Filter>} filter to convert.
|
* params - {Object} The parameters object.
|
*
|
* Returns:
|
* {Object} The resulting parameters object.
|
*/
|
write: function(filter, params) {
|
params = params || {};
|
var className = filter.CLASS_NAME;
|
var filterType = className.substring(className.lastIndexOf(".") + 1);
|
switch (filterType) {
|
case "Spatial":
|
switch (filter.type) {
|
case OpenLayers.Filter.Spatial.BBOX:
|
params.bbox = filter.value.toArray();
|
if (this.srsInBBOX && filter.projection) {
|
params.bbox.push(filter.projection.getCode());
|
}
|
break;
|
case OpenLayers.Filter.Spatial.DWITHIN:
|
params.tolerance = filter.distance;
|
// no break here
|
case OpenLayers.Filter.Spatial.WITHIN:
|
params.lon = filter.value.x;
|
params.lat = filter.value.y;
|
break;
|
default:
|
OpenLayers.Console.warn(
|
"Unknown spatial filter type " + filter.type);
|
}
|
break;
|
case "Comparison":
|
var op = cmpToStr[filter.type];
|
if (op !== undefined) {
|
var value = filter.value;
|
if (filter.type == OpenLayers.Filter.Comparison.LIKE) {
|
value = regex2value(value);
|
if (this.wildcarded) {
|
value = "%" + value + "%";
|
}
|
}
|
params[filter.property + "__" + op] = value;
|
params.queryable = params.queryable || [];
|
params.queryable.push(filter.property);
|
} else {
|
OpenLayers.Console.warn(
|
"Unknown comparison filter type " + filter.type);
|
}
|
break;
|
case "Logical":
|
if (filter.type === OpenLayers.Filter.Logical.AND) {
|
for (var i=0,len=filter.filters.length; i<len; i++) {
|
params = this.write(filter.filters[i], params);
|
}
|
} else {
|
OpenLayers.Console.warn(
|
"Unsupported logical filter type " + filter.type);
|
}
|
break;
|
default:
|
OpenLayers.Console.warn("Unknown filter type " + filterType);
|
}
|
return params;
|
},
|
|
CLASS_NAME: "OpenLayers.Format.QueryStringFilter"
|
|
});
|
|
|
})();
|
/* ======================================================================
|
OpenLayers/Control/MousePosition.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Control.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.MousePosition
|
* The MousePosition control displays geographic coordinates of the mouse
|
* pointer, as it is moved about the map.
|
*
|
* You can use the <prefix>- or <suffix>-properties to provide more information
|
* about the displayed coordinates to the user:
|
*
|
* (code)
|
* var mousePositionCtrl = new OpenLayers.Control.MousePosition({
|
* prefix: '<a target="_blank" ' +
|
* 'href="http://spatialreference.org/ref/epsg/4326/">' +
|
* 'EPSG:4326</a> coordinates: '
|
* }
|
* );
|
* (end code)
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.MousePosition = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* APIProperty: autoActivate
|
* {Boolean} Activate the control when it is added to a map. Default is
|
* true.
|
*/
|
autoActivate: true,
|
|
/**
|
* Property: element
|
* {DOMElement}
|
*/
|
element: null,
|
|
/**
|
* APIProperty: prefix
|
* {String} A string to be prepended to the current pointers coordinates
|
* when it is rendered. Defaults to the empty string ''.
|
*/
|
prefix: '',
|
|
/**
|
* APIProperty: separator
|
* {String} A string to be used to seperate the two coordinates from each
|
* other. Defaults to the string ', ', which will result in a
|
* rendered coordinate of e.g. '42.12, 21.22'.
|
*/
|
separator: ', ',
|
|
/**
|
* APIProperty: suffix
|
* {String} A string to be appended to the current pointers coordinates
|
* when it is rendered. Defaults to the empty string ''.
|
*/
|
suffix: '',
|
|
/**
|
* APIProperty: numDigits
|
* {Integer} The number of digits each coordinate shall have when being
|
* rendered, Defaults to 5.
|
*/
|
numDigits: 5,
|
|
/**
|
* APIProperty: granularity
|
* {Integer}
|
*/
|
granularity: 10,
|
|
/**
|
* APIProperty: emptyString
|
* {String} Set this to some value to set when the mouse is outside the
|
* map.
|
*/
|
emptyString: null,
|
|
/**
|
* Property: lastXy
|
* {<OpenLayers.Pixel>}
|
*/
|
lastXy: null,
|
|
/**
|
* APIProperty: displayProjection
|
* {<OpenLayers.Projection>} The projection in which the mouse position is
|
* displayed.
|
*/
|
displayProjection: null,
|
|
/**
|
* Constructor: OpenLayers.Control.MousePosition
|
*
|
* Parameters:
|
* options - {Object} Options for control.
|
*/
|
|
/**
|
* Method: destroy
|
*/
|
destroy: function() {
|
this.deactivate();
|
OpenLayers.Control.prototype.destroy.apply(this, arguments);
|
},
|
|
/**
|
* APIMethod: activate
|
*/
|
activate: function() {
|
if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {
|
this.map.events.register('mousemove', this, this.redraw);
|
this.map.events.register('mouseout', this, this.reset);
|
this.redraw();
|
return true;
|
} else {
|
return false;
|
}
|
},
|
|
/**
|
* APIMethod: deactivate
|
*/
|
deactivate: function() {
|
if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {
|
this.map.events.unregister('mousemove', this, this.redraw);
|
this.map.events.unregister('mouseout', this, this.reset);
|
this.element.innerHTML = "";
|
return true;
|
} else {
|
return false;
|
}
|
},
|
|
/**
|
* Method: draw
|
* {DOMElement}
|
*/
|
draw: function() {
|
OpenLayers.Control.prototype.draw.apply(this, arguments);
|
|
if (!this.element) {
|
this.div.left = "";
|
this.div.top = "";
|
this.element = this.div;
|
}
|
|
return this.div;
|
},
|
|
/**
|
* Method: redraw
|
*/
|
redraw: function(evt) {
|
|
var lonLat;
|
|
if (evt == null) {
|
this.reset();
|
return;
|
} else {
|
if (this.lastXy == null ||
|
Math.abs(evt.xy.x - this.lastXy.x) > this.granularity ||
|
Math.abs(evt.xy.y - this.lastXy.y) > this.granularity)
|
{
|
this.lastXy = evt.xy;
|
return;
|
}
|
|
lonLat = this.map.getLonLatFromPixel(evt.xy);
|
if (!lonLat) {
|
// map has not yet been properly initialized
|
return;
|
}
|
if (this.displayProjection) {
|
lonLat.transform(this.map.getProjectionObject(),
|
this.displayProjection );
|
}
|
this.lastXy = evt.xy;
|
|
}
|
|
var newHtml = this.formatOutput(lonLat);
|
|
if (newHtml != this.element.innerHTML) {
|
this.element.innerHTML = newHtml;
|
}
|
},
|
|
/**
|
* Method: reset
|
*/
|
reset: function(evt) {
|
if (this.emptyString != null) {
|
this.element.innerHTML = this.emptyString;
|
}
|
},
|
|
/**
|
* Method: formatOutput
|
* Override to provide custom display output
|
*
|
* Parameters:
|
* lonLat - {<OpenLayers.LonLat>} Location to display
|
*/
|
formatOutput: function(lonLat) {
|
var digits = parseInt(this.numDigits);
|
var newHtml =
|
this.prefix +
|
lonLat.lon.toFixed(digits) +
|
this.separator +
|
lonLat.lat.toFixed(digits) +
|
this.suffix;
|
return newHtml;
|
},
|
|
CLASS_NAME: "OpenLayers.Control.MousePosition"
|
});
|
/* ======================================================================
|
OpenLayers/Control/Geolocate.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/Geometry/Point.js
|
* @requires OpenLayers/Projection.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.Geolocate
|
* The Geolocate control wraps w3c geolocation API into control that can be
|
* bound to a map, and generate events on location update
|
*
|
* To use this control requires to load the proj4js library if the projection
|
* of the map is not EPSG:4326 or EPSG:900913.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.Geolocate = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* APIProperty: events
|
* {<OpenLayers.Events>} Events instance for listeners and triggering
|
* control specific events.
|
*
|
* Register a listener for a particular event with the following syntax:
|
* (code)
|
* control.events.register(type, obj, listener);
|
* (end)
|
*
|
* Supported event types (in addition to those from <OpenLayers.Control.events>):
|
* locationupdated - Triggered when browser return a new position. Listeners will
|
* receive an object with a 'position' property which is the browser.geolocation.position
|
* native object, as well as a 'point' property which is the location transformed in the
|
* current map projection.
|
* locationfailed - Triggered when geolocation has failed
|
* locationuncapable - Triggered when control is activated on a browser
|
* which doesn't support geolocation
|
*/
|
|
/**
|
* Property: geolocation
|
* {Object} The geolocation engine, as a property to be possibly mocked.
|
* This is set lazily to avoid a memory leak in IE9.
|
*/
|
geolocation: null,
|
|
/**
|
* Property: available
|
* {Boolean} The navigator.geolocation object is available.
|
*/
|
available: ('geolocation' in navigator),
|
|
/**
|
* APIProperty: bind
|
* {Boolean} If true, map center will be set on location update.
|
*/
|
bind: true,
|
|
/**
|
* APIProperty: watch
|
* {Boolean} If true, position will be update regularly.
|
*/
|
watch: false,
|
|
/**
|
* APIProperty: geolocationOptions
|
* {Object} Options to pass to the navigator's geolocation API. See
|
* <http://dev.w3.org/geo/api/spec-source.html>. No specific
|
* option is passed to the geolocation API by default.
|
*/
|
geolocationOptions: null,
|
|
/**
|
* Constructor: OpenLayers.Control.Geolocate
|
* Create a new control to deal with browser geolocation API
|
*
|
*/
|
|
/**
|
* Method: destroy
|
*/
|
destroy: function() {
|
this.deactivate();
|
OpenLayers.Control.prototype.destroy.apply(this, arguments);
|
},
|
|
/**
|
* Method: activate
|
* Activates the control.
|
*
|
* Returns:
|
* {Boolean} The control was effectively activated.
|
*/
|
activate: function () {
|
if (this.available && !this.geolocation) {
|
// set lazily to avoid IE9 memory leak
|
this.geolocation = navigator.geolocation;
|
}
|
if (!this.geolocation) {
|
this.events.triggerEvent("locationuncapable");
|
return false;
|
}
|
if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {
|
if (this.watch) {
|
this.watchId = this.geolocation.watchPosition(
|
OpenLayers.Function.bind(this.geolocate, this),
|
OpenLayers.Function.bind(this.failure, this),
|
this.geolocationOptions
|
);
|
} else {
|
this.getCurrentLocation();
|
}
|
return true;
|
}
|
return false;
|
},
|
|
/**
|
* Method: deactivate
|
* Deactivates the control.
|
*
|
* Returns:
|
* {Boolean} The control was effectively deactivated.
|
*/
|
deactivate: function () {
|
if (this.active && this.watchId !== null) {
|
this.geolocation.clearWatch(this.watchId);
|
}
|
return OpenLayers.Control.prototype.deactivate.apply(
|
this, arguments
|
);
|
},
|
|
/**
|
* Method: geolocate
|
* Activates the control.
|
*
|
*/
|
geolocate: function (position) {
|
var center = new OpenLayers.LonLat(
|
position.coords.longitude,
|
position.coords.latitude
|
).transform(
|
new OpenLayers.Projection("EPSG:4326"),
|
this.map.getProjectionObject()
|
);
|
if (this.bind) {
|
this.map.setCenter(center);
|
}
|
this.events.triggerEvent("locationupdated", {
|
position: position,
|
point: new OpenLayers.Geometry.Point(
|
center.lon, center.lat
|
)
|
});
|
},
|
|
/**
|
* APIMethod: getCurrentLocation
|
*
|
* Returns:
|
* {Boolean} Returns true if a event will be fired (successfull
|
* registration)
|
*/
|
getCurrentLocation: function() {
|
if (!this.active || this.watch) {
|
return false;
|
}
|
this.geolocation.getCurrentPosition(
|
OpenLayers.Function.bind(this.geolocate, this),
|
OpenLayers.Function.bind(this.failure, this),
|
this.geolocationOptions
|
);
|
return true;
|
},
|
|
/**
|
* Method: failure
|
* method called on browser's geolocation failure
|
*
|
*/
|
failure: function (error) {
|
this.events.triggerEvent("locationfailed", {error: error});
|
},
|
|
CLASS_NAME: "OpenLayers.Control.Geolocate"
|
});
|
/* ======================================================================
|
OpenLayers/Tile/UTFGrid.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Tile.js
|
* @requires OpenLayers/Format/JSON.js
|
* @requires OpenLayers/Request.js
|
*/
|
|
/**
|
* Class: OpenLayers.Tile.UTFGrid
|
* Instances of OpenLayers.Tile.UTFGrid are used to manage
|
* UTFGrids. This is an unusual tile type in that it doesn't have a
|
* rendered image; only a 'hit grid' that can be used to
|
* look up feature attributes.
|
*
|
* See the <OpenLayers.Tile.UTFGrid> constructor for details on constructing a
|
* new instance.
|
*
|
* Inherits from:
|
* - <OpenLayers.Tile>
|
*/
|
OpenLayers.Tile.UTFGrid = OpenLayers.Class(OpenLayers.Tile, {
|
|
/**
|
* Property: url
|
* {String}
|
* The URL of the UTFGrid file being requested. Provided by the <getURL>
|
* method.
|
*/
|
url: null,
|
|
/**
|
* Property: utfgridResolution
|
* {Number}
|
* Ratio of the pixel width to the width of a UTFGrid data point. If an
|
* entry in the grid represents a 4x4 block of pixels, the
|
* utfgridResolution would be 4. Default is 2.
|
*/
|
utfgridResolution: 2,
|
|
/**
|
* Property: json
|
* {Object}
|
* Stores the parsed JSON tile data structure.
|
*/
|
json: null,
|
|
/**
|
* Property: format
|
* {OpenLayers.Format.JSON}
|
* Parser instance used to parse JSON for cross browser support. The native
|
* JSON.parse method will be used where available (all except IE<8).
|
*/
|
format: null,
|
|
/**
|
* Constructor: OpenLayers.Tile.UTFGrid
|
* Constructor for a new <OpenLayers.Tile.UTFGrid> instance.
|
*
|
* Parameters:
|
* layer - {<OpenLayers.Layer>} layer that the tile will go in.
|
* position - {<OpenLayers.Pixel>}
|
* bounds - {<OpenLayers.Bounds>}
|
* url - {<String>} Deprecated. Remove me in 3.0.
|
* size - {<OpenLayers.Size>}
|
* options - {Object}
|
*/
|
|
/**
|
* APIMethod: destroy
|
* Clean up.
|
*/
|
destroy: function() {
|
this.clear();
|
OpenLayers.Tile.prototype.destroy.apply(this, arguments);
|
},
|
|
/**
|
* Method: draw
|
* Check that a tile should be drawn, and draw it.
|
* In the case of UTFGrids, "drawing" it means fetching and
|
* parsing the json.
|
*
|
* Returns:
|
* {Boolean} Was a tile drawn?
|
*/
|
draw: function() {
|
var drawn = OpenLayers.Tile.prototype.draw.apply(this, arguments);
|
if (drawn) {
|
if (this.isLoading) {
|
this.abortLoading();
|
//if we're already loading, send 'reload' instead of 'loadstart'.
|
this.events.triggerEvent("reload");
|
} else {
|
this.isLoading = true;
|
this.events.triggerEvent("loadstart");
|
}
|
this.url = this.layer.getURL(this.bounds);
|
|
if (this.layer.useJSONP) {
|
// Use JSONP method to avoid xbrowser policy
|
var ols = new OpenLayers.Protocol.Script({
|
url: this.url,
|
callback: function(response) {
|
this.isLoading = false;
|
this.events.triggerEvent("loadend");
|
this.json = response.data;
|
},
|
scope: this
|
});
|
ols.read();
|
this.request = ols;
|
} else {
|
// Use standard XHR
|
this.request = OpenLayers.Request.GET({
|
url: this.url,
|
callback: function(response) {
|
this.isLoading = false;
|
this.events.triggerEvent("loadend");
|
if (response.status === 200) {
|
this.parseData(response.responseText);
|
}
|
},
|
scope: this
|
});
|
}
|
} else {
|
this.unload();
|
}
|
return drawn;
|
},
|
|
/**
|
* Method: abortLoading
|
* Cancel a pending request.
|
*/
|
abortLoading: function() {
|
if (this.request) {
|
this.request.abort();
|
delete this.request;
|
}
|
this.isLoading = false;
|
},
|
|
/**
|
* Method: getFeatureInfo
|
* Get feature information associated with a pixel offset. If the pixel
|
* offset corresponds to a feature, the returned object will have id
|
* and data properties. Otherwise, null will be returned.
|
*
|
*
|
* Parameters:
|
* i - {Number} X-axis pixel offset (from top left of tile)
|
* j - {Number} Y-axis pixel offset (from top left of tile)
|
*
|
* Returns:
|
* {Object} Object with feature id and data properties corresponding to the
|
* given pixel offset.
|
*/
|
getFeatureInfo: function(i, j) {
|
var info = null;
|
if (this.json) {
|
var id = this.getFeatureId(i, j);
|
if (id !== null) {
|
info = {id: id, data: this.json.data[id]};
|
}
|
}
|
return info;
|
},
|
|
/**
|
* Method: getFeatureId
|
* Get the identifier for the feature associated with a pixel offset.
|
*
|
* Parameters:
|
* i - {Number} X-axis pixel offset (from top left of tile)
|
* j - {Number} Y-axis pixel offset (from top left of tile)
|
*
|
* Returns:
|
* {Object} The feature identifier corresponding to the given pixel offset.
|
* Returns null if pixel doesn't correspond to a feature.
|
*/
|
getFeatureId: function(i, j) {
|
var id = null;
|
if (this.json) {
|
var resolution = this.utfgridResolution;
|
var row = Math.floor(j / resolution);
|
var col = Math.floor(i / resolution);
|
var charCode = this.json.grid[row].charCodeAt(col);
|
var index = this.indexFromCharCode(charCode);
|
var keys = this.json.keys;
|
if (!isNaN(index) && (index in keys)) {
|
id = keys[index];
|
}
|
}
|
return id;
|
},
|
|
/**
|
* Method: indexFromCharCode
|
* Given a character code for one of the UTFGrid "grid" characters,
|
* resolve the integer index for the feature id in the UTFGrid "keys"
|
* array.
|
*
|
* Parameters:
|
* charCode - {Integer}
|
*
|
* Returns:
|
* {Integer} Index for the feature id from the keys array.
|
*/
|
indexFromCharCode: function(charCode) {
|
if (charCode >= 93) {
|
charCode--;
|
}
|
if (charCode >= 35) {
|
charCode --;
|
}
|
return charCode - 32;
|
},
|
|
/**
|
* Method: parseData
|
* Parse the JSON from a request
|
*
|
* Parameters:
|
* str - {String} UTFGrid as a JSON string.
|
*
|
* Returns:
|
* {Object} parsed javascript data
|
*/
|
parseData: function(str) {
|
if (!this.format) {
|
this.format = new OpenLayers.Format.JSON();
|
}
|
this.json = this.format.read(str);
|
},
|
|
/**
|
* Method: clear
|
* Delete data stored with this tile.
|
*/
|
clear: function() {
|
this.json = null;
|
},
|
|
CLASS_NAME: "OpenLayers.Tile.UTFGrid"
|
|
});
|
/* ======================================================================
|
OpenLayers/Protocol/HTTP.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Protocol.js
|
* @requires OpenLayers/Request/XMLHttpRequest.js
|
*/
|
|
/**
|
* if application uses the query string, for example, for BBOX parameters,
|
* OpenLayers/Format/QueryStringFilter.js should be included in the build config file
|
*/
|
|
/**
|
* Class: OpenLayers.Protocol.HTTP
|
* A basic HTTP protocol for vector layers. Create a new instance with the
|
* <OpenLayers.Protocol.HTTP> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Protocol>
|
*/
|
OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, {
|
|
/**
|
* Property: url
|
* {String} Service URL, read-only, set through the options
|
* passed to constructor.
|
*/
|
url: null,
|
|
/**
|
* Property: headers
|
* {Object} HTTP request headers, read-only, set through the options
|
* passed to the constructor,
|
* Example: {'Content-Type': 'plain/text'}
|
*/
|
headers: null,
|
|
/**
|
* Property: params
|
* {Object} Parameters of GET requests, read-only, set through the options
|
* passed to the constructor,
|
* Example: {'bbox': '5,5,5,5'}
|
*/
|
params: null,
|
|
/**
|
* Property: callback
|
* {Object} Function to be called when the <read>, <create>,
|
* <update>, <delete> or <commit> operation completes, read-only,
|
* set through the options passed to the constructor.
|
*/
|
callback: null,
|
|
/**
|
* Property: scope
|
* {Object} Callback execution scope, read-only, set through the
|
* options passed to the constructor.
|
*/
|
scope: null,
|
|
/**
|
* APIProperty: readWithPOST
|
* {Boolean} true if read operations are done with POST requests
|
* instead of GET, defaults to false.
|
*/
|
readWithPOST: false,
|
|
/**
|
* APIProperty: updateWithPOST
|
* {Boolean} true if update operations are done with POST requests
|
* defaults to false.
|
*/
|
updateWithPOST: false,
|
|
/**
|
* APIProperty: deleteWithPOST
|
* {Boolean} true if delete operations are done with POST requests
|
* defaults to false.
|
* if true, POST data is set to output of format.write().
|
*/
|
deleteWithPOST: false,
|
|
/**
|
* Property: wildcarded.
|
* {Boolean} If true percent signs are added around values
|
* read from LIKE filters, for example if the protocol
|
* read method is passed a LIKE filter whose property
|
* is "foo" and whose value is "bar" the string
|
* "foo__ilike=%bar%" will be sent in the query string;
|
* defaults to false.
|
*/
|
wildcarded: false,
|
|
/**
|
* APIProperty: srsInBBOX
|
* {Boolean} Include the SRS identifier in BBOX query string parameter.
|
* Default is false. If true and the layer has a projection object set,
|
* any BBOX filter will be serialized with a fifth item identifying the
|
* projection. E.g. bbox=-1000,-1000,1000,1000,EPSG:900913
|
*/
|
srsInBBOX: false,
|
|
/**
|
* Constructor: OpenLayers.Protocol.HTTP
|
* A class for giving layers generic HTTP protocol.
|
*
|
* Parameters:
|
* options - {Object} Optional object whose properties will be set on the
|
* instance.
|
*
|
* Valid options include:
|
* url - {String}
|
* headers - {Object}
|
* params - {Object} URL parameters for GET requests
|
* format - {<OpenLayers.Format>}
|
* callback - {Function}
|
* scope - {Object}
|
*/
|
initialize: function(options) {
|
options = options || {};
|
this.params = {};
|
this.headers = {};
|
OpenLayers.Protocol.prototype.initialize.apply(this, arguments);
|
|
if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) {
|
var format = new OpenLayers.Format.QueryStringFilter({
|
wildcarded: this.wildcarded,
|
srsInBBOX: this.srsInBBOX
|
});
|
this.filterToParams = function(filter, params) {
|
return format.write(filter, params);
|
};
|
}
|
},
|
|
/**
|
* APIMethod: destroy
|
* Clean up the protocol.
|
*/
|
destroy: function() {
|
this.params = null;
|
this.headers = null;
|
OpenLayers.Protocol.prototype.destroy.apply(this);
|
},
|
|
/**
|
* APIMethod: filterToParams
|
* Optional method to translate an <OpenLayers.Filter> object into an object
|
* that can be serialized as request query string provided. If a custom
|
* method is not provided, the filter will be serialized using the
|
* <OpenLayers.Format.QueryStringFilter> class.
|
*
|
* Parameters:
|
* filter - {<OpenLayers.Filter>} filter to convert.
|
* params - {Object} The parameters object.
|
*
|
* Returns:
|
* {Object} The resulting parameters object.
|
*/
|
|
/**
|
* APIMethod: read
|
* Construct a request for reading new features.
|
*
|
* Parameters:
|
* options - {Object} Optional object for configuring the request.
|
* This object is modified and should not be reused.
|
*
|
* Valid options:
|
* url - {String} Url for the request.
|
* params - {Object} Parameters to get serialized as a query string.
|
* headers - {Object} Headers to be set on the request.
|
* filter - {<OpenLayers.Filter>} Filter to get serialized as a
|
* query string.
|
* readWithPOST - {Boolean} If the request should be done with POST.
|
*
|
* Returns:
|
* {<OpenLayers.Protocol.Response>} A response object, whose "priv" property
|
* references the HTTP request, this object is also passed to the
|
* callback function when the request completes, its "features" property
|
* is then populated with the features received from the server.
|
*/
|
read: function(options) {
|
OpenLayers.Protocol.prototype.read.apply(this, arguments);
|
options = options || {};
|
options.params = OpenLayers.Util.applyDefaults(
|
options.params, this.options.params);
|
options = OpenLayers.Util.applyDefaults(options, this.options);
|
if (options.filter && this.filterToParams) {
|
options.params = this.filterToParams(
|
options.filter, options.params
|
);
|
}
|
var readWithPOST = (options.readWithPOST !== undefined) ?
|
options.readWithPOST : this.readWithPOST;
|
var resp = new OpenLayers.Protocol.Response({requestType: "read"});
|
if(readWithPOST) {
|
var headers = options.headers || {};
|
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
resp.priv = OpenLayers.Request.POST({
|
url: options.url,
|
callback: this.createCallback(this.handleRead, resp, options),
|
data: OpenLayers.Util.getParameterString(options.params),
|
headers: headers
|
});
|
} else {
|
resp.priv = OpenLayers.Request.GET({
|
url: options.url,
|
callback: this.createCallback(this.handleRead, resp, options),
|
params: options.params,
|
headers: options.headers
|
});
|
}
|
return resp;
|
},
|
|
/**
|
* Method: handleRead
|
* Individual callbacks are created for read, create and update, should
|
* a subclass need to override each one separately.
|
*
|
* Parameters:
|
* resp - {<OpenLayers.Protocol.Response>} The response object to pass to
|
* the user callback.
|
* options - {Object} The user options passed to the read call.
|
*/
|
handleRead: function(resp, options) {
|
this.handleResponse(resp, options);
|
},
|
|
/**
|
* APIMethod: create
|
* Construct a request for writing newly created features.
|
*
|
* Parameters:
|
* features - {Array({<OpenLayers.Feature.Vector>})} or
|
* {<OpenLayers.Feature.Vector>}
|
* options - {Object} Optional object for configuring the request.
|
* This object is modified and should not be reused.
|
*
|
* Returns:
|
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
|
* object, whose "priv" property references the HTTP request, this
|
* object is also passed to the callback function when the request
|
* completes, its "features" property is then populated with the
|
* the features received from the server.
|
*/
|
create: function(features, options) {
|
options = OpenLayers.Util.applyDefaults(options, this.options);
|
|
var resp = new OpenLayers.Protocol.Response({
|
reqFeatures: features,
|
requestType: "create"
|
});
|
|
resp.priv = OpenLayers.Request.POST({
|
url: options.url,
|
callback: this.createCallback(this.handleCreate, resp, options),
|
headers: options.headers,
|
data: this.format.write(features)
|
});
|
|
return resp;
|
},
|
|
/**
|
* Method: handleCreate
|
* Called the the request issued by <create> is complete. May be overridden
|
* by subclasses.
|
*
|
* Parameters:
|
* resp - {<OpenLayers.Protocol.Response>} The response object to pass to
|
* any user callback.
|
* options - {Object} The user options passed to the create call.
|
*/
|
handleCreate: function(resp, options) {
|
this.handleResponse(resp, options);
|
},
|
|
/**
|
* APIMethod: update
|
* Construct a request updating modified feature.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
* options - {Object} Optional object for configuring the request.
|
* This object is modified and should not be reused.
|
*
|
* Returns:
|
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
|
* object, whose "priv" property references the HTTP request, this
|
* object is also passed to the callback function when the request
|
* completes, its "features" property is then populated with the
|
* the feature received from the server.
|
*/
|
update: function(feature, options) {
|
options = options || {};
|
var url = options.url ||
|
feature.url ||
|
this.options.url + "/" + feature.fid;
|
options = OpenLayers.Util.applyDefaults(options, this.options);
|
|
var resp = new OpenLayers.Protocol.Response({
|
reqFeatures: feature,
|
requestType: "update"
|
});
|
|
var method = this.updateWithPOST ? "POST" : "PUT";
|
resp.priv = OpenLayers.Request[method]({
|
url: url,
|
callback: this.createCallback(this.handleUpdate, resp, options),
|
headers: options.headers,
|
data: this.format.write(feature)
|
});
|
|
return resp;
|
},
|
|
/**
|
* Method: handleUpdate
|
* Called the the request issued by <update> is complete. May be overridden
|
* by subclasses.
|
*
|
* Parameters:
|
* resp - {<OpenLayers.Protocol.Response>} The response object to pass to
|
* any user callback.
|
* options - {Object} The user options passed to the update call.
|
*/
|
handleUpdate: function(resp, options) {
|
this.handleResponse(resp, options);
|
},
|
|
/**
|
* APIMethod: delete
|
* Construct a request deleting a removed feature.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
* options - {Object} Optional object for configuring the request.
|
* This object is modified and should not be reused.
|
*
|
* Returns:
|
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
|
* object, whose "priv" property references the HTTP request, this
|
* object is also passed to the callback function when the request
|
* completes.
|
*/
|
"delete": function(feature, options) {
|
options = options || {};
|
var url = options.url ||
|
feature.url ||
|
this.options.url + "/" + feature.fid;
|
options = OpenLayers.Util.applyDefaults(options, this.options);
|
|
var resp = new OpenLayers.Protocol.Response({
|
reqFeatures: feature,
|
requestType: "delete"
|
});
|
|
var method = this.deleteWithPOST ? "POST" : "DELETE";
|
var requestOptions = {
|
url: url,
|
callback: this.createCallback(this.handleDelete, resp, options),
|
headers: options.headers
|
};
|
if (this.deleteWithPOST) {
|
requestOptions.data = this.format.write(feature);
|
}
|
resp.priv = OpenLayers.Request[method](requestOptions);
|
|
return resp;
|
},
|
|
/**
|
* Method: handleDelete
|
* Called the the request issued by <delete> is complete. May be overridden
|
* by subclasses.
|
*
|
* Parameters:
|
* resp - {<OpenLayers.Protocol.Response>} The response object to pass to
|
* any user callback.
|
* options - {Object} The user options passed to the delete call.
|
*/
|
handleDelete: function(resp, options) {
|
this.handleResponse(resp, options);
|
},
|
|
/**
|
* Method: handleResponse
|
* Called by CRUD specific handlers.
|
*
|
* Parameters:
|
* resp - {<OpenLayers.Protocol.Response>} The response object to pass to
|
* any user callback.
|
* options - {Object} The user options passed to the create, read, update,
|
* or delete call.
|
*/
|
handleResponse: function(resp, options) {
|
var request = resp.priv;
|
if(options.callback) {
|
if(request.status >= 200 && request.status < 300) {
|
// success
|
if(resp.requestType != "delete") {
|
resp.features = this.parseFeatures(request);
|
}
|
resp.code = OpenLayers.Protocol.Response.SUCCESS;
|
} else {
|
// failure
|
resp.code = OpenLayers.Protocol.Response.FAILURE;
|
}
|
options.callback.call(options.scope, resp);
|
}
|
},
|
|
/**
|
* Method: parseFeatures
|
* Read HTTP response body and return features.
|
*
|
* Parameters:
|
* request - {XMLHttpRequest} The request object
|
*
|
* Returns:
|
* {Array({<OpenLayers.Feature.Vector>})} or
|
* {<OpenLayers.Feature.Vector>} Array of features or a single feature.
|
*/
|
parseFeatures: function(request) {
|
var doc = request.responseXML;
|
if (!doc || !doc.documentElement) {
|
doc = request.responseText;
|
}
|
if (!doc || doc.length <= 0) {
|
return null;
|
}
|
return this.format.read(doc);
|
},
|
|
/**
|
* APIMethod: commit
|
* Iterate over each feature and take action based on the feature state.
|
* Possible actions are create, update and delete.
|
*
|
* Parameters:
|
* features - {Array({<OpenLayers.Feature.Vector>})}
|
* options - {Object} Optional object for setting up intermediate commit
|
* callbacks.
|
*
|
* Valid options:
|
* create - {Object} Optional object to be passed to the <create> method.
|
* update - {Object} Optional object to be passed to the <update> method.
|
* delete - {Object} Optional object to be passed to the <delete> method.
|
* callback - {Function} Optional function to be called when the commit
|
* is complete.
|
* scope - {Object} Optional object to be set as the scope of the callback.
|
*
|
* Returns:
|
* {Array(<OpenLayers.Protocol.Response>)} An array of response objects,
|
* one per request made to the server, each object's "priv" property
|
* references the corresponding HTTP request.
|
*/
|
commit: function(features, options) {
|
options = OpenLayers.Util.applyDefaults(options, this.options);
|
var resp = [], nResponses = 0;
|
|
// Divide up features before issuing any requests. This properly
|
// counts requests in the event that any responses come in before
|
// all requests have been issued.
|
var types = {};
|
types[OpenLayers.State.INSERT] = [];
|
types[OpenLayers.State.UPDATE] = [];
|
types[OpenLayers.State.DELETE] = [];
|
var feature, list, requestFeatures = [];
|
for(var i=0, len=features.length; i<len; ++i) {
|
feature = features[i];
|
list = types[feature.state];
|
if(list) {
|
list.push(feature);
|
requestFeatures.push(feature);
|
}
|
}
|
// tally up number of requests
|
var nRequests = (types[OpenLayers.State.INSERT].length > 0 ? 1 : 0) +
|
types[OpenLayers.State.UPDATE].length +
|
types[OpenLayers.State.DELETE].length;
|
|
// This response will be sent to the final callback after all the others
|
// have been fired.
|
var success = true;
|
var finalResponse = new OpenLayers.Protocol.Response({
|
reqFeatures: requestFeatures
|
});
|
|
function insertCallback(response) {
|
var len = response.features ? response.features.length : 0;
|
var fids = new Array(len);
|
for(var i=0; i<len; ++i) {
|
fids[i] = response.features[i].fid;
|
}
|
finalResponse.insertIds = fids;
|
callback.apply(this, [response]);
|
}
|
|
function callback(response) {
|
this.callUserCallback(response, options);
|
success = success && response.success();
|
nResponses++;
|
if (nResponses >= nRequests) {
|
if (options.callback) {
|
finalResponse.code = success ?
|
OpenLayers.Protocol.Response.SUCCESS :
|
OpenLayers.Protocol.Response.FAILURE;
|
options.callback.apply(options.scope, [finalResponse]);
|
}
|
}
|
}
|
|
// start issuing requests
|
var queue = types[OpenLayers.State.INSERT];
|
if(queue.length > 0) {
|
resp.push(this.create(
|
queue, OpenLayers.Util.applyDefaults(
|
{callback: insertCallback, scope: this}, options.create
|
)
|
));
|
}
|
queue = types[OpenLayers.State.UPDATE];
|
for(var i=queue.length-1; i>=0; --i) {
|
resp.push(this.update(
|
queue[i], OpenLayers.Util.applyDefaults(
|
{callback: callback, scope: this}, options.update
|
))
|
);
|
}
|
queue = types[OpenLayers.State.DELETE];
|
for(var i=queue.length-1; i>=0; --i) {
|
resp.push(this["delete"](
|
queue[i], OpenLayers.Util.applyDefaults(
|
{callback: callback, scope: this}, options["delete"]
|
))
|
);
|
}
|
return resp;
|
},
|
|
/**
|
* APIMethod: abort
|
* Abort an ongoing request, the response object passed to
|
* this method must come from this HTTP protocol (as a result
|
* of a create, read, update, delete or commit operation).
|
*
|
* Parameters:
|
* response - {<OpenLayers.Protocol.Response>}
|
*/
|
abort: function(response) {
|
if (response) {
|
response.priv.abort();
|
}
|
},
|
|
/**
|
* Method: callUserCallback
|
* This method is used from within the commit method each time an
|
* an HTTP response is received from the server, it is responsible
|
* for calling the user-supplied callbacks.
|
*
|
* Parameters:
|
* resp - {<OpenLayers.Protocol.Response>}
|
* options - {Object} The map of options passed to the commit call.
|
*/
|
callUserCallback: function(resp, options) {
|
var opt = options[resp.requestType];
|
if(opt && opt.callback) {
|
opt.callback.call(opt.scope, resp);
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Protocol.HTTP"
|
});
|
/* ======================================================================
|
OpenLayers/Strategy/Cluster.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Strategy.js
|
*/
|
|
/**
|
* Class: OpenLayers.Strategy.Cluster
|
* Strategy for vector feature clustering.
|
*
|
* Inherits from:
|
* - <OpenLayers.Strategy>
|
*/
|
OpenLayers.Strategy.Cluster = OpenLayers.Class(OpenLayers.Strategy, {
|
|
/**
|
* APIProperty: distance
|
* {Integer} Pixel distance between features that should be considered a
|
* single cluster. Default is 20 pixels.
|
*/
|
distance: 20,
|
|
/**
|
* APIProperty: threshold
|
* {Integer} Optional threshold below which original features will be
|
* added to the layer instead of clusters. For example, a threshold
|
* of 3 would mean that any time there are 2 or fewer features in
|
* a cluster, those features will be added directly to the layer instead
|
* of a cluster representing those features. Default is null (which is
|
* equivalent to 1 - meaning that clusters may contain just one feature).
|
*/
|
threshold: null,
|
|
/**
|
* Property: features
|
* {Array(<OpenLayers.Feature.Vector>)} Cached features.
|
*/
|
features: null,
|
|
/**
|
* Property: clusters
|
* {Array(<OpenLayers.Feature.Vector>)} Calculated clusters.
|
*/
|
clusters: null,
|
|
/**
|
* Property: clustering
|
* {Boolean} The strategy is currently clustering features.
|
*/
|
clustering: false,
|
|
/**
|
* Property: resolution
|
* {Float} The resolution (map units per pixel) of the current cluster set.
|
*/
|
resolution: null,
|
|
/**
|
* Constructor: OpenLayers.Strategy.Cluster
|
* Create a new clustering strategy.
|
*
|
* Parameters:
|
* options - {Object} Optional object whose properties will be set on the
|
* instance.
|
*/
|
|
/**
|
* APIMethod: activate
|
* Activate the strategy. Register any listeners, do appropriate setup.
|
*
|
* Returns:
|
* {Boolean} The strategy was successfully activated.
|
*/
|
activate: function() {
|
var activated = OpenLayers.Strategy.prototype.activate.call(this);
|
if(activated) {
|
this.layer.events.on({
|
"beforefeaturesadded": this.cacheFeatures,
|
"featuresremoved": this.clearCache,
|
"moveend": this.cluster,
|
scope: this
|
});
|
}
|
return activated;
|
},
|
|
/**
|
* APIMethod: deactivate
|
* Deactivate the strategy. Unregister any listeners, do appropriate
|
* tear-down.
|
*
|
* Returns:
|
* {Boolean} The strategy was successfully deactivated.
|
*/
|
deactivate: function() {
|
var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
|
if(deactivated) {
|
this.clearCache();
|
this.layer.events.un({
|
"beforefeaturesadded": this.cacheFeatures,
|
"featuresremoved": this.clearCache,
|
"moveend": this.cluster,
|
scope: this
|
});
|
}
|
return deactivated;
|
},
|
|
/**
|
* Method: cacheFeatures
|
* Cache features before they are added to the layer.
|
*
|
* Parameters:
|
* event - {Object} The event that this was listening for. This will come
|
* with a batch of features to be clustered.
|
*
|
* Returns:
|
* {Boolean} False to stop features from being added to the layer.
|
*/
|
cacheFeatures: function(event) {
|
var propagate = true;
|
if(!this.clustering) {
|
this.clearCache();
|
this.features = event.features;
|
this.cluster();
|
propagate = false;
|
}
|
return propagate;
|
},
|
|
/**
|
* Method: clearCache
|
* Clear out the cached features.
|
*/
|
clearCache: function() {
|
if(!this.clustering) {
|
this.features = null;
|
}
|
},
|
|
/**
|
* Method: cluster
|
* Cluster features based on some threshold distance.
|
*
|
* Parameters:
|
* event - {Object} The event received when cluster is called as a
|
* result of a moveend event.
|
*/
|
cluster: function(event) {
|
if((!event || event.zoomChanged) && this.features) {
|
var resolution = this.layer.map.getResolution();
|
if(resolution != this.resolution || !this.clustersExist()) {
|
this.resolution = resolution;
|
var clusters = [];
|
var feature, clustered, cluster;
|
for(var i=0; i<this.features.length; ++i) {
|
feature = this.features[i];
|
if(feature.geometry) {
|
clustered = false;
|
for(var j=clusters.length-1; j>=0; --j) {
|
cluster = clusters[j];
|
if(this.shouldCluster(cluster, feature)) {
|
this.addToCluster(cluster, feature);
|
clustered = true;
|
break;
|
}
|
}
|
if(!clustered) {
|
clusters.push(this.createCluster(this.features[i]));
|
}
|
}
|
}
|
this.clustering = true;
|
this.layer.removeAllFeatures();
|
this.clustering = false;
|
if(clusters.length > 0) {
|
if(this.threshold > 1) {
|
var clone = clusters.slice();
|
clusters = [];
|
var candidate;
|
for(var i=0, len=clone.length; i<len; ++i) {
|
candidate = clone[i];
|
if(candidate.attributes.count < this.threshold) {
|
Array.prototype.push.apply(clusters, candidate.cluster);
|
} else {
|
clusters.push(candidate);
|
}
|
}
|
}
|
this.clustering = true;
|
// A legitimate feature addition could occur during this
|
// addFeatures call. For clustering to behave well, features
|
// should be removed from a layer before requesting a new batch.
|
this.layer.addFeatures(clusters);
|
this.clustering = false;
|
}
|
this.clusters = clusters;
|
}
|
}
|
},
|
|
/**
|
* Method: clustersExist
|
* Determine whether calculated clusters are already on the layer.
|
*
|
* Returns:
|
* {Boolean} The calculated clusters are already on the layer.
|
*/
|
clustersExist: function() {
|
var exist = false;
|
if(this.clusters && this.clusters.length > 0 &&
|
this.clusters.length == this.layer.features.length) {
|
exist = true;
|
for(var i=0; i<this.clusters.length; ++i) {
|
if(this.clusters[i] != this.layer.features[i]) {
|
exist = false;
|
break;
|
}
|
}
|
}
|
return exist;
|
},
|
|
/**
|
* Method: shouldCluster
|
* Determine whether to include a feature in a given cluster.
|
*
|
* Parameters:
|
* cluster - {<OpenLayers.Feature.Vector>} A cluster.
|
* feature - {<OpenLayers.Feature.Vector>} A feature.
|
*
|
* Returns:
|
* {Boolean} The feature should be included in the cluster.
|
*/
|
shouldCluster: function(cluster, feature) {
|
var cc = cluster.geometry.getBounds().getCenterLonLat();
|
var fc = feature.geometry.getBounds().getCenterLonLat();
|
var distance = (
|
Math.sqrt(
|
Math.pow((cc.lon - fc.lon), 2) + Math.pow((cc.lat - fc.lat), 2)
|
) / this.resolution
|
);
|
return (distance <= this.distance);
|
},
|
|
/**
|
* Method: addToCluster
|
* Add a feature to a cluster.
|
*
|
* Parameters:
|
* cluster - {<OpenLayers.Feature.Vector>} A cluster.
|
* feature - {<OpenLayers.Feature.Vector>} A feature.
|
*/
|
addToCluster: function(cluster, feature) {
|
cluster.cluster.push(feature);
|
cluster.attributes.count += 1;
|
},
|
|
/**
|
* Method: createCluster
|
* Given a feature, create a cluster.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
*
|
* Returns:
|
* {<OpenLayers.Feature.Vector>} A cluster.
|
*/
|
createCluster: function(feature) {
|
var center = feature.geometry.getBounds().getCenterLonLat();
|
var cluster = new OpenLayers.Feature.Vector(
|
new OpenLayers.Geometry.Point(center.lon, center.lat),
|
{count: 1}
|
);
|
cluster.cluster = [feature];
|
return cluster;
|
},
|
|
CLASS_NAME: "OpenLayers.Strategy.Cluster"
|
});
|
/* ======================================================================
|
OpenLayers/Strategy/Filter.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Strategy.js
|
* @requires OpenLayers/Filter.js
|
*/
|
|
/**
|
* Class: OpenLayers.Strategy.Filter
|
* Strategy for limiting features that get added to a layer by
|
* evaluating a filter. The strategy maintains a cache of
|
* all features until removeFeatures is called on the layer.
|
*
|
* Inherits from:
|
* - <OpenLayers.Strategy>
|
*/
|
OpenLayers.Strategy.Filter = OpenLayers.Class(OpenLayers.Strategy, {
|
|
/**
|
* APIProperty: filter
|
* {<OpenLayers.Filter>} Filter for limiting features sent to the layer.
|
* Use the <setFilter> method to update this filter after construction.
|
*/
|
filter: null,
|
|
/**
|
* Property: cache
|
* {Array(<OpenLayers.Feature.Vector>)} List of currently cached
|
* features.
|
*/
|
cache: null,
|
|
/**
|
* Property: caching
|
* {Boolean} The filter is currently caching features.
|
*/
|
caching: false,
|
|
/**
|
* Constructor: OpenLayers.Strategy.Filter
|
* Create a new filter strategy.
|
*
|
* Parameters:
|
* options - {Object} Optional object whose properties will be set on the
|
* instance.
|
*/
|
|
/**
|
* APIMethod: activate
|
* Activate the strategy. Register any listeners, do appropriate setup.
|
* By default, this strategy automatically activates itself when a layer
|
* is added to a map.
|
*
|
* Returns:
|
* {Boolean} True if the strategy was successfully activated or false if
|
* the strategy was already active.
|
*/
|
activate: function() {
|
var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments);
|
if (activated) {
|
this.cache = [];
|
this.layer.events.on({
|
"beforefeaturesadded": this.handleAdd,
|
"beforefeaturesremoved": this.handleRemove,
|
scope: this
|
});
|
}
|
return activated;
|
},
|
|
/**
|
* APIMethod: deactivate
|
* Deactivate the strategy. Clear the feature cache.
|
*
|
* Returns:
|
* {Boolean} True if the strategy was successfully deactivated or false if
|
* the strategy was already inactive.
|
*/
|
deactivate: function() {
|
this.cache = null;
|
if (this.layer && this.layer.events) {
|
this.layer.events.un({
|
"beforefeaturesadded": this.handleAdd,
|
"beforefeaturesremoved": this.handleRemove,
|
scope: this
|
});
|
}
|
return OpenLayers.Strategy.prototype.deactivate.apply(this, arguments);
|
},
|
|
/**
|
* Method: handleAdd
|
*/
|
handleAdd: function(event) {
|
if (!this.caching && this.filter) {
|
var features = event.features;
|
event.features = [];
|
var feature;
|
for (var i=0, ii=features.length; i<ii; ++i) {
|
feature = features[i];
|
if (this.filter.evaluate(feature)) {
|
event.features.push(feature);
|
} else {
|
this.cache.push(feature);
|
}
|
}
|
}
|
},
|
|
/**
|
* Method: handleRemove
|
*/
|
handleRemove: function(event) {
|
if (!this.caching) {
|
this.cache = [];
|
}
|
},
|
|
/**
|
* APIMethod: setFilter
|
* Update the filter for this strategy. This will re-evaluate
|
* any features on the layer and in the cache. Only features
|
* for which filter.evalute(feature) returns true will be
|
* added to the layer. Others will be cached by the strategy.
|
*
|
* Parameters:
|
* filter - {<OpenLayers.Filter>} A filter for evaluating features.
|
*/
|
setFilter: function(filter) {
|
this.filter = filter;
|
var previousCache = this.cache;
|
this.cache = [];
|
// look through layer for features to remove from layer
|
this.handleAdd({features: this.layer.features});
|
// cache now contains features to remove from layer
|
if (this.cache.length > 0) {
|
this.caching = true;
|
this.layer.removeFeatures(this.cache.slice());
|
this.caching = false;
|
}
|
// now look through previous cache for features to add to layer
|
if (previousCache.length > 0) {
|
var event = {features: previousCache};
|
this.handleAdd(event);
|
if (event.features.length > 0) {
|
// event has features to add to layer
|
this.caching = true;
|
this.layer.addFeatures(event.features);
|
this.caching = false;
|
}
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Strategy.Filter"
|
|
});
|
/* ======================================================================
|
OpenLayers/Protocol/SOS.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Protocol.js
|
*/
|
|
/**
|
* Function: OpenLayers.Protocol.SOS
|
* Used to create a versioned SOS protocol. Default version is 1.0.0.
|
*
|
* Returns:
|
* {<OpenLayers.Protocol>} An SOS protocol for the given version.
|
*/
|
OpenLayers.Protocol.SOS = function(options) {
|
options = OpenLayers.Util.applyDefaults(
|
options, OpenLayers.Protocol.SOS.DEFAULTS
|
);
|
var cls = OpenLayers.Protocol.SOS["v"+options.version.replace(/\./g, "_")];
|
if(!cls) {
|
throw "Unsupported SOS version: " + options.version;
|
}
|
return new cls(options);
|
};
|
|
/**
|
* Constant: OpenLayers.Protocol.SOS.DEFAULTS
|
*/
|
OpenLayers.Protocol.SOS.DEFAULTS = {
|
"version": "1.0.0"
|
};
|
/* ======================================================================
|
OpenLayers/Format/WFSDescribeFeatureType.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML.js
|
* @requires OpenLayers/Format/OGCExceptionReport.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WFSDescribeFeatureType
|
* Read WFS DescribeFeatureType response
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.WFSDescribeFeatureType = OpenLayers.Class(
|
OpenLayers.Format.XML, {
|
|
/**
|
* Property: regExes
|
* Compiled regular expressions for manipulating strings.
|
*/
|
regExes: {
|
trimSpace: (/^\s*|\s*$/g)
|
},
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
xsd: "http://www.w3.org/2001/XMLSchema"
|
},
|
|
/**
|
* Constructor: OpenLayers.Format.WFSDescribeFeatureType
|
* Create a new parser for WFS DescribeFeatureType responses.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"xsd": {
|
"schema": function(node, obj) {
|
var complexTypes = [];
|
var customTypes = {};
|
var schema = {
|
complexTypes: complexTypes,
|
customTypes: customTypes
|
};
|
var i, len;
|
|
this.readChildNodes(node, schema);
|
|
var attributes = node.attributes;
|
var attr, name;
|
for(i=0, len=attributes.length; i<len; ++i) {
|
attr = attributes[i];
|
name = attr.name;
|
if(name.indexOf("xmlns") === 0) {
|
this.setNamespace(name.split(":")[1] || "", attr.value);
|
} else {
|
obj[name] = attr.value;
|
}
|
}
|
obj.featureTypes = complexTypes;
|
obj.targetPrefix = this.namespaceAlias[obj.targetNamespace];
|
|
// map complexTypes to names of customTypes
|
var complexType, customType;
|
for(i=0, len=complexTypes.length; i<len; ++i) {
|
complexType = complexTypes[i];
|
customType = customTypes[complexType.typeName];
|
if(customTypes[complexType.typeName]) {
|
complexType.typeName = customType.name;
|
}
|
}
|
},
|
"complexType": function(node, obj) {
|
var complexType = {
|
// this is a temporary typeName, it will be overwritten by
|
// the schema reader with the metadata found in the
|
// customTypes hash
|
"typeName": node.getAttribute("name")
|
};
|
this.readChildNodes(node, complexType);
|
obj.complexTypes.push(complexType);
|
},
|
"complexContent": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"extension": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"sequence": function(node, obj) {
|
var sequence = {
|
elements: []
|
};
|
this.readChildNodes(node, sequence);
|
obj.properties = sequence.elements;
|
},
|
"element": function(node, obj) {
|
var type;
|
if(obj.elements) {
|
var element = {};
|
var attributes = node.attributes;
|
var attr;
|
for(var i=0, len=attributes.length; i<len; ++i) {
|
attr = attributes[i];
|
element[attr.name] = attr.value;
|
}
|
|
type = element.type;
|
if(!type) {
|
type = {};
|
this.readChildNodes(node, type);
|
element.restriction = type;
|
element.type = type.base;
|
}
|
var fullType = type.base || type;
|
element.localType = fullType.split(":").pop();
|
obj.elements.push(element);
|
this.readChildNodes(node, element);
|
}
|
|
if(obj.complexTypes) {
|
type = node.getAttribute("type");
|
var localType = type.split(":").pop();
|
obj.customTypes[localType] = {
|
"name": node.getAttribute("name"),
|
"type": type
|
};
|
}
|
},
|
"annotation": function(node, obj) {
|
obj.annotation = {};
|
this.readChildNodes(node, obj.annotation);
|
},
|
"appinfo": function(node, obj) {
|
if (!obj.appinfo) {
|
obj.appinfo = [];
|
}
|
obj.appinfo.push(this.getChildValue(node));
|
},
|
"documentation": function(node, obj) {
|
if (!obj.documentation) {
|
obj.documentation = [];
|
}
|
var value = this.getChildValue(node);
|
obj.documentation.push({
|
lang: node.getAttribute("xml:lang"),
|
textContent: value.replace(this.regExes.trimSpace, "")
|
});
|
},
|
"simpleType": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"restriction": function(node, obj) {
|
obj.base = node.getAttribute("base");
|
this.readRestriction(node, obj);
|
}
|
}
|
},
|
|
/**
|
* Method: readRestriction
|
* Reads restriction defined in the child nodes of a restriction element
|
*
|
* Parameters:
|
* node - {DOMElement} the node to parse
|
* obj - {Object} the object that receives the read result
|
*/
|
readRestriction: function(node, obj) {
|
var children = node.childNodes;
|
var child, nodeName, value;
|
for(var i=0, len=children.length; i<len; ++i) {
|
child = children[i];
|
if(child.nodeType == 1) {
|
nodeName = child.nodeName.split(":").pop();
|
value = child.getAttribute("value");
|
if(!obj[nodeName]) {
|
obj[nodeName] = value;
|
} else {
|
if(typeof obj[nodeName] == "string") {
|
obj[nodeName] = [obj[nodeName]];
|
}
|
obj[nodeName].push(value);
|
}
|
}
|
}
|
},
|
|
/**
|
* Method: read
|
*
|
* Parameters:
|
* data - {DOMElement|String} A WFS DescribeFeatureType document.
|
*
|
* Returns:
|
* {Object} An object representing the WFS DescribeFeatureType response.
|
*/
|
read: function(data) {
|
if(typeof data == "string") {
|
data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
|
}
|
if(data && data.nodeType == 9) {
|
data = data.documentElement;
|
}
|
var schema = {};
|
if (data.nodeName.split(":").pop() === 'ExceptionReport') {
|
// an exception must have occurred, so parse it
|
var parser = new OpenLayers.Format.OGCExceptionReport();
|
schema.error = parser.read(data);
|
} else {
|
this.readNode(data, schema);
|
}
|
return schema;
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WFSDescribeFeatureType"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/GeoRSS.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML.js
|
* @requires OpenLayers/Feature/Vector.js
|
* @requires OpenLayers/Geometry/Point.js
|
* @requires OpenLayers/Geometry/LineString.js
|
* @requires OpenLayers/Geometry/Polygon.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.GeoRSS
|
* Read/write GeoRSS parser. Create a new instance with the
|
* <OpenLayers.Format.GeoRSS> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.GeoRSS = OpenLayers.Class(OpenLayers.Format.XML, {
|
|
/**
|
* APIProperty: rssns
|
* {String} RSS namespace to use. Defaults to
|
* "http://backend.userland.com/rss2"
|
*/
|
rssns: "http://backend.userland.com/rss2",
|
|
/**
|
* APIProperty: featurens
|
* {String} Feature Attributes namespace. Defaults to
|
* "http://mapserver.gis.umn.edu/mapserver"
|
*/
|
featureNS: "http://mapserver.gis.umn.edu/mapserver",
|
|
/**
|
* APIProperty: georssns
|
* {String} GeoRSS namespace to use. Defaults to
|
* "http://www.georss.org/georss"
|
*/
|
georssns: "http://www.georss.org/georss",
|
|
/**
|
* APIProperty: geons
|
* {String} W3C Geo namespace to use. Defaults to
|
* "http://www.w3.org/2003/01/geo/wgs84_pos#"
|
*/
|
geons: "http://www.w3.org/2003/01/geo/wgs84_pos#",
|
|
/**
|
* APIProperty: featureTitle
|
* {String} Default title for features. Defaults to "Untitled"
|
*/
|
featureTitle: "Untitled",
|
|
/**
|
* APIProperty: featureDescription
|
* {String} Default description for features. Defaults to "No Description"
|
*/
|
featureDescription: "No Description",
|
|
/**
|
* Property: gmlParse
|
* {Object} GML Format object for parsing features
|
* Non-API and only created if necessary
|
*/
|
gmlParser: null,
|
|
/**
|
* APIProperty: xy
|
* {Boolean} Order of the GML coordinate: true:(x,y) or false:(y,x)
|
* For GeoRSS the default is (y,x), therefore: false
|
*/
|
xy: false,
|
|
/**
|
* Constructor: OpenLayers.Format.GeoRSS
|
* Create a new parser for GeoRSS.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* Method: createGeometryFromItem
|
* Return a geometry from a GeoRSS Item.
|
*
|
* Parameters:
|
* item - {DOMElement} A GeoRSS item node.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry>} A geometry representing the node.
|
*/
|
createGeometryFromItem: function(item) {
|
var point = this.getElementsByTagNameNS(item, this.georssns, "point");
|
var lat = this.getElementsByTagNameNS(item, this.geons, 'lat');
|
var lon = this.getElementsByTagNameNS(item, this.geons, 'long');
|
|
var line = this.getElementsByTagNameNS(item,
|
this.georssns,
|
"line");
|
var polygon = this.getElementsByTagNameNS(item,
|
this.georssns,
|
"polygon");
|
var where = this.getElementsByTagNameNS(item,
|
this.georssns,
|
"where");
|
var box = this.getElementsByTagNameNS(item,
|
this.georssns,
|
"box");
|
|
if (point.length > 0 || (lat.length > 0 && lon.length > 0)) {
|
var location;
|
if (point.length > 0) {
|
location = OpenLayers.String.trim(
|
point[0].firstChild.nodeValue).split(/\s+/);
|
if (location.length !=2) {
|
location = OpenLayers.String.trim(
|
point[0].firstChild.nodeValue).split(/\s*,\s*/);
|
}
|
} else {
|
location = [parseFloat(lat[0].firstChild.nodeValue),
|
parseFloat(lon[0].firstChild.nodeValue)];
|
}
|
|
var geometry = new OpenLayers.Geometry.Point(location[1], location[0]);
|
|
} else if (line.length > 0) {
|
var coords = OpenLayers.String.trim(this.getChildValue(line[0])).split(/\s+/);
|
var components = [];
|
var point;
|
for (var i=0, len=coords.length; i<len; i+=2) {
|
point = new OpenLayers.Geometry.Point(coords[i+1], coords[i]);
|
components.push(point);
|
}
|
geometry = new OpenLayers.Geometry.LineString(components);
|
} else if (polygon.length > 0) {
|
var coords = OpenLayers.String.trim(this.getChildValue(polygon[0])).split(/\s+/);
|
var components = [];
|
var point;
|
for (var i=0, len=coords.length; i<len; i+=2) {
|
point = new OpenLayers.Geometry.Point(coords[i+1], coords[i]);
|
components.push(point);
|
}
|
geometry = new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(components)]);
|
} else if (where.length > 0) {
|
if (!this.gmlParser) {
|
this.gmlParser = new OpenLayers.Format.GML({'xy': this.xy});
|
}
|
var feature = this.gmlParser.parseFeature(where[0]);
|
geometry = feature.geometry;
|
} else if (box.length > 0) {
|
var coords = OpenLayers.String.trim(box[0].firstChild.nodeValue).split(/\s+/);
|
var components = [];
|
var point;
|
if (coords.length > 3) {
|
point = new OpenLayers.Geometry.Point(coords[1], coords[0]);
|
components.push(point);
|
point = new OpenLayers.Geometry.Point(coords[1], coords[2]);
|
components.push(point);
|
point = new OpenLayers.Geometry.Point(coords[3], coords[2]);
|
components.push(point);
|
point = new OpenLayers.Geometry.Point(coords[3], coords[0]);
|
components.push(point);
|
point = new OpenLayers.Geometry.Point(coords[1], coords[0]);
|
components.push(point);
|
}
|
geometry = new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(components)]);
|
}
|
|
if (geometry && this.internalProjection && this.externalProjection) {
|
geometry.transform(this.externalProjection,
|
this.internalProjection);
|
}
|
|
return geometry;
|
},
|
|
/**
|
* Method: createFeatureFromItem
|
* Return a feature from a GeoRSS Item.
|
*
|
* Parameters:
|
* item - {DOMElement} A GeoRSS item node.
|
*
|
* Returns:
|
* {<OpenLayers.Feature.Vector>} A feature representing the item.
|
*/
|
createFeatureFromItem: function(item) {
|
var geometry = this.createGeometryFromItem(item);
|
|
/* Provide defaults for title and description */
|
var title = this._getChildValue(item, "*", "title", this.featureTitle);
|
|
/* First try RSS descriptions, then Atom summaries */
|
var description = this._getChildValue(
|
item, "*", "description",
|
this._getChildValue(item, "*", "content",
|
this._getChildValue(item, "*", "summary", this.featureDescription)));
|
|
/* If no link URL is found in the first child node, try the
|
href attribute */
|
var link = this._getChildValue(item, "*", "link");
|
if(!link) {
|
try {
|
link = this.getElementsByTagNameNS(item, "*", "link")[0].getAttribute("href");
|
} catch(e) {
|
link = null;
|
}
|
}
|
|
var id = this._getChildValue(item, "*", "id", null);
|
|
var data = {
|
"title": title,
|
"description": description,
|
"link": link
|
};
|
var feature = new OpenLayers.Feature.Vector(geometry, data);
|
feature.fid = id;
|
return feature;
|
},
|
|
/**
|
* Method: _getChildValue
|
*
|
* Parameters:
|
* node - {DOMElement}
|
* nsuri - {String} Child node namespace uri ("*" for any).
|
* name - {String} Child node name.
|
* def - {String} Optional string default to return if no child found.
|
*
|
* Returns:
|
* {String} The value of the first child with the given tag name. Returns
|
* default value or empty string if none found.
|
*/
|
_getChildValue: function(node, nsuri, name, def) {
|
var value;
|
var eles = this.getElementsByTagNameNS(node, nsuri, name);
|
if(eles && eles[0] && eles[0].firstChild
|
&& eles[0].firstChild.nodeValue) {
|
value = this.getChildValue(eles[0]);
|
} else {
|
value = (def == undefined) ? "" : def;
|
}
|
return value;
|
},
|
|
/**
|
* APIMethod: read
|
* Return a list of features from a GeoRSS doc
|
*
|
* Parameters:
|
* doc - {Element}
|
*
|
* Returns:
|
* {Array(<OpenLayers.Feature.Vector>)}
|
*/
|
read: function(doc) {
|
if (typeof doc == "string") {
|
doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);
|
}
|
|
/* Try RSS items first, then Atom entries */
|
var itemlist = null;
|
itemlist = this.getElementsByTagNameNS(doc, '*', 'item');
|
if (itemlist.length == 0) {
|
itemlist = this.getElementsByTagNameNS(doc, '*', 'entry');
|
}
|
|
var numItems = itemlist.length;
|
var features = new Array(numItems);
|
for(var i=0; i<numItems; i++) {
|
features[i] = this.createFeatureFromItem(itemlist[i]);
|
}
|
return features;
|
},
|
|
|
/**
|
* APIMethod: write
|
* Accept Feature Collection, and return a string.
|
*
|
* Parameters:
|
* features - {Array(<OpenLayers.Feature.Vector>)} List of features to serialize into a string.
|
*/
|
write: function(features) {
|
var georss;
|
if(OpenLayers.Util.isArray(features)) {
|
georss = this.createElementNS(this.rssns, "rss");
|
for(var i=0, len=features.length; i<len; i++) {
|
georss.appendChild(this.createFeatureXML(features[i]));
|
}
|
} else {
|
georss = this.createFeatureXML(features);
|
}
|
return OpenLayers.Format.XML.prototype.write.apply(this, [georss]);
|
},
|
|
/**
|
* Method: createFeatureXML
|
* Accept an <OpenLayers.Feature.Vector>, and build a geometry for it.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
*
|
* Returns:
|
* {DOMElement}
|
*/
|
createFeatureXML: function(feature) {
|
var geometryNode = this.buildGeometryNode(feature.geometry);
|
var featureNode = this.createElementNS(this.rssns, "item");
|
var titleNode = this.createElementNS(this.rssns, "title");
|
titleNode.appendChild(this.createTextNode(feature.attributes.title ? feature.attributes.title : ""));
|
var descNode = this.createElementNS(this.rssns, "description");
|
descNode.appendChild(this.createTextNode(feature.attributes.description ? feature.attributes.description : ""));
|
featureNode.appendChild(titleNode);
|
featureNode.appendChild(descNode);
|
if (feature.attributes.link) {
|
var linkNode = this.createElementNS(this.rssns, "link");
|
linkNode.appendChild(this.createTextNode(feature.attributes.link));
|
featureNode.appendChild(linkNode);
|
}
|
for(var attr in feature.attributes) {
|
if (attr == "link" || attr == "title" || attr == "description") { continue; }
|
var attrText = this.createTextNode(feature.attributes[attr]);
|
var nodename = attr;
|
if (attr.search(":") != -1) {
|
nodename = attr.split(":")[1];
|
}
|
var attrContainer = this.createElementNS(this.featureNS, "feature:"+nodename);
|
attrContainer.appendChild(attrText);
|
featureNode.appendChild(attrContainer);
|
}
|
featureNode.appendChild(geometryNode);
|
return featureNode;
|
},
|
|
/**
|
* Method: buildGeometryNode
|
* builds a GeoRSS node with a given geometry
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {DOMElement} A gml node.
|
*/
|
buildGeometryNode: function(geometry) {
|
if (this.internalProjection && this.externalProjection) {
|
geometry = geometry.clone();
|
geometry.transform(this.internalProjection,
|
this.externalProjection);
|
}
|
var node;
|
// match Polygon
|
if (geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") {
|
node = this.createElementNS(this.georssns, 'georss:polygon');
|
|
node.appendChild(this.buildCoordinatesNode(geometry.components[0]));
|
}
|
// match LineString
|
else if (geometry.CLASS_NAME == "OpenLayers.Geometry.LineString") {
|
node = this.createElementNS(this.georssns, 'georss:line');
|
|
node.appendChild(this.buildCoordinatesNode(geometry));
|
}
|
// match Point
|
else if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
|
node = this.createElementNS(this.georssns, 'georss:point');
|
node.appendChild(this.buildCoordinatesNode(geometry));
|
} else {
|
throw "Couldn't parse " + geometry.CLASS_NAME;
|
}
|
return node;
|
},
|
|
/**
|
* Method: buildCoordinatesNode
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
*/
|
buildCoordinatesNode: function(geometry) {
|
var points = null;
|
|
if (geometry.components) {
|
points = geometry.components;
|
}
|
|
var path;
|
if (points) {
|
var numPoints = points.length;
|
var parts = new Array(numPoints);
|
for (var i = 0; i < numPoints; i++) {
|
parts[i] = points[i].y + " " + points[i].x;
|
}
|
path = parts.join(" ");
|
} else {
|
path = geometry.y + " " + geometry.x;
|
}
|
return this.createTextNode(path);
|
},
|
|
CLASS_NAME: "OpenLayers.Format.GeoRSS"
|
});
|
/* ======================================================================
|
OpenLayers/Format/WPSCapabilities.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML/VersionedOGC.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WPSCapabilities
|
* Read WPS Capabilities.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML.VersionedOGC>
|
*/
|
OpenLayers.Format.WPSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
|
|
/**
|
* APIProperty: defaultVersion
|
* {String} Version number to assume if none found. Default is "1.0.0".
|
*/
|
defaultVersion: "1.0.0",
|
|
/**
|
* Constructor: OpenLayers.Format.WPSCapabilities
|
* Create a new parser for WPS Capabilities.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* APIMethod: read
|
* Read capabilities data from a string, and return information about
|
* the service.
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
*
|
* Returns:
|
* {Object} Info about the WPS
|
*/
|
|
CLASS_NAME: "OpenLayers.Format.WPSCapabilities"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/WPSCapabilities/v1_0_0.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/WPSCapabilities.js
|
* @requires OpenLayers/Format/OWSCommon/v1_1_0.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WPSCapabilities.v1_0_0
|
* Read WPS Capabilities version 1.0.0.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.WPSCapabilities.v1_0_0 = OpenLayers.Class(
|
OpenLayers.Format.XML, {
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
ows: "http://www.opengis.net/ows/1.1",
|
wps: "http://www.opengis.net/wps/1.0.0",
|
xlink: "http://www.w3.org/1999/xlink"
|
},
|
|
/**
|
* Property: regExes
|
* Compiled regular expressions for manipulating strings.
|
*/
|
regExes: {
|
trimSpace: (/^\s*|\s*$/g),
|
removeSpace: (/\s*/g),
|
splitSpace: (/\s+/),
|
trimComma: (/\s*,\s*/g)
|
},
|
|
/**
|
* Constructor: OpenLayers.Format.WPSCapabilities.v1_0_0
|
* Create a new parser for WPS capabilities version 1.0.0.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
initialize: function(options) {
|
OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
|
},
|
|
/**
|
* APIMethod: read
|
* Read capabilities data from a string, and return info about the WPS.
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
*
|
* Returns:
|
* {Object} Information about the WPS service.
|
*/
|
read: function(data) {
|
if(typeof data == "string") {
|
data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
|
}
|
if(data && data.nodeType == 9) {
|
data = data.documentElement;
|
}
|
var capabilities = {};
|
this.readNode(data, capabilities);
|
return capabilities;
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"wps": {
|
"Capabilities": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"ProcessOfferings": function(node, obj) {
|
obj.processOfferings = {};
|
this.readChildNodes(node, obj.processOfferings);
|
},
|
"Process": function(node, processOfferings) {
|
var processVersion = this.getAttributeNS(node, this.namespaces.wps, "processVersion");
|
var process = {processVersion: processVersion};
|
this.readChildNodes(node, process);
|
processOfferings[process.identifier] = process;
|
},
|
"Languages": function(node, obj) {
|
obj.languages = [];
|
this.readChildNodes(node, obj.languages);
|
},
|
"Default": function(node, languages) {
|
var language = {isDefault: true};
|
this.readChildNodes(node, language);
|
languages.push(language);
|
},
|
"Supported": function(node, languages) {
|
var language = {};
|
this.readChildNodes(node, language);
|
languages.push(language);
|
}
|
},
|
"ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"]
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WPSCapabilities.v1_0_0"
|
|
});
|
/* ======================================================================
|
OpenLayers/Control/PinchZoom.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Handler/Pinch.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.PinchZoom
|
*
|
* Inherits:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.PinchZoom = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* Property: type
|
* {OpenLayers.Control.TYPES}
|
*/
|
type: OpenLayers.Control.TYPE_TOOL,
|
|
/**
|
* Property: pinchOrigin
|
* {Object} Cached object representing the pinch start (in pixels).
|
*/
|
pinchOrigin: null,
|
|
/**
|
* Property: currentCenter
|
* {Object} Cached object representing the latest pinch center (in pixels).
|
*/
|
currentCenter: null,
|
|
/**
|
* APIProperty: autoActivate
|
* {Boolean} Activate the control when it is added to a map. Default is
|
* true.
|
*/
|
autoActivate: true,
|
|
/**
|
* APIProperty: preserveCenter
|
* {Boolean} Set this to true if you don't want the map center to change
|
* while pinching. For example you may want to set preserveCenter to
|
* true when the user location is being watched and you want to preserve
|
* the user location at the center of the map even if he zooms in or
|
* out using pinch. This property's value can be changed any time on an
|
* existing instance. Default is false.
|
*/
|
preserveCenter: false,
|
|
/**
|
* APIProperty: handlerOptions
|
* {Object} Used to set non-default properties on the pinch handler
|
*/
|
|
/**
|
* Constructor: OpenLayers.Control.PinchZoom
|
* Create a control for zooming with pinch gestures. This works on devices
|
* with multi-touch support.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* the control
|
*/
|
initialize: function(options) {
|
OpenLayers.Control.prototype.initialize.apply(this, arguments);
|
this.handler = new OpenLayers.Handler.Pinch(this, {
|
start: this.pinchStart,
|
move: this.pinchMove,
|
done: this.pinchDone
|
}, this.handlerOptions);
|
},
|
|
/**
|
* Method: pinchStart
|
*
|
* Parameters:
|
* evt - {Event}
|
* pinchData - {Object} pinch data object related to the current touchmove
|
* of the pinch gesture. This give us the current scale of the pinch.
|
*/
|
pinchStart: function(evt, pinchData) {
|
var xy = (this.preserveCenter) ?
|
this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy;
|
this.pinchOrigin = xy;
|
this.currentCenter = xy;
|
},
|
|
/**
|
* Method: pinchMove
|
*
|
* Parameters:
|
* evt - {Event}
|
* pinchData - {Object} pinch data object related to the current touchmove
|
* of the pinch gesture. This give us the current scale of the pinch.
|
*/
|
pinchMove: function(evt, pinchData) {
|
var scale = pinchData.scale;
|
var containerOrigin = this.map.layerContainerOriginPx;
|
var pinchOrigin = this.pinchOrigin;
|
var current = (this.preserveCenter) ?
|
this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy;
|
|
var dx = Math.round((containerOrigin.x + current.x - pinchOrigin.x) + (scale - 1) * (containerOrigin.x - pinchOrigin.x));
|
var dy = Math.round((containerOrigin.y + current.y - pinchOrigin.y) + (scale - 1) * (containerOrigin.y - pinchOrigin.y));
|
|
this.map.applyTransform(dx, dy, scale);
|
this.currentCenter = current;
|
},
|
|
/**
|
* Method: pinchDone
|
*
|
* Parameters:
|
* evt - {Event}
|
* start - {Object} pinch data object related to the touchstart event that
|
* started the pinch gesture.
|
* last - {Object} pinch data object related to the last touchmove event
|
* of the pinch gesture. This give us the final scale of the pinch.
|
*/
|
pinchDone: function(evt, start, last) {
|
this.map.applyTransform();
|
var zoom = this.map.getZoomForResolution(this.map.getResolution() / last.scale, true);
|
if (zoom !== this.map.getZoom() || !this.currentCenter.equals(this.pinchOrigin)) {
|
var resolution = this.map.getResolutionForZoom(zoom);
|
|
var location = this.map.getLonLatFromPixel(this.pinchOrigin);
|
var zoomPixel = this.currentCenter;
|
var size = this.map.getSize();
|
|
location.lon += resolution * ((size.w / 2) - zoomPixel.x);
|
location.lat -= resolution * ((size.h / 2) - zoomPixel.y);
|
|
// Force a reflow before calling setCenter. This is to work
|
// around an issue occuring in iOS.
|
//
|
// See https://github.com/openlayers/openlayers/pull/351.
|
//
|
// Without a reflow setting the layer container div's top left
|
// style properties to "0px" - as done in Map.moveTo when zoom
|
// is changed - won't actually correctly reposition the layer
|
// container div.
|
//
|
// Also, we need to use a statement that the Google Closure
|
// compiler won't optimize away.
|
this.map.div.clientWidth = this.map.div.clientWidth;
|
|
this.map.setCenter(location, zoom);
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Control.PinchZoom"
|
|
});
|
/* ======================================================================
|
OpenLayers/Control/TouchNavigation.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control/DragPan.js
|
* @requires OpenLayers/Control/PinchZoom.js
|
* @requires OpenLayers/Handler/Click.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.TouchNavigation
|
* The navigation control handles map browsing with touch events (dragging,
|
* double-tapping, tap with two fingers, and pinch zoom). Create a new
|
* control with the <OpenLayers.Control.TouchNavigation> constructor.
|
*
|
* If you’re only targeting touch enabled devices with your mapping application,
|
* you can create a map with only a TouchNavigation control. The
|
* <OpenLayers.Control.Navigation> control is mobile ready by default, but
|
* you can generate a smaller build of the library by only including this
|
* touch navigation control if you aren't concerned about mouse interaction.
|
*
|
* Inherits:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.TouchNavigation = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* Property: dragPan
|
* {<OpenLayers.Control.DragPan>}
|
*/
|
dragPan: null,
|
|
/**
|
* APIProperty: dragPanOptions
|
* {Object} Options passed to the DragPan control.
|
*/
|
dragPanOptions: null,
|
|
/**
|
* Property: pinchZoom
|
* {<OpenLayers.Control.PinchZoom>}
|
*/
|
pinchZoom: null,
|
|
/**
|
* APIProperty: pinchZoomOptions
|
* {Object} Options passed to the PinchZoom control.
|
*/
|
pinchZoomOptions: null,
|
|
/**
|
* APIProperty: clickHandlerOptions
|
* {Object} Options passed to the Click handler.
|
*/
|
clickHandlerOptions: null,
|
|
/**
|
* APIProperty: documentDrag
|
* {Boolean} Allow panning of the map by dragging outside map viewport.
|
* Default is false.
|
*/
|
documentDrag: false,
|
|
/**
|
* APIProperty: autoActivate
|
* {Boolean} Activate the control when it is added to a map. Default is
|
* true.
|
*/
|
autoActivate: true,
|
|
/**
|
* Constructor: OpenLayers.Control.TouchNavigation
|
* Create a new navigation control
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* the control
|
*/
|
initialize: function(options) {
|
this.handlers = {};
|
OpenLayers.Control.prototype.initialize.apply(this, arguments);
|
},
|
|
/**
|
* Method: destroy
|
* The destroy method is used to perform any clean up before the control
|
* is dereferenced. Typically this is where event listeners are removed
|
* to prevent memory leaks.
|
*/
|
destroy: function() {
|
this.deactivate();
|
if(this.dragPan) {
|
this.dragPan.destroy();
|
}
|
this.dragPan = null;
|
if (this.pinchZoom) {
|
this.pinchZoom.destroy();
|
delete this.pinchZoom;
|
}
|
OpenLayers.Control.prototype.destroy.apply(this,arguments);
|
},
|
|
/**
|
* Method: activate
|
*/
|
activate: function() {
|
if(OpenLayers.Control.prototype.activate.apply(this,arguments)) {
|
this.dragPan.activate();
|
this.handlers.click.activate();
|
this.pinchZoom.activate();
|
return true;
|
}
|
return false;
|
},
|
|
/**
|
* Method: deactivate
|
*/
|
deactivate: function() {
|
if(OpenLayers.Control.prototype.deactivate.apply(this,arguments)) {
|
this.dragPan.deactivate();
|
this.handlers.click.deactivate();
|
this.pinchZoom.deactivate();
|
return true;
|
}
|
return false;
|
},
|
|
/**
|
* Method: draw
|
*/
|
draw: function() {
|
var clickCallbacks = {
|
click: this.defaultClick,
|
dblclick: this.defaultDblClick
|
};
|
var clickOptions = OpenLayers.Util.extend({
|
"double": true,
|
stopDouble: true,
|
pixelTolerance: 2
|
}, this.clickHandlerOptions);
|
this.handlers.click = new OpenLayers.Handler.Click(
|
this, clickCallbacks, clickOptions
|
);
|
this.dragPan = new OpenLayers.Control.DragPan(
|
OpenLayers.Util.extend({
|
map: this.map,
|
documentDrag: this.documentDrag
|
}, this.dragPanOptions)
|
);
|
this.dragPan.draw();
|
this.pinchZoom = new OpenLayers.Control.PinchZoom(
|
OpenLayers.Util.extend({map: this.map}, this.pinchZoomOptions)
|
);
|
},
|
|
/**
|
* Method: defaultClick
|
*
|
* Parameters:
|
* evt - {Event}
|
*/
|
defaultClick: function (evt) {
|
if(evt.lastTouches && evt.lastTouches.length == 2) {
|
this.map.zoomOut();
|
}
|
},
|
|
/**
|
* Method: defaultDblClick
|
*
|
* Parameters:
|
* evt - {Event}
|
*/
|
defaultDblClick: function (evt) {
|
this.map.zoomTo(this.map.zoom + 1, evt.xy);
|
},
|
|
CLASS_NAME: "OpenLayers.Control.TouchNavigation"
|
});
|
/* ======================================================================
|
Rico/Color.js
|
====================================================================== */
|
|
/**
|
* @requires Rico/license.js
|
* @requires OpenLayers/Console.js
|
* @requires OpenLayers/BaseTypes/Class.js
|
* @requires OpenLayers/BaseTypes/Element.js
|
*/
|
|
|
/*
|
* This file has been edited substantially from the Rico-released version by
|
* the OpenLayers development team.
|
*/
|
|
OpenLayers.Console.warn("OpenLayers.Rico is deprecated");
|
|
OpenLayers.Rico = OpenLayers.Rico || {};
|
OpenLayers.Rico.Color = OpenLayers.Class({
|
|
initialize: function(red, green, blue) {
|
this.rgb = { r: red, g : green, b : blue };
|
},
|
|
setRed: function(r) {
|
this.rgb.r = r;
|
},
|
|
setGreen: function(g) {
|
this.rgb.g = g;
|
},
|
|
setBlue: function(b) {
|
this.rgb.b = b;
|
},
|
|
setHue: function(h) {
|
|
// get an HSB model, and set the new hue...
|
var hsb = this.asHSB();
|
hsb.h = h;
|
|
// convert back to RGB...
|
this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
|
},
|
|
setSaturation: function(s) {
|
// get an HSB model, and set the new hue...
|
var hsb = this.asHSB();
|
hsb.s = s;
|
|
// convert back to RGB and set values...
|
this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
|
},
|
|
setBrightness: function(b) {
|
// get an HSB model, and set the new hue...
|
var hsb = this.asHSB();
|
hsb.b = b;
|
|
// convert back to RGB and set values...
|
this.rgb = OpenLayers.Rico.Color.HSBtoRGB( hsb.h, hsb.s, hsb.b );
|
},
|
|
darken: function(percent) {
|
var hsb = this.asHSB();
|
this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.max(hsb.b - percent,0));
|
},
|
|
brighten: function(percent) {
|
var hsb = this.asHSB();
|
this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.min(hsb.b + percent,1));
|
},
|
|
blend: function(other) {
|
this.rgb.r = Math.floor((this.rgb.r + other.rgb.r)/2);
|
this.rgb.g = Math.floor((this.rgb.g + other.rgb.g)/2);
|
this.rgb.b = Math.floor((this.rgb.b + other.rgb.b)/2);
|
},
|
|
isBright: function() {
|
var hsb = this.asHSB();
|
return this.asHSB().b > 0.5;
|
},
|
|
isDark: function() {
|
return ! this.isBright();
|
},
|
|
asRGB: function() {
|
return "rgb(" + this.rgb.r + "," + this.rgb.g + "," + this.rgb.b + ")";
|
},
|
|
asHex: function() {
|
return "#" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart();
|
},
|
|
asHSB: function() {
|
return OpenLayers.Rico.Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b);
|
},
|
|
toString: function() {
|
return this.asHex();
|
}
|
|
});
|
|
OpenLayers.Rico.Color.createFromHex = function(hexCode) {
|
if(hexCode.length==4) {
|
var shortHexCode = hexCode;
|
var hexCode = '#';
|
for(var i=1;i<4;i++) {
|
hexCode += (shortHexCode.charAt(i) +
|
shortHexCode.charAt(i));
|
}
|
}
|
if ( hexCode.indexOf('#') == 0 ) {
|
hexCode = hexCode.substring(1);
|
}
|
var red = hexCode.substring(0,2);
|
var green = hexCode.substring(2,4);
|
var blue = hexCode.substring(4,6);
|
return new OpenLayers.Rico.Color( parseInt(red,16), parseInt(green,16), parseInt(blue,16) );
|
};
|
|
/**
|
* Factory method for creating a color from the background of
|
* an HTML element.
|
*/
|
OpenLayers.Rico.Color.createColorFromBackground = function(elem) {
|
|
var actualColor =
|
OpenLayers.Element.getStyle(OpenLayers.Util.getElement(elem),
|
"backgroundColor");
|
|
if ( actualColor == "transparent" && elem.parentNode ) {
|
return OpenLayers.Rico.Color.createColorFromBackground(elem.parentNode);
|
}
|
if ( actualColor == null ) {
|
return new OpenLayers.Rico.Color(255,255,255);
|
}
|
if ( actualColor.indexOf("rgb(") == 0 ) {
|
var colors = actualColor.substring(4, actualColor.length - 1 );
|
var colorArray = colors.split(",");
|
return new OpenLayers.Rico.Color( parseInt( colorArray[0] ),
|
parseInt( colorArray[1] ),
|
parseInt( colorArray[2] ) );
|
|
}
|
else if ( actualColor.indexOf("#") == 0 ) {
|
return OpenLayers.Rico.Color.createFromHex(actualColor);
|
}
|
else {
|
return new OpenLayers.Rico.Color(255,255,255);
|
}
|
};
|
|
OpenLayers.Rico.Color.HSBtoRGB = function(hue, saturation, brightness) {
|
|
var red = 0;
|
var green = 0;
|
var blue = 0;
|
|
if (saturation == 0) {
|
red = parseInt(brightness * 255.0 + 0.5);
|
green = red;
|
blue = red;
|
}
|
else {
|
var h = (hue - Math.floor(hue)) * 6.0;
|
var f = h - Math.floor(h);
|
var p = brightness * (1.0 - saturation);
|
var q = brightness * (1.0 - saturation * f);
|
var t = brightness * (1.0 - (saturation * (1.0 - f)));
|
|
switch (parseInt(h)) {
|
case 0:
|
red = (brightness * 255.0 + 0.5);
|
green = (t * 255.0 + 0.5);
|
blue = (p * 255.0 + 0.5);
|
break;
|
case 1:
|
red = (q * 255.0 + 0.5);
|
green = (brightness * 255.0 + 0.5);
|
blue = (p * 255.0 + 0.5);
|
break;
|
case 2:
|
red = (p * 255.0 + 0.5);
|
green = (brightness * 255.0 + 0.5);
|
blue = (t * 255.0 + 0.5);
|
break;
|
case 3:
|
red = (p * 255.0 + 0.5);
|
green = (q * 255.0 + 0.5);
|
blue = (brightness * 255.0 + 0.5);
|
break;
|
case 4:
|
red = (t * 255.0 + 0.5);
|
green = (p * 255.0 + 0.5);
|
blue = (brightness * 255.0 + 0.5);
|
break;
|
case 5:
|
red = (brightness * 255.0 + 0.5);
|
green = (p * 255.0 + 0.5);
|
blue = (q * 255.0 + 0.5);
|
break;
|
}
|
}
|
|
return { r : parseInt(red), g : parseInt(green) , b : parseInt(blue) };
|
};
|
|
OpenLayers.Rico.Color.RGBtoHSB = function(r, g, b) {
|
|
var hue;
|
var saturation;
|
var brightness;
|
|
var cmax = (r > g) ? r : g;
|
if (b > cmax) {
|
cmax = b;
|
}
|
var cmin = (r < g) ? r : g;
|
if (b < cmin) {
|
cmin = b;
|
}
|
brightness = cmax / 255.0;
|
if (cmax != 0) {
|
saturation = (cmax - cmin)/cmax;
|
} else {
|
saturation = 0;
|
}
|
if (saturation == 0) {
|
hue = 0;
|
} else {
|
var redc = (cmax - r)/(cmax - cmin);
|
var greenc = (cmax - g)/(cmax - cmin);
|
var bluec = (cmax - b)/(cmax - cmin);
|
|
if (r == cmax) {
|
hue = bluec - greenc;
|
} else if (g == cmax) {
|
hue = 2.0 + redc - bluec;
|
} else {
|
hue = 4.0 + greenc - redc;
|
}
|
hue = hue / 6.0;
|
if (hue < 0) {
|
hue = hue + 1.0;
|
}
|
}
|
|
return { h : hue, s : saturation, b : brightness };
|
};
|
|
/* ======================================================================
|
OpenLayers/Style2.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/BaseTypes/Class.js
|
* @requires OpenLayers/Rule.js
|
* @requires OpenLayers/Symbolizer/Point.js
|
* @requires OpenLayers/Symbolizer/Line.js
|
* @requires OpenLayers/Symbolizer/Polygon.js
|
* @requires OpenLayers/Symbolizer/Text.js
|
* @requires OpenLayers/Symbolizer/Raster.js
|
*/
|
|
/**
|
* Class: OpenLayers.Style2
|
* This class represents a collection of rules for rendering features.
|
*/
|
OpenLayers.Style2 = OpenLayers.Class({
|
|
/**
|
* Property: id
|
* {String} A unique id for this session.
|
*/
|
id: null,
|
|
/**
|
* APIProperty: name
|
* {String} Style identifier.
|
*/
|
name: null,
|
|
/**
|
* APIProperty: title
|
* {String} Title of this style.
|
*/
|
title: null,
|
|
/**
|
* APIProperty: description
|
* {String} Description of this style.
|
*/
|
description: null,
|
|
/**
|
* APIProperty: layerName
|
* {<String>} Name of the layer that this style belongs to, usually
|
* according to the NamedLayer attribute of an SLD document.
|
*/
|
layerName: null,
|
|
/**
|
* APIProperty: isDefault
|
* {Boolean}
|
*/
|
isDefault: false,
|
|
/**
|
* APIProperty: rules
|
* {Array(<OpenLayers.Rule>)} Collection of rendering rules.
|
*/
|
rules: null,
|
|
/**
|
* Constructor: OpenLayers.Style2
|
* Creates a style representing a collection of rendering rules.
|
*
|
* Parameters:
|
* config - {Object} An object containing properties to be set on the
|
* style. Any documented properties may be set at construction.
|
*
|
* Returns:
|
* {<OpenLayers.Style2>} A new style object.
|
*/
|
initialize: function(config) {
|
OpenLayers.Util.extend(this, config);
|
this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
|
},
|
|
/**
|
* APIMethod: destroy
|
* nullify references to prevent circular references and memory leaks
|
*/
|
destroy: function() {
|
for (var i=0, len=this.rules.length; i<len; i++) {
|
this.rules[i].destroy();
|
}
|
delete this.rules;
|
},
|
|
/**
|
* APIMethod: clone
|
* Clones this style.
|
*
|
* Returns:
|
* {<OpenLayers.Style2>} Clone of this style.
|
*/
|
clone: function() {
|
var config = OpenLayers.Util.extend({}, this);
|
// clone rules
|
if (this.rules) {
|
config.rules = [];
|
for (var i=0, len=this.rules.length; i<len; ++i) {
|
config.rules.push(this.rules[i].clone());
|
}
|
}
|
return new OpenLayers.Style2(config);
|
},
|
|
CLASS_NAME: "OpenLayers.Style2"
|
});
|
/* ======================================================================
|
OpenLayers/Format/WFS.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/GML.js
|
* @requires OpenLayers/Console.js
|
* @requires OpenLayers/Lang.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WFS
|
* Read/Write WFS.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.GML>
|
*/
|
OpenLayers.Format.WFS = OpenLayers.Class(OpenLayers.Format.GML, {
|
|
/**
|
* Property: layer
|
* {<OpenLayers.Layer>}
|
*/
|
layer: null,
|
|
/**
|
* APIProperty: wfsns
|
* {String}
|
*/
|
wfsns: "http://www.opengis.net/wfs",
|
|
/**
|
* Property: ogcns
|
* {String}
|
*/
|
ogcns: "http://www.opengis.net/ogc",
|
|
/**
|
* Constructor: OpenLayers.Format.WFS
|
* Create a WFS-T formatter. This requires a layer: that layer should
|
* have two properties: geometry_column and typename. The parser
|
* for this format is subclassed entirely from GML: There is a writer
|
* only, which uses most of the code from the GML layer, and wraps
|
* it in transactional elements.
|
*
|
* Parameters:
|
* options - {Object}
|
* layer - {<OpenLayers.Layer>}
|
*/
|
initialize: function(options, layer) {
|
OpenLayers.Format.GML.prototype.initialize.apply(this, [options]);
|
this.layer = layer;
|
if (this.layer.featureNS) {
|
this.featureNS = this.layer.featureNS;
|
}
|
if (this.layer.options.geometry_column) {
|
this.geometryName = this.layer.options.geometry_column;
|
}
|
if (this.layer.options.typename) {
|
this.featureName = this.layer.options.typename;
|
}
|
},
|
|
/**
|
* Method: write
|
* Takes a feature list, and generates a WFS-T Transaction
|
*
|
* Parameters:
|
* features - {Array(<OpenLayers.Feature.Vector>)}
|
*/
|
write: function(features) {
|
|
var transaction = this.createElementNS(this.wfsns, 'wfs:Transaction');
|
transaction.setAttribute("version","1.0.0");
|
transaction.setAttribute("service","WFS");
|
for (var i=0; i < features.length; i++) {
|
switch (features[i].state) {
|
case OpenLayers.State.INSERT:
|
transaction.appendChild(this.insert(features[i]));
|
break;
|
case OpenLayers.State.UPDATE:
|
transaction.appendChild(this.update(features[i]));
|
break;
|
case OpenLayers.State.DELETE:
|
transaction.appendChild(this.remove(features[i]));
|
break;
|
}
|
}
|
|
return OpenLayers.Format.XML.prototype.write.apply(this,[transaction]);
|
},
|
|
/**
|
* Method: createFeatureXML
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
*/
|
createFeatureXML: function(feature) {
|
var geometryNode = this.buildGeometryNode(feature.geometry);
|
var geomContainer = this.createElementNS(this.featureNS, "feature:" + this.geometryName);
|
geomContainer.appendChild(geometryNode);
|
var featureContainer = this.createElementNS(this.featureNS, "feature:" + this.featureName);
|
featureContainer.appendChild(geomContainer);
|
for(var attr in feature.attributes) {
|
var attrText = this.createTextNode(feature.attributes[attr]);
|
var nodename = attr;
|
if (attr.search(":") != -1) {
|
nodename = attr.split(":")[1];
|
}
|
var attrContainer = this.createElementNS(this.featureNS, "feature:" + nodename);
|
attrContainer.appendChild(attrText);
|
featureContainer.appendChild(attrContainer);
|
}
|
return featureContainer;
|
},
|
|
/**
|
* Method: insert
|
* Takes a feature, and generates a WFS-T Transaction "Insert"
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
*/
|
insert: function(feature) {
|
var insertNode = this.createElementNS(this.wfsns, 'wfs:Insert');
|
insertNode.appendChild(this.createFeatureXML(feature));
|
return insertNode;
|
},
|
|
/**
|
* Method: update
|
* Takes a feature, and generates a WFS-T Transaction "Update"
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
*/
|
update: function(feature) {
|
if (!feature.fid) { OpenLayers.Console.userError(OpenLayers.i18n("noFID")); }
|
var updateNode = this.createElementNS(this.wfsns, 'wfs:Update');
|
updateNode.setAttribute("typeName", this.featurePrefix + ':' + this.featureName);
|
updateNode.setAttribute("xmlns:" + this.featurePrefix, this.featureNS);
|
|
var propertyNode = this.createElementNS(this.wfsns, 'wfs:Property');
|
var nameNode = this.createElementNS(this.wfsns, 'wfs:Name');
|
|
var txtNode = this.createTextNode(this.geometryName);
|
nameNode.appendChild(txtNode);
|
propertyNode.appendChild(nameNode);
|
|
var valueNode = this.createElementNS(this.wfsns, 'wfs:Value');
|
|
var geometryNode = this.buildGeometryNode(feature.geometry);
|
|
if(feature.layer){
|
geometryNode.setAttribute(
|
"srsName", feature.layer.projection.getCode()
|
);
|
}
|
|
valueNode.appendChild(geometryNode);
|
|
propertyNode.appendChild(valueNode);
|
updateNode.appendChild(propertyNode);
|
|
// add in attributes
|
for(var propName in feature.attributes) {
|
propertyNode = this.createElementNS(this.wfsns, 'wfs:Property');
|
nameNode = this.createElementNS(this.wfsns, 'wfs:Name');
|
nameNode.appendChild(this.createTextNode(propName));
|
propertyNode.appendChild(nameNode);
|
valueNode = this.createElementNS(this.wfsns, 'wfs:Value');
|
valueNode.appendChild(this.createTextNode(feature.attributes[propName]));
|
propertyNode.appendChild(valueNode);
|
updateNode.appendChild(propertyNode);
|
}
|
|
|
var filterNode = this.createElementNS(this.ogcns, 'ogc:Filter');
|
var filterIdNode = this.createElementNS(this.ogcns, 'ogc:FeatureId');
|
filterIdNode.setAttribute("fid", feature.fid);
|
filterNode.appendChild(filterIdNode);
|
updateNode.appendChild(filterNode);
|
|
return updateNode;
|
},
|
|
/**
|
* Method: remove
|
* Takes a feature, and generates a WFS-T Transaction "Delete"
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
*/
|
remove: function(feature) {
|
if (!feature.fid) {
|
OpenLayers.Console.userError(OpenLayers.i18n("noFID"));
|
return false;
|
}
|
var deleteNode = this.createElementNS(this.wfsns, 'wfs:Delete');
|
deleteNode.setAttribute("typeName", this.featurePrefix + ':' + this.featureName);
|
deleteNode.setAttribute("xmlns:" + this.featurePrefix, this.featureNS);
|
|
var filterNode = this.createElementNS(this.ogcns, 'ogc:Filter');
|
var filterIdNode = this.createElementNS(this.ogcns, 'ogc:FeatureId');
|
filterIdNode.setAttribute("fid", feature.fid);
|
filterNode.appendChild(filterIdNode);
|
deleteNode.appendChild(filterNode);
|
|
return deleteNode;
|
},
|
|
/**
|
* APIMethod: destroy
|
* Remove ciruclar ref to layer
|
*/
|
destroy: function() {
|
this.layer = null;
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WFS"
|
});
|
/* ======================================================================
|
OpenLayers/Format/SLD/v1_0_0_GeoServer.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/SLD/v1_0_0.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.SLD/v1_0_0_GeoServer
|
* Read and write SLD version 1.0.0 with GeoServer-specific enhanced options.
|
* See http://svn.osgeo.org/geotools/trunk/modules/extension/xsd/xsd-sld/src/main/resources/org/geotools/sld/bindings/StyledLayerDescriptor.xsd
|
* for more information.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.SLD.v1_0_0>
|
*/
|
OpenLayers.Format.SLD.v1_0_0_GeoServer = OpenLayers.Class(
|
OpenLayers.Format.SLD.v1_0_0, {
|
|
/**
|
* Property: version
|
* {String} The specific parser version.
|
*/
|
version: "1.0.0",
|
|
/**
|
* Property: profile
|
* {String} The specific profile
|
*/
|
profile: "GeoServer",
|
|
/**
|
* Constructor: OpenLayers.Format.SLD.v1_0_0_GeoServer
|
* Create a new parser for GeoServer-enhanced SLD version 1.0.0.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: OpenLayers.Util.applyDefaults({
|
"sld": OpenLayers.Util.applyDefaults({
|
"Priority": function(node, obj) {
|
var value = this.readers.ogc._expression.call(this, node);
|
if (value) {
|
obj.priority = value;
|
}
|
},
|
"VendorOption": function(node, obj) {
|
if (!obj.vendorOptions) {
|
obj.vendorOptions = {};
|
}
|
obj.vendorOptions[node.getAttribute("name")] = this.getChildValue(node);
|
},
|
"TextSymbolizer": function(node, rule) {
|
OpenLayers.Format.SLD.v1_0_0.prototype.readers.sld.TextSymbolizer.apply(this, arguments);
|
var symbolizer = this.multipleSymbolizers ? rule.symbolizers[rule.symbolizers.length-1] : rule.symbolizer["Text"];
|
if (symbolizer.graphic === undefined) {
|
symbolizer.graphic = false;
|
}
|
}
|
}, OpenLayers.Format.SLD.v1_0_0.prototype.readers["sld"])
|
}, OpenLayers.Format.SLD.v1_0_0.prototype.readers),
|
|
/**
|
* Property: writers
|
* As a compliment to the readers property, this structure contains public
|
* writing functions grouped by namespace alias and named like the
|
* node names they produce.
|
*/
|
writers: OpenLayers.Util.applyDefaults({
|
"sld": OpenLayers.Util.applyDefaults({
|
"Priority": function(priority) {
|
return this.writers.sld._OGCExpression.call(
|
this, "sld:Priority", priority
|
);
|
},
|
"VendorOption": function(option) {
|
return this.createElementNSPlus("sld:VendorOption", {
|
attributes: {name: option.name},
|
value: option.value
|
});
|
},
|
"TextSymbolizer": function(symbolizer) {
|
var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers;
|
var node = writers["sld"]["TextSymbolizer"].apply(this, arguments);
|
if (symbolizer.graphic !== false && (symbolizer.externalGraphic || symbolizer.graphicName)) {
|
this.writeNode("Graphic", symbolizer, node);
|
}
|
if ("priority" in symbolizer) {
|
this.writeNode("Priority", symbolizer.priority, node);
|
}
|
return this.addVendorOptions(node, symbolizer);
|
},
|
"PointSymbolizer": function(symbolizer) {
|
var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers;
|
var node = writers["sld"]["PointSymbolizer"].apply(this, arguments);
|
return this.addVendorOptions(node, symbolizer);
|
},
|
"LineSymbolizer": function(symbolizer) {
|
var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers;
|
var node = writers["sld"]["LineSymbolizer"].apply(this, arguments);
|
return this.addVendorOptions(node, symbolizer);
|
},
|
"PolygonSymbolizer": function(symbolizer) {
|
var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers;
|
var node = writers["sld"]["PolygonSymbolizer"].apply(this, arguments);
|
return this.addVendorOptions(node, symbolizer);
|
}
|
}, OpenLayers.Format.SLD.v1_0_0.prototype.writers["sld"])
|
}, OpenLayers.Format.SLD.v1_0_0.prototype.writers),
|
|
/**
|
* Method: addVendorOptions
|
* Add in the VendorOption tags and return the node again.
|
*
|
* Parameters:
|
* node - {DOMElement} A DOM node.
|
* symbolizer - {Object}
|
*
|
* Returns:
|
* {DOMElement} A DOM node.
|
*/
|
addVendorOptions: function(node, symbolizer) {
|
var options = symbolizer.vendorOptions;
|
if (options) {
|
for (var key in symbolizer.vendorOptions) {
|
this.writeNode("VendorOption", {
|
name: key,
|
value: symbolizer.vendorOptions[key]
|
}, node);
|
}
|
}
|
return node;
|
},
|
|
CLASS_NAME: "OpenLayers.Format.SLD.v1_0_0_GeoServer"
|
|
});
|
/* ======================================================================
|
OpenLayers/Layer/Boxes.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Layer.js
|
* @requires OpenLayers/Layer/Markers.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.Boxes
|
* Draw divs as 'boxes' on the layer.
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer.Markers>
|
*/
|
OpenLayers.Layer.Boxes = OpenLayers.Class(OpenLayers.Layer.Markers, {
|
|
/**
|
* Constructor: OpenLayers.Layer.Boxes
|
*
|
* Parameters:
|
* name - {String}
|
* options - {Object} Hashtable of extra options to tag onto the layer
|
*/
|
|
/**
|
* Method: drawMarker
|
* Calculate the pixel location for the marker, create it, and
|
* add it to the layer's div
|
*
|
* Parameters:
|
* marker - {<OpenLayers.Marker.Box>}
|
*/
|
drawMarker: function(marker) {
|
var topleft = this.map.getLayerPxFromLonLat({
|
lon: marker.bounds.left,
|
lat: marker.bounds.top
|
});
|
var botright = this.map.getLayerPxFromLonLat({
|
lon: marker.bounds.right,
|
lat: marker.bounds.bottom
|
});
|
if (botright == null || topleft == null) {
|
marker.display(false);
|
} else {
|
var markerDiv = marker.draw(topleft, {
|
w: Math.max(1, botright.x - topleft.x),
|
h: Math.max(1, botright.y - topleft.y)
|
});
|
if (!marker.drawn) {
|
this.div.appendChild(markerDiv);
|
marker.drawn = true;
|
}
|
}
|
},
|
|
|
/**
|
* APIMethod: removeMarker
|
*
|
* Parameters:
|
* marker - {<OpenLayers.Marker.Box>}
|
*/
|
removeMarker: function(marker) {
|
OpenLayers.Util.removeItem(this.markers, marker);
|
if ((marker.div != null) &&
|
(marker.div.parentNode == this.div) ) {
|
this.div.removeChild(marker.div);
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.Boxes"
|
});
|
/* ======================================================================
|
OpenLayers/Format/WFSCapabilities/v1_0_0.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/WFSCapabilities/v1.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WFSCapabilities/v1_0_0
|
* Read WFS Capabilities version 1.0.0.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.WFSCapabilities.v1>
|
*/
|
OpenLayers.Format.WFSCapabilities.v1_0_0 = OpenLayers.Class(
|
OpenLayers.Format.WFSCapabilities.v1, {
|
|
/**
|
* Constructor: OpenLayers.Format.WFSCapabilities.v1_0_0
|
* Create a new parser for WFS capabilities version 1.0.0.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"wfs": OpenLayers.Util.applyDefaults({
|
"Service": function(node, capabilities) {
|
capabilities.service = {};
|
this.readChildNodes(node, capabilities.service);
|
},
|
"Fees": function(node, service) {
|
var fees = this.getChildValue(node);
|
if (fees && fees.toLowerCase() != "none") {
|
service.fees = fees;
|
}
|
},
|
"AccessConstraints": function(node, service) {
|
var constraints = this.getChildValue(node);
|
if (constraints && constraints.toLowerCase() != "none") {
|
service.accessConstraints = constraints;
|
}
|
},
|
"OnlineResource": function(node, service) {
|
var onlineResource = this.getChildValue(node);
|
if (onlineResource && onlineResource.toLowerCase() != "none") {
|
service.onlineResource = onlineResource;
|
}
|
},
|
"Keywords": function(node, service) {
|
var keywords = this.getChildValue(node);
|
if (keywords && keywords.toLowerCase() != "none") {
|
service.keywords = keywords.split(', ');
|
}
|
},
|
"Capability": function(node, capabilities) {
|
capabilities.capability = {};
|
this.readChildNodes(node, capabilities.capability);
|
},
|
"Request": function(node, obj) {
|
obj.request = {};
|
this.readChildNodes(node, obj.request);
|
},
|
"GetFeature": function(node, request) {
|
request.getfeature = {
|
href: {}, // DCPType
|
formats: [] // ResultFormat
|
};
|
this.readChildNodes(node, request.getfeature);
|
},
|
"ResultFormat": function(node, obj) {
|
var children = node.childNodes;
|
var childNode;
|
for(var i=0; i<children.length; i++) {
|
childNode = children[i];
|
if(childNode.nodeType == 1) {
|
obj.formats.push(childNode.nodeName);
|
}
|
}
|
},
|
"DCPType": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"HTTP": function(node, obj) {
|
this.readChildNodes(node, obj.href);
|
},
|
"Get": function(node, obj) {
|
obj.get = node.getAttribute("onlineResource");
|
},
|
"Post": function(node, obj) {
|
obj.post = node.getAttribute("onlineResource");
|
},
|
"SRS": function(node, obj) {
|
var srs = this.getChildValue(node);
|
if (srs) {
|
obj.srs = srs;
|
}
|
}
|
}, OpenLayers.Format.WFSCapabilities.v1.prototype.readers["wfs"])
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WFSCapabilities.v1_0_0"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/WMSCapabilities/v1_3.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/WMSCapabilities/v1.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WMSCapabilities/v1_3
|
* Abstract base class for WMS Capabilities version 1.3.X.
|
* SLD 1.1.0 adds in the extra operations DescribeLayer and GetLegendGraphic,
|
* see: http://schemas.opengis.net/sld/1.1.0/sld_capabilities.xsd
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.WMSCapabilities.v1>
|
*/
|
OpenLayers.Format.WMSCapabilities.v1_3 = OpenLayers.Class(
|
OpenLayers.Format.WMSCapabilities.v1, {
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"wms": OpenLayers.Util.applyDefaults({
|
"WMS_Capabilities": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"LayerLimit": function(node, obj) {
|
obj.layerLimit = parseInt(this.getChildValue(node));
|
},
|
"MaxWidth": function(node, obj) {
|
obj.maxWidth = parseInt(this.getChildValue(node));
|
},
|
"MaxHeight": function(node, obj) {
|
obj.maxHeight = parseInt(this.getChildValue(node));
|
},
|
"BoundingBox": function(node, obj) {
|
var bbox = OpenLayers.Format.WMSCapabilities.v1.prototype.readers["wms"].BoundingBox.apply(this, [node, obj]);
|
bbox.srs = node.getAttribute("CRS");
|
obj.bbox[bbox.srs] = bbox;
|
},
|
"CRS": function(node, obj) {
|
// CRS is the synonym of SRS
|
this.readers.wms.SRS.apply(this, [node, obj]);
|
},
|
"EX_GeographicBoundingBox": function(node, obj) {
|
// replacement of LatLonBoundingBox
|
obj.llbbox = [];
|
this.readChildNodes(node, obj.llbbox);
|
|
},
|
"westBoundLongitude": function(node, obj) {
|
obj[0] = this.getChildValue(node);
|
},
|
"eastBoundLongitude": function(node, obj) {
|
obj[2] = this.getChildValue(node);
|
},
|
"southBoundLatitude": function(node, obj) {
|
obj[1] = this.getChildValue(node);
|
},
|
"northBoundLatitude": function(node, obj) {
|
obj[3] = this.getChildValue(node);
|
},
|
"MinScaleDenominator": function(node, obj) {
|
obj.maxScale = parseFloat(this.getChildValue(node)).toPrecision(16);
|
},
|
"MaxScaleDenominator": function(node, obj) {
|
obj.minScale = parseFloat(this.getChildValue(node)).toPrecision(16);
|
},
|
"Dimension": function(node, obj) {
|
// dimension has extra attributes: default, multipleValues,
|
// nearestValue, current which used to be part of Extent. It now
|
// also contains the values.
|
var name = node.getAttribute("name").toLowerCase();
|
var dim = {
|
name: name,
|
units: node.getAttribute("units"),
|
unitsymbol: node.getAttribute("unitSymbol"),
|
nearestVal: node.getAttribute("nearestValue") === "1",
|
multipleVal: node.getAttribute("multipleValues") === "1",
|
"default": node.getAttribute("default") || "",
|
current: node.getAttribute("current") === "1",
|
values: this.getChildValue(node).split(",")
|
|
};
|
// Theoretically there can be more dimensions with the same
|
// name, but with a different unit. Until we meet such a case,
|
// let's just keep the same structure as the WMS 1.1
|
// GetCapabilities parser uses. We will store the last
|
// one encountered.
|
obj.dimensions[dim.name] = dim;
|
},
|
"Keyword": function(node, obj) {
|
// TODO: should we change the structure of keyword in v1.js?
|
// Make it an object with a value instead of a string?
|
var keyword = {value: this.getChildValue(node),
|
vocabulary: node.getAttribute("vocabulary")};
|
if (obj.keywords) {
|
obj.keywords.push(keyword);
|
}
|
}
|
}, OpenLayers.Format.WMSCapabilities.v1.prototype.readers["wms"]),
|
"sld": {
|
"UserDefinedSymbolization": function(node, obj) {
|
this.readers.wms.UserDefinedSymbolization.apply(this, [node, obj]);
|
// add the two extra attributes
|
obj.userSymbols.inlineFeature = parseInt(node.getAttribute("InlineFeature")) == 1;
|
obj.userSymbols.remoteWCS = parseInt(node.getAttribute("RemoteWCS")) == 1;
|
},
|
"DescribeLayer": function(node, obj) {
|
this.readers.wms.DescribeLayer.apply(this, [node, obj]);
|
},
|
"GetLegendGraphic": function(node, obj) {
|
this.readers.wms.GetLegendGraphic.apply(this, [node, obj]);
|
}
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_3"
|
|
});
|
/* ======================================================================
|
OpenLayers/Layer/Zoomify.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/*
|
* Development supported by a R&D grant DC08P02OUK006 - Old Maps Online
|
* (www.oldmapsonline.org) from Ministry of Culture of the Czech Republic.
|
*/
|
|
|
/**
|
* @requires OpenLayers/Layer/Grid.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.Zoomify
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer.Grid>
|
*/
|
OpenLayers.Layer.Zoomify = OpenLayers.Class(OpenLayers.Layer.Grid, {
|
|
/**
|
* Property: size
|
* {<OpenLayers.Size>} The Zoomify image size in pixels.
|
*/
|
size: null,
|
|
/**
|
* APIProperty: isBaseLayer
|
* {Boolean}
|
*/
|
isBaseLayer: true,
|
|
/**
|
* Property: standardTileSize
|
* {Integer} The size of a standard (non-border) square tile in pixels.
|
*/
|
standardTileSize: 256,
|
|
/**
|
* Property: tileOriginCorner
|
* {String} This layer uses top-left as tile origin
|
**/
|
tileOriginCorner: "tl",
|
|
/**
|
* Property: numberOfTiers
|
* {Integer} Depth of the Zoomify pyramid, number of tiers (zoom levels)
|
* - filled during Zoomify pyramid initialization.
|
*/
|
numberOfTiers: 0,
|
|
/**
|
* Property: tileCountUpToTier
|
* {Array(Integer)} Number of tiles up to the given tier of pyramid.
|
* - filled during Zoomify pyramid initialization.
|
*/
|
tileCountUpToTier: null,
|
|
/**
|
* Property: tierSizeInTiles
|
* {Array(<OpenLayers.Size>)} Size (in tiles) for each tier of pyramid.
|
* - filled during Zoomify pyramid initialization.
|
*/
|
tierSizeInTiles: null,
|
|
/**
|
* Property: tierImageSize
|
* {Array(<OpenLayers.Size>)} Image size in pixels for each pyramid tier.
|
* - filled during Zoomify pyramid initialization.
|
*/
|
tierImageSize: null,
|
|
/**
|
* Constructor: OpenLayers.Layer.Zoomify
|
*
|
* Parameters:
|
* name - {String} A name for the layer.
|
* url - {String} - Relative or absolute path to the image or more
|
* precisly to the TileGroup[X] directories root.
|
* Flash plugin use the variable name "zoomifyImagePath" for this.
|
* size - {<OpenLayers.Size>} The size (in pixels) of the image.
|
* options - {Object} Hashtable of extra options to tag onto the layer
|
*/
|
initialize: function(name, url, size, options) {
|
|
// initilize the Zoomify pyramid for given size
|
this.initializeZoomify(size);
|
|
OpenLayers.Layer.Grid.prototype.initialize.apply(this, [
|
name, url, size, {}, options
|
]);
|
},
|
|
/**
|
* Method: initializeZoomify
|
* It generates constants for all tiers of the Zoomify pyramid
|
*
|
* Parameters:
|
* size - {<OpenLayers.Size>} The size of the image in pixels
|
*
|
*/
|
initializeZoomify: function( size ) {
|
|
var imageSize = size.clone();
|
this.size = size.clone();
|
var tiles = new OpenLayers.Size(
|
Math.ceil( imageSize.w / this.standardTileSize ),
|
Math.ceil( imageSize.h / this.standardTileSize )
|
);
|
|
this.tierSizeInTiles = [tiles];
|
this.tierImageSize = [imageSize];
|
|
while (imageSize.w > this.standardTileSize ||
|
imageSize.h > this.standardTileSize ) {
|
|
imageSize = new OpenLayers.Size(
|
Math.floor( imageSize.w / 2 ),
|
Math.floor( imageSize.h / 2 )
|
);
|
tiles = new OpenLayers.Size(
|
Math.ceil( imageSize.w / this.standardTileSize ),
|
Math.ceil( imageSize.h / this.standardTileSize )
|
);
|
this.tierSizeInTiles.push( tiles );
|
this.tierImageSize.push( imageSize );
|
}
|
|
this.tierSizeInTiles.reverse();
|
this.tierImageSize.reverse();
|
|
this.numberOfTiers = this.tierSizeInTiles.length;
|
var resolutions = [1];
|
this.tileCountUpToTier = [0];
|
for (var i = 1; i < this.numberOfTiers; i++) {
|
resolutions.unshift(Math.pow(2, i));
|
this.tileCountUpToTier.push(
|
this.tierSizeInTiles[i-1].w * this.tierSizeInTiles[i-1].h +
|
this.tileCountUpToTier[i-1]
|
);
|
}
|
if (!this.serverResolutions) {
|
this.serverResolutions = resolutions;
|
}
|
},
|
|
/**
|
* APIMethod:destroy
|
*/
|
destroy: function() {
|
// for now, nothing special to do here.
|
OpenLayers.Layer.Grid.prototype.destroy.apply(this, arguments);
|
|
// Remove from memory the Zoomify pyramid - is that enough?
|
this.tileCountUpToTier.length = 0;
|
this.tierSizeInTiles.length = 0;
|
this.tierImageSize.length = 0;
|
|
},
|
|
/**
|
* APIMethod: clone
|
*
|
* Parameters:
|
* obj - {Object}
|
*
|
* Returns:
|
* {<OpenLayers.Layer.Zoomify>} An exact clone of this <OpenLayers.Layer.Zoomify>
|
*/
|
clone: function (obj) {
|
|
if (obj == null) {
|
obj = new OpenLayers.Layer.Zoomify(this.name,
|
this.url,
|
this.size,
|
this.options);
|
}
|
|
//get all additions from superclasses
|
obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
|
|
// copy/set any non-init, non-simple values here
|
|
return obj;
|
},
|
|
/**
|
* Method: getURL
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
*
|
* Returns:
|
* {String} A string with the layer's url and parameters and also the
|
* passed-in bounds and appropriate tile size specified as
|
* parameters
|
*/
|
getURL: function (bounds) {
|
bounds = this.adjustBounds(bounds);
|
var res = this.getServerResolution();
|
var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w));
|
var y = Math.round((this.tileOrigin.lat - bounds.top) / (res * this.tileSize.h));
|
var z = this.getZoomForResolution( res );
|
|
var tileIndex = x + y * this.tierSizeInTiles[z].w + this.tileCountUpToTier[z];
|
var path = "TileGroup" + Math.floor( (tileIndex) / 256 ) +
|
"/" + z + "-" + x + "-" + y + ".jpg";
|
var url = this.url;
|
if (OpenLayers.Util.isArray(url)) {
|
url = this.selectUrl(path, url);
|
}
|
return url + path;
|
},
|
|
/**
|
* Method: getImageSize
|
* getImageSize returns size for a particular tile. If bounds are given as
|
* first argument, size is calculated (bottom-right tiles are non square).
|
*
|
*/
|
getImageSize: function() {
|
if (arguments.length > 0) {
|
var bounds = this.adjustBounds(arguments[0]);
|
var res = this.getServerResolution();
|
var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w));
|
var y = Math.round((this.tileOrigin.lat - bounds.top) / (res * this.tileSize.h));
|
var z = this.getZoomForResolution( res );
|
var w = this.standardTileSize;
|
var h = this.standardTileSize;
|
if (x == this.tierSizeInTiles[z].w -1 ) {
|
var w = this.tierImageSize[z].w % this.standardTileSize;
|
}
|
if (y == this.tierSizeInTiles[z].h -1 ) {
|
var h = this.tierImageSize[z].h % this.standardTileSize;
|
}
|
return (new OpenLayers.Size(w, h));
|
} else {
|
return this.tileSize;
|
}
|
},
|
|
/**
|
* APIMethod: setMap
|
* When the layer is added to a map, then we can fetch our origin
|
* (if we don't have one.)
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
setMap: function(map) {
|
OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);
|
this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left,
|
this.map.maxExtent.top);
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.Zoomify"
|
});
|
/* ======================================================================
|
OpenLayers/Layer/MapServer.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Layer/Grid.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.MapServer
|
* Instances of OpenLayers.Layer.MapServer are used to display
|
* data from a MapServer CGI instance.
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer.Grid>
|
*/
|
OpenLayers.Layer.MapServer = OpenLayers.Class(OpenLayers.Layer.Grid, {
|
|
/**
|
* Constant: DEFAULT_PARAMS
|
* {Object} Hashtable of default parameter key/value pairs
|
*/
|
DEFAULT_PARAMS: {
|
mode: "map",
|
map_imagetype: "png"
|
},
|
|
/**
|
* Constructor: OpenLayers.Layer.MapServer
|
* Create a new MapServer layer object
|
*
|
* Parameters:
|
* name - {String} A name for the layer
|
* url - {String} Base url for the MapServer CGI
|
* (e.g. http://www2.dmsolutions.ca/cgi-bin/mapserv)
|
* params - {Object} An object with key/value pairs representing the
|
* GetMap query string parameters and parameter values.
|
* options - {Object} Hashtable of extra options to tag onto the layer
|
*/
|
initialize: function(name, url, params, options) {
|
OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments);
|
|
this.params = OpenLayers.Util.applyDefaults(
|
this.params, this.DEFAULT_PARAMS
|
);
|
|
// unless explicitly set in options, if the layer is transparent,
|
// it will be an overlay
|
if (options == null || options.isBaseLayer == null) {
|
this.isBaseLayer = ((this.params.transparent != "true") &&
|
(this.params.transparent != true));
|
}
|
},
|
|
/**
|
* Method: clone
|
* Create a clone of this layer
|
*
|
* Returns:
|
* {<OpenLayers.Layer.MapServer>} An exact clone of this layer
|
*/
|
clone: function (obj) {
|
if (obj == null) {
|
obj = new OpenLayers.Layer.MapServer(this.name,
|
this.url,
|
this.params,
|
this.getOptions());
|
}
|
//get all additions from superclasses
|
obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
|
|
// copy/set any non-init, non-simple values here
|
|
return obj;
|
},
|
|
/**
|
* Method: getURL
|
* Return a query string for this layer
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>} A bounds representing the bbox
|
* for the request
|
*
|
* Returns:
|
* {String} A string with the layer's url and parameters and also
|
* the passed-in bounds and appropriate tile size specified
|
* as parameters.
|
*/
|
getURL: function (bounds) {
|
bounds = this.adjustBounds(bounds);
|
// Make a list, so that getFullRequestString uses literal ","
|
var extent = [bounds.left, bounds. bottom, bounds.right, bounds.top];
|
|
var imageSize = this.getImageSize();
|
|
// make lists, so that literal ','s are used
|
var url = this.getFullRequestString(
|
{mapext: extent,
|
imgext: extent,
|
map_size: [imageSize.w, imageSize.h],
|
imgx: imageSize.w / 2,
|
imgy: imageSize.h / 2,
|
imgxy: [imageSize.w, imageSize.h]
|
});
|
|
return url;
|
},
|
|
/**
|
* Method: getFullRequestString
|
* combine the layer's url with its params and these newParams.
|
*
|
* Parameters:
|
* newParams - {Object} New parameters that should be added to the
|
* request string.
|
* altUrl - {String} (optional) Replace the URL in the full request
|
* string with the provided URL.
|
*
|
* Returns:
|
* {String} A string with the layer's url and parameters embedded in it.
|
*/
|
getFullRequestString:function(newParams, altUrl) {
|
// use layer's url unless altUrl passed in
|
var url = (altUrl == null) ? this.url : altUrl;
|
|
// create a new params hashtable with all the layer params and the
|
// new params together. then convert to string
|
var allParams = OpenLayers.Util.extend({}, this.params);
|
allParams = OpenLayers.Util.extend(allParams, newParams);
|
var paramsString = OpenLayers.Util.getParameterString(allParams);
|
|
// if url is not a string, it should be an array of strings,
|
// in which case we will deterministically select one of them in
|
// order to evenly distribute requests to different urls.
|
if (OpenLayers.Util.isArray(url)) {
|
url = this.selectUrl(paramsString, url);
|
}
|
|
// ignore parameters that are already in the url search string
|
var urlParams = OpenLayers.Util.upperCaseObject(
|
OpenLayers.Util.getParameters(url));
|
for(var key in allParams) {
|
if(key.toUpperCase() in urlParams) {
|
delete allParams[key];
|
}
|
}
|
paramsString = OpenLayers.Util.getParameterString(allParams);
|
|
// requestString always starts with url
|
var requestString = url;
|
|
// MapServer needs '+' seperating things like bounds/height/width.
|
// Since typically this is URL encoded, we use a slight hack: we
|
// depend on the list-like functionality of getParameterString to
|
// leave ',' only in the case of list items (since otherwise it is
|
// encoded) then do a regular expression replace on the , characters
|
// to '+'
|
//
|
paramsString = paramsString.replace(/,/g, "+");
|
|
if (paramsString != "") {
|
var lastServerChar = url.charAt(url.length - 1);
|
if ((lastServerChar == "&") || (lastServerChar == "?")) {
|
requestString += paramsString;
|
} else {
|
if (url.indexOf('?') == -1) {
|
//serverPath has no ? -- add one
|
requestString += '?' + paramsString;
|
} else {
|
//serverPath contains ?, so must already have paramsString at the end
|
requestString += '&' + paramsString;
|
}
|
}
|
}
|
return requestString;
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.MapServer"
|
});
|
/* ======================================================================
|
OpenLayers/Renderer/VML.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Renderer/Elements.js
|
*/
|
|
/**
|
* Class: OpenLayers.Renderer.VML
|
* Render vector features in browsers with VML capability. Construct a new
|
* VML renderer with the <OpenLayers.Renderer.VML> constructor.
|
*
|
* Note that for all calculations in this class, we use (num | 0) to truncate a
|
* float value to an integer. This is done because it seems that VML doesn't
|
* support float values.
|
*
|
* Inherits from:
|
* - <OpenLayers.Renderer.Elements>
|
*/
|
OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
|
|
/**
|
* Property: xmlns
|
* {String} XML Namespace URN
|
*/
|
xmlns: "urn:schemas-microsoft-com:vml",
|
|
/**
|
* Property: symbolCache
|
* {DOMElement} node holding symbols. This hash is keyed by symbol name,
|
* and each value is a hash with a "path" and an "extent" property.
|
*/
|
symbolCache: {},
|
|
/**
|
* Property: offset
|
* {Object} Hash with "x" and "y" properties
|
*/
|
offset: null,
|
|
/**
|
* Constructor: OpenLayers.Renderer.VML
|
* Create a new VML renderer.
|
*
|
* Parameters:
|
* containerID - {String} The id for the element that contains the renderer
|
*/
|
initialize: function(containerID) {
|
if (!this.supported()) {
|
return;
|
}
|
if (!document.namespaces.olv) {
|
document.namespaces.add("olv", this.xmlns);
|
var style = document.createStyleSheet();
|
var shapes = ['shape','rect', 'oval', 'fill', 'stroke', 'imagedata', 'group','textbox'];
|
for (var i = 0, len = shapes.length; i < len; i++) {
|
|
style.addRule('olv\\:' + shapes[i], "behavior: url(#default#VML); " +
|
"position: absolute; display: inline-block;");
|
}
|
}
|
|
OpenLayers.Renderer.Elements.prototype.initialize.apply(this,
|
arguments);
|
},
|
|
/**
|
* APIMethod: supported
|
* Determine whether a browser supports this renderer.
|
*
|
* Returns:
|
* {Boolean} The browser supports the VML renderer
|
*/
|
supported: function() {
|
return !!(document.namespaces);
|
},
|
|
/**
|
* Method: setExtent
|
* Set the renderer's extent
|
*
|
* Parameters:
|
* extent - {<OpenLayers.Bounds>}
|
* resolutionChanged - {Boolean}
|
*
|
* Returns:
|
* {Boolean} true to notify the layer that the new extent does not exceed
|
* the coordinate range, and the features will not need to be redrawn.
|
*/
|
setExtent: function(extent, resolutionChanged) {
|
var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments);
|
var resolution = this.getResolution();
|
|
var left = (extent.left/resolution) | 0;
|
var top = (extent.top/resolution - this.size.h) | 0;
|
if (resolutionChanged || !this.offset) {
|
this.offset = {x: left, y: top};
|
left = 0;
|
top = 0;
|
} else {
|
left = left - this.offset.x;
|
top = top - this.offset.y;
|
}
|
|
|
var org = (left - this.xOffset) + " " + top;
|
this.root.coordorigin = org;
|
var roots = [this.root, this.vectorRoot, this.textRoot];
|
var root;
|
for(var i=0, len=roots.length; i<len; ++i) {
|
root = roots[i];
|
|
var size = this.size.w + " " + this.size.h;
|
root.coordsize = size;
|
|
}
|
// flip the VML display Y axis upside down so it
|
// matches the display Y axis of the map
|
this.root.style.flip = "y";
|
|
return coordSysUnchanged;
|
},
|
|
|
/**
|
* Method: setSize
|
* Set the size of the drawing surface
|
*
|
* Parameters:
|
* size - {<OpenLayers.Size>} the size of the drawing surface
|
*/
|
setSize: function(size) {
|
OpenLayers.Renderer.prototype.setSize.apply(this, arguments);
|
|
// setting width and height on all roots to avoid flicker which we
|
// would get with 100% width and height on child roots
|
var roots = [
|
this.rendererRoot,
|
this.root,
|
this.vectorRoot,
|
this.textRoot
|
];
|
var w = this.size.w + "px";
|
var h = this.size.h + "px";
|
var root;
|
for(var i=0, len=roots.length; i<len; ++i) {
|
root = roots[i];
|
root.style.width = w;
|
root.style.height = h;
|
}
|
},
|
|
/**
|
* Method: getNodeType
|
* Get the node type for a geometry and style
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
* style - {Object}
|
*
|
* Returns:
|
* {String} The corresponding node type for the specified geometry
|
*/
|
getNodeType: function(geometry, style) {
|
var nodeType = null;
|
switch (geometry.CLASS_NAME) {
|
case "OpenLayers.Geometry.Point":
|
if (style.externalGraphic) {
|
nodeType = "olv:rect";
|
} else if (this.isComplexSymbol(style.graphicName)) {
|
nodeType = "olv:shape";
|
} else {
|
nodeType = "olv:oval";
|
}
|
break;
|
case "OpenLayers.Geometry.Rectangle":
|
nodeType = "olv:rect";
|
break;
|
case "OpenLayers.Geometry.LineString":
|
case "OpenLayers.Geometry.LinearRing":
|
case "OpenLayers.Geometry.Polygon":
|
case "OpenLayers.Geometry.Curve":
|
nodeType = "olv:shape";
|
break;
|
default:
|
break;
|
}
|
return nodeType;
|
},
|
|
/**
|
* Method: setStyle
|
* Use to set all the style attributes to a VML node.
|
*
|
* Parameters:
|
* node - {DOMElement} An VML element to decorate
|
* style - {Object}
|
* options - {Object} Currently supported options include
|
* 'isFilled' {Boolean} and
|
* 'isStroked' {Boolean}
|
* geometry - {<OpenLayers.Geometry>}
|
*/
|
setStyle: function(node, style, options, geometry) {
|
style = style || node._style;
|
options = options || node._options;
|
var fillColor = style.fillColor;
|
|
var title = style.title || style.graphicTitle;
|
if (title) {
|
node.title = title;
|
}
|
|
if (node._geometryClass === "OpenLayers.Geometry.Point") {
|
if (style.externalGraphic) {
|
options.isFilled = true;
|
var width = style.graphicWidth || style.graphicHeight;
|
var height = style.graphicHeight || style.graphicWidth;
|
width = width ? width : style.pointRadius*2;
|
height = height ? height : style.pointRadius*2;
|
|
var resolution = this.getResolution();
|
var xOffset = (style.graphicXOffset != undefined) ?
|
style.graphicXOffset : -(0.5 * width);
|
var yOffset = (style.graphicYOffset != undefined) ?
|
style.graphicYOffset : -(0.5 * height);
|
|
node.style.left = ((((geometry.x - this.featureDx)/resolution - this.offset.x)+xOffset) | 0) + "px";
|
node.style.top = (((geometry.y/resolution - this.offset.y)-(yOffset+height)) | 0) + "px";
|
node.style.width = width + "px";
|
node.style.height = height + "px";
|
node.style.flip = "y";
|
|
// modify fillColor and options for stroke styling below
|
fillColor = "none";
|
options.isStroked = false;
|
} else if (this.isComplexSymbol(style.graphicName)) {
|
var cache = this.importSymbol(style.graphicName);
|
node.path = cache.path;
|
node.coordorigin = cache.left + "," + cache.bottom;
|
var size = cache.size;
|
node.coordsize = size + "," + size;
|
this.drawCircle(node, geometry, style.pointRadius);
|
node.style.flip = "y";
|
} else {
|
this.drawCircle(node, geometry, style.pointRadius);
|
}
|
}
|
|
// fill
|
if (options.isFilled) {
|
node.fillcolor = fillColor;
|
} else {
|
node.filled = "false";
|
}
|
var fills = node.getElementsByTagName("fill");
|
var fill = (fills.length == 0) ? null : fills[0];
|
if (!options.isFilled) {
|
if (fill) {
|
node.removeChild(fill);
|
}
|
} else {
|
if (!fill) {
|
fill = this.createNode('olv:fill', node.id + "_fill");
|
}
|
fill.opacity = style.fillOpacity;
|
|
if (node._geometryClass === "OpenLayers.Geometry.Point" &&
|
style.externalGraphic) {
|
|
// override fillOpacity
|
if (style.graphicOpacity) {
|
fill.opacity = style.graphicOpacity;
|
}
|
|
fill.src = style.externalGraphic;
|
fill.type = "frame";
|
|
if (!(style.graphicWidth && style.graphicHeight)) {
|
fill.aspect = "atmost";
|
}
|
}
|
if (fill.parentNode != node) {
|
node.appendChild(fill);
|
}
|
}
|
|
// additional rendering for rotated graphics or symbols
|
var rotation = style.rotation;
|
if ((rotation !== undefined || node._rotation !== undefined)) {
|
node._rotation = rotation;
|
if (style.externalGraphic) {
|
this.graphicRotate(node, xOffset, yOffset, style);
|
// make the fill fully transparent, because we now have
|
// the graphic as imagedata element. We cannot just remove
|
// the fill, because this is part of the hack described
|
// in graphicRotate
|
fill.opacity = 0;
|
} else if(node._geometryClass === "OpenLayers.Geometry.Point") {
|
node.style.rotation = rotation || 0;
|
}
|
}
|
|
// stroke
|
var strokes = node.getElementsByTagName("stroke");
|
var stroke = (strokes.length == 0) ? null : strokes[0];
|
if (!options.isStroked) {
|
node.stroked = false;
|
if (stroke) {
|
stroke.on = false;
|
}
|
} else {
|
if (!stroke) {
|
stroke = this.createNode('olv:stroke', node.id + "_stroke");
|
node.appendChild(stroke);
|
}
|
stroke.on = true;
|
stroke.color = style.strokeColor;
|
stroke.weight = style.strokeWidth + "px";
|
stroke.opacity = style.strokeOpacity;
|
stroke.endcap = style.strokeLinecap == 'butt' ? 'flat' :
|
(style.strokeLinecap || 'round');
|
if (style.strokeDashstyle) {
|
stroke.dashstyle = this.dashStyle(style);
|
}
|
}
|
|
if (style.cursor != "inherit" && style.cursor != null) {
|
node.style.cursor = style.cursor;
|
}
|
return node;
|
},
|
|
/**
|
* Method: graphicRotate
|
* If a point is to be styled with externalGraphic and rotation, VML fills
|
* cannot be used to display the graphic, because rotation of graphic
|
* fills is not supported by the VML implementation of Internet Explorer.
|
* This method creates a olv:imagedata element inside the VML node,
|
* DXImageTransform.Matrix and BasicImage filters for rotation and
|
* opacity, and a 3-step hack to remove rendering artefacts from the
|
* graphic and preserve the ability of graphics to trigger events.
|
* Finally, OpenLayers methods are used to determine the correct
|
* insertion point of the rotated image, because DXImageTransform.Matrix
|
* does the rotation without the ability to specify a rotation center
|
* point.
|
*
|
* Parameters:
|
* node - {DOMElement}
|
* xOffset - {Number} rotation center relative to image, x coordinate
|
* yOffset - {Number} rotation center relative to image, y coordinate
|
* style - {Object}
|
*/
|
graphicRotate: function(node, xOffset, yOffset, style) {
|
var style = style || node._style;
|
var rotation = style.rotation || 0;
|
|
var aspectRatio, size;
|
if (!(style.graphicWidth && style.graphicHeight)) {
|
// load the image to determine its size
|
var img = new Image();
|
img.onreadystatechange = OpenLayers.Function.bind(function() {
|
if(img.readyState == "complete" ||
|
img.readyState == "interactive") {
|
aspectRatio = img.width / img.height;
|
size = Math.max(style.pointRadius * 2,
|
style.graphicWidth || 0,
|
style.graphicHeight || 0);
|
xOffset = xOffset * aspectRatio;
|
style.graphicWidth = size * aspectRatio;
|
style.graphicHeight = size;
|
this.graphicRotate(node, xOffset, yOffset, style);
|
}
|
}, this);
|
img.src = style.externalGraphic;
|
|
// will be called again by the onreadystate handler
|
return;
|
} else {
|
size = Math.max(style.graphicWidth, style.graphicHeight);
|
aspectRatio = style.graphicWidth / style.graphicHeight;
|
}
|
|
var width = Math.round(style.graphicWidth || size * aspectRatio);
|
var height = Math.round(style.graphicHeight || size);
|
node.style.width = width + "px";
|
node.style.height = height + "px";
|
|
// Three steps are required to remove artefacts for images with
|
// transparent backgrounds (resulting from using DXImageTransform
|
// filters on svg objects), while preserving awareness for browser
|
// events on images:
|
// - Use the fill as usual (like for unrotated images) to handle
|
// events
|
// - specify an imagedata element with the same src as the fill
|
// - style the imagedata element with an AlphaImageLoader filter
|
// with empty src
|
var image = document.getElementById(node.id + "_image");
|
if (!image) {
|
image = this.createNode("olv:imagedata", node.id + "_image");
|
node.appendChild(image);
|
}
|
image.style.width = width + "px";
|
image.style.height = height + "px";
|
image.src = style.externalGraphic;
|
image.style.filter =
|
"progid:DXImageTransform.Microsoft.AlphaImageLoader(" +
|
"src='', sizingMethod='scale')";
|
|
var rot = rotation * Math.PI / 180;
|
var sintheta = Math.sin(rot);
|
var costheta = Math.cos(rot);
|
|
// do the rotation on the image
|
var filter =
|
"progid:DXImageTransform.Microsoft.Matrix(M11=" + costheta +
|
",M12=" + (-sintheta) + ",M21=" + sintheta + ",M22=" + costheta +
|
",SizingMethod='auto expand')\n";
|
|
// set the opacity (needed for the imagedata)
|
var opacity = style.graphicOpacity || style.fillOpacity;
|
if (opacity && opacity != 1) {
|
filter +=
|
"progid:DXImageTransform.Microsoft.BasicImage(opacity=" +
|
opacity+")\n";
|
}
|
node.style.filter = filter;
|
|
// do the rotation again on a box, so we know the insertion point
|
var centerPoint = new OpenLayers.Geometry.Point(-xOffset, -yOffset);
|
var imgBox = new OpenLayers.Bounds(0, 0, width, height).toGeometry();
|
imgBox.rotate(style.rotation, centerPoint);
|
var imgBounds = imgBox.getBounds();
|
|
node.style.left = Math.round(
|
parseInt(node.style.left) + imgBounds.left) + "px";
|
node.style.top = Math.round(
|
parseInt(node.style.top) - imgBounds.bottom) + "px";
|
},
|
|
/**
|
* Method: postDraw
|
* Does some node postprocessing to work around browser issues:
|
* - Some versions of Internet Explorer seem to be unable to set fillcolor
|
* and strokecolor to "none" correctly before the fill node is appended
|
* to a visible vml node. This method takes care of that and sets
|
* fillcolor and strokecolor again if needed.
|
* - In some cases, a node won't become visible after being drawn. Setting
|
* style.visibility to "visible" works around that.
|
*
|
* Parameters:
|
* node - {DOMElement}
|
*/
|
postDraw: function(node) {
|
node.style.visibility = "visible";
|
var fillColor = node._style.fillColor;
|
var strokeColor = node._style.strokeColor;
|
if (fillColor == "none" &&
|
node.fillcolor != fillColor) {
|
node.fillcolor = fillColor;
|
}
|
if (strokeColor == "none" &&
|
node.strokecolor != strokeColor) {
|
node.strokecolor = strokeColor;
|
}
|
},
|
|
|
/**
|
* Method: setNodeDimension
|
* Get the geometry's bounds, convert it to our vml coordinate system,
|
* then set the node's position, size, and local coordinate system.
|
*
|
* Parameters:
|
* node - {DOMElement}
|
* geometry - {<OpenLayers.Geometry>}
|
*/
|
setNodeDimension: function(node, geometry) {
|
|
var bbox = geometry.getBounds();
|
if(bbox) {
|
var resolution = this.getResolution();
|
|
var scaledBox =
|
new OpenLayers.Bounds(((bbox.left - this.featureDx)/resolution - this.offset.x) | 0,
|
(bbox.bottom/resolution - this.offset.y) | 0,
|
((bbox.right - this.featureDx)/resolution - this.offset.x) | 0,
|
(bbox.top/resolution - this.offset.y) | 0);
|
|
// Set the internal coordinate system to draw the path
|
node.style.left = scaledBox.left + "px";
|
node.style.top = scaledBox.top + "px";
|
node.style.width = scaledBox.getWidth() + "px";
|
node.style.height = scaledBox.getHeight() + "px";
|
|
node.coordorigin = scaledBox.left + " " + scaledBox.top;
|
node.coordsize = scaledBox.getWidth()+ " " + scaledBox.getHeight();
|
}
|
},
|
|
/**
|
* Method: dashStyle
|
*
|
* Parameters:
|
* style - {Object}
|
*
|
* Returns:
|
* {String} A VML compliant 'stroke-dasharray' value
|
*/
|
dashStyle: function(style) {
|
var dash = style.strokeDashstyle;
|
switch (dash) {
|
case 'solid':
|
case 'dot':
|
case 'dash':
|
case 'dashdot':
|
case 'longdash':
|
case 'longdashdot':
|
return dash;
|
default:
|
// very basic guessing of dash style patterns
|
var parts = dash.split(/[ ,]/);
|
if (parts.length == 2) {
|
if (1*parts[0] >= 2*parts[1]) {
|
return "longdash";
|
}
|
return (parts[0] == 1 || parts[1] == 1) ? "dot" : "dash";
|
} else if (parts.length == 4) {
|
return (1*parts[0] >= 2*parts[1]) ? "longdashdot" :
|
"dashdot";
|
}
|
return "solid";
|
}
|
},
|
|
/**
|
* Method: createNode
|
* Create a new node
|
*
|
* Parameters:
|
* type - {String} Kind of node to draw
|
* id - {String} Id for node
|
*
|
* Returns:
|
* {DOMElement} A new node of the given type and id
|
*/
|
createNode: function(type, id) {
|
var node = document.createElement(type);
|
if (id) {
|
node.id = id;
|
}
|
|
// IE hack to make elements unselectable, to prevent 'blue flash'
|
// while dragging vectors; #1410
|
node.unselectable = 'on';
|
node.onselectstart = OpenLayers.Function.False;
|
|
return node;
|
},
|
|
/**
|
* Method: nodeTypeCompare
|
* Determine whether a node is of a given type
|
*
|
* Parameters:
|
* node - {DOMElement} An VML element
|
* type - {String} Kind of node
|
*
|
* Returns:
|
* {Boolean} Whether or not the specified node is of the specified type
|
*/
|
nodeTypeCompare: function(node, type) {
|
|
//split type
|
var subType = type;
|
var splitIndex = subType.indexOf(":");
|
if (splitIndex != -1) {
|
subType = subType.substr(splitIndex+1);
|
}
|
|
//split nodeName
|
var nodeName = node.nodeName;
|
splitIndex = nodeName.indexOf(":");
|
if (splitIndex != -1) {
|
nodeName = nodeName.substr(splitIndex+1);
|
}
|
|
return (subType == nodeName);
|
},
|
|
/**
|
* Method: createRenderRoot
|
* Create the renderer root
|
*
|
* Returns:
|
* {DOMElement} The specific render engine's root element
|
*/
|
createRenderRoot: function() {
|
return this.nodeFactory(this.container.id + "_vmlRoot", "div");
|
},
|
|
/**
|
* Method: createRoot
|
* Create the main root element
|
*
|
* Parameters:
|
* suffix - {String} suffix to append to the id
|
*
|
* Returns:
|
* {DOMElement}
|
*/
|
createRoot: function(suffix) {
|
return this.nodeFactory(this.container.id + suffix, "olv:group");
|
},
|
|
/**************************************
|
* *
|
* GEOMETRY DRAWING FUNCTIONS *
|
* *
|
**************************************/
|
|
/**
|
* Method: drawPoint
|
* Render a point
|
*
|
* Parameters:
|
* node - {DOMElement}
|
* geometry - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {DOMElement} or false if the point could not be drawn
|
*/
|
drawPoint: function(node, geometry) {
|
return this.drawCircle(node, geometry, 1);
|
},
|
|
/**
|
* Method: drawCircle
|
* Render a circle.
|
* Size and Center a circle given geometry (x,y center) and radius
|
*
|
* Parameters:
|
* node - {DOMElement}
|
* geometry - {<OpenLayers.Geometry>}
|
* radius - {float}
|
*
|
* Returns:
|
* {DOMElement} or false if the circle could not ne drawn
|
*/
|
drawCircle: function(node, geometry, radius) {
|
if(!isNaN(geometry.x)&& !isNaN(geometry.y)) {
|
var resolution = this.getResolution();
|
|
node.style.left = ((((geometry.x - this.featureDx) /resolution - this.offset.x) | 0) - radius) + "px";
|
node.style.top = (((geometry.y /resolution - this.offset.y) | 0) - radius) + "px";
|
|
var diameter = radius * 2;
|
|
node.style.width = diameter + "px";
|
node.style.height = diameter + "px";
|
return node;
|
}
|
return false;
|
},
|
|
|
/**
|
* Method: drawLineString
|
* Render a linestring.
|
*
|
* Parameters:
|
* node - {DOMElement}
|
* geometry - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {DOMElement}
|
*/
|
drawLineString: function(node, geometry) {
|
return this.drawLine(node, geometry, false);
|
},
|
|
/**
|
* Method: drawLinearRing
|
* Render a linearring
|
*
|
* Parameters:
|
* node - {DOMElement}
|
* geometry - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {DOMElement}
|
*/
|
drawLinearRing: function(node, geometry) {
|
return this.drawLine(node, geometry, true);
|
},
|
|
/**
|
* Method: DrawLine
|
* Render a line.
|
*
|
* Parameters:
|
* node - {DOMElement}
|
* geometry - {<OpenLayers.Geometry>}
|
* closeLine - {Boolean} Close the line? (make it a ring?)
|
*
|
* Returns:
|
* {DOMElement}
|
*/
|
drawLine: function(node, geometry, closeLine) {
|
|
this.setNodeDimension(node, geometry);
|
|
var resolution = this.getResolution();
|
var numComponents = geometry.components.length;
|
var parts = new Array(numComponents);
|
|
var comp, x, y;
|
for (var i = 0; i < numComponents; i++) {
|
comp = geometry.components[i];
|
x = ((comp.x - this.featureDx)/resolution - this.offset.x) | 0;
|
y = (comp.y/resolution - this.offset.y) | 0;
|
parts[i] = " " + x + "," + y + " l ";
|
}
|
var end = (closeLine) ? " x e" : " e";
|
node.path = "m" + parts.join("") + end;
|
return node;
|
},
|
|
/**
|
* Method: drawPolygon
|
* Render a polygon
|
*
|
* Parameters:
|
* node - {DOMElement}
|
* geometry - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {DOMElement}
|
*/
|
drawPolygon: function(node, geometry) {
|
this.setNodeDimension(node, geometry);
|
|
var resolution = this.getResolution();
|
|
var path = [];
|
var j, jj, points, area, first, second, i, ii, comp, pathComp, x, y;
|
for (j=0, jj=geometry.components.length; j<jj; j++) {
|
path.push("m");
|
points = geometry.components[j].components;
|
// we only close paths of interior rings with area
|
area = (j === 0);
|
first = null;
|
second = null;
|
for (i=0, ii=points.length; i<ii; i++) {
|
comp = points[i];
|
x = ((comp.x - this.featureDx) / resolution - this.offset.x) | 0;
|
y = (comp.y / resolution - this.offset.y) | 0;
|
pathComp = " " + x + "," + y;
|
path.push(pathComp);
|
if (i==0) {
|
path.push(" l");
|
}
|
if (!area) {
|
// IE improperly renders sub-paths that have no area.
|
// Instead of checking the area of every ring, we confirm
|
// the ring has at least three distinct points. This does
|
// not catch all non-zero area cases, but it greatly improves
|
// interior ring digitizing and is a minor performance hit
|
// when rendering rings with many points.
|
if (!first) {
|
first = pathComp;
|
} else if (first != pathComp) {
|
if (!second) {
|
second = pathComp;
|
} else if (second != pathComp) {
|
// stop looking
|
area = true;
|
}
|
}
|
}
|
}
|
path.push(area ? " x " : " ");
|
}
|
path.push("e");
|
node.path = path.join("");
|
return node;
|
},
|
|
/**
|
* Method: drawRectangle
|
* Render a rectangle
|
*
|
* Parameters:
|
* node - {DOMElement}
|
* geometry - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {DOMElement}
|
*/
|
drawRectangle: function(node, geometry) {
|
var resolution = this.getResolution();
|
|
node.style.left = (((geometry.x - this.featureDx)/resolution - this.offset.x) | 0) + "px";
|
node.style.top = ((geometry.y/resolution - this.offset.y) | 0) + "px";
|
node.style.width = ((geometry.width/resolution) | 0) + "px";
|
node.style.height = ((geometry.height/resolution) | 0) + "px";
|
|
return node;
|
},
|
|
/**
|
* Method: drawText
|
* This method is only called by the renderer itself.
|
*
|
* Parameters:
|
* featureId - {String}
|
* style -
|
* location - {<OpenLayers.Geometry.Point>}
|
*/
|
drawText: function(featureId, style, location) {
|
var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "olv:rect");
|
var textbox = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_textbox", "olv:textbox");
|
|
var resolution = this.getResolution();
|
label.style.left = (((location.x - this.featureDx)/resolution - this.offset.x) | 0) + "px";
|
label.style.top = ((location.y/resolution - this.offset.y) | 0) + "px";
|
label.style.flip = "y";
|
|
textbox.innerText = style.label;
|
|
if (style.cursor != "inherit" && style.cursor != null) {
|
textbox.style.cursor = style.cursor;
|
}
|
if (style.fontColor) {
|
textbox.style.color = style.fontColor;
|
}
|
if (style.fontOpacity) {
|
textbox.style.filter = 'alpha(opacity=' + (style.fontOpacity * 100) + ')';
|
}
|
if (style.fontFamily) {
|
textbox.style.fontFamily = style.fontFamily;
|
}
|
if (style.fontSize) {
|
textbox.style.fontSize = style.fontSize;
|
}
|
if (style.fontWeight) {
|
textbox.style.fontWeight = style.fontWeight;
|
}
|
if (style.fontStyle) {
|
textbox.style.fontStyle = style.fontStyle;
|
}
|
if(style.labelSelect === true) {
|
label._featureId = featureId;
|
textbox._featureId = featureId;
|
textbox._geometry = location;
|
textbox._geometryClass = location.CLASS_NAME;
|
}
|
textbox.style.whiteSpace = "nowrap";
|
// fun with IE: IE7 in standards compliant mode does not display any
|
// text with a left inset of 0. So we set this to 1px and subtract one
|
// pixel later when we set label.style.left
|
textbox.inset = "1px,0px,0px,0px";
|
|
if(!label.parentNode) {
|
label.appendChild(textbox);
|
this.textRoot.appendChild(label);
|
}
|
|
var align = style.labelAlign || "cm";
|
if (align.length == 1) {
|
align += "m";
|
}
|
var xshift = textbox.clientWidth *
|
(OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(0,1)]);
|
var yshift = textbox.clientHeight *
|
(OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(1,1)]);
|
label.style.left = parseInt(label.style.left)-xshift-1+"px";
|
label.style.top = parseInt(label.style.top)+yshift+"px";
|
|
},
|
|
/**
|
* Method: moveRoot
|
* moves this renderer's root to a different renderer.
|
*
|
* Parameters:
|
* renderer - {<OpenLayers.Renderer>} target renderer for the moved root
|
* root - {DOMElement} optional root node. To be used when this renderer
|
* holds roots from multiple layers to tell this method which one to
|
* detach
|
*
|
* Returns:
|
* {Boolean} true if successful, false otherwise
|
*/
|
moveRoot: function(renderer) {
|
var layer = this.map.getLayer(renderer.container.id);
|
if(layer instanceof OpenLayers.Layer.Vector.RootContainer) {
|
layer = this.map.getLayer(this.container.id);
|
}
|
layer && layer.renderer.clear();
|
OpenLayers.Renderer.Elements.prototype.moveRoot.apply(this, arguments);
|
layer && layer.redraw();
|
},
|
|
/**
|
* Method: importSymbol
|
* add a new symbol definition from the rendererer's symbol hash
|
*
|
* Parameters:
|
* graphicName - {String} name of the symbol to import
|
*
|
* Returns:
|
* {Object} - hash of {DOMElement} "symbol" and {Number} "size"
|
*/
|
importSymbol: function (graphicName) {
|
var id = this.container.id + "-" + graphicName;
|
|
// check if symbol already exists in the cache
|
var cache = this.symbolCache[id];
|
if (cache) {
|
return cache;
|
}
|
|
var symbol = OpenLayers.Renderer.symbol[graphicName];
|
if (!symbol) {
|
throw new Error(graphicName + ' is not a valid symbol name');
|
}
|
|
var symbolExtent = new OpenLayers.Bounds(
|
Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
|
|
var pathitems = ["m"];
|
for (var i=0; i<symbol.length; i=i+2) {
|
var x = symbol[i];
|
var y = symbol[i+1];
|
symbolExtent.left = Math.min(symbolExtent.left, x);
|
symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
|
symbolExtent.right = Math.max(symbolExtent.right, x);
|
symbolExtent.top = Math.max(symbolExtent.top, y);
|
|
pathitems.push(x);
|
pathitems.push(y);
|
if (i == 0) {
|
pathitems.push("l");
|
}
|
}
|
pathitems.push("x e");
|
var path = pathitems.join(" ");
|
|
var diff = (symbolExtent.getWidth() - symbolExtent.getHeight()) / 2;
|
if(diff > 0) {
|
symbolExtent.bottom = symbolExtent.bottom - diff;
|
symbolExtent.top = symbolExtent.top + diff;
|
} else {
|
symbolExtent.left = symbolExtent.left + diff;
|
symbolExtent.right = symbolExtent.right - diff;
|
}
|
|
cache = {
|
path: path,
|
size: symbolExtent.getWidth(), // equals getHeight() now
|
left: symbolExtent.left,
|
bottom: symbolExtent.bottom
|
};
|
this.symbolCache[id] = cache;
|
|
return cache;
|
},
|
|
CLASS_NAME: "OpenLayers.Renderer.VML"
|
});
|
|
/**
|
* Constant: OpenLayers.Renderer.VML.LABEL_SHIFT
|
* {Object}
|
*/
|
OpenLayers.Renderer.VML.LABEL_SHIFT = {
|
"l": 0,
|
"c": .5,
|
"r": 1,
|
"t": 0,
|
"m": .5,
|
"b": 1
|
};
|
/* ======================================================================
|
OpenLayers/Control/CacheRead.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.CacheRead
|
* A control for using image tiles cached with <OpenLayers.Control.CacheWrite>
|
* from the browser's local storage.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.CacheRead = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* APIProperty: fetchEvent
|
* {String} The layer event to listen to for replacing remote resource tile
|
* URLs with cached data URIs. Supported values are "tileerror" (try
|
* remote first, fall back to cached) and "tileloadstart" (try cache
|
* first, fall back to remote). Default is "tileloadstart".
|
*
|
* Note that "tileerror" will not work for CORS enabled images (see
|
* https://developer.mozilla.org/en/CORS_Enabled_Image), i.e. layers
|
* configured with a <OpenLayers.Tile.Image.crossOriginKeyword> in
|
* <OpenLayers.Layer.Grid.tileOptions>.
|
*/
|
fetchEvent: "tileloadstart",
|
|
/**
|
* APIProperty: layers
|
* {Array(<OpenLayers.Layer.Grid>)}. Optional. If provided, only these
|
* layers will receive tiles from the cache.
|
*/
|
layers: null,
|
|
/**
|
* APIProperty: autoActivate
|
* {Boolean} Activate the control when it is added to a map. Default is
|
* true.
|
*/
|
autoActivate: true,
|
|
/**
|
* Constructor: OpenLayers.Control.CacheRead
|
*
|
* Parameters:
|
* options - {Object} Object with API properties for this control
|
*/
|
|
/**
|
* Method: setMap
|
* Set the map property for the control.
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
setMap: function(map) {
|
OpenLayers.Control.prototype.setMap.apply(this, arguments);
|
var i, layers = this.layers || map.layers;
|
for (i=layers.length-1; i>=0; --i) {
|
this.addLayer({layer: layers[i]});
|
}
|
if (!this.layers) {
|
map.events.on({
|
addlayer: this.addLayer,
|
removeLayer: this.removeLayer,
|
scope: this
|
});
|
}
|
},
|
|
/**
|
* Method: addLayer
|
* Adds a layer to the control. Once added, tiles requested for this layer
|
* will be cached.
|
*
|
* Parameters:
|
* evt - {Object} Object with a layer property referencing an
|
* <OpenLayers.Layer> instance
|
*/
|
addLayer: function(evt) {
|
evt.layer.events.register(this.fetchEvent, this, this.fetch);
|
},
|
|
/**
|
* Method: removeLayer
|
* Removes a layer from the control. Once removed, tiles requested for this
|
* layer will no longer be cached.
|
*
|
* Parameters:
|
* evt - {Object} Object with a layer property referencing an
|
* <OpenLayers.Layer> instance
|
*/
|
removeLayer: function(evt) {
|
evt.layer.events.unregister(this.fetchEvent, this, this.fetch);
|
},
|
|
/**
|
* Method: fetch
|
* Listener to the <fetchEvent> event. Replaces a tile's url with a data
|
* URI from the cache.
|
*
|
* Parameters:
|
* evt - {Object} Event object with a tile property.
|
*/
|
fetch: function(evt) {
|
if (this.active && window.localStorage &&
|
evt.tile instanceof OpenLayers.Tile.Image) {
|
var tile = evt.tile,
|
url = tile.url;
|
// deal with modified tile urls when both CacheWrite and CacheRead
|
// are active
|
if (!tile.layer.crossOriginKeyword && OpenLayers.ProxyHost &&
|
url.indexOf(OpenLayers.ProxyHost) === 0) {
|
url = OpenLayers.Control.CacheWrite.urlMap[url];
|
}
|
var dataURI = window.localStorage.getItem("olCache_" + url);
|
if (dataURI) {
|
tile.url = dataURI;
|
if (evt.type === "tileerror") {
|
tile.setImgSrc(dataURI);
|
}
|
}
|
}
|
},
|
|
/**
|
* Method: destroy
|
* The destroy method is used to perform any clean up before the control
|
* is dereferenced. Typically this is where event listeners are removed
|
* to prevent memory leaks.
|
*/
|
destroy: function() {
|
if (this.layers || this.map) {
|
var i, layers = this.layers || this.map.layers;
|
for (i=layers.length-1; i>=0; --i) {
|
this.removeLayer({layer: layers[i]});
|
}
|
}
|
if (this.map) {
|
this.map.events.un({
|
addlayer: this.addLayer,
|
removeLayer: this.removeLayer,
|
scope: this
|
});
|
}
|
OpenLayers.Control.prototype.destroy.apply(this, arguments);
|
},
|
|
CLASS_NAME: "OpenLayers.Control.CacheRead"
|
});
|
/* ======================================================================
|
OpenLayers/Protocol/WFS/v1_0_0.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Protocol/WFS/v1.js
|
* @requires OpenLayers/Format/WFST/v1_0_0.js
|
*/
|
|
/**
|
* Class: OpenLayers.Protocol.WFS.v1_0_0
|
* A WFS v1.0.0 protocol for vector layers. Create a new instance with the
|
* <OpenLayers.Protocol.WFS.v1_0_0> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Protocol.WFS.v1>
|
*/
|
OpenLayers.Protocol.WFS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, {
|
|
/**
|
* Property: version
|
* {String} WFS version number.
|
*/
|
version: "1.0.0",
|
|
/**
|
* Constructor: OpenLayers.Protocol.WFS.v1_0_0
|
* A class for giving layers WFS v1.0.0 protocol.
|
*
|
* Parameters:
|
* options - {Object} Optional object whose properties will be set on the
|
* instance.
|
*
|
* Valid options properties:
|
* featureType - {String} Local (without prefix) feature typeName (required).
|
* featureNS - {String} Feature namespace (optional).
|
* featurePrefix - {String} Feature namespace alias (optional - only used
|
* if featureNS is provided). Default is 'feature'.
|
* geometryName - {String} Name of geometry attribute. Default is 'the_geom'.
|
*/
|
|
CLASS_NAME: "OpenLayers.Protocol.WFS.v1_0_0"
|
});
|
/* ======================================================================
|
OpenLayers/Format/WMSGetFeatureInfo.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WMSGetFeatureInfo
|
* Class to read GetFeatureInfo responses from Web Mapping Services
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Format.XML, {
|
|
/**
|
* APIProperty: layerIdentifier
|
* {String} All xml nodes containing this search criteria will populate an
|
* internal array of layer nodes.
|
*/
|
layerIdentifier: '_layer',
|
|
/**
|
* APIProperty: featureIdentifier
|
* {String} All xml nodes containing this search criteria will populate an
|
* internal array of feature nodes for each layer node found.
|
*/
|
featureIdentifier: '_feature',
|
|
/**
|
* Property: regExes
|
* Compiled regular expressions for manipulating strings.
|
*/
|
regExes: {
|
trimSpace: (/^\s*|\s*$/g),
|
removeSpace: (/\s*/g),
|
splitSpace: (/\s+/),
|
trimComma: (/\s*,\s*/g)
|
},
|
|
/**
|
* Property: gmlFormat
|
* {<OpenLayers.Format.GML>} internal GML format for parsing geometries
|
* in msGMLOutput
|
*/
|
gmlFormat: null,
|
|
/**
|
* Constructor: OpenLayers.Format.WMSGetFeatureInfo
|
* Create a new parser for WMS GetFeatureInfo responses
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* APIMethod: read
|
* Read WMS GetFeatureInfo data from a string, and return an array of features
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
*
|
* Returns:
|
* {Array(<OpenLayers.Feature.Vector>)} An array of features.
|
*/
|
read: function(data) {
|
var result;
|
if(typeof data == "string") {
|
data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
|
}
|
var root = data.documentElement;
|
if(root) {
|
var scope = this;
|
var read = this["read_" + root.nodeName];
|
if(read) {
|
result = read.call(this, root);
|
} else {
|
// fall-back to GML since this is a common output format for WMS
|
// GetFeatureInfo responses
|
result = new OpenLayers.Format.GML((this.options ? this.options : {})).read(data);
|
}
|
} else {
|
result = data;
|
}
|
return result;
|
},
|
|
|
/**
|
* Method: read_msGMLOutput
|
* Parse msGMLOutput nodes.
|
*
|
* Parameters:
|
* data - {DOMElement}
|
*
|
* Returns:
|
* {Array}
|
*/
|
read_msGMLOutput: function(data) {
|
var response = [];
|
var layerNodes = this.getSiblingNodesByTagCriteria(data,
|
this.layerIdentifier);
|
if (layerNodes) {
|
for (var i=0, len=layerNodes.length; i<len; ++i) {
|
var node = layerNodes[i];
|
var layerName = node.nodeName;
|
if (node.prefix) {
|
layerName = layerName.split(':')[1];
|
}
|
var layerName = layerName.replace(this.layerIdentifier, '');
|
var featureNodes = this.getSiblingNodesByTagCriteria(node,
|
this.featureIdentifier);
|
if (featureNodes) {
|
for (var j = 0; j < featureNodes.length; j++) {
|
var featureNode = featureNodes[j];
|
var geomInfo = this.parseGeometry(featureNode);
|
var attributes = this.parseAttributes(featureNode);
|
var feature = new OpenLayers.Feature.Vector(geomInfo.geometry,
|
attributes, null);
|
feature.bounds = geomInfo.bounds;
|
feature.type = layerName;
|
response.push(feature);
|
}
|
}
|
}
|
}
|
return response;
|
},
|
|
/**
|
* Method: read_FeatureInfoResponse
|
* Parse FeatureInfoResponse nodes.
|
*
|
* Parameters:
|
* data - {DOMElement}
|
*
|
* Returns:
|
* {Array}
|
*/
|
read_FeatureInfoResponse: function(data) {
|
var response = [];
|
var featureNodes = this.getElementsByTagNameNS(data, '*',
|
'FIELDS');
|
|
for(var i=0, len=featureNodes.length;i<len;i++) {
|
var featureNode = featureNodes[i];
|
var geom = null;
|
|
// attributes can be actual attributes on the FIELDS tag,
|
// or FIELD children
|
var attributes = {};
|
var j;
|
var jlen = featureNode.attributes.length;
|
if (jlen > 0) {
|
for(j=0; j<jlen; j++) {
|
var attribute = featureNode.attributes[j];
|
attributes[attribute.nodeName] = attribute.nodeValue;
|
}
|
} else {
|
var nodes = featureNode.childNodes;
|
for (j=0, jlen=nodes.length; j<jlen; ++j) {
|
var node = nodes[j];
|
if (node.nodeType != 3) {
|
attributes[node.getAttribute("name")] =
|
node.getAttribute("value");
|
}
|
}
|
}
|
|
response.push(
|
new OpenLayers.Feature.Vector(geom, attributes, null)
|
);
|
}
|
return response;
|
},
|
|
/**
|
* Method: getSiblingNodesByTagCriteria
|
* Recursively searches passed xml node and all it's descendant levels for
|
* nodes whose tagName contains the passed search string. This returns an
|
* array of all sibling nodes which match the criteria from the highest
|
* hierarchial level from which a match is found.
|
*
|
* Parameters:
|
* node - {DOMElement} An xml node
|
* criteria - {String} Search string which will match some part of a tagName
|
*
|
* Returns:
|
* Array({DOMElement}) An array of sibling xml nodes
|
*/
|
getSiblingNodesByTagCriteria: function(node, criteria){
|
var nodes = [];
|
var children, tagName, n, matchNodes, child;
|
if (node && node.hasChildNodes()) {
|
children = node.childNodes;
|
n = children.length;
|
|
for(var k=0; k<n; k++){
|
child = children[k];
|
while (child && child.nodeType != 1) {
|
child = child.nextSibling;
|
k++;
|
}
|
tagName = (child ? child.nodeName : '');
|
if (tagName.length > 0 && tagName.indexOf(criteria) > -1) {
|
nodes.push(child);
|
} else {
|
matchNodes = this.getSiblingNodesByTagCriteria(
|
child, criteria);
|
|
if(matchNodes.length > 0){
|
(nodes.length == 0) ?
|
nodes = matchNodes : nodes.push(matchNodes);
|
}
|
}
|
}
|
|
}
|
return nodes;
|
},
|
|
/**
|
* Method: parseAttributes
|
*
|
* Parameters:
|
* node - {<DOMElement>}
|
*
|
* Returns:
|
* {Object} An attributes object.
|
*
|
* Notes:
|
* Assumes that attributes are direct child xml nodes of the passed node
|
* and contain only a single text node.
|
*/
|
parseAttributes: function(node){
|
var attributes = {};
|
if (node.nodeType == 1) {
|
var children = node.childNodes;
|
var n = children.length;
|
for (var i = 0; i < n; ++i) {
|
var child = children[i];
|
if (child.nodeType == 1) {
|
var grandchildren = child.childNodes;
|
var name = (child.prefix) ?
|
child.nodeName.split(":")[1] : child.nodeName;
|
if (grandchildren.length == 0) {
|
attributes[name] = null;
|
} else if (grandchildren.length == 1) {
|
var grandchild = grandchildren[0];
|
if (grandchild.nodeType == 3 ||
|
grandchild.nodeType == 4) {
|
var value = grandchild.nodeValue.replace(
|
this.regExes.trimSpace, "");
|
attributes[name] = value;
|
}
|
}
|
}
|
}
|
}
|
return attributes;
|
},
|
|
/**
|
* Method: parseGeometry
|
* Parse the geometry and the feature bounds out of the node using
|
* Format.GML
|
*
|
* Parameters:
|
* node - {<DOMElement>}
|
*
|
* Returns:
|
* {Object} An object containing the geometry and the feature bounds
|
*/
|
parseGeometry: function(node) {
|
// we need to use the old Format.GML parser since we do not know the
|
// geometry name
|
if (!this.gmlFormat) {
|
this.gmlFormat = new OpenLayers.Format.GML();
|
}
|
var feature = this.gmlFormat.parseFeature(node);
|
var geometry, bounds = null;
|
if (feature) {
|
geometry = feature.geometry && feature.geometry.clone();
|
bounds = feature.bounds && feature.bounds.clone();
|
feature.destroy();
|
}
|
return {geometry: geometry, bounds: bounds};
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WMSGetFeatureInfo"
|
|
});
|
/* ======================================================================
|
OpenLayers/Control/WMTSGetFeatureInfo.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/Handler/Click.js
|
* @requires OpenLayers/Handler/Hover.js
|
* @requires OpenLayers/Request.js
|
* @requires OpenLayers/Format/WMSGetFeatureInfo.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.WMTSGetFeatureInfo
|
* The WMTSGetFeatureInfo control uses a WMTS query to get information about a
|
* point on the map. The information may be in a display-friendly format
|
* such as HTML, or a machine-friendly format such as GML, depending on the
|
* server's capabilities and the client's configuration. This control
|
* handles click or hover events, attempts to parse the results using an
|
* OpenLayers.Format, and fires a 'getfeatureinfo' event for each layer
|
* queried.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.WMTSGetFeatureInfo = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* APIProperty: hover
|
* {Boolean} Send GetFeatureInfo requests when mouse stops moving.
|
* Default is false.
|
*/
|
hover: false,
|
|
/**
|
* Property: requestEncoding
|
* {String} One of "KVP" or "REST". Only KVP encoding is supported at this
|
* time.
|
*/
|
requestEncoding: "KVP",
|
|
/**
|
* APIProperty: drillDown
|
* {Boolean} Drill down over all WMTS layers in the map. When
|
* using drillDown mode, hover is not possible. A getfeatureinfo event
|
* will be fired for each layer queried.
|
*/
|
drillDown: false,
|
|
/**
|
* APIProperty: maxFeatures
|
* {Integer} Maximum number of features to return from a WMTS query. This
|
* sets the feature_count parameter on WMTS GetFeatureInfo
|
* requests.
|
*/
|
maxFeatures: 10,
|
|
/** APIProperty: clickCallback
|
* {String} The click callback to register in the
|
* {<OpenLayers.Handler.Click>} object created when the hover
|
* option is set to false. Default is "click".
|
*/
|
clickCallback: "click",
|
|
/**
|
* Property: layers
|
* {Array(<OpenLayers.Layer.WMTS>)} The layers to query for feature info.
|
* If omitted, all map WMTS layers will be considered.
|
*/
|
layers: null,
|
|
/**
|
* APIProperty: queryVisible
|
* {Boolean} Filter out hidden layers when searching the map for layers to
|
* query. Default is true.
|
*/
|
queryVisible: true,
|
|
/**
|
* Property: infoFormat
|
* {String} The mimetype to request from the server
|
*/
|
infoFormat: 'text/html',
|
|
/**
|
* Property: vendorParams
|
* {Object} Additional parameters that will be added to the request, for
|
* WMTS implementations that support them. This could e.g. look like
|
* (start code)
|
* {
|
* radius: 5
|
* }
|
* (end)
|
*/
|
vendorParams: {},
|
|
/**
|
* Property: format
|
* {<OpenLayers.Format>} A format for parsing GetFeatureInfo responses.
|
* Default is <OpenLayers.Format.WMSGetFeatureInfo>.
|
*/
|
format: null,
|
|
/**
|
* Property: formatOptions
|
* {Object} Optional properties to set on the format (if one is not provided
|
* in the <format> property.
|
*/
|
formatOptions: null,
|
|
/**
|
* APIProperty: handlerOptions
|
* {Object} Additional options for the handlers used by this control, e.g.
|
* (start code)
|
* {
|
* "click": {delay: 100},
|
* "hover": {delay: 300}
|
* }
|
* (end)
|
*/
|
|
/**
|
* Property: handler
|
* {Object} Reference to the <OpenLayers.Handler> for this control
|
*/
|
handler: null,
|
|
/**
|
* Property: hoverRequest
|
* {<OpenLayers.Request>} contains the currently running hover request
|
* (if any).
|
*/
|
hoverRequest: null,
|
|
/**
|
* APIProperty: events
|
* {<OpenLayers.Events>} Events instance for listeners and triggering
|
* control specific events.
|
*
|
* Register a listener for a particular event with the following syntax:
|
* (code)
|
* control.events.register(type, obj, listener);
|
* (end)
|
*
|
* Supported event types (in addition to those from <OpenLayers.Control.events>):
|
* beforegetfeatureinfo - Triggered before each request is sent.
|
* The event object has an *xy* property with the position of the
|
* mouse click or hover event that triggers the request and a *layer*
|
* property referencing the layer about to be queried. If a listener
|
* returns false, the request will not be issued.
|
* getfeatureinfo - Triggered when a GetFeatureInfo response is received.
|
* The event object has a *text* property with the body of the
|
* response (String), a *features* property with an array of the
|
* parsed features, an *xy* property with the position of the mouse
|
* click or hover event that triggered the request, a *layer* property
|
* referencing the layer queried and a *request* property with the
|
* request itself. If drillDown is set to true, one event will be fired
|
* for each layer queried.
|
* exception - Triggered when a GetFeatureInfo request fails (with a
|
* status other than 200) or whenparsing fails. Listeners will receive
|
* an event with *request*, *xy*, and *layer* properties. In the case
|
* of a parsing error, the event will also contain an *error* property.
|
*/
|
|
/**
|
* Property: pending
|
* {Number} The number of pending requests.
|
*/
|
pending: 0,
|
|
/**
|
* Constructor: <OpenLayers.Control.WMTSGetFeatureInfo>
|
*
|
* Parameters:
|
* options - {Object}
|
*/
|
initialize: function(options) {
|
options = options || {};
|
options.handlerOptions = options.handlerOptions || {};
|
|
OpenLayers.Control.prototype.initialize.apply(this, [options]);
|
|
if (!this.format) {
|
this.format = new OpenLayers.Format.WMSGetFeatureInfo(
|
options.formatOptions
|
);
|
}
|
|
if (this.drillDown === true) {
|
this.hover = false;
|
}
|
|
if (this.hover) {
|
this.handler = new OpenLayers.Handler.Hover(
|
this, {
|
move: this.cancelHover,
|
pause: this.getInfoForHover
|
},
|
OpenLayers.Util.extend(
|
this.handlerOptions.hover || {}, {delay: 250}
|
)
|
);
|
} else {
|
var callbacks = {};
|
callbacks[this.clickCallback] = this.getInfoForClick;
|
this.handler = new OpenLayers.Handler.Click(
|
this, callbacks, this.handlerOptions.click || {}
|
);
|
}
|
},
|
|
/**
|
* Method: getInfoForClick
|
* Called on click
|
*
|
* Parameters:
|
* evt - {<OpenLayers.Event>}
|
*/
|
getInfoForClick: function(evt) {
|
this.request(evt.xy, {});
|
},
|
|
/**
|
* Method: getInfoForHover
|
* Pause callback for the hover handler
|
*
|
* Parameters:
|
* evt - {Object}
|
*/
|
getInfoForHover: function(evt) {
|
this.request(evt.xy, {hover: true});
|
},
|
|
/**
|
* Method: cancelHover
|
* Cancel callback for the hover handler
|
*/
|
cancelHover: function() {
|
if (this.hoverRequest) {
|
--this.pending;
|
if (this.pending <= 0) {
|
OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait");
|
this.pending = 0;
|
}
|
this.hoverRequest.abort();
|
this.hoverRequest = null;
|
}
|
},
|
|
/**
|
* Method: findLayers
|
* Internal method to get the layers, independent of whether we are
|
* inspecting the map or using a client-provided array
|
*/
|
findLayers: function() {
|
var candidates = this.layers || this.map.layers;
|
var layers = [];
|
var layer;
|
for (var i=candidates.length-1; i>=0; --i) {
|
layer = candidates[i];
|
if (layer instanceof OpenLayers.Layer.WMTS &&
|
layer.requestEncoding === this.requestEncoding &&
|
(!this.queryVisible || layer.getVisibility())) {
|
layers.push(layer);
|
if (!this.drillDown || this.hover) {
|
break;
|
}
|
}
|
}
|
return layers;
|
},
|
|
/**
|
* Method: buildRequestOptions
|
* Build an object with the relevant options for the GetFeatureInfo request.
|
*
|
* Parameters:
|
* layer - {<OpenLayers.Layer.WMTS>} A WMTS layer.
|
* xy - {<OpenLayers.Pixel>} The position on the map where the
|
* mouse event occurred.
|
*/
|
buildRequestOptions: function(layer, xy) {
|
var loc = this.map.getLonLatFromPixel(xy);
|
var getTileUrl = layer.getURL(
|
new OpenLayers.Bounds(loc.lon, loc.lat, loc.lon, loc.lat)
|
);
|
var params = OpenLayers.Util.getParameters(getTileUrl);
|
var tileInfo = layer.getTileInfo(loc);
|
OpenLayers.Util.extend(params, {
|
service: "WMTS",
|
version: layer.version,
|
request: "GetFeatureInfo",
|
infoFormat: this.infoFormat,
|
i: tileInfo.i,
|
j: tileInfo.j
|
});
|
OpenLayers.Util.applyDefaults(params, this.vendorParams);
|
return {
|
url: OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url,
|
params: OpenLayers.Util.upperCaseObject(params),
|
callback: function(request) {
|
this.handleResponse(xy, request, layer);
|
},
|
scope: this
|
};
|
},
|
|
/**
|
* Method: request
|
* Sends a GetFeatureInfo request to the WMTS
|
*
|
* Parameters:
|
* xy - {<OpenLayers.Pixel>} The position on the map where the mouse event
|
* occurred.
|
* options - {Object} additional options for this method.
|
*
|
* Valid options:
|
* - *hover* {Boolean} true if we do the request for the hover handler
|
*/
|
request: function(xy, options) {
|
options = options || {};
|
var layers = this.findLayers();
|
if (layers.length > 0) {
|
var issue, layer;
|
for (var i=0, len=layers.length; i<len; i++) {
|
layer = layers[i];
|
issue = this.events.triggerEvent("beforegetfeatureinfo", {
|
xy: xy,
|
layer: layer
|
});
|
if (issue !== false) {
|
++this.pending;
|
var requestOptions = this.buildRequestOptions(layer, xy);
|
var request = OpenLayers.Request.GET(requestOptions);
|
if (options.hover === true) {
|
this.hoverRequest = request;
|
}
|
}
|
}
|
if (this.pending > 0) {
|
OpenLayers.Element.addClass(this.map.viewPortDiv, "olCursorWait");
|
}
|
}
|
},
|
|
/**
|
* Method: handleResponse
|
* Handler for the GetFeatureInfo response.
|
*
|
* Parameters:
|
* xy - {<OpenLayers.Pixel>} The position on the map where the mouse event
|
* occurred.
|
* request - {XMLHttpRequest} The request object.
|
* layer - {<OpenLayers.Layer.WMTS>} The queried layer.
|
*/
|
handleResponse: function(xy, request, layer) {
|
--this.pending;
|
if (this.pending <= 0) {
|
OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait");
|
this.pending = 0;
|
}
|
if (request.status && (request.status < 200 || request.status >= 300)) {
|
this.events.triggerEvent("exception", {
|
xy: xy,
|
request: request,
|
layer: layer
|
});
|
} else {
|
var doc = request.responseXML;
|
if (!doc || !doc.documentElement) {
|
doc = request.responseText;
|
}
|
var features, except;
|
try {
|
features = this.format.read(doc);
|
} catch (error) {
|
except = true;
|
this.events.triggerEvent("exception", {
|
xy: xy,
|
request: request,
|
error: error,
|
layer: layer
|
});
|
}
|
if (!except) {
|
this.events.triggerEvent("getfeatureinfo", {
|
text: request.responseText,
|
features: features,
|
request: request,
|
xy: xy,
|
layer: layer
|
});
|
}
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Control.WMTSGetFeatureInfo"
|
});
|
/* ======================================================================
|
OpenLayers/Protocol/CSW/v2_0_2.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Protocol/CSW.js
|
* @requires OpenLayers/Format/CSWGetRecords/v2_0_2.js
|
*/
|
|
/**
|
* Class: OpenLayers.Protocol.CSW.v2_0_2
|
* CS-W (Catalogue services for the Web) version 2.0.2 protocol.
|
*
|
* Inherits from:
|
* - <OpenLayers.Protocol>
|
*/
|
OpenLayers.Protocol.CSW.v2_0_2 = OpenLayers.Class(OpenLayers.Protocol, {
|
|
/**
|
* Property: formatOptions
|
* {Object} Optional options for the format. If a format is not provided,
|
* this property can be used to extend the default format options.
|
*/
|
formatOptions: null,
|
|
/**
|
* Constructor: OpenLayers.Protocol.CSW.v2_0_2
|
* A class for CSW version 2.0.2 protocol management.
|
*
|
* Parameters:
|
* options - {Object} Optional object whose properties will be set on the
|
* instance.
|
*/
|
initialize: function(options) {
|
OpenLayers.Protocol.prototype.initialize.apply(this, [options]);
|
if(!options.format) {
|
this.format = new OpenLayers.Format.CSWGetRecords.v2_0_2(OpenLayers.Util.extend({
|
}, this.formatOptions));
|
}
|
},
|
|
/**
|
* APIMethod: destroy
|
* Clean up the protocol.
|
*/
|
destroy: function() {
|
if(this.options && !this.options.format) {
|
this.format.destroy();
|
}
|
this.format = null;
|
OpenLayers.Protocol.prototype.destroy.apply(this);
|
},
|
|
/**
|
* Method: read
|
* Construct a request for reading new records from the Catalogue.
|
*/
|
read: function(options) {
|
options = OpenLayers.Util.extend({}, options);
|
OpenLayers.Util.applyDefaults(options, this.options || {});
|
var response = new OpenLayers.Protocol.Response({requestType: "read"});
|
|
var data = this.format.write(options.params || options);
|
|
response.priv = OpenLayers.Request.POST({
|
url: options.url,
|
callback: this.createCallback(this.handleRead, response, options),
|
params: options.params,
|
headers: options.headers,
|
data: data
|
});
|
|
return response;
|
},
|
|
/**
|
* Method: handleRead
|
* Deal with response from the read request.
|
*
|
* Parameters:
|
* response - {<OpenLayers.Protocol.Response>} The response object to pass
|
* to the user callback.
|
* This response is given a code property, and optionally a data property.
|
* The latter represents the CSW records as returned by the call to
|
* the CSW format read method.
|
* options - {Object} The user options passed to the read call.
|
*/
|
handleRead: function(response, options) {
|
if(options.callback) {
|
var request = response.priv;
|
if(request.status >= 200 && request.status < 300) {
|
// success
|
response.data = this.parseData(request);
|
response.code = OpenLayers.Protocol.Response.SUCCESS;
|
} else {
|
// failure
|
response.code = OpenLayers.Protocol.Response.FAILURE;
|
}
|
options.callback.call(options.scope, response);
|
}
|
},
|
|
/**
|
* Method: parseData
|
* Read HTTP response body and return records
|
*
|
* Parameters:
|
* request - {XMLHttpRequest} The request object
|
*
|
* Returns:
|
* {Object} The CSW records as returned by the call to the format read method.
|
*/
|
parseData: function(request) {
|
var doc = request.responseXML;
|
if(!doc || !doc.documentElement) {
|
doc = request.responseText;
|
}
|
if(!doc || doc.length <= 0) {
|
return null;
|
}
|
return this.format.read(doc);
|
},
|
|
CLASS_NAME: "OpenLayers.Protocol.CSW.v2_0_2"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/WCSCapabilities/v1_1_0.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/WCSCapabilities/v1.js
|
* @requires OpenLayers/Format/OWSCommon/v1_1_0.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WCSCapabilities/v1_1_0
|
* Read WCS Capabilities version 1.1.0.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.WCSCapabilities.v1>
|
*/
|
OpenLayers.Format.WCSCapabilities.v1_1_0 = OpenLayers.Class(
|
OpenLayers.Format.WCSCapabilities.v1, {
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
wcs: "http://www.opengis.net/wcs/1.1",
|
xlink: "http://www.w3.org/1999/xlink",
|
xsi: "http://www.w3.org/2001/XMLSchema-instance",
|
ows: "http://www.opengis.net/ows/1.1"
|
},
|
|
/**
|
* APIProperty: errorProperty
|
* {String} Which property of the returned object to check for in order to
|
* determine whether or not parsing has failed. In the case that the
|
* errorProperty is undefined on the returned object, the document will be
|
* run through an OGCExceptionReport parser.
|
*/
|
errorProperty: "operationsMetadata",
|
|
/**
|
* Constructor: OpenLayers.Format.WCSCapabilities.v1_1_0
|
* Create a new parser for WCS capabilities version 1.1.0.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"wcs": OpenLayers.Util.applyDefaults({
|
// In 1.0.0, this was WCS_Capabilties, in 1.1.0, it's Capabilities
|
"Capabilities": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"Contents": function(node, request) {
|
request.contentMetadata = [];
|
this.readChildNodes(node, request.contentMetadata);
|
},
|
"CoverageSummary": function(node, contentMetadata) {
|
var coverageSummary = {};
|
// Read the summary:
|
this.readChildNodes(node, coverageSummary);
|
|
// Add it to the contentMetadata array:
|
contentMetadata.push(coverageSummary);
|
},
|
"Identifier": function(node, coverageSummary) {
|
coverageSummary.identifier = this.getChildValue(node);
|
},
|
"Title": function(node, coverageSummary) {
|
coverageSummary.title = this.getChildValue(node);
|
},
|
"Abstract": function(node, coverageSummary) {
|
coverageSummary["abstract"] = this.getChildValue(node);
|
},
|
"SupportedCRS": function(node, coverageSummary) {
|
var crs = this.getChildValue(node);
|
if(crs) {
|
if(!coverageSummary.supportedCRS) {
|
coverageSummary.supportedCRS = [];
|
}
|
coverageSummary.supportedCRS.push(crs);
|
}
|
},
|
"SupportedFormat": function(node, coverageSummary) {
|
var format = this.getChildValue(node);
|
if(format) {
|
if(!coverageSummary.supportedFormat) {
|
coverageSummary.supportedFormat = [];
|
}
|
coverageSummary.supportedFormat.push(format);
|
}
|
}
|
}, OpenLayers.Format.WCSCapabilities.v1.prototype.readers["wcs"]),
|
"ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"]
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WCSCapabilities.v1_1_0"
|
|
});
|
/* ======================================================================
|
OpenLayers/Control/Graticule.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/Lang.js
|
* @requires OpenLayers/Rule.js
|
* @requires OpenLayers/StyleMap.js
|
* @requires OpenLayers/Layer/Vector.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.Graticule
|
* The Graticule displays a grid of latitude/longitude lines reprojected on
|
* the map.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*
|
*/
|
OpenLayers.Control.Graticule = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* APIProperty: autoActivate
|
* {Boolean} Activate the control when it is added to a map. Default is
|
* true.
|
*/
|
autoActivate: true,
|
|
/**
|
* APIProperty: intervals
|
* {Array(Float)} A list of possible graticule widths in degrees.
|
*/
|
intervals: [ 45, 30, 20, 10, 5, 2, 1,
|
0.5, 0.2, 0.1, 0.05, 0.01,
|
0.005, 0.002, 0.001 ],
|
|
/**
|
* APIProperty: displayInLayerSwitcher
|
* {Boolean} Allows the Graticule control to be switched on and off by
|
* LayerSwitcher control. Defaults is true.
|
*/
|
displayInLayerSwitcher: true,
|
|
/**
|
* APIProperty: visible
|
* {Boolean} should the graticule be initially visible (default=true)
|
*/
|
visible: true,
|
|
/**
|
* APIProperty: numPoints
|
* {Integer} The number of points to use in each graticule line. Higher
|
* numbers result in a smoother curve for projected maps
|
*/
|
numPoints: 50,
|
|
/**
|
* APIProperty: targetSize
|
* {Integer} The maximum size of the grid in pixels on the map
|
*/
|
targetSize: 200,
|
|
/**
|
* APIProperty: layerName
|
* {String} The name to be displayed in the layer switcher, default is set
|
* by {<OpenLayers.Lang>}.
|
*/
|
layerName: null,
|
|
/**
|
* APIProperty: labelled
|
* {Boolean} Should the graticule lines be labelled?. default=true
|
*/
|
labelled: true,
|
|
/**
|
* APIProperty: labelFormat
|
* {String} the format of the labels, default = 'dm'. See
|
* <OpenLayers.Util.getFormattedLonLat> for other options.
|
*/
|
labelFormat: 'dm',
|
|
/**
|
* APIProperty: lineSymbolizer
|
* {symbolizer} the symbolizer used to render lines
|
*/
|
lineSymbolizer: {
|
strokeColor: "#333",
|
strokeWidth: 1,
|
strokeOpacity: 0.5
|
},
|
|
/**
|
* APIProperty: labelSymbolizer
|
* {symbolizer} the symbolizer used to render labels
|
*/
|
labelSymbolizer: {},
|
|
/**
|
* Property: gratLayer
|
* {<OpenLayers.Layer.Vector>} vector layer used to draw the graticule on
|
*/
|
gratLayer: null,
|
|
/**
|
* Constructor: OpenLayers.Control.Graticule
|
* Create a new graticule control to display a grid of latitude longitude
|
* lines.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be used
|
* to extend the control.
|
*/
|
initialize: function(options) {
|
options = options || {};
|
options.layerName = options.layerName || OpenLayers.i18n("Graticule");
|
OpenLayers.Control.prototype.initialize.apply(this, [options]);
|
|
this.labelSymbolizer.stroke = false;
|
this.labelSymbolizer.fill = false;
|
this.labelSymbolizer.label = "${label}";
|
this.labelSymbolizer.labelAlign = "${labelAlign}";
|
this.labelSymbolizer.labelXOffset = "${xOffset}";
|
this.labelSymbolizer.labelYOffset = "${yOffset}";
|
},
|
|
/**
|
* APIMethod: destroy
|
*/
|
destroy: function() {
|
this.deactivate();
|
OpenLayers.Control.prototype.destroy.apply(this, arguments);
|
if (this.gratLayer) {
|
this.gratLayer.destroy();
|
this.gratLayer = null;
|
}
|
},
|
|
/**
|
* Method: draw
|
*
|
* initializes the graticule layer and does the initial update
|
*
|
* Returns:
|
* {DOMElement}
|
*/
|
draw: function() {
|
OpenLayers.Control.prototype.draw.apply(this, arguments);
|
if (!this.gratLayer) {
|
var gratStyle = new OpenLayers.Style({},{
|
rules: [new OpenLayers.Rule({'symbolizer':
|
{"Point":this.labelSymbolizer,
|
"Line":this.lineSymbolizer}
|
})]
|
});
|
this.gratLayer = new OpenLayers.Layer.Vector(this.layerName, {
|
styleMap: new OpenLayers.StyleMap({'default':gratStyle}),
|
visibility: this.visible,
|
displayInLayerSwitcher: this.displayInLayerSwitcher
|
});
|
}
|
return this.div;
|
},
|
|
/**
|
* APIMethod: activate
|
*/
|
activate: function() {
|
if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {
|
this.map.addLayer(this.gratLayer);
|
this.map.events.register('moveend', this, this.update);
|
this.update();
|
return true;
|
} else {
|
return false;
|
}
|
},
|
|
/**
|
* APIMethod: deactivate
|
*/
|
deactivate: function() {
|
if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {
|
this.map.events.unregister('moveend', this, this.update);
|
this.map.removeLayer(this.gratLayer);
|
return true;
|
} else {
|
return false;
|
}
|
},
|
/**
|
* Method: update
|
*
|
* calculates the grid to be displayed and actually draws it
|
*
|
* Returns:
|
* {DOMElement}
|
*/
|
update: function() {
|
//wait for the map to be initialized before proceeding
|
var mapBounds = this.map.getExtent();
|
if (!mapBounds) {
|
return;
|
}
|
|
//clear out the old grid
|
this.gratLayer.destroyFeatures();
|
|
//get the projection objects required
|
var llProj = new OpenLayers.Projection("EPSG:4326");
|
var mapProj = this.map.getProjectionObject();
|
var mapRes = this.map.getResolution();
|
|
//if the map is in lon/lat, then the lines are straight and only one
|
//point is required
|
if (mapProj.proj && mapProj.proj.projName == "longlat") {
|
this.numPoints = 1;
|
}
|
|
//get the map center in EPSG:4326
|
var mapCenter = this.map.getCenter(); //lon and lat here are really map x and y
|
var mapCenterLL = new OpenLayers.Pixel(mapCenter.lon, mapCenter.lat);
|
OpenLayers.Projection.transform(mapCenterLL, mapProj, llProj);
|
|
/* This block of code determines the lon/lat interval to use for the
|
* grid by calculating the diagonal size of one grid cell at the map
|
* center. Iterates through the intervals array until the diagonal
|
* length is less than the targetSize option.
|
*/
|
//find lat/lon interval that results in a grid of less than the target size
|
var testSq = this.targetSize*mapRes;
|
testSq *= testSq; //compare squares rather than doing a square root to save time
|
var llInterval;
|
for (var i=0; i<this.intervals.length; ++i) {
|
llInterval = this.intervals[i]; //could do this for both x and y??
|
var delta = llInterval/2;
|
var p1 = mapCenterLL.offset({x: -delta, y: -delta}); //test coords in EPSG:4326 space
|
var p2 = mapCenterLL.offset({x: delta, y: delta});
|
OpenLayers.Projection.transform(p1, llProj, mapProj); // convert them back to map projection
|
OpenLayers.Projection.transform(p2, llProj, mapProj);
|
var distSq = (p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y);
|
if (distSq <= testSq) {
|
break;
|
}
|
}
|
//alert(llInterval);
|
|
//round the LL center to an even number based on the interval
|
mapCenterLL.x = Math.floor(mapCenterLL.x/llInterval)*llInterval;
|
mapCenterLL.y = Math.floor(mapCenterLL.y/llInterval)*llInterval;
|
//TODO adjust for minutses/seconds?
|
|
/* The following 2 blocks calculate the nodes of the grid along a
|
* line of constant longitude (then latitiude) running through the
|
* center of the map until it reaches the map edge. The calculation
|
* goes from the center in both directions to the edge.
|
*/
|
//get the central longitude line, increment the latitude
|
var iter = 0;
|
var centerLonPoints = [mapCenterLL.clone()];
|
var newPoint = mapCenterLL.clone();
|
var mapXY;
|
do {
|
newPoint = newPoint.offset({x: 0, y: llInterval});
|
mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);
|
centerLonPoints.unshift(newPoint);
|
} while (mapBounds.containsPixel(mapXY) && ++iter<1000);
|
newPoint = mapCenterLL.clone();
|
do {
|
newPoint = newPoint.offset({x: 0, y: -llInterval});
|
mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);
|
centerLonPoints.push(newPoint);
|
} while (mapBounds.containsPixel(mapXY) && ++iter<1000);
|
|
//get the central latitude line, increment the longitude
|
iter = 0;
|
var centerLatPoints = [mapCenterLL.clone()];
|
newPoint = mapCenterLL.clone();
|
do {
|
newPoint = newPoint.offset({x: -llInterval, y: 0});
|
mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);
|
centerLatPoints.unshift(newPoint);
|
} while (mapBounds.containsPixel(mapXY) && ++iter<1000);
|
newPoint = mapCenterLL.clone();
|
do {
|
newPoint = newPoint.offset({x: llInterval, y: 0});
|
mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);
|
centerLatPoints.push(newPoint);
|
} while (mapBounds.containsPixel(mapXY) && ++iter<1000);
|
|
//now generate a line for each node in the central lat and lon lines
|
//first loop over constant longitude
|
var lines = [];
|
for(var i=0; i < centerLatPoints.length; ++i) {
|
var lon = centerLatPoints[i].x;
|
var pointList = [];
|
var labelPoint = null;
|
var latEnd = Math.min(centerLonPoints[0].y, 90);
|
var latStart = Math.max(centerLonPoints[centerLonPoints.length - 1].y, -90);
|
var latDelta = (latEnd - latStart)/this.numPoints;
|
var lat = latStart;
|
for(var j=0; j<= this.numPoints; ++j) {
|
var gridPoint = new OpenLayers.Geometry.Point(lon,lat);
|
gridPoint.transform(llProj, mapProj);
|
pointList.push(gridPoint);
|
lat += latDelta;
|
if (gridPoint.y >= mapBounds.bottom && !labelPoint) {
|
labelPoint = gridPoint;
|
}
|
}
|
if (this.labelled) {
|
//keep track of when this grid line crosses the map bounds to set
|
//the label position
|
//labels along the bottom, add 10 pixel offset up into the map
|
//TODO add option for labels on top
|
var labelPos = new OpenLayers.Geometry.Point(labelPoint.x,mapBounds.bottom);
|
var labelAttrs = {
|
value: lon,
|
label: this.labelled?OpenLayers.Util.getFormattedLonLat(lon, "lon", this.labelFormat):"",
|
labelAlign: "cb",
|
xOffset: 0,
|
yOffset: 2
|
};
|
this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(labelPos,labelAttrs));
|
}
|
var geom = new OpenLayers.Geometry.LineString(pointList);
|
lines.push(new OpenLayers.Feature.Vector(geom));
|
}
|
|
//now draw the lines of constant latitude
|
for (var j=0; j < centerLonPoints.length; ++j) {
|
lat = centerLonPoints[j].y;
|
if (lat<-90 || lat>90) { //latitudes only valid between -90 and 90
|
continue;
|
}
|
var pointList = [];
|
var lonStart = centerLatPoints[0].x;
|
var lonEnd = centerLatPoints[centerLatPoints.length - 1].x;
|
var lonDelta = (lonEnd - lonStart)/this.numPoints;
|
var lon = lonStart;
|
var labelPoint = null;
|
for(var i=0; i <= this.numPoints ; ++i) {
|
var gridPoint = new OpenLayers.Geometry.Point(lon,lat);
|
gridPoint.transform(llProj, mapProj);
|
pointList.push(gridPoint);
|
lon += lonDelta;
|
if (gridPoint.x < mapBounds.right) {
|
labelPoint = gridPoint;
|
}
|
}
|
if (this.labelled) {
|
//keep track of when this grid line crosses the map bounds to set
|
//the label position
|
//labels along the right, 30 pixel offset left into the map
|
//TODO add option for labels on left
|
var labelPos = new OpenLayers.Geometry.Point(mapBounds.right, labelPoint.y);
|
var labelAttrs = {
|
value: lat,
|
label: this.labelled?OpenLayers.Util.getFormattedLonLat(lat, "lat", this.labelFormat):"",
|
labelAlign: "rb",
|
xOffset: -2,
|
yOffset: 2
|
};
|
this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(labelPos,labelAttrs));
|
}
|
var geom = new OpenLayers.Geometry.LineString(pointList);
|
lines.push(new OpenLayers.Feature.Vector(geom));
|
}
|
this.gratLayer.addFeatures(lines);
|
},
|
|
CLASS_NAME: "OpenLayers.Control.Graticule"
|
});
|
|
/* ======================================================================
|
Rico/Corner.js
|
====================================================================== */
|
|
/**
|
* @requires OpenLayers/Console.js
|
* @requires Rico/Color.js
|
*/
|
|
|
/*
|
* This file has been edited substantially from the Rico-released
|
* version by the OpenLayers development team.
|
*
|
* Copyright 2005 Sabre Airline Solutions
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the
|
* License. You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the * License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or
|
* implied. See the License for the specific language governing
|
* permissions * and limitations under the License.
|
*
|
*/
|
|
OpenLayers.Console.warn("OpenLayers.Rico is deprecated");
|
|
OpenLayers.Rico = OpenLayers.Rico || {};
|
OpenLayers.Rico.Corner = {
|
|
round: function(e, options) {
|
e = OpenLayers.Util.getElement(e);
|
this._setOptions(options);
|
|
var color = this.options.color;
|
if ( this.options.color == "fromElement" ) {
|
color = this._background(e);
|
}
|
var bgColor = this.options.bgColor;
|
if ( this.options.bgColor == "fromParent" ) {
|
bgColor = this._background(e.offsetParent);
|
}
|
this._roundCornersImpl(e, color, bgColor);
|
},
|
|
/** This is a helper function to change the background
|
* color of <div> that has had Rico rounded corners added.
|
*
|
* It seems we cannot just set the background color for the
|
* outer <div> so each <span> element used to create the
|
* corners must have its background color set individually.
|
*
|
* @param {DOM} theDiv - A child of the outer <div> that was
|
* supplied to the `round` method.
|
*
|
* @param {String} newColor - The new background color to use.
|
*/
|
changeColor: function(theDiv, newColor) {
|
|
theDiv.style.backgroundColor = newColor;
|
|
var spanElements = theDiv.parentNode.getElementsByTagName("span");
|
|
for (var currIdx = 0; currIdx < spanElements.length; currIdx++) {
|
spanElements[currIdx].style.backgroundColor = newColor;
|
}
|
},
|
|
|
/** This is a helper function to change the background
|
* opacity of <div> that has had Rico rounded corners added.
|
*
|
* See changeColor (above) for algorithm explanation
|
*
|
* @param {DOM} theDiv A child of the outer <div> that was
|
* supplied to the `round` method.
|
*
|
* @param {int} newOpacity The new opacity to use (0-1).
|
*/
|
changeOpacity: function(theDiv, newOpacity) {
|
|
var mozillaOpacity = newOpacity;
|
var ieOpacity = 'alpha(opacity=' + newOpacity * 100 + ')';
|
|
theDiv.style.opacity = mozillaOpacity;
|
theDiv.style.filter = ieOpacity;
|
|
var spanElements = theDiv.parentNode.getElementsByTagName("span");
|
|
for (var currIdx = 0; currIdx < spanElements.length; currIdx++) {
|
spanElements[currIdx].style.opacity = mozillaOpacity;
|
spanElements[currIdx].style.filter = ieOpacity;
|
}
|
|
},
|
|
/** this function takes care of redoing the rico cornering
|
*
|
* you can't just call updateRicoCorners() again and pass it a
|
* new options string. you have to first remove the divs that
|
* rico puts on top and below the content div.
|
*
|
* @param {DOM} theDiv - A child of the outer <div> that was
|
* supplied to the `round` method.
|
*
|
* @param {Object} options - list of options
|
*/
|
reRound: function(theDiv, options) {
|
|
var topRico = theDiv.parentNode.childNodes[0];
|
//theDiv would be theDiv.parentNode.childNodes[1]
|
var bottomRico = theDiv.parentNode.childNodes[2];
|
|
theDiv.parentNode.removeChild(topRico);
|
theDiv.parentNode.removeChild(bottomRico);
|
|
this.round(theDiv.parentNode, options);
|
},
|
|
_roundCornersImpl: function(e, color, bgColor) {
|
if(this.options.border) {
|
this._renderBorder(e,bgColor);
|
}
|
if(this._isTopRounded()) {
|
this._roundTopCorners(e,color,bgColor);
|
}
|
if(this._isBottomRounded()) {
|
this._roundBottomCorners(e,color,bgColor);
|
}
|
},
|
|
_renderBorder: function(el,bgColor) {
|
var borderValue = "1px solid " + this._borderColor(bgColor);
|
var borderL = "border-left: " + borderValue;
|
var borderR = "border-right: " + borderValue;
|
var style = "style='" + borderL + ";" + borderR + "'";
|
el.innerHTML = "<div " + style + ">" + el.innerHTML + "</div>";
|
},
|
|
_roundTopCorners: function(el, color, bgColor) {
|
var corner = this._createCorner(bgColor);
|
for(var i=0 ; i < this.options.numSlices ; i++ ) {
|
corner.appendChild(this._createCornerSlice(color,bgColor,i,"top"));
|
}
|
el.style.paddingTop = 0;
|
el.insertBefore(corner,el.firstChild);
|
},
|
|
_roundBottomCorners: function(el, color, bgColor) {
|
var corner = this._createCorner(bgColor);
|
for(var i=(this.options.numSlices-1) ; i >= 0 ; i-- ) {
|
corner.appendChild(this._createCornerSlice(color,bgColor,i,"bottom"));
|
}
|
el.style.paddingBottom = 0;
|
el.appendChild(corner);
|
},
|
|
_createCorner: function(bgColor) {
|
var corner = document.createElement("div");
|
corner.style.backgroundColor = (this._isTransparent() ? "transparent" : bgColor);
|
return corner;
|
},
|
|
_createCornerSlice: function(color,bgColor, n, position) {
|
var slice = document.createElement("span");
|
|
var inStyle = slice.style;
|
inStyle.backgroundColor = color;
|
inStyle.display = "block";
|
inStyle.height = "1px";
|
inStyle.overflow = "hidden";
|
inStyle.fontSize = "1px";
|
|
var borderColor = this._borderColor(color,bgColor);
|
if ( this.options.border && n == 0 ) {
|
inStyle.borderTopStyle = "solid";
|
inStyle.borderTopWidth = "1px";
|
inStyle.borderLeftWidth = "0px";
|
inStyle.borderRightWidth = "0px";
|
inStyle.borderBottomWidth = "0px";
|
inStyle.height = "0px"; // assumes css compliant box model
|
inStyle.borderColor = borderColor;
|
}
|
else if(borderColor) {
|
inStyle.borderColor = borderColor;
|
inStyle.borderStyle = "solid";
|
inStyle.borderWidth = "0px 1px";
|
}
|
|
if ( !this.options.compact && (n == (this.options.numSlices-1)) ) {
|
inStyle.height = "2px";
|
}
|
this._setMargin(slice, n, position);
|
this._setBorder(slice, n, position);
|
return slice;
|
},
|
|
_setOptions: function(options) {
|
this.options = {
|
corners : "all",
|
color : "fromElement",
|
bgColor : "fromParent",
|
blend : true,
|
border : false,
|
compact : false
|
};
|
OpenLayers.Util.extend(this.options, options || {});
|
|
this.options.numSlices = this.options.compact ? 2 : 4;
|
if ( this._isTransparent() ) {
|
this.options.blend = false;
|
}
|
},
|
|
_whichSideTop: function() {
|
if ( this._hasString(this.options.corners, "all", "top") ) {
|
return "";
|
}
|
if ( this.options.corners.indexOf("tl") >= 0 && this.options.corners.indexOf("tr") >= 0 ) {
|
return "";
|
}
|
if (this.options.corners.indexOf("tl") >= 0) {
|
return "left";
|
} else if (this.options.corners.indexOf("tr") >= 0) {
|
return "right";
|
}
|
return "";
|
},
|
|
_whichSideBottom: function() {
|
if ( this._hasString(this.options.corners, "all", "bottom") ) {
|
return "";
|
}
|
if ( this.options.corners.indexOf("bl")>=0 && this.options.corners.indexOf("br")>=0 ) {
|
return "";
|
}
|
|
if(this.options.corners.indexOf("bl") >=0) {
|
return "left";
|
} else if(this.options.corners.indexOf("br")>=0) {
|
return "right";
|
}
|
return "";
|
},
|
|
_borderColor : function(color,bgColor) {
|
if ( color == "transparent" ) {
|
return bgColor;
|
} else if ( this.options.border ) {
|
return this.options.border;
|
} else if ( this.options.blend ) {
|
return this._blend( bgColor, color );
|
} else {
|
return "";
|
}
|
},
|
|
|
_setMargin: function(el, n, corners) {
|
var marginSize = this._marginSize(n);
|
var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
|
|
if ( whichSide == "left" ) {
|
el.style.marginLeft = marginSize + "px"; el.style.marginRight = "0px";
|
}
|
else if ( whichSide == "right" ) {
|
el.style.marginRight = marginSize + "px"; el.style.marginLeft = "0px";
|
}
|
else {
|
el.style.marginLeft = marginSize + "px"; el.style.marginRight = marginSize + "px";
|
}
|
},
|
|
_setBorder: function(el,n,corners) {
|
var borderSize = this._borderSize(n);
|
var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
|
if ( whichSide == "left" ) {
|
el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = "0px";
|
}
|
else if ( whichSide == "right" ) {
|
el.style.borderRightWidth = borderSize + "px"; el.style.borderLeftWidth = "0px";
|
}
|
else {
|
el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
|
}
|
if (this.options.border != false) {
|
el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
|
}
|
},
|
|
_marginSize: function(n) {
|
if ( this._isTransparent() ) {
|
return 0;
|
}
|
var marginSizes = [ 5, 3, 2, 1 ];
|
var blendedMarginSizes = [ 3, 2, 1, 0 ];
|
var compactMarginSizes = [ 2, 1 ];
|
var smBlendedMarginSizes = [ 1, 0 ];
|
|
if ( this.options.compact && this.options.blend ) {
|
return smBlendedMarginSizes[n];
|
} else if ( this.options.compact ) {
|
return compactMarginSizes[n];
|
} else if ( this.options.blend ) {
|
return blendedMarginSizes[n];
|
} else {
|
return marginSizes[n];
|
}
|
},
|
|
_borderSize: function(n) {
|
var transparentBorderSizes = [ 5, 3, 2, 1 ];
|
var blendedBorderSizes = [ 2, 1, 1, 1 ];
|
var compactBorderSizes = [ 1, 0 ];
|
var actualBorderSizes = [ 0, 2, 0, 0 ];
|
|
if ( this.options.compact && (this.options.blend || this._isTransparent()) ) {
|
return 1;
|
} else if ( this.options.compact ) {
|
return compactBorderSizes[n];
|
} else if ( this.options.blend ) {
|
return blendedBorderSizes[n];
|
} else if ( this.options.border ) {
|
return actualBorderSizes[n];
|
} else if ( this._isTransparent() ) {
|
return transparentBorderSizes[n];
|
}
|
return 0;
|
},
|
|
_hasString: function(str) { for(var i=1 ; i<arguments.length ; i++) if (str.indexOf(arguments[i]) >= 0) { return true; } return false; },
|
_blend: function(c1, c2) { var cc1 = OpenLayers.Rico.Color.createFromHex(c1); cc1.blend(OpenLayers.Rico.Color.createFromHex(c2)); return cc1; },
|
_background: function(el) { try { return OpenLayers.Rico.Color.createColorFromBackground(el).asHex(); } catch(err) { return "#ffffff"; } },
|
_isTransparent: function() { return this.options.color == "transparent"; },
|
_isTopRounded: function() { return this._hasString(this.options.corners, "all", "top", "tl", "tr"); },
|
_isBottomRounded: function() { return this._hasString(this.options.corners, "all", "bottom", "bl", "br"); },
|
_hasSingleTextChild: function(el) { return el.childNodes.length == 1 && el.childNodes[0].nodeType == 3; }
|
};
|
/* ======================================================================
|
OpenLayers/Control/NavigationHistory.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/Control/Button.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.NavigationHistory
|
* A navigation history control. This is a meta-control, that creates two
|
* dependent controls: <previous> and <next>. Call the trigger method
|
* on the <previous> and <next> controls to restore previous and next
|
* history states. The previous and next controls will become active
|
* when there are available states to restore and will become deactive
|
* when there are no states to restore.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.NavigationHistory = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* Property: type
|
* {String} Note that this control is not intended to be added directly
|
* to a control panel. Instead, add the sub-controls previous and
|
* next. These sub-controls are button type controls that activate
|
* and deactivate themselves. If this parent control is added to
|
* a panel, it will act as a toggle.
|
*/
|
type: OpenLayers.Control.TYPE_TOGGLE,
|
|
/**
|
* APIProperty: previous
|
* {<OpenLayers.Control>} A button type control whose trigger method restores
|
* the previous state managed by this control.
|
*/
|
previous: null,
|
|
/**
|
* APIProperty: previousOptions
|
* {Object} Set this property on the options argument of the constructor
|
* to set optional properties on the <previous> control.
|
*/
|
previousOptions: null,
|
|
/**
|
* APIProperty: next
|
* {<OpenLayers.Control>} A button type control whose trigger method restores
|
* the next state managed by this control.
|
*/
|
next: null,
|
|
/**
|
* APIProperty: nextOptions
|
* {Object} Set this property on the options argument of the constructor
|
* to set optional properties on the <next> control.
|
*/
|
nextOptions: null,
|
|
/**
|
* APIProperty: limit
|
* {Integer} Optional limit on the number of history items to retain. If
|
* null, there is no limit. Default is 50.
|
*/
|
limit: 50,
|
|
/**
|
* APIProperty: autoActivate
|
* {Boolean} Activate the control when it is added to a map. Default is
|
* true.
|
*/
|
autoActivate: true,
|
|
/**
|
* Property: clearOnDeactivate
|
* {Boolean} Clear the history when the control is deactivated. Default
|
* is false.
|
*/
|
clearOnDeactivate: false,
|
|
/**
|
* Property: registry
|
* {Object} An object with keys corresponding to event types. Values
|
* are functions that return an object representing the current state.
|
*/
|
registry: null,
|
|
/**
|
* Property: nextStack
|
* {Array} Array of items in the history.
|
*/
|
nextStack: null,
|
|
/**
|
* Property: previousStack
|
* {Array} List of items in the history. First item represents the current
|
* state.
|
*/
|
previousStack: null,
|
|
/**
|
* Property: listeners
|
* {Object} An object containing properties corresponding to event types.
|
* This object is used to configure the control and is modified on
|
* construction.
|
*/
|
listeners: null,
|
|
/**
|
* Property: restoring
|
* {Boolean} Currently restoring a history state. This is set to true
|
* before calling restore and set to false after restore returns.
|
*/
|
restoring: false,
|
|
/**
|
* Constructor: OpenLayers.Control.NavigationHistory
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be used
|
* to extend the control.
|
*/
|
initialize: function(options) {
|
OpenLayers.Control.prototype.initialize.apply(this, [options]);
|
|
this.registry = OpenLayers.Util.extend({
|
"moveend": this.getState
|
}, this.registry);
|
|
var previousOptions = {
|
trigger: OpenLayers.Function.bind(this.previousTrigger, this),
|
displayClass: this.displayClass + " " + this.displayClass + "Previous"
|
};
|
OpenLayers.Util.extend(previousOptions, this.previousOptions);
|
this.previous = new OpenLayers.Control.Button(previousOptions);
|
|
var nextOptions = {
|
trigger: OpenLayers.Function.bind(this.nextTrigger, this),
|
displayClass: this.displayClass + " " + this.displayClass + "Next"
|
};
|
OpenLayers.Util.extend(nextOptions, this.nextOptions);
|
this.next = new OpenLayers.Control.Button(nextOptions);
|
|
this.clear();
|
},
|
|
/**
|
* Method: onPreviousChange
|
* Called when the previous history stack changes.
|
*
|
* Parameters:
|
* state - {Object} An object representing the state to be restored
|
* if previous is triggered again or null if no previous states remain.
|
* length - {Integer} The number of remaining previous states that can
|
* be restored.
|
*/
|
onPreviousChange: function(state, length) {
|
if(state && !this.previous.active) {
|
this.previous.activate();
|
} else if(!state && this.previous.active) {
|
this.previous.deactivate();
|
}
|
},
|
|
/**
|
* Method: onNextChange
|
* Called when the next history stack changes.
|
*
|
* Parameters:
|
* state - {Object} An object representing the state to be restored
|
* if next is triggered again or null if no next states remain.
|
* length - {Integer} The number of remaining next states that can
|
* be restored.
|
*/
|
onNextChange: function(state, length) {
|
if(state && !this.next.active) {
|
this.next.activate();
|
} else if(!state && this.next.active) {
|
this.next.deactivate();
|
}
|
},
|
|
/**
|
* APIMethod: destroy
|
* Destroy the control.
|
*/
|
destroy: function() {
|
OpenLayers.Control.prototype.destroy.apply(this);
|
this.previous.destroy();
|
this.next.destroy();
|
this.deactivate();
|
for(var prop in this) {
|
this[prop] = null;
|
}
|
},
|
|
/**
|
* Method: setMap
|
* Set the map property for the control and <previous> and <next> child
|
* controls.
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
setMap: function(map) {
|
this.map = map;
|
this.next.setMap(map);
|
this.previous.setMap(map);
|
},
|
|
/**
|
* Method: draw
|
* Called when the control is added to the map.
|
*/
|
draw: function() {
|
OpenLayers.Control.prototype.draw.apply(this, arguments);
|
this.next.draw();
|
this.previous.draw();
|
},
|
|
/**
|
* Method: previousTrigger
|
* Restore the previous state. If no items are in the previous history
|
* stack, this has no effect.
|
*
|
* Returns:
|
* {Object} Item representing state that was restored. Undefined if no
|
* items are in the previous history stack.
|
*/
|
previousTrigger: function() {
|
var current = this.previousStack.shift();
|
var state = this.previousStack.shift();
|
if(state != undefined) {
|
this.nextStack.unshift(current);
|
this.previousStack.unshift(state);
|
this.restoring = true;
|
this.restore(state);
|
this.restoring = false;
|
this.onNextChange(this.nextStack[0], this.nextStack.length);
|
this.onPreviousChange(
|
this.previousStack[1], this.previousStack.length - 1
|
);
|
} else {
|
this.previousStack.unshift(current);
|
}
|
return state;
|
},
|
|
/**
|
* APIMethod: nextTrigger
|
* Restore the next state. If no items are in the next history
|
* stack, this has no effect. The next history stack is populated
|
* as states are restored from the previous history stack.
|
*
|
* Returns:
|
* {Object} Item representing state that was restored. Undefined if no
|
* items are in the next history stack.
|
*/
|
nextTrigger: function() {
|
var state = this.nextStack.shift();
|
if(state != undefined) {
|
this.previousStack.unshift(state);
|
this.restoring = true;
|
this.restore(state);
|
this.restoring = false;
|
this.onNextChange(this.nextStack[0], this.nextStack.length);
|
this.onPreviousChange(
|
this.previousStack[1], this.previousStack.length - 1
|
);
|
}
|
return state;
|
},
|
|
/**
|
* APIMethod: clear
|
* Clear history.
|
*/
|
clear: function() {
|
this.previousStack = [];
|
this.previous.deactivate();
|
this.nextStack = [];
|
this.next.deactivate();
|
},
|
|
/**
|
* Method: getState
|
* Get the current state and return it.
|
*
|
* Returns:
|
* {Object} An object representing the current state.
|
*/
|
getState: function() {
|
return {
|
center: this.map.getCenter(),
|
resolution: this.map.getResolution(),
|
projection: this.map.getProjectionObject(),
|
units: this.map.getProjectionObject().getUnits() ||
|
this.map.units || this.map.baseLayer.units
|
};
|
},
|
|
/**
|
* Method: restore
|
* Update the state with the given object.
|
*
|
* Parameters:
|
* state - {Object} An object representing the state to restore.
|
*/
|
restore: function(state) {
|
var center, zoom;
|
if (this.map.getProjectionObject() == state.projection) {
|
zoom = this.map.getZoomForResolution(state.resolution);
|
center = state.center;
|
} else {
|
center = state.center.clone();
|
center.transform(state.projection, this.map.getProjectionObject());
|
var sourceUnits = state.units;
|
var targetUnits = this.map.getProjectionObject().getUnits() ||
|
this.map.units || this.map.baseLayer.units;
|
var resolutionFactor = sourceUnits && targetUnits ?
|
OpenLayers.INCHES_PER_UNIT[sourceUnits] / OpenLayers.INCHES_PER_UNIT[targetUnits] : 1;
|
zoom = this.map.getZoomForResolution(resolutionFactor*state.resolution);
|
}
|
this.map.setCenter(center, zoom);
|
},
|
|
/**
|
* Method: setListeners
|
* Sets functions to be registered in the listeners object.
|
*/
|
setListeners: function() {
|
this.listeners = {};
|
for(var type in this.registry) {
|
this.listeners[type] = OpenLayers.Function.bind(function() {
|
if(!this.restoring) {
|
var state = this.registry[type].apply(this, arguments);
|
this.previousStack.unshift(state);
|
if(this.previousStack.length > 1) {
|
this.onPreviousChange(
|
this.previousStack[1], this.previousStack.length - 1
|
);
|
}
|
if(this.previousStack.length > (this.limit + 1)) {
|
this.previousStack.pop();
|
}
|
if(this.nextStack.length > 0) {
|
this.nextStack = [];
|
this.onNextChange(null, 0);
|
}
|
}
|
return true;
|
}, this);
|
}
|
},
|
|
/**
|
* APIMethod: activate
|
* Activate the control. This registers any listeners.
|
*
|
* Returns:
|
* {Boolean} Control successfully activated.
|
*/
|
activate: function() {
|
var activated = false;
|
if(this.map) {
|
if(OpenLayers.Control.prototype.activate.apply(this)) {
|
if(this.listeners == null) {
|
this.setListeners();
|
}
|
for(var type in this.listeners) {
|
this.map.events.register(type, this, this.listeners[type]);
|
}
|
activated = true;
|
if(this.previousStack.length == 0) {
|
this.initStack();
|
}
|
}
|
}
|
return activated;
|
},
|
|
/**
|
* Method: initStack
|
* Called after the control is activated if the previous history stack is
|
* empty.
|
*/
|
initStack: function() {
|
if(this.map.getCenter()) {
|
this.listeners.moveend();
|
}
|
},
|
|
/**
|
* APIMethod: deactivate
|
* Deactivate the control. This unregisters any listeners.
|
*
|
* Returns:
|
* {Boolean} Control successfully deactivated.
|
*/
|
deactivate: function() {
|
var deactivated = false;
|
if(this.map) {
|
if(OpenLayers.Control.prototype.deactivate.apply(this)) {
|
for(var type in this.listeners) {
|
this.map.events.unregister(
|
type, this, this.listeners[type]
|
);
|
}
|
if(this.clearOnDeactivate) {
|
this.clear();
|
}
|
deactivated = true;
|
}
|
}
|
return deactivated;
|
},
|
|
CLASS_NAME: "OpenLayers.Control.NavigationHistory"
|
});
|
|
/* ======================================================================
|
OpenLayers/Layer/UTFGrid.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Layer/XYZ.js
|
* @requires OpenLayers/Tile/UTFGrid.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.UTFGrid
|
* This Layer reads from UTFGrid tiled data sources. Since UTFGrids are
|
* essentially JSON-based ASCII art with attached attributes, they are not
|
* visibly rendered. In order to use them in the map, you must add a
|
* <OpenLayers.Control.UTFGrid> control as well.
|
*
|
* Example:
|
*
|
* (start code)
|
* var world_utfgrid = new OpenLayers.Layer.UTFGrid({
|
* url: "/tiles/world_utfgrid/${z}/${x}/${y}.json",
|
* utfgridResolution: 4,
|
* displayInLayerSwitcher: false
|
* );
|
* map.addLayer(world_utfgrid);
|
*
|
* var control = new OpenLayers.Control.UTFGrid({
|
* layers: [world_utfgrid],
|
* handlerMode: 'move',
|
* callback: function(dataLookup) {
|
* // do something with returned data
|
* }
|
* })
|
* (end code)
|
*
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer.XYZ>
|
*/
|
OpenLayers.Layer.UTFGrid = OpenLayers.Class(OpenLayers.Layer.XYZ, {
|
|
/**
|
* APIProperty: isBaseLayer
|
* Default is false, as UTFGrids are designed to be a transparent overlay layer.
|
*/
|
isBaseLayer: false,
|
|
/**
|
* APIProperty: projection
|
* {<OpenLayers.Projection>}
|
* Source projection for the UTFGrids. Default is "EPSG:900913".
|
*/
|
projection: new OpenLayers.Projection("EPSG:900913"),
|
|
/**
|
* Property: useJSONP
|
* {Boolean}
|
* Should we use a JSONP script approach instead of a standard AJAX call?
|
*
|
* Set to true for using utfgrids from another server.
|
* Avoids same-domain policy restrictions.
|
* Note that this only works if the server accepts
|
* the callback GET parameter and dynamically
|
* wraps the returned json in a function call.
|
*
|
* Default is false
|
*/
|
useJSONP: false,
|
|
/**
|
* APIProperty: url
|
* {String}
|
* URL tempate for UTFGrid tiles. Include x, y, and z parameters.
|
* E.g. "/tiles/${z}/${x}/${y}.json"
|
*/
|
|
/**
|
* APIProperty: utfgridResolution
|
* {Number}
|
* Ratio of the pixel width to the width of a UTFGrid data point. If an
|
* entry in the grid represents a 4x4 block of pixels, the
|
* utfgridResolution would be 4. Default is 2 (specified in
|
* <OpenLayers.Tile.UTFGrid>).
|
*/
|
|
/**
|
* Property: tileClass
|
* {<OpenLayers.Tile>} The tile class to use for this layer.
|
* Defaults is <OpenLayers.Tile.UTFGrid>.
|
*/
|
tileClass: OpenLayers.Tile.UTFGrid,
|
|
/**
|
* Constructor: OpenLayers.Layer.UTFGrid
|
* Create a new UTFGrid layer.
|
*
|
* Parameters:
|
* config - {Object} Configuration properties for the layer.
|
*
|
* Required configuration properties:
|
* url - {String} The url template for UTFGrid tiles. See the <url> property.
|
*/
|
initialize: function(options) {
|
OpenLayers.Layer.Grid.prototype.initialize.apply(
|
this, [options.name, options.url, {}, options]
|
);
|
this.tileOptions = OpenLayers.Util.extend({
|
utfgridResolution: this.utfgridResolution
|
}, this.tileOptions);
|
},
|
|
/**
|
* Method: createBackBuffer
|
* The UTFGrid cannot create a back buffer, so this method is overriden.
|
*/
|
createBackBuffer: function() {},
|
|
/**
|
* APIMethod: clone
|
* Create a clone of this layer
|
*
|
* Parameters:
|
* obj - {Object} Only used by a subclass of this layer.
|
*
|
* Returns:
|
* {<OpenLayers.Layer.UTFGrid>} An exact clone of this OpenLayers.Layer.UTFGrid
|
*/
|
clone: function (obj) {
|
if (obj == null) {
|
obj = new OpenLayers.Layer.UTFGrid(this.getOptions());
|
}
|
|
// get all additions from superclasses
|
obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
|
|
return obj;
|
},
|
|
/**
|
* APIProperty: getFeatureInfo
|
* Get details about a feature associated with a map location. The object
|
* returned will have id and data properties. If the given location
|
* doesn't correspond to a feature, null will be returned.
|
*
|
* Parameters:
|
* location - {<OpenLayers.LonLat>} map location
|
*
|
* Returns:
|
* {Object} Object representing the feature id and UTFGrid data
|
* corresponding to the given map location. Returns null if the given
|
* location doesn't hit a feature.
|
*/
|
getFeatureInfo: function(location) {
|
var info = null;
|
var tileInfo = this.getTileData(location);
|
if (tileInfo && tileInfo.tile) {
|
info = tileInfo.tile.getFeatureInfo(tileInfo.i, tileInfo.j);
|
}
|
return info;
|
},
|
|
/**
|
* APIMethod: getFeatureId
|
* Get the identifier for the feature associated with a map location.
|
*
|
* Parameters:
|
* location - {<OpenLayers.LonLat>} map location
|
*
|
* Returns:
|
* {String} The feature identifier corresponding to the given map location.
|
* Returns null if the location doesn't hit a feature.
|
*/
|
getFeatureId: function(location) {
|
var id = null;
|
var info = this.getTileData(location);
|
if (info.tile) {
|
id = info.tile.getFeatureId(info.i, info.j);
|
}
|
return id;
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.UTFGrid"
|
});
|
/* ======================================================================
|
OpenLayers/TileManager.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Util.js
|
* @requires OpenLayers/BaseTypes.js
|
* @requires OpenLayers/BaseTypes/Element.js
|
* @requires OpenLayers/Layer/Grid.js
|
* @requires OpenLayers/Tile/Image.js
|
*/
|
|
/**
|
* Class: OpenLayers.TileManager
|
* Provides queueing of image requests and caching of image elements.
|
*
|
* Queueing avoids unnecessary image requests while changing zoom levels
|
* quickly, and helps improve dragging performance on mobile devices that show
|
* a lag in dragging when loading of new images starts. <zoomDelay> and
|
* <moveDelay> are the configuration options to control this behavior.
|
*
|
* Caching avoids setting the src on image elements for images that have already
|
* been used. Several maps can share a TileManager instance, in which case each
|
* map gets its own tile queue, but all maps share the same tile cache.
|
*/
|
OpenLayers.TileManager = OpenLayers.Class({
|
|
/**
|
* APIProperty: cacheSize
|
* {Number} Number of image elements to keep referenced in this instance's
|
* cache for fast reuse. Default is 256.
|
*/
|
cacheSize: 256,
|
|
/**
|
* APIProperty: tilesPerFrame
|
* {Number} Number of queued tiles to load per frame (see <frameDelay>).
|
* Default is 2.
|
*/
|
tilesPerFrame: 2,
|
|
/**
|
* APIProperty: frameDelay
|
* {Number} Delay between tile loading frames (see <tilesPerFrame>) in
|
* milliseconds. Default is 16.
|
*/
|
frameDelay: 16,
|
|
/**
|
* APIProperty: moveDelay
|
* {Number} Delay in milliseconds after a map's move event before loading
|
* tiles. Default is 100.
|
*/
|
moveDelay: 100,
|
|
/**
|
* APIProperty: zoomDelay
|
* {Number} Delay in milliseconds after a map's zoomend event before loading
|
* tiles. Default is 200.
|
*/
|
zoomDelay: 200,
|
|
/**
|
* Property: maps
|
* {Array(<OpenLayers.Map>)} The maps to manage tiles on.
|
*/
|
maps: null,
|
|
/**
|
* Property: tileQueueId
|
* {Object} The ids of the <drawTilesFromQueue> loop, keyed by map id.
|
*/
|
tileQueueId: null,
|
|
/**
|
* Property: tileQueue
|
* {Object(Array(<OpenLayers.Tile>))} Tiles queued for drawing, keyed by
|
* map id.
|
*/
|
tileQueue: null,
|
|
/**
|
* Property: tileCache
|
* {Object} Cached image elements, keyed by URL.
|
*/
|
tileCache: null,
|
|
/**
|
* Property: tileCacheIndex
|
* {Array(String)} URLs of cached tiles. First entry is the least recently
|
* used.
|
*/
|
tileCacheIndex: null,
|
|
/**
|
* Constructor: OpenLayers.TileManager
|
* Constructor for a new <OpenLayers.TileManager> instance.
|
*
|
* Parameters:
|
* options - {Object} Configuration for this instance.
|
*/
|
initialize: function(options) {
|
OpenLayers.Util.extend(this, options);
|
this.maps = [];
|
this.tileQueueId = {};
|
this.tileQueue = {};
|
this.tileCache = {};
|
this.tileCacheIndex = [];
|
},
|
|
/**
|
* Method: addMap
|
* Binds this instance to a map
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
addMap: function(map) {
|
if (this._destroyed || !OpenLayers.Layer.Grid) {
|
return;
|
}
|
this.maps.push(map);
|
this.tileQueue[map.id] = [];
|
for (var i=0, ii=map.layers.length; i<ii; ++i) {
|
this.addLayer({layer: map.layers[i]});
|
}
|
map.events.on({
|
move: this.move,
|
zoomend: this.zoomEnd,
|
changelayer: this.changeLayer,
|
addlayer: this.addLayer,
|
preremovelayer: this.removeLayer,
|
scope: this
|
});
|
},
|
|
/**
|
* Method: removeMap
|
* Unbinds this instance from a map
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>}
|
*/
|
removeMap: function(map) {
|
if (this._destroyed || !OpenLayers.Layer.Grid) {
|
return;
|
}
|
window.clearTimeout(this.tileQueueId[map.id]);
|
if (map.layers) {
|
for (var i=0, ii=map.layers.length; i<ii; ++i) {
|
this.removeLayer({layer: map.layers[i]});
|
}
|
}
|
if (map.events) {
|
map.events.un({
|
move: this.move,
|
zoomend: this.zoomEnd,
|
changelayer: this.changeLayer,
|
addlayer: this.addLayer,
|
preremovelayer: this.removeLayer,
|
scope: this
|
});
|
}
|
delete this.tileQueue[map.id];
|
delete this.tileQueueId[map.id];
|
OpenLayers.Util.removeItem(this.maps, map);
|
},
|
|
/**
|
* Method: move
|
* Handles the map's move event
|
*
|
* Parameters:
|
* evt - {Object} Listener argument
|
*/
|
move: function(evt) {
|
this.updateTimeout(evt.object, this.moveDelay, true);
|
},
|
|
/**
|
* Method: zoomEnd
|
* Handles the map's zoomEnd event
|
*
|
* Parameters:
|
* evt - {Object} Listener argument
|
*/
|
zoomEnd: function(evt) {
|
this.updateTimeout(evt.object, this.zoomDelay);
|
},
|
|
/**
|
* Method: changeLayer
|
* Handles the map's changeLayer event
|
*
|
* Parameters:
|
* evt - {Object} Listener argument
|
*/
|
changeLayer: function(evt) {
|
if (evt.property === 'visibility' || evt.property === 'params') {
|
this.updateTimeout(evt.object, 0);
|
}
|
},
|
|
/**
|
* Method: addLayer
|
* Handles the map's addlayer event
|
*
|
* Parameters:
|
* evt - {Object} The listener argument
|
*/
|
addLayer: function(evt) {
|
var layer = evt.layer;
|
if (layer instanceof OpenLayers.Layer.Grid) {
|
layer.events.on({
|
addtile: this.addTile,
|
retile: this.clearTileQueue,
|
scope: this
|
});
|
var i, j, tile;
|
for (i=layer.grid.length-1; i>=0; --i) {
|
for (j=layer.grid[i].length-1; j>=0; --j) {
|
tile = layer.grid[i][j];
|
this.addTile({tile: tile});
|
if (tile.url && !tile.imgDiv) {
|
this.manageTileCache({object: tile});
|
}
|
}
|
}
|
}
|
},
|
|
/**
|
* Method: removeLayer
|
* Handles the map's preremovelayer event
|
*
|
* Parameters:
|
* evt - {Object} The listener argument
|
*/
|
removeLayer: function(evt) {
|
var layer = evt.layer;
|
if (layer instanceof OpenLayers.Layer.Grid) {
|
this.clearTileQueue({object: layer});
|
if (layer.events) {
|
layer.events.un({
|
addtile: this.addTile,
|
retile: this.clearTileQueue,
|
scope: this
|
});
|
}
|
if (layer.grid) {
|
var i, j, tile;
|
for (i=layer.grid.length-1; i>=0; --i) {
|
for (j=layer.grid[i].length-1; j>=0; --j) {
|
tile = layer.grid[i][j];
|
this.unloadTile({object: tile});
|
}
|
}
|
}
|
}
|
},
|
|
/**
|
* Method: updateTimeout
|
* Applies the <moveDelay> or <zoomDelay> to the <drawTilesFromQueue> loop,
|
* and schedules more queue processing after <frameDelay> if there are still
|
* tiles in the queue.
|
*
|
* Parameters:
|
* map - {<OpenLayers.Map>} The map to update the timeout for
|
* delay - {Number} The delay to apply
|
* nice - {Boolean} If true, the timeout function will only be created if
|
* the tilequeue is not empty. This is used by the move handler to
|
* avoid impacts on dragging performance. For other events, the tile
|
* queue may not be populated yet, so we need to set the timer
|
* regardless of the queue size.
|
*/
|
updateTimeout: function(map, delay, nice) {
|
window.clearTimeout(this.tileQueueId[map.id]);
|
var tileQueue = this.tileQueue[map.id];
|
if (!nice || tileQueue.length) {
|
this.tileQueueId[map.id] = window.setTimeout(
|
OpenLayers.Function.bind(function() {
|
this.drawTilesFromQueue(map);
|
if (tileQueue.length) {
|
this.updateTimeout(map, this.frameDelay);
|
}
|
}, this), delay
|
);
|
}
|
},
|
|
/**
|
* Method: addTile
|
* Listener for the layer's addtile event
|
*
|
* Parameters:
|
* evt - {Object} The listener argument
|
*/
|
addTile: function(evt) {
|
if (evt.tile instanceof OpenLayers.Tile.Image) {
|
evt.tile.events.on({
|
beforedraw: this.queueTileDraw,
|
beforeload: this.manageTileCache,
|
loadend: this.addToCache,
|
unload: this.unloadTile,
|
scope: this
|
});
|
} else {
|
// Layer has the wrong tile type, so don't handle it any longer
|
this.removeLayer({layer: evt.tile.layer});
|
}
|
},
|
|
/**
|
* Method: unloadTile
|
* Listener for the tile's unload event
|
*
|
* Parameters:
|
* evt - {Object} The listener argument
|
*/
|
unloadTile: function(evt) {
|
var tile = evt.object;
|
tile.events.un({
|
beforedraw: this.queueTileDraw,
|
beforeload: this.manageTileCache,
|
loadend: this.addToCache,
|
unload: this.unloadTile,
|
scope: this
|
});
|
OpenLayers.Util.removeItem(this.tileQueue[tile.layer.map.id], tile);
|
},
|
|
/**
|
* Method: queueTileDraw
|
* Adds a tile to the queue that will draw it.
|
*
|
* Parameters:
|
* evt - {Object} Listener argument of the tile's beforedraw event
|
*/
|
queueTileDraw: function(evt) {
|
var tile = evt.object;
|
var queued = false;
|
var layer = tile.layer;
|
var url = layer.getURL(tile.bounds);
|
var img = this.tileCache[url];
|
if (img && img.className !== 'olTileImage') {
|
// cached image no longer valid, e.g. because we're olTileReplacing
|
delete this.tileCache[url];
|
OpenLayers.Util.removeItem(this.tileCacheIndex, url);
|
img = null;
|
}
|
// queue only if image with same url not cached already
|
if (layer.url && (layer.async || !img)) {
|
// add to queue only if not in queue already
|
var tileQueue = this.tileQueue[layer.map.id];
|
if (!~OpenLayers.Util.indexOf(tileQueue, tile)) {
|
tileQueue.push(tile);
|
}
|
queued = true;
|
}
|
return !queued;
|
},
|
|
/**
|
* Method: drawTilesFromQueue
|
* Draws tiles from the tileQueue, and unqueues the tiles
|
*/
|
drawTilesFromQueue: function(map) {
|
var tileQueue = this.tileQueue[map.id];
|
var limit = this.tilesPerFrame;
|
var animating = map.zoomTween && map.zoomTween.playing;
|
while (!animating && tileQueue.length && limit) {
|
tileQueue.shift().draw(true);
|
--limit;
|
}
|
},
|
|
/**
|
* Method: manageTileCache
|
* Adds, updates, removes and fetches cache entries.
|
*
|
* Parameters:
|
* evt - {Object} Listener argument of the tile's beforeload event
|
*/
|
manageTileCache: function(evt) {
|
var tile = evt.object;
|
var img = this.tileCache[tile.url];
|
if (img) {
|
// if image is on its layer's backbuffer, remove it from backbuffer
|
if (img.parentNode &&
|
OpenLayers.Element.hasClass(img.parentNode, 'olBackBuffer')) {
|
img.parentNode.removeChild(img);
|
img.id = null;
|
}
|
// only use image from cache if it is not on a layer already
|
if (!img.parentNode) {
|
img.style.visibility = 'hidden';
|
img.style.opacity = 0;
|
tile.setImage(img);
|
// LRU - move tile to the end of the array to mark it as the most
|
// recently used
|
OpenLayers.Util.removeItem(this.tileCacheIndex, tile.url);
|
this.tileCacheIndex.push(tile.url);
|
}
|
}
|
},
|
|
/**
|
* Method: addToCache
|
*
|
* Parameters:
|
* evt - {Object} Listener argument for the tile's loadend event
|
*/
|
addToCache: function(evt) {
|
var tile = evt.object;
|
if (!this.tileCache[tile.url]) {
|
if (!OpenLayers.Element.hasClass(tile.imgDiv, 'olImageLoadError')) {
|
if (this.tileCacheIndex.length >= this.cacheSize) {
|
delete this.tileCache[this.tileCacheIndex[0]];
|
this.tileCacheIndex.shift();
|
}
|
this.tileCache[tile.url] = tile.imgDiv;
|
this.tileCacheIndex.push(tile.url);
|
}
|
}
|
},
|
|
/**
|
* Method: clearTileQueue
|
* Clears the tile queue from tiles of a specific layer
|
*
|
* Parameters:
|
* evt - {Object} Listener argument of the layer's retile event
|
*/
|
clearTileQueue: function(evt) {
|
var layer = evt.object;
|
var tileQueue = this.tileQueue[layer.map.id];
|
for (var i=tileQueue.length-1; i>=0; --i) {
|
if (tileQueue[i].layer === layer) {
|
tileQueue.splice(i, 1);
|
}
|
}
|
},
|
|
/**
|
* Method: destroy
|
*/
|
destroy: function() {
|
for (var i=this.maps.length-1; i>=0; --i) {
|
this.removeMap(this.maps[i]);
|
}
|
this.maps = null;
|
this.tileQueue = null;
|
this.tileQueueId = null;
|
this.tileCache = null;
|
this.tileCacheIndex = null;
|
this._destroyed = true;
|
}
|
|
});
|
/* ======================================================================
|
OpenLayers/Layer/ArcGISCache.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Layer/XYZ.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.ArcGISCache
|
* Layer for accessing cached map tiles from an ArcGIS Server style mapcache.
|
* Tile must already be cached for this layer to access it. This does not require
|
* ArcGIS Server itself.
|
*
|
* A few attempts have been made at this kind of layer before. See
|
* http://trac.osgeo.org/openlayers/ticket/1967
|
* and
|
* http://trac.osgeo.org/openlayers/browser/sandbox/tschaub/arcgiscache/lib/OpenLayers/Layer/ArcGISCache.js
|
*
|
* Typically the problem encountered is that the tiles seem to "jump around".
|
* This is due to the fact that the actual max extent for the tiles on AGS layers
|
* changes at each zoom level due to the way these caches are constructed.
|
* We have attempted to use the resolutions, tile size, and tile origin
|
* from the cache meta data to make the appropriate changes to the max extent
|
* of the tile to compensate for this behavior. This must be done as zoom levels change
|
* and before tiles are requested, which is why methods from base classes are overridden.
|
*
|
* For reference, you can access mapcache meta data in two ways. For accessing a
|
* mapcache through ArcGIS Server, you can simply go to the landing page for the
|
* layer. (ie. http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer)
|
* For accessing it directly through HTTP, there should always be a conf.xml file
|
* in the root directory.
|
* (ie. http://serverx.esri.com/arcgiscache/DG_County_roads_yesA_backgroundDark/Layers/conf.xml)
|
*
|
*Inherits from:
|
* - <OpenLayers.Layer.XYZ>
|
*/
|
OpenLayers.Layer.ArcGISCache = OpenLayers.Class(OpenLayers.Layer.XYZ, {
|
|
/**
|
* APIProperty: url
|
* {String | Array} The base URL for the layer cache. You can also
|
* provide a list of URL strings for the layer if your cache is
|
* available from multiple origins. This must be set before the layer
|
* is drawn.
|
*/
|
url: null,
|
|
/**
|
* APIProperty: tileOrigin
|
* {<OpenLayers.LonLat>} The location of the tile origin for the cache.
|
* An ArcGIS cache has it's origin at the upper-left (lowest x value
|
* and highest y value of the coordinate system). The units for the
|
* tile origin should be the same as the units for the cached data.
|
*/
|
tileOrigin: null,
|
|
/**
|
* APIProperty: tileSize
|
* {<OpenLayers.Size>} This size of each tile. Defaults to 256 by 256 pixels.
|
*/
|
tileSize: new OpenLayers.Size(256, 256),
|
|
/**
|
* APIProperty: useAGS
|
* {Boolean} Indicates if we are going to be accessing the ArcGIS Server (AGS)
|
* cache via an AGS MapServer or directly through HTTP. When accessing via
|
* AGS the path structure uses a standard z/y/x structure. But AGS actually
|
* stores the tile images on disk using a hex based folder structure that looks
|
* like "http://example.com/mylayer/L00/R00000000/C00000000.png". Learn more
|
* about this here:
|
* http://blogs.esri.com/Support/blogs/mappingcenter/archive/2010/08/20/Checking-Your-Local-Cache-Folders.aspx
|
* Defaults to true;
|
*/
|
useArcGISServer: true,
|
|
/**
|
* APIProperty: type
|
* {String} Image type for the layer. This becomes the filename extension
|
* in tile requests. Default is "png" (generating a url like
|
* "http://example.com/mylayer/L00/R00000000/C00000000.png").
|
*/
|
type: 'png',
|
|
/**
|
* APIProperty: useScales
|
* {Boolean} Optional override to indicate that the layer should use 'scale' information
|
* returned from the server capabilities object instead of 'resolution' information.
|
* This can be important if your tile server uses an unusual DPI for the tiles.
|
*/
|
useScales: false,
|
|
/**
|
* APIProperty: overrideDPI
|
* {Boolean} Optional override to change the OpenLayers.DOTS_PER_INCH setting based
|
* on the tile information in the server capabilities object. This can be useful
|
* if your server has a non-standard DPI setting on its tiles, and you're only using
|
* tiles with that DPI. This value is used while OpenLayers is calculating resolution
|
* using scales, and is not necessary if you have resolution information. (This is
|
* typically the case) Regardless, this setting can be useful, but is dangerous
|
* because it will impact other layers while calculating resolution. Only use this
|
* if you know what you are doing. (See OpenLayers.Util.getResolutionFromScale)
|
*/
|
overrideDPI: false,
|
|
/**
|
* Constructor: OpenLayers.Layer.ArcGISCache
|
* Creates a new instance of this class
|
*
|
* Parameters:
|
* name - {String}
|
* url - {String}
|
* options - {Object} extra layer options
|
*/
|
initialize: function(name, url, options) {
|
OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments);
|
|
if (this.resolutions) {
|
this.serverResolutions = this.resolutions;
|
this.maxExtent = this.getMaxExtentForResolution(this.resolutions[0]);
|
}
|
|
// this block steps through translating the values from the server layer JSON
|
// capabilities object into values that we can use. This is also a helpful
|
// reference when configuring this layer directly.
|
if (this.layerInfo) {
|
// alias the object
|
var info = this.layerInfo;
|
|
// build our extents
|
var startingTileExtent = new OpenLayers.Bounds(
|
info.fullExtent.xmin,
|
info.fullExtent.ymin,
|
info.fullExtent.xmax,
|
info.fullExtent.ymax
|
);
|
|
// set our projection based on the given spatial reference.
|
// esri uses slightly different IDs, so this may not be comprehensive
|
this.projection = 'EPSG:' + info.spatialReference.wkid;
|
this.sphericalMercator = (info.spatialReference.wkid == 102100);
|
|
// convert esri units into openlayers units (basic feet or meters only)
|
this.units = (info.units == "esriFeet") ? 'ft' : 'm';
|
|
// optional extended section based on whether or not the server returned
|
// specific tile information
|
if (!!info.tileInfo) {
|
// either set the tiles based on rows/columns, or specific width/height
|
this.tileSize = new OpenLayers.Size(
|
info.tileInfo.width || info.tileInfo.cols,
|
info.tileInfo.height || info.tileInfo.rows
|
);
|
|
// this must be set when manually configuring this layer
|
this.tileOrigin = new OpenLayers.LonLat(
|
info.tileInfo.origin.x,
|
info.tileInfo.origin.y
|
);
|
|
var upperLeft = new OpenLayers.Geometry.Point(
|
startingTileExtent.left,
|
startingTileExtent.top
|
);
|
|
var bottomRight = new OpenLayers.Geometry.Point(
|
startingTileExtent.right,
|
startingTileExtent.bottom
|
);
|
|
if (this.useScales) {
|
this.scales = [];
|
} else {
|
this.resolutions = [];
|
}
|
|
this.lods = [];
|
for(var key in info.tileInfo.lods) {
|
if (info.tileInfo.lods.hasOwnProperty(key)) {
|
var lod = info.tileInfo.lods[key];
|
if (this.useScales) {
|
this.scales.push(lod.scale);
|
} else {
|
this.resolutions.push(lod.resolution);
|
}
|
|
var start = this.getContainingTileCoords(upperLeft, lod.resolution);
|
lod.startTileCol = start.x;
|
lod.startTileRow = start.y;
|
|
var end = this.getContainingTileCoords(bottomRight, lod.resolution);
|
lod.endTileCol = end.x;
|
lod.endTileRow = end.y;
|
this.lods.push(lod);
|
}
|
}
|
|
this.maxExtent = this.calculateMaxExtentWithLOD(this.lods[0]);
|
this.serverResolutions = this.resolutions;
|
if (this.overrideDPI && info.tileInfo.dpi) {
|
// see comment above for 'overrideDPI'
|
OpenLayers.DOTS_PER_INCH = info.tileInfo.dpi;
|
}
|
}
|
}
|
},
|
|
/**
|
* Method: getContainingTileCoords
|
* Calculates the x/y pixel corresponding to the position of the tile
|
* that contains the given point and for the for the given resolution.
|
*
|
* Parameters:
|
* point - {<OpenLayers.Geometry.Point>}
|
* res - {Float} The resolution for which to compute the extent.
|
*
|
* Returns:
|
* {<OpenLayers.Pixel>} The x/y pixel corresponding to the position
|
* of the upper left tile for the given resolution.
|
*/
|
getContainingTileCoords: function(point, res) {
|
return new OpenLayers.Pixel(
|
Math.max(Math.floor((point.x - this.tileOrigin.lon) / (this.tileSize.w * res)),0),
|
Math.max(Math.floor((this.tileOrigin.lat - point.y) / (this.tileSize.h * res)),0)
|
);
|
},
|
|
/**
|
* Method: calculateMaxExtentWithLOD
|
* Given a Level of Detail object from the server, this function
|
* calculates the actual max extent
|
*
|
* Parameters:
|
* lod - {Object} a Level of Detail Object from the server capabilities object
|
representing a particular zoom level
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} The actual extent of the tiles for the given zoom level
|
*/
|
calculateMaxExtentWithLOD: function(lod) {
|
// the max extent we're provided with just overlaps some tiles
|
// our real extent is the bounds of all the tiles we touch
|
|
var numTileCols = (lod.endTileCol - lod.startTileCol) + 1;
|
var numTileRows = (lod.endTileRow - lod.startTileRow) + 1;
|
|
var minX = this.tileOrigin.lon + (lod.startTileCol * this.tileSize.w * lod.resolution);
|
var maxX = minX + (numTileCols * this.tileSize.w * lod.resolution);
|
|
var maxY = this.tileOrigin.lat - (lod.startTileRow * this.tileSize.h * lod.resolution);
|
var minY = maxY - (numTileRows * this.tileSize.h * lod.resolution);
|
return new OpenLayers.Bounds(minX, minY, maxX, maxY);
|
},
|
|
/**
|
* Method: calculateMaxExtentWithExtent
|
* Given a 'suggested' max extent from the server, this function uses
|
* information about the actual tile sizes to determine the actual
|
* extent of the layer.
|
*
|
* Parameters:
|
* extent - {<OpenLayers.Bounds>} The 'suggested' extent for the layer
|
* res - {Float} The resolution for which to compute the extent.
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} The actual extent of the tiles for the given zoom level
|
*/
|
calculateMaxExtentWithExtent: function(extent, res) {
|
var upperLeft = new OpenLayers.Geometry.Point(extent.left, extent.top);
|
var bottomRight = new OpenLayers.Geometry.Point(extent.right, extent.bottom);
|
var start = this.getContainingTileCoords(upperLeft, res);
|
var end = this.getContainingTileCoords(bottomRight, res);
|
var lod = {
|
resolution: res,
|
startTileCol: start.x,
|
startTileRow: start.y,
|
endTileCol: end.x,
|
endTileRow: end.y
|
};
|
return this.calculateMaxExtentWithLOD(lod);
|
},
|
|
/**
|
* Method: getUpperLeftTileCoord
|
* Calculates the x/y pixel corresponding to the position
|
* of the upper left tile for the given resolution.
|
*
|
* Parameters:
|
* res - {Float} The resolution for which to compute the extent.
|
*
|
* Returns:
|
* {<OpenLayers.Pixel>} The x/y pixel corresponding to the position
|
* of the upper left tile for the given resolution.
|
*/
|
getUpperLeftTileCoord: function(res) {
|
var upperLeft = new OpenLayers.Geometry.Point(
|
this.maxExtent.left,
|
this.maxExtent.top);
|
return this.getContainingTileCoords(upperLeft, res);
|
},
|
|
/**
|
* Method: getLowerRightTileCoord
|
* Calculates the x/y pixel corresponding to the position
|
* of the lower right tile for the given resolution.
|
*
|
* Parameters:
|
* res - {Float} The resolution for which to compute the extent.
|
*
|
* Returns:
|
* {<OpenLayers.Pixel>} The x/y pixel corresponding to the position
|
* of the lower right tile for the given resolution.
|
*/
|
getLowerRightTileCoord: function(res) {
|
var bottomRight = new OpenLayers.Geometry.Point(
|
this.maxExtent.right,
|
this.maxExtent.bottom);
|
return this.getContainingTileCoords(bottomRight, res);
|
},
|
|
/**
|
* Method: getMaxExtentForResolution
|
* Since the max extent of a set of tiles can change from zoom level
|
* to zoom level, we need to be able to calculate that max extent
|
* for a given resolution.
|
*
|
* Parameters:
|
* res - {Float} The resolution for which to compute the extent.
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>} The extent for this resolution
|
*/
|
getMaxExtentForResolution: function(res) {
|
var start = this.getUpperLeftTileCoord(res);
|
var end = this.getLowerRightTileCoord(res);
|
|
var numTileCols = (end.x - start.x) + 1;
|
var numTileRows = (end.y - start.y) + 1;
|
|
var minX = this.tileOrigin.lon + (start.x * this.tileSize.w * res);
|
var maxX = minX + (numTileCols * this.tileSize.w * res);
|
|
var maxY = this.tileOrigin.lat - (start.y * this.tileSize.h * res);
|
var minY = maxY - (numTileRows * this.tileSize.h * res);
|
return new OpenLayers.Bounds(minX, minY, maxX, maxY);
|
},
|
|
/**
|
* APIMethod: clone
|
* Returns an exact clone of this OpenLayers.Layer.ArcGISCache
|
*
|
* Parameters:
|
* [obj] - {Object} optional object to assign the cloned instance to.
|
*
|
* Returns:
|
* {<OpenLayers.Layer.ArcGISCache>} clone of this instance
|
*/
|
clone: function (obj) {
|
if (obj == null) {
|
obj = new OpenLayers.Layer.ArcGISCache(this.name, this.url, this.options);
|
}
|
return OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);
|
},
|
|
/**
|
* Method: initGriddedTiles
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
*/
|
initGriddedTiles: function(bounds) {
|
delete this._tileOrigin;
|
OpenLayers.Layer.XYZ.prototype.initGriddedTiles.apply(this, arguments);
|
},
|
|
/**
|
* Method: getMaxExtent
|
* Get this layer's maximum extent.
|
*
|
* Returns:
|
* {<OpenLayers.Bounds>}
|
*/
|
getMaxExtent: function() {
|
var resolution = this.map.getResolution();
|
return this.maxExtent = this.getMaxExtentForResolution(resolution);
|
},
|
|
/**
|
* Method: getTileOrigin
|
* Determine the origin for aligning the grid of tiles.
|
* The origin will be derived from the layer's <maxExtent> property.
|
*
|
* Returns:
|
* {<OpenLayers.LonLat>} The tile origin.
|
*/
|
getTileOrigin: function() {
|
if (!this._tileOrigin) {
|
var extent = this.getMaxExtent();
|
this._tileOrigin = new OpenLayers.LonLat(extent.left, extent.bottom);
|
}
|
return this._tileOrigin;
|
},
|
|
/**
|
* Method: getURL
|
* Determine the URL for a tile given the tile bounds. This is should support
|
* urls that access tiles through an ArcGIS Server MapServer or directly through
|
* the hex folder structure using HTTP. Just be sure to set the useArcGISServer
|
* property appropriately! This is basically the same as
|
* 'OpenLayers.Layer.TMS.getURL', but with the addition of hex addressing,
|
* and tile rounding.
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
*
|
* Returns:
|
* {String} The URL for a tile based on given bounds.
|
*/
|
getURL: function (bounds) {
|
var res = this.getResolution();
|
|
// tile center
|
var originTileX = (this.tileOrigin.lon + (res * this.tileSize.w/2));
|
var originTileY = (this.tileOrigin.lat - (res * this.tileSize.h/2));
|
|
var center = bounds.getCenterLonLat();
|
var point = { x: center.lon, y: center.lat };
|
var x = (Math.round(Math.abs((center.lon - originTileX) / (res * this.tileSize.w))));
|
var y = (Math.round(Math.abs((originTileY - center.lat) / (res * this.tileSize.h))));
|
var z = this.map.getZoom();
|
|
// this prevents us from getting pink tiles (non-existant tiles)
|
if (this.lods) {
|
var lod = this.lods[this.map.getZoom()];
|
if ((x < lod.startTileCol || x > lod.endTileCol)
|
|| (y < lod.startTileRow || y > lod.endTileRow)) {
|
return null;
|
}
|
}
|
else {
|
var start = this.getUpperLeftTileCoord(res);
|
var end = this.getLowerRightTileCoord(res);
|
if ((x < start.x || x >= end.x)
|
|| (y < start.y || y >= end.y)) {
|
return null;
|
}
|
}
|
|
// Construct the url string
|
var url = this.url;
|
var s = '' + x + y + z;
|
|
if (OpenLayers.Util.isArray(url)) {
|
url = this.selectUrl(s, url);
|
}
|
|
// Accessing tiles through ArcGIS Server uses a different path
|
// structure than direct access via the folder structure.
|
if (this.useArcGISServer) {
|
// AGS MapServers have pretty url access to tiles
|
url = url + '/tile/${z}/${y}/${x}';
|
} else {
|
// The tile images are stored using hex values on disk.
|
x = 'C' + OpenLayers.Number.zeroPad(x, 8, 16);
|
y = 'R' + OpenLayers.Number.zeroPad(y, 8, 16);
|
z = 'L' + OpenLayers.Number.zeroPad(z, 2, 10);
|
url = url + '/${z}/${y}/${x}.' + this.type;
|
}
|
|
// Write the values into our formatted url
|
url = OpenLayers.String.format(url, {'x': x, 'y': y, 'z': z});
|
|
return OpenLayers.Util.urlAppend(
|
url, OpenLayers.Util.getParameterString(this.params)
|
);
|
},
|
|
CLASS_NAME: 'OpenLayers.Layer.ArcGISCache'
|
});
|
/* ======================================================================
|
OpenLayers/Control/WMSGetFeatureInfo.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/Handler/Click.js
|
* @requires OpenLayers/Handler/Hover.js
|
* @requires OpenLayers/Request.js
|
* @requires OpenLayers/Format/WMSGetFeatureInfo.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.WMSGetFeatureInfo
|
* The WMSGetFeatureInfo control uses a WMS query to get information about a point on the map. The
|
* information may be in a display-friendly format such as HTML, or a machine-friendly format such
|
* as GML, depending on the server's capabilities and the client's configuration. This control
|
* handles click or hover events, attempts to parse the results using an OpenLayers.Format, and
|
* fires a 'getfeatureinfo' event with the click position, the raw body of the response, and an
|
* array of features if it successfully read the response.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* APIProperty: hover
|
* {Boolean} Send GetFeatureInfo requests when mouse stops moving.
|
* Default is false.
|
*/
|
hover: false,
|
|
/**
|
* APIProperty: drillDown
|
* {Boolean} Drill down over all WMS layers in the map. When
|
* using drillDown mode, hover is not possible, and an infoFormat that
|
* returns parseable features is required. Default is false.
|
*/
|
drillDown: false,
|
|
/**
|
* APIProperty: maxFeatures
|
* {Integer} Maximum number of features to return from a WMS query. This
|
* sets the feature_count parameter on WMS GetFeatureInfo
|
* requests.
|
*/
|
maxFeatures: 10,
|
|
/**
|
* APIProperty: clickCallback
|
* {String} The click callback to register in the
|
* {<OpenLayers.Handler.Click>} object created when the hover
|
* option is set to false. Default is "click".
|
*/
|
clickCallback: "click",
|
|
/**
|
* APIProperty: output
|
* {String} Either "features" or "object". When triggering a getfeatureinfo
|
* request should we pass on an array of features or an object with with
|
* a "features" property and other properties (such as the url of the
|
* WMS). Default is "features".
|
*/
|
output: "features",
|
|
/**
|
* APIProperty: layers
|
* {Array(<OpenLayers.Layer.WMS>)} The layers to query for feature info.
|
* If omitted, all map WMS layers with a url that matches this <url> or
|
* <layerUrls> will be considered.
|
*/
|
layers: null,
|
|
/**
|
* APIProperty: queryVisible
|
* {Boolean} If true, filter out hidden layers when searching the map for
|
* layers to query. Default is false.
|
*/
|
queryVisible: false,
|
|
/**
|
* APIProperty: url
|
* {String} The URL of the WMS service to use. If not provided, the url
|
* of the first eligible layer will be used.
|
*/
|
url: null,
|
|
/**
|
* APIProperty: layerUrls
|
* {Array(String)} Optional list of urls for layers that should be queried.
|
* This can be used when the layer url differs from the url used for
|
* making GetFeatureInfo requests (in the case of a layer using cached
|
* tiles).
|
*/
|
layerUrls: null,
|
|
/**
|
* APIProperty: infoFormat
|
* {String} The mimetype to request from the server. If you are using
|
* drillDown mode and have multiple servers that do not share a common
|
* infoFormat, you can override the control's infoFormat by providing an
|
* INFO_FORMAT parameter in your <OpenLayers.Layer.WMS> instance(s).
|
*/
|
infoFormat: 'text/html',
|
|
/**
|
* APIProperty: vendorParams
|
* {Object} Additional parameters that will be added to the request, for
|
* WMS implementations that support them. This could e.g. look like
|
* (start code)
|
* {
|
* radius: 5
|
* }
|
* (end)
|
*/
|
vendorParams: {},
|
|
/**
|
* APIProperty: format
|
* {<OpenLayers.Format>} A format for parsing GetFeatureInfo responses.
|
* Default is <OpenLayers.Format.WMSGetFeatureInfo>.
|
*/
|
format: null,
|
|
/**
|
* APIProperty: formatOptions
|
* {Object} Optional properties to set on the format (if one is not provided
|
* in the <format> property.
|
*/
|
formatOptions: null,
|
|
/**
|
* APIProperty: handlerOptions
|
* {Object} Additional options for the handlers used by this control, e.g.
|
* (start code)
|
* {
|
* "click": {delay: 100},
|
* "hover": {delay: 300}
|
* }
|
* (end)
|
*/
|
|
/**
|
* Property: handler
|
* {Object} Reference to the <OpenLayers.Handler> for this control
|
*/
|
handler: null,
|
|
/**
|
* Property: hoverRequest
|
* {<OpenLayers.Request>} contains the currently running hover request
|
* (if any).
|
*/
|
hoverRequest: null,
|
|
/**
|
* APIProperty: events
|
* {<OpenLayers.Events>} Events instance for listeners and triggering
|
* control specific events.
|
*
|
* Register a listener for a particular event with the following syntax:
|
* (code)
|
* control.events.register(type, obj, listener);
|
* (end)
|
*
|
* Supported event types (in addition to those from <OpenLayers.Control.events>):
|
* beforegetfeatureinfo - Triggered before the request is sent.
|
* The event object has an *xy* property with the position of the
|
* mouse click or hover event that triggers the request.
|
* nogetfeatureinfo - no queryable layers were found.
|
* getfeatureinfo - Triggered when a GetFeatureInfo response is received.
|
* The event object has a *text* property with the body of the
|
* response (String), a *features* property with an array of the
|
* parsed features, an *xy* property with the position of the mouse
|
* click or hover event that triggered the request, and a *request*
|
* property with the request itself. If drillDown is set to true and
|
* multiple requests were issued to collect feature info from all
|
* layers, *text* and *request* will only contain the response body
|
* and request object of the last request.
|
*/
|
|
/**
|
* Constructor: <OpenLayers.Control.WMSGetFeatureInfo>
|
*
|
* Parameters:
|
* options - {Object}
|
*/
|
initialize: function(options) {
|
options = options || {};
|
options.handlerOptions = options.handlerOptions || {};
|
|
OpenLayers.Control.prototype.initialize.apply(this, [options]);
|
|
if(!this.format) {
|
this.format = new OpenLayers.Format.WMSGetFeatureInfo(
|
options.formatOptions
|
);
|
}
|
|
if(this.drillDown === true) {
|
this.hover = false;
|
}
|
|
if(this.hover) {
|
this.handler = new OpenLayers.Handler.Hover(
|
this, {
|
'move': this.cancelHover,
|
'pause': this.getInfoForHover
|
},
|
OpenLayers.Util.extend(this.handlerOptions.hover || {}, {
|
'delay': 250
|
}));
|
} else {
|
var callbacks = {};
|
callbacks[this.clickCallback] = this.getInfoForClick;
|
this.handler = new OpenLayers.Handler.Click(
|
this, callbacks, this.handlerOptions.click || {});
|
}
|
},
|
|
/**
|
* Method: getInfoForClick
|
* Called on click
|
*
|
* Parameters:
|
* evt - {<OpenLayers.Event>}
|
*/
|
getInfoForClick: function(evt) {
|
this.events.triggerEvent("beforegetfeatureinfo", {xy: evt.xy});
|
// Set the cursor to "wait" to tell the user we're working on their
|
// click.
|
OpenLayers.Element.addClass(this.map.viewPortDiv, "olCursorWait");
|
this.request(evt.xy, {});
|
},
|
|
/**
|
* Method: getInfoForHover
|
* Pause callback for the hover handler
|
*
|
* Parameters:
|
* evt - {Object}
|
*/
|
getInfoForHover: function(evt) {
|
this.events.triggerEvent("beforegetfeatureinfo", {xy: evt.xy});
|
this.request(evt.xy, {hover: true});
|
},
|
|
/**
|
* Method: cancelHover
|
* Cancel callback for the hover handler
|
*/
|
cancelHover: function() {
|
if (this.hoverRequest) {
|
this.hoverRequest.abort();
|
this.hoverRequest = null;
|
}
|
},
|
|
/**
|
* Method: findLayers
|
* Internal method to get the layers, independent of whether we are
|
* inspecting the map or using a client-provided array
|
*/
|
findLayers: function() {
|
|
var candidates = this.layers || this.map.layers;
|
var layers = [];
|
var layer, url;
|
for(var i = candidates.length - 1; i >= 0; --i) {
|
layer = candidates[i];
|
if(layer instanceof OpenLayers.Layer.WMS &&
|
(!this.queryVisible || layer.getVisibility())) {
|
url = OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url;
|
// if the control was not configured with a url, set it
|
// to the first layer url
|
if(this.drillDown === false && !this.url) {
|
this.url = url;
|
}
|
if(this.drillDown === true || this.urlMatches(url)) {
|
layers.push(layer);
|
}
|
}
|
}
|
return layers;
|
},
|
|
/**
|
* Method: urlMatches
|
* Test to see if the provided url matches either the control <url> or one
|
* of the <layerUrls>.
|
*
|
* Parameters:
|
* url - {String} The url to test.
|
*
|
* Returns:
|
* {Boolean} The provided url matches the control <url> or one of the
|
* <layerUrls>.
|
*/
|
urlMatches: function(url) {
|
var matches = OpenLayers.Util.isEquivalentUrl(this.url, url);
|
if(!matches && this.layerUrls) {
|
for(var i=0, len=this.layerUrls.length; i<len; ++i) {
|
if(OpenLayers.Util.isEquivalentUrl(this.layerUrls[i], url)) {
|
matches = true;
|
break;
|
}
|
}
|
}
|
return matches;
|
},
|
|
/**
|
* Method: buildWMSOptions
|
* Build an object with the relevant WMS options for the GetFeatureInfo request
|
*
|
* Parameters:
|
* url - {String} The url to be used for sending the request
|
* layers - {Array(<OpenLayers.Layer.WMS)} An array of layers
|
* clickPosition - {<OpenLayers.Pixel>} The position on the map where the mouse
|
* event occurred.
|
* format - {String} The format from the corresponding GetMap request
|
*/
|
buildWMSOptions: function(url, layers, clickPosition, format) {
|
var layerNames = [], styleNames = [];
|
for (var i = 0, len = layers.length; i < len; i++) {
|
if (layers[i].params.LAYERS != null) {
|
layerNames = layerNames.concat(layers[i].params.LAYERS);
|
styleNames = styleNames.concat(this.getStyleNames(layers[i]));
|
}
|
}
|
var firstLayer = layers[0];
|
// use the firstLayer's projection if it matches the map projection -
|
// this assumes that all layers will be available in this projection
|
var projection = this.map.getProjection();
|
var layerProj = firstLayer.projection;
|
if (layerProj && layerProj.equals(this.map.getProjectionObject())) {
|
projection = layerProj.getCode();
|
}
|
var params = OpenLayers.Util.extend({
|
service: "WMS",
|
version: firstLayer.params.VERSION,
|
request: "GetFeatureInfo",
|
exceptions: firstLayer.params.EXCEPTIONS,
|
bbox: this.map.getExtent().toBBOX(null,
|
firstLayer.reverseAxisOrder()),
|
feature_count: this.maxFeatures,
|
height: this.map.getSize().h,
|
width: this.map.getSize().w,
|
format: format,
|
info_format: firstLayer.params.INFO_FORMAT || this.infoFormat
|
}, (parseFloat(firstLayer.params.VERSION) >= 1.3) ?
|
{
|
crs: projection,
|
i: parseInt(clickPosition.x),
|
j: parseInt(clickPosition.y)
|
} :
|
{
|
srs: projection,
|
x: parseInt(clickPosition.x),
|
y: parseInt(clickPosition.y)
|
}
|
);
|
if (layerNames.length != 0) {
|
params = OpenLayers.Util.extend({
|
layers: layerNames,
|
query_layers: layerNames,
|
styles: styleNames
|
}, params);
|
}
|
OpenLayers.Util.applyDefaults(params, this.vendorParams);
|
return {
|
url: url,
|
params: OpenLayers.Util.upperCaseObject(params),
|
callback: function(request) {
|
this.handleResponse(clickPosition, request, url);
|
},
|
scope: this
|
};
|
},
|
|
/**
|
* Method: getStyleNames
|
* Gets the STYLES parameter for the layer. Make sure the STYLES parameter
|
* matches the LAYERS parameter
|
*
|
* Parameters:
|
* layer - {<OpenLayers.Layer.WMS>}
|
*
|
* Returns:
|
* {Array(String)} The STYLES parameter
|
*/
|
getStyleNames: function(layer) {
|
// in the event of a WMS layer bundling multiple layers but not
|
// specifying styles,we need the same number of commas to specify
|
// the default style for each of the layers. We can't just leave it
|
// blank as we may be including other layers that do specify styles.
|
var styleNames;
|
if (layer.params.STYLES) {
|
styleNames = layer.params.STYLES;
|
} else {
|
if (OpenLayers.Util.isArray(layer.params.LAYERS)) {
|
styleNames = new Array(layer.params.LAYERS.length);
|
} else { // Assume it's a String
|
styleNames = layer.params.LAYERS.replace(/[^,]/g, "");
|
}
|
}
|
return styleNames;
|
},
|
|
/**
|
* Method: request
|
* Sends a GetFeatureInfo request to the WMS
|
*
|
* Parameters:
|
* clickPosition - {<OpenLayers.Pixel>} The position on the map where the
|
* mouse event occurred.
|
* options - {Object} additional options for this method.
|
*
|
* Valid options:
|
* - *hover* {Boolean} true if we do the request for the hover handler
|
*/
|
request: function(clickPosition, options) {
|
var layers = this.findLayers();
|
if(layers.length == 0) {
|
this.events.triggerEvent("nogetfeatureinfo");
|
// Reset the cursor.
|
OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait");
|
return;
|
}
|
|
options = options || {};
|
if(this.drillDown === false) {
|
var wmsOptions = this.buildWMSOptions(this.url, layers,
|
clickPosition, layers[0].params.FORMAT);
|
var request = OpenLayers.Request.GET(wmsOptions);
|
|
if (options.hover === true) {
|
this.hoverRequest = request;
|
}
|
} else {
|
this._requestCount = 0;
|
this._numRequests = 0;
|
this.features = [];
|
// group according to service url to combine requests
|
var services = {}, url;
|
for(var i=0, len=layers.length; i<len; i++) {
|
var layer = layers[i];
|
var service, found = false;
|
url = OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url;
|
if(url in services) {
|
services[url].push(layer);
|
} else {
|
this._numRequests++;
|
services[url] = [layer];
|
}
|
}
|
var layers;
|
for (var url in services) {
|
layers = services[url];
|
var wmsOptions = this.buildWMSOptions(url, layers,
|
clickPosition, layers[0].params.FORMAT);
|
OpenLayers.Request.GET(wmsOptions);
|
}
|
}
|
},
|
|
/**
|
* Method: triggerGetFeatureInfo
|
* Trigger the getfeatureinfo event when all is done
|
*
|
* Parameters:
|
* request - {XMLHttpRequest} The request object
|
* xy - {<OpenLayers.Pixel>} The position on the map where the
|
* mouse event occurred.
|
* features - {Array(<OpenLayers.Feature.Vector>)} or
|
* {Array({Object}) when output is "object". The object has a url and a
|
* features property which contains an array of features.
|
*/
|
triggerGetFeatureInfo: function(request, xy, features) {
|
this.events.triggerEvent("getfeatureinfo", {
|
text: request.responseText,
|
features: features,
|
request: request,
|
xy: xy
|
});
|
|
// Reset the cursor.
|
OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait");
|
},
|
|
/**
|
* Method: handleResponse
|
* Handler for the GetFeatureInfo response.
|
*
|
* Parameters:
|
* xy - {<OpenLayers.Pixel>} The position on the map where the
|
* mouse event occurred.
|
* request - {XMLHttpRequest} The request object.
|
* url - {String} The url which was used for this request.
|
*/
|
handleResponse: function(xy, request, url) {
|
|
var doc = request.responseXML;
|
if(!doc || !doc.documentElement) {
|
doc = request.responseText;
|
}
|
var features = this.format.read(doc);
|
if (this.drillDown === false) {
|
this.triggerGetFeatureInfo(request, xy, features);
|
} else {
|
this._requestCount++;
|
if (this.output === "object") {
|
this._features = (this._features || []).concat(
|
{url: url, features: features}
|
);
|
} else {
|
this._features = (this._features || []).concat(features);
|
}
|
if (this._requestCount === this._numRequests) {
|
this.triggerGetFeatureInfo(request, xy, this._features.concat());
|
delete this._features;
|
delete this._requestCount;
|
delete this._numRequests;
|
}
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Control.WMSGetFeatureInfo"
|
});
|
/* ======================================================================
|
OpenLayers/Format/WMSCapabilities/v1_3_0.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/WMSCapabilities/v1_3.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WMSCapabilities/v1_3_0
|
* Read WMS Capabilities version 1.3.0.
|
* SLD 1.1.0 adds in the extra operations DescribeLayer and GetLegendGraphic,
|
* see: http://schemas.opengis.net/sld/1.1.0/sld_capabilities.xsd
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.WMSCapabilities.v1_3>
|
*/
|
OpenLayers.Format.WMSCapabilities.v1_3_0 = OpenLayers.Class(
|
OpenLayers.Format.WMSCapabilities.v1_3, {
|
|
/**
|
* Property: version
|
* {String} The specific parser version.
|
*/
|
version: "1.3.0",
|
|
CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_3_0"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/SOSGetFeatureOfInterest.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML.js
|
* @requires OpenLayers/Format/GML/v3.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.SOSGetFeatureOfInterest
|
* Read and write SOS GetFeatureOfInterest. This is used to get to
|
* the location of the features (stations). The stations can have 1 or more
|
* sensors.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.SOSGetFeatureOfInterest = OpenLayers.Class(
|
OpenLayers.Format.XML, {
|
|
/**
|
* Constant: VERSION
|
* {String} 1.0.0
|
*/
|
VERSION: "1.0.0",
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
sos: "http://www.opengis.net/sos/1.0",
|
gml: "http://www.opengis.net/gml",
|
sa: "http://www.opengis.net/sampling/1.0",
|
xsi: "http://www.w3.org/2001/XMLSchema-instance"
|
},
|
|
/**
|
* Property: schemaLocation
|
* {String} Schema location
|
*/
|
schemaLocation: "http://www.opengis.net/sos/1.0 http://schemas.opengis.net/sos/1.0.0/sosAll.xsd",
|
|
/**
|
* Property: defaultPrefix
|
*/
|
defaultPrefix: "sos",
|
|
/**
|
* Property: regExes
|
* Compiled regular expressions for manipulating strings.
|
*/
|
regExes: {
|
trimSpace: (/^\s*|\s*$/g),
|
removeSpace: (/\s*/g),
|
splitSpace: (/\s+/),
|
trimComma: (/\s*,\s*/g)
|
},
|
|
/**
|
* Constructor: OpenLayers.Format.SOSGetFeatureOfInterest
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* APIMethod: read
|
* Parse a GetFeatureOfInterest response and return an array of features
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
*
|
* Returns:
|
* {Array(<OpenLayers.Feature.Vector>)} An array of features.
|
*/
|
read: function(data) {
|
if(typeof data == "string") {
|
data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
|
}
|
if(data && data.nodeType == 9) {
|
data = data.documentElement;
|
}
|
|
var info = {features: []};
|
this.readNode(data, info);
|
|
var features = [];
|
for (var i=0, len=info.features.length; i<len; i++) {
|
var container = info.features[i];
|
// reproject features if needed
|
if(this.internalProjection && this.externalProjection &&
|
container.components[0]) {
|
container.components[0].transform(
|
this.externalProjection, this.internalProjection
|
);
|
}
|
var feature = new OpenLayers.Feature.Vector(
|
container.components[0], container.attributes);
|
features.push(feature);
|
}
|
return features;
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"sa": {
|
"SamplingPoint": function(node, obj) {
|
// sampling point can also be without a featureMember if
|
// there is only 1
|
if (!obj.attributes) {
|
var feature = {attributes: {}};
|
obj.features.push(feature);
|
obj = feature;
|
}
|
obj.attributes.id = this.getAttributeNS(node,
|
this.namespaces.gml, "id");
|
this.readChildNodes(node, obj);
|
},
|
"position": function (node, obj) {
|
this.readChildNodes(node, obj);
|
}
|
},
|
"gml": OpenLayers.Util.applyDefaults({
|
"FeatureCollection": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"featureMember": function(node, obj) {
|
var feature = {attributes: {}};
|
obj.features.push(feature);
|
this.readChildNodes(node, feature);
|
},
|
"name": function(node, obj) {
|
obj.attributes.name = this.getChildValue(node);
|
},
|
"pos": function(node, obj) {
|
// we need to parse the srsName to get to the
|
// externalProjection, that's why we cannot use
|
// GML v3 for this
|
if (!this.externalProjection) {
|
this.externalProjection = new OpenLayers.Projection(
|
node.getAttribute("srsName"));
|
}
|
OpenLayers.Format.GML.v3.prototype.readers.gml.pos.apply(
|
this, [node, obj]);
|
}
|
}, OpenLayers.Format.GML.v3.prototype.readers.gml)
|
},
|
|
/**
|
* Property: writers
|
* As a compliment to the readers property, this structure contains public
|
* writing functions grouped by namespace alias and named like the
|
* node names they produce.
|
*/
|
writers: {
|
"sos": {
|
"GetFeatureOfInterest": function(options) {
|
var node = this.createElementNSPlus("GetFeatureOfInterest", {
|
attributes: {
|
version: this.VERSION,
|
service: 'SOS',
|
"xsi:schemaLocation": this.schemaLocation
|
}
|
});
|
for (var i=0, len=options.fois.length; i<len; i++) {
|
this.writeNode("FeatureOfInterestId", {foi: options.fois[i]}, node);
|
}
|
return node;
|
},
|
"FeatureOfInterestId": function(options) {
|
var node = this.createElementNSPlus("FeatureOfInterestId", {value: options.foi});
|
return node;
|
}
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Format.SOSGetFeatureOfInterest"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/SOSGetObservation.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML.js
|
* @requires OpenLayers/Format/SOSGetFeatureOfInterest.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.SOSGetObservation
|
* Read and write SOS GetObersation (to get the actual values from a sensor)
|
* version 1.0.0
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.SOSGetObservation = OpenLayers.Class(OpenLayers.Format.XML, {
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
ows: "http://www.opengis.net/ows",
|
gml: "http://www.opengis.net/gml",
|
sos: "http://www.opengis.net/sos/1.0",
|
ogc: "http://www.opengis.net/ogc",
|
om: "http://www.opengis.net/om/1.0",
|
sa: "http://www.opengis.net/sampling/1.0",
|
xlink: "http://www.w3.org/1999/xlink",
|
xsi: "http://www.w3.org/2001/XMLSchema-instance"
|
},
|
|
/**
|
* Property: regExes
|
* Compiled regular expressions for manipulating strings.
|
*/
|
regExes: {
|
trimSpace: (/^\s*|\s*$/g),
|
removeSpace: (/\s*/g),
|
splitSpace: (/\s+/),
|
trimComma: (/\s*,\s*/g)
|
},
|
|
/**
|
* Constant: VERSION
|
* {String} 1.0.0
|
*/
|
VERSION: "1.0.0",
|
|
/**
|
* Property: schemaLocation
|
* {String} Schema location
|
*/
|
schemaLocation: "http://www.opengis.net/sos/1.0 http://schemas.opengis.net/sos/1.0.0/sosGetObservation.xsd",
|
|
/**
|
* Property: defaultPrefix
|
*/
|
defaultPrefix: "sos",
|
|
/**
|
* Constructor: OpenLayers.Format.SOSGetObservation
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* Method: read
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
*
|
* Returns:
|
* {Object} An object containing the measurements
|
*/
|
read: function(data) {
|
if(typeof data == "string") {
|
data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
|
}
|
if(data && data.nodeType == 9) {
|
data = data.documentElement;
|
}
|
var info = {measurements: [], observations: []};
|
this.readNode(data, info);
|
return info;
|
},
|
|
/**
|
* Method: write
|
*
|
* Parameters:
|
* options - {Object} Optional object.
|
*
|
* Returns:
|
* {String} An SOS GetObservation request XML string.
|
*/
|
write: function(options) {
|
var node = this.writeNode("sos:GetObservation", options);
|
node.setAttribute("xmlns:om", this.namespaces.om);
|
node.setAttribute("xmlns:ogc", this.namespaces.ogc);
|
this.setAttributeNS(
|
node, this.namespaces.xsi,
|
"xsi:schemaLocation", this.schemaLocation
|
);
|
return OpenLayers.Format.XML.prototype.write.apply(this, [node]);
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"om": {
|
"ObservationCollection": function(node, obj) {
|
obj.id = this.getAttributeNS(node, this.namespaces.gml, "id");
|
this.readChildNodes(node, obj);
|
},
|
"member": function(node, observationCollection) {
|
this.readChildNodes(node, observationCollection);
|
},
|
"Measurement": function(node, observationCollection) {
|
var measurement = {};
|
observationCollection.measurements.push(measurement);
|
this.readChildNodes(node, measurement);
|
},
|
"Observation": function(node, observationCollection) {
|
var observation = {};
|
observationCollection.observations.push(observation);
|
this.readChildNodes(node, observation);
|
},
|
"samplingTime": function(node, measurement) {
|
var samplingTime = {};
|
measurement.samplingTime = samplingTime;
|
this.readChildNodes(node, samplingTime);
|
},
|
"observedProperty": function(node, measurement) {
|
measurement.observedProperty =
|
this.getAttributeNS(node, this.namespaces.xlink, "href");
|
this.readChildNodes(node, measurement);
|
},
|
"procedure": function(node, measurement) {
|
measurement.procedure =
|
this.getAttributeNS(node, this.namespaces.xlink, "href");
|
this.readChildNodes(node, measurement);
|
},
|
"featureOfInterest": function(node, observation) {
|
var foi = {features: []};
|
observation.fois = [];
|
observation.fois.push(foi);
|
this.readChildNodes(node, foi);
|
// postprocessing to get actual features
|
var features = [];
|
for (var i=0, len=foi.features.length; i<len; i++) {
|
var feature = foi.features[i];
|
features.push(new OpenLayers.Feature.Vector(
|
feature.components[0], feature.attributes));
|
}
|
foi.features = features;
|
},
|
"result": function(node, measurement) {
|
var result = {};
|
measurement.result = result;
|
if (this.getChildValue(node) !== '') {
|
result.value = this.getChildValue(node);
|
result.uom = node.getAttribute("uom");
|
} else {
|
this.readChildNodes(node, result);
|
}
|
}
|
},
|
"sa": OpenLayers.Format.SOSGetFeatureOfInterest.prototype.readers.sa,
|
"gml": OpenLayers.Util.applyDefaults({
|
"TimeInstant": function(node, samplingTime) {
|
var timeInstant = {};
|
samplingTime.timeInstant = timeInstant;
|
this.readChildNodes(node, timeInstant);
|
},
|
"timePosition": function(node, timeInstant) {
|
timeInstant.timePosition = this.getChildValue(node);
|
}
|
}, OpenLayers.Format.SOSGetFeatureOfInterest.prototype.readers.gml)
|
},
|
|
/**
|
* Property: writers
|
* As a compliment to the readers property, this structure contains public
|
* writing functions grouped by namespace alias and named like the
|
* node names they produce.
|
*/
|
writers: {
|
"sos": {
|
"GetObservation": function(options) {
|
var node = this.createElementNSPlus("GetObservation", {
|
attributes: {
|
version: this.VERSION,
|
service: 'SOS'
|
}
|
});
|
this.writeNode("offering", options, node);
|
if (options.eventTime) {
|
this.writeNode("eventTime", options, node);
|
}
|
for (var procedure in options.procedures) {
|
this.writeNode("procedure", options.procedures[procedure], node);
|
}
|
for (var observedProperty in options.observedProperties) {
|
this.writeNode("observedProperty", options.observedProperties[observedProperty], node);
|
}
|
if (options.foi) {
|
this.writeNode("featureOfInterest", options.foi, node);
|
}
|
this.writeNode("responseFormat", options, node);
|
if (options.resultModel) {
|
this.writeNode("resultModel", options, node);
|
}
|
if (options.responseMode) {
|
this.writeNode("responseMode", options, node);
|
}
|
return node;
|
},
|
"featureOfInterest": function(foi) {
|
var node = this.createElementNSPlus("featureOfInterest");
|
this.writeNode("ObjectID", foi.objectId, node);
|
return node;
|
},
|
"ObjectID": function(options) {
|
return this.createElementNSPlus("ObjectID",
|
{value: options});
|
},
|
"responseFormat": function(options) {
|
return this.createElementNSPlus("responseFormat",
|
{value: options.responseFormat});
|
},
|
"procedure": function(procedure) {
|
return this.createElementNSPlus("procedure",
|
{value: procedure});
|
},
|
"offering": function(options) {
|
return this.createElementNSPlus("offering", {value:
|
options.offering});
|
},
|
"observedProperty": function(observedProperty) {
|
return this.createElementNSPlus("observedProperty",
|
{value: observedProperty});
|
},
|
"eventTime": function(options) {
|
var node = this.createElementNSPlus("eventTime");
|
if (options.eventTime === 'latest') {
|
this.writeNode("ogc:TM_Equals", options, node);
|
}
|
return node;
|
},
|
"resultModel": function(options) {
|
return this.createElementNSPlus("resultModel", {value:
|
options.resultModel});
|
},
|
"responseMode": function(options) {
|
return this.createElementNSPlus("responseMode", {value:
|
options.responseMode});
|
}
|
},
|
"ogc": {
|
"TM_Equals": function(options) {
|
var node = this.createElementNSPlus("ogc:TM_Equals");
|
this.writeNode("ogc:PropertyName", {property:
|
"urn:ogc:data:time:iso8601"}, node);
|
if (options.eventTime === 'latest') {
|
this.writeNode("gml:TimeInstant", {value: 'latest'}, node);
|
}
|
return node;
|
},
|
"PropertyName": function(options) {
|
return this.createElementNSPlus("ogc:PropertyName",
|
{value: options.property});
|
}
|
},
|
"gml": {
|
"TimeInstant": function(options) {
|
var node = this.createElementNSPlus("gml:TimeInstant");
|
this.writeNode("gml:timePosition", options, node);
|
return node;
|
},
|
"timePosition": function(options) {
|
var node = this.createElementNSPlus("gml:timePosition",
|
{value: options.value});
|
return node;
|
}
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Format.SOSGetObservation"
|
|
});
|
/* ======================================================================
|
OpenLayers/Control/UTFGrid.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/Handler/Hover.js
|
* @requires OpenLayers/Handler/Click.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.UTFGrid
|
*
|
* This Control provides behavior associated with UTFGrid Layers.
|
* These 'hit grids' provide underlying feature attributes without
|
* calling the server (again). This control allows Mousemove, Hovering
|
* and Click events to trigger callbacks that use the attributes in
|
* whatever way you need.
|
*
|
* The most common example may be a UTFGrid layer containing feature
|
* attributes that are displayed in a div as you mouseover.
|
*
|
* Example Code:
|
*
|
* (start code)
|
* var world_utfgrid = new OpenLayers.Layer.UTFGrid(
|
* 'UTFGrid Layer',
|
* "http://tiles/world_utfgrid/${z}/${x}/${y}.json"
|
* );
|
* map.addLayer(world_utfgrid);
|
*
|
* var control = new OpenLayers.Control.UTFGrid({
|
* layers: [world_utfgrid],
|
* handlerMode: 'move',
|
* callback: function(infoLookup) {
|
* // do something with returned data
|
*
|
* }
|
* })
|
* (end code)
|
*
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.UTFGrid = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* APIProperty: autoActivate
|
* {Boolean} Activate the control when it is added to a map. Default is
|
* true.
|
*/
|
autoActivate: true,
|
|
/**
|
* APIProperty: Layers
|
* List of layers to consider. Must be Layer.UTFGrids
|
* `null` is the default indicating all UTFGrid Layers are queried.
|
* {Array} <OpenLayers.Layer.UTFGrid>
|
*/
|
layers: null,
|
|
/* Property: defaultHandlerOptions
|
* The default opts passed to the handler constructors
|
*/
|
defaultHandlerOptions: {
|
'delay': 300,
|
'pixelTolerance': 4,
|
'stopMove': false,
|
'single': true,
|
'double': false,
|
'stopSingle': false,
|
'stopDouble': false
|
},
|
|
/* APIProperty: handlerMode
|
* Defaults to 'click'. Can be 'hover' or 'move'.
|
*/
|
handlerMode: 'click',
|
|
/**
|
* APIMethod: setHandler
|
* sets this.handlerMode and calls resetHandler()
|
*
|
* Parameters:
|
* hm - {String} Handler Mode string; 'click', 'hover' or 'move'.
|
*/
|
setHandler: function(hm) {
|
this.handlerMode = hm;
|
this.resetHandler();
|
},
|
|
/**
|
* Method: resetHandler
|
* Deactivates the old hanlder and creates a new
|
* <OpenLayers.Handler> based on the mode specified in
|
* this.handlerMode
|
*
|
*/
|
resetHandler: function() {
|
if (this.handler) {
|
this.handler.deactivate();
|
this.handler.destroy();
|
this.handler = null;
|
}
|
|
if (this.handlerMode == 'hover') {
|
// Handle this event on hover
|
this.handler = new OpenLayers.Handler.Hover(
|
this,
|
{'pause': this.handleEvent, 'move': this.reset},
|
this.handlerOptions
|
);
|
} else if (this.handlerMode == 'click') {
|
// Handle this event on click
|
this.handler = new OpenLayers.Handler.Click(
|
this, {
|
'click': this.handleEvent
|
}, this.handlerOptions
|
);
|
} else if (this.handlerMode == 'move') {
|
this.handler = new OpenLayers.Handler.Hover(
|
this,
|
// Handle this event while hovering OR moving
|
{'pause': this.handleEvent, 'move': this.handleEvent},
|
this.handlerOptions
|
);
|
}
|
if (this.handler) {
|
return true;
|
} else {
|
return false;
|
}
|
},
|
|
/**
|
* Constructor: <OpenLayers.Control.UTFGrid>
|
*
|
* Parameters:
|
* options - {Object}
|
*/
|
initialize: function(options) {
|
options = options || {};
|
options.handlerOptions = options.handlerOptions || this.defaultHandlerOptions;
|
OpenLayers.Control.prototype.initialize.apply(this, [options]);
|
this.resetHandler();
|
},
|
|
/**
|
* Method: handleEvent
|
* Internal method called when specified event is triggered.
|
*
|
* This method does several things:
|
*
|
* Gets the lonLat of the event.
|
*
|
* Loops through the appropriate hit grid layers and gathers the attributes.
|
*
|
* Passes the attributes to the callback
|
*
|
* Parameters:
|
* evt - {<OpenLayers.Event>}
|
*/
|
handleEvent: function(evt) {
|
if (evt == null) {
|
this.reset();
|
return;
|
}
|
|
var lonLat = this.map.getLonLatFromPixel(evt.xy);
|
if (!lonLat) {
|
return;
|
}
|
|
var layers = this.findLayers();
|
if (layers.length > 0) {
|
var infoLookup = {};
|
var layer, idx;
|
for (var i=0, len=layers.length; i<len; i++) {
|
layer = layers[i];
|
idx = OpenLayers.Util.indexOf(this.map.layers, layer);
|
infoLookup[idx] = layer.getFeatureInfo(lonLat);
|
}
|
this.callback(infoLookup, lonLat, evt.xy);
|
}
|
},
|
|
/**
|
* APIMethod: callback
|
* Function to be called when a mouse event corresponds with a location that
|
* includes data in one of the configured UTFGrid layers.
|
*
|
* Parameters:
|
* infoLookup - {Object} Keys of this object are layer indexes and can be
|
* used to resolve a layer in the map.layers array. The structure of
|
* the property values depend on the data included in the underlying
|
* UTFGrid and may be any valid JSON type.
|
*/
|
callback: function(infoLookup) {
|
// to be provided in the constructor
|
},
|
|
/**
|
* Method: reset
|
* Calls the callback with null.
|
*/
|
reset: function(evt) {
|
this.callback(null);
|
},
|
|
/**
|
* Method: findLayers
|
* Internal method to get the layers, independent of whether we are
|
* inspecting the map or using a client-provided array
|
*
|
* The default value of this.layers is null; this causes the
|
* findLayers method to return ALL UTFGrid layers encountered.
|
*
|
* Parameters:
|
* None
|
*
|
* Returns:
|
* {Array} Layers to handle on each event
|
*/
|
findLayers: function() {
|
var candidates = this.layers || this.map.layers;
|
var layers = [];
|
var layer;
|
for (var i=candidates.length-1; i>=0; --i) {
|
layer = candidates[i];
|
if (layer instanceof OpenLayers.Layer.UTFGrid ) {
|
layers.push(layer);
|
}
|
}
|
return layers;
|
},
|
|
CLASS_NAME: "OpenLayers.Control.UTFGrid"
|
});
|
/* ======================================================================
|
OpenLayers/Format/CQL.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/WKT.js
|
* @requires OpenLayers/Filter/Comparison.js
|
* @requires OpenLayers/Filter/Logical.js
|
* @requires OpenLayers/Filter/Spatial.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.CQL
|
* Read CQL strings to get <OpenLayers.Filter> objects. Write
|
* <OpenLayers.Filter> objects to get CQL strings. Create a new parser with
|
* the <OpenLayers.Format.CQL> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format>
|
*/
|
OpenLayers.Format.CQL = (function() {
|
|
var tokens = [
|
"PROPERTY", "COMPARISON", "VALUE", "LOGICAL"
|
],
|
|
patterns = {
|
PROPERTY: /^[_a-zA-Z]\w*/,
|
COMPARISON: /^(=|<>|<=|<|>=|>|LIKE)/i,
|
IS_NULL: /^IS NULL/i,
|
COMMA: /^,/,
|
LOGICAL: /^(AND|OR)/i,
|
VALUE: /^('([^']|'')*'|\d+(\.\d*)?|\.\d+)/,
|
LPAREN: /^\(/,
|
RPAREN: /^\)/,
|
SPATIAL: /^(BBOX|INTERSECTS|DWITHIN|WITHIN|CONTAINS)/i,
|
NOT: /^NOT/i,
|
BETWEEN: /^BETWEEN/i,
|
GEOMETRY: function(text) {
|
var type = /^(POINT|LINESTRING|POLYGON|MULTIPOINT|MULTILINESTRING|MULTIPOLYGON|GEOMETRYCOLLECTION)/.exec(text);
|
if (type) {
|
var len = text.length;
|
var idx = text.indexOf("(", type[0].length);
|
if (idx > -1) {
|
var depth = 1;
|
while (idx < len && depth > 0) {
|
idx++;
|
switch(text.charAt(idx)) {
|
case '(':
|
depth++;
|
break;
|
case ')':
|
depth--;
|
break;
|
default:
|
// in default case, do nothing
|
}
|
}
|
}
|
return [text.substr(0, idx+1)];
|
}
|
},
|
END: /^$/
|
},
|
|
follows = {
|
LPAREN: ['GEOMETRY', 'SPATIAL', 'PROPERTY', 'VALUE', 'LPAREN'],
|
RPAREN: ['NOT', 'LOGICAL', 'END', 'RPAREN'],
|
PROPERTY: ['COMPARISON', 'BETWEEN', 'COMMA', 'IS_NULL'],
|
BETWEEN: ['VALUE'],
|
IS_NULL: ['END'],
|
COMPARISON: ['VALUE'],
|
COMMA: ['GEOMETRY', 'VALUE', 'PROPERTY'],
|
VALUE: ['LOGICAL', 'COMMA', 'RPAREN', 'END'],
|
SPATIAL: ['LPAREN'],
|
LOGICAL: ['NOT', 'VALUE', 'SPATIAL', 'PROPERTY', 'LPAREN'],
|
NOT: ['PROPERTY', 'LPAREN'],
|
GEOMETRY: ['COMMA', 'RPAREN']
|
},
|
|
operators = {
|
'=': OpenLayers.Filter.Comparison.EQUAL_TO,
|
'<>': OpenLayers.Filter.Comparison.NOT_EQUAL_TO,
|
'<': OpenLayers.Filter.Comparison.LESS_THAN,
|
'<=': OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO,
|
'>': OpenLayers.Filter.Comparison.GREATER_THAN,
|
'>=': OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO,
|
'LIKE': OpenLayers.Filter.Comparison.LIKE,
|
'BETWEEN': OpenLayers.Filter.Comparison.BETWEEN,
|
'IS NULL': OpenLayers.Filter.Comparison.IS_NULL
|
},
|
|
operatorReverse = {},
|
|
logicals = {
|
'AND': OpenLayers.Filter.Logical.AND,
|
'OR': OpenLayers.Filter.Logical.OR
|
},
|
|
logicalReverse = {},
|
|
precedence = {
|
'RPAREN': 3,
|
'LOGICAL': 2,
|
'COMPARISON': 1
|
};
|
|
var i;
|
for (i in operators) {
|
if (operators.hasOwnProperty(i)) {
|
operatorReverse[operators[i]] = i;
|
}
|
}
|
|
for (i in logicals) {
|
if (logicals.hasOwnProperty(i)) {
|
logicalReverse[logicals[i]] = i;
|
}
|
}
|
|
function tryToken(text, pattern) {
|
if (pattern instanceof RegExp) {
|
return pattern.exec(text);
|
} else {
|
return pattern(text);
|
}
|
}
|
|
function nextToken(text, tokens) {
|
var i, token, len = tokens.length;
|
for (i=0; i<len; i++) {
|
token = tokens[i];
|
var pat = patterns[token];
|
var matches = tryToken(text, pat);
|
if (matches) {
|
var match = matches[0];
|
var remainder = text.substr(match.length).replace(/^\s*/, "");
|
return {
|
type: token,
|
text: match,
|
remainder: remainder
|
};
|
}
|
}
|
|
var msg = "ERROR: In parsing: [" + text + "], expected one of: ";
|
for (i=0; i<len; i++) {
|
token = tokens[i];
|
msg += "\n " + token + ": " + patterns[token];
|
}
|
|
throw new Error(msg);
|
}
|
|
function tokenize(text) {
|
var results = [];
|
var token, expect = ["NOT", "GEOMETRY", "SPATIAL", "PROPERTY", "LPAREN"];
|
|
do {
|
token = nextToken(text, expect);
|
text = token.remainder;
|
expect = follows[token.type];
|
if (token.type != "END" && !expect) {
|
throw new Error("No follows list for " + token.type);
|
}
|
results.push(token);
|
} while (token.type != "END");
|
|
return results;
|
}
|
|
function buildAst(tokens) {
|
var operatorStack = [],
|
postfix = [];
|
|
while (tokens.length) {
|
var tok = tokens.shift();
|
switch (tok.type) {
|
case "PROPERTY":
|
case "GEOMETRY":
|
case "VALUE":
|
postfix.push(tok);
|
break;
|
case "COMPARISON":
|
case "BETWEEN":
|
case "IS_NULL":
|
case "LOGICAL":
|
var p = precedence[tok.type];
|
|
while (operatorStack.length > 0 &&
|
(precedence[operatorStack[operatorStack.length - 1].type] <= p)
|
) {
|
postfix.push(operatorStack.pop());
|
}
|
|
operatorStack.push(tok);
|
break;
|
case "SPATIAL":
|
case "NOT":
|
case "LPAREN":
|
operatorStack.push(tok);
|
break;
|
case "RPAREN":
|
while (operatorStack.length > 0 &&
|
(operatorStack[operatorStack.length - 1].type != "LPAREN")
|
) {
|
postfix.push(operatorStack.pop());
|
}
|
operatorStack.pop(); // toss out the LPAREN
|
|
if (operatorStack.length > 0 &&
|
operatorStack[operatorStack.length-1].type == "SPATIAL") {
|
postfix.push(operatorStack.pop());
|
}
|
case "COMMA":
|
case "END":
|
break;
|
default:
|
throw new Error("Unknown token type " + tok.type);
|
}
|
}
|
|
while (operatorStack.length > 0) {
|
postfix.push(operatorStack.pop());
|
}
|
|
function buildTree() {
|
var tok = postfix.pop();
|
switch (tok.type) {
|
case "LOGICAL":
|
var rhs = buildTree(),
|
lhs = buildTree();
|
return new OpenLayers.Filter.Logical({
|
filters: [lhs, rhs],
|
type: logicals[tok.text.toUpperCase()]
|
});
|
case "NOT":
|
var operand = buildTree();
|
return new OpenLayers.Filter.Logical({
|
filters: [operand],
|
type: OpenLayers.Filter.Logical.NOT
|
});
|
case "BETWEEN":
|
var min, max, property;
|
postfix.pop(); // unneeded AND token here
|
max = buildTree();
|
min = buildTree();
|
property = buildTree();
|
return new OpenLayers.Filter.Comparison({
|
property: property,
|
lowerBoundary: min,
|
upperBoundary: max,
|
type: OpenLayers.Filter.Comparison.BETWEEN
|
});
|
case "COMPARISON":
|
var value = buildTree(),
|
property = buildTree();
|
return new OpenLayers.Filter.Comparison({
|
property: property,
|
value: value,
|
type: operators[tok.text.toUpperCase()]
|
});
|
case "IS_NULL":
|
var property = buildTree();
|
return new OpenLayers.Filter.Comparison({
|
property: property,
|
type: operators[tok.text.toUpperCase()]
|
});
|
case "VALUE":
|
var match = tok.text.match(/^'(.*)'$/);
|
if (match) {
|
return match[1].replace(/''/g, "'");
|
} else {
|
return Number(tok.text);
|
}
|
case "SPATIAL":
|
switch(tok.text.toUpperCase()) {
|
case "BBOX":
|
var maxy = buildTree(),
|
maxx = buildTree(),
|
miny = buildTree(),
|
minx = buildTree(),
|
prop = buildTree();
|
|
return new OpenLayers.Filter.Spatial({
|
type: OpenLayers.Filter.Spatial.BBOX,
|
property: prop,
|
value: OpenLayers.Bounds.fromArray(
|
[minx, miny, maxx, maxy]
|
)
|
});
|
case "INTERSECTS":
|
var value = buildTree(),
|
property = buildTree();
|
return new OpenLayers.Filter.Spatial({
|
type: OpenLayers.Filter.Spatial.INTERSECTS,
|
property: property,
|
value: value
|
});
|
case "WITHIN":
|
var value = buildTree(),
|
property = buildTree();
|
return new OpenLayers.Filter.Spatial({
|
type: OpenLayers.Filter.Spatial.WITHIN,
|
property: property,
|
value: value
|
});
|
case "CONTAINS":
|
var value = buildTree(),
|
property = buildTree();
|
return new OpenLayers.Filter.Spatial({
|
type: OpenLayers.Filter.Spatial.CONTAINS,
|
property: property,
|
value: value
|
});
|
case "DWITHIN":
|
var distance = buildTree(),
|
value = buildTree(),
|
property = buildTree();
|
return new OpenLayers.Filter.Spatial({
|
type: OpenLayers.Filter.Spatial.DWITHIN,
|
value: value,
|
property: property,
|
distance: Number(distance)
|
});
|
}
|
case "GEOMETRY":
|
return OpenLayers.Geometry.fromWKT(tok.text);
|
default:
|
return tok.text;
|
}
|
}
|
|
var result = buildTree();
|
if (postfix.length > 0) {
|
var msg = "Remaining tokens after building AST: \n";
|
for (var i = postfix.length - 1; i >= 0; i--) {
|
msg += postfix[i].type + ": " + postfix[i].text + "\n";
|
}
|
throw new Error(msg);
|
}
|
|
return result;
|
}
|
|
return OpenLayers.Class(OpenLayers.Format, {
|
/**
|
* APIMethod: read
|
* Generate a filter from a CQL string.
|
|
* Parameters:
|
* text - {String} The CQL text.
|
*
|
* Returns:
|
* {<OpenLayers.Filter>} A filter based on the CQL text.
|
*/
|
read: function(text) {
|
var result = buildAst(tokenize(text));
|
if (this.keepData) {
|
this.data = result;
|
}
|
return result;
|
},
|
|
/**
|
* APIMethod: write
|
* Convert a filter into a CQL string.
|
|
* Parameters:
|
* filter - {<OpenLayers.Filter>} The filter.
|
*
|
* Returns:
|
* {String} A CQL string based on the filter.
|
*/
|
write: function(filter) {
|
if (filter instanceof OpenLayers.Geometry) {
|
return filter.toString();
|
}
|
switch (filter.CLASS_NAME) {
|
case "OpenLayers.Filter.Spatial":
|
switch(filter.type) {
|
case OpenLayers.Filter.Spatial.BBOX:
|
return "BBOX(" +
|
filter.property + "," +
|
filter.value.toBBOX() +
|
")";
|
case OpenLayers.Filter.Spatial.DWITHIN:
|
return "DWITHIN(" +
|
filter.property + ", " +
|
this.write(filter.value) + ", " +
|
filter.distance + ")";
|
case OpenLayers.Filter.Spatial.WITHIN:
|
return "WITHIN(" +
|
filter.property + ", " +
|
this.write(filter.value) + ")";
|
case OpenLayers.Filter.Spatial.INTERSECTS:
|
return "INTERSECTS(" +
|
filter.property + ", " +
|
this.write(filter.value) + ")";
|
case OpenLayers.Filter.Spatial.CONTAINS:
|
return "CONTAINS(" +
|
filter.property + ", " +
|
this.write(filter.value) + ")";
|
default:
|
throw new Error("Unknown spatial filter type: " + filter.type);
|
}
|
case "OpenLayers.Filter.Logical":
|
if (filter.type == OpenLayers.Filter.Logical.NOT) {
|
// TODO: deal with precedence of logical operators to
|
// avoid extra parentheses (not urgent)
|
return "NOT (" + this.write(filter.filters[0]) + ")";
|
} else {
|
var res = "(";
|
var first = true;
|
for (var i = 0; i < filter.filters.length; i++) {
|
if (first) {
|
first = false;
|
} else {
|
res += ") " + logicalReverse[filter.type] + " (";
|
}
|
res += this.write(filter.filters[i]);
|
}
|
return res + ")";
|
}
|
case "OpenLayers.Filter.Comparison":
|
if (filter.type == OpenLayers.Filter.Comparison.BETWEEN) {
|
return filter.property + " BETWEEN " +
|
this.write(filter.lowerBoundary) + " AND " +
|
this.write(filter.upperBoundary);
|
} else {
|
return (filter.value !== null) ? filter.property +
|
" " + operatorReverse[filter.type] + " " +
|
this.write(filter.value) : filter.property +
|
" " + operatorReverse[filter.type];
|
}
|
case undefined:
|
if (typeof filter === "string") {
|
return "'" + filter.replace(/'/g, "''") + "'";
|
} else if (typeof filter === "number") {
|
return String(filter);
|
}
|
default:
|
throw new Error("Can't encode: " + filter.CLASS_NAME + " " + filter);
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Format.CQL"
|
|
});
|
})();
|
|
/* ======================================================================
|
OpenLayers/Control/Split.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/Handler/Path.js
|
* @requires OpenLayers/Layer/Vector.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.Split
|
* Acts as a split feature agent while editing vector features.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.Split = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* APIProperty: events
|
* {<OpenLayers.Events>} Events instance for listeners and triggering
|
* control specific events.
|
*
|
* Register a listener for a particular event with the following syntax:
|
* (code)
|
* control.events.register(type, obj, listener);
|
* (end)
|
*
|
* Supported event types (in addition to those from <OpenLayers.Control.events>):
|
* beforesplit - Triggered before a split occurs. Listeners receive an
|
* event object with *source* and *target* properties.
|
* split - Triggered when a split occurs. Listeners receive an event with
|
* an *original* property and a *features* property. The original
|
* is a reference to the target feature that the sketch or modified
|
* feature intersects. The features property is a list of all features
|
* that result from this single split. This event is triggered before
|
* the resulting features are added to the layer (while the layer still
|
* has a reference to the original).
|
* aftersplit - Triggered after all splits resulting from a single sketch
|
* or feature modification have occurred. The original features
|
* have been destroyed and features that result from the split
|
* have already been added to the layer. Listeners receive an event
|
* with a *source* and *features* property. The source references the
|
* sketch or modified feature used as a splitter. The features
|
* property is a list of all resulting features.
|
*/
|
|
/**
|
* APIProperty: layer
|
* {<OpenLayers.Layer.Vector>} The target layer with features to be split.
|
* Set at construction or after construction with <setLayer>.
|
*/
|
layer: null,
|
|
/**
|
* Property: source
|
* {<OpenLayers.Layer.Vector>} Optional source layer. Any newly created
|
* or modified features from this layer will be used to split features
|
* on the target layer. If not provided, a temporary sketch layer will
|
* be created.
|
*/
|
source: null,
|
|
/**
|
* Property: sourceOptions
|
* {Options} If a temporary sketch layer is created, these layer options
|
* will be applied.
|
*/
|
sourceOptions: null,
|
|
/**
|
* APIProperty: tolerance
|
* {Number} Distance between the calculated intersection and a vertex on
|
* the source geometry below which the existing vertex will be used
|
* for the split. Default is null.
|
*/
|
tolerance: null,
|
|
/**
|
* APIProperty: edge
|
* {Boolean} Allow splits given intersection of edges only. Default is
|
* true. If false, a vertex on the source must be within the
|
* <tolerance> distance of the calculated intersection for a split
|
* to occur.
|
*/
|
edge: true,
|
|
/**
|
* APIProperty: deferDelete
|
* {Boolean} Instead of removing features from the layer, set feature
|
* states of split features to DELETE. This assumes a save strategy
|
* or other component is in charge of removing features from the
|
* layer. Default is false. If false, split features will be
|
* immediately deleted from the layer.
|
*/
|
deferDelete: false,
|
|
/**
|
* APIProperty: mutual
|
* {Boolean} If source and target layers are the same, split source
|
* features and target features where they intersect. Default is
|
* true. If false, only target features will be split.
|
*/
|
mutual: true,
|
|
/**
|
* APIProperty: targetFilter
|
* {<OpenLayers.Filter>} Optional filter that will be evaluated
|
* to determine if a feature from the target layer is eligible for
|
* splitting.
|
*/
|
targetFilter: null,
|
|
/**
|
* APIProperty: sourceFilter
|
* {<OpenLayers.Filter>} Optional filter that will be evaluated
|
* to determine if a feature from the source layer is eligible for
|
* splitting.
|
*/
|
sourceFilter: null,
|
|
/**
|
* Property: handler
|
* {<OpenLayers.Handler.Path>} The temporary sketch handler created if
|
* no source layer is provided.
|
*/
|
handler: null,
|
|
/**
|
* Constructor: OpenLayers.Control.Split
|
* Creates a new split control. A control is constructed with a target
|
* layer and an optional source layer. While the control is active,
|
* creating new features or modifying existing features on the source
|
* layer will result in splitting any eligible features on the target
|
* layer. If no source layer is provided, a temporary sketch layer will
|
* be created to create lines for splitting features on the target.
|
*
|
* Parameters:
|
* options - {Object} An object containing all configuration properties for
|
* the control.
|
*
|
* Valid options:
|
* layer - {<OpenLayers.Layer.Vector>} The target layer. Features from this
|
* layer will be split by new or modified features on the source layer
|
* or temporary sketch layer.
|
* source - {<OpenLayers.Layer.Vector>} Optional source layer. If provided
|
* newly created features or modified features will be used to split
|
* features on the target layer. If not provided, a temporary sketch
|
* layer will be created for drawing lines.
|
* tolerance - {Number} Optional value for the distance between a source
|
* vertex and the calculated intersection below which the split will
|
* occur at the vertex.
|
* edge - {Boolean} Allow splits given intersection of edges only. Default
|
* is true. If false, a vertex on the source must be within the
|
* <tolerance> distance of the calculated intersection for a split
|
* to occur.
|
* mutual - {Boolean} If source and target are the same, split source
|
* features and target features where they intersect. Default is
|
* true. If false, only target features will be split.
|
* targetFilter - {<OpenLayers.Filter>} Optional filter that will be evaluated
|
* to determine if a feature from the target layer is eligible for
|
* splitting.
|
* sourceFilter - {<OpenLayers.Filter>} Optional filter that will be evaluated
|
* to determine if a feature from the target layer is eligible for
|
* splitting.
|
*/
|
initialize: function(options) {
|
OpenLayers.Control.prototype.initialize.apply(this, [options]);
|
this.options = options || {}; // TODO: this could be done by the super
|
|
// set the source layer if provided
|
if(this.options.source) {
|
this.setSource(this.options.source);
|
}
|
},
|
|
/**
|
* APIMethod: setSource
|
* Set the source layer for edits layer.
|
*
|
* Parameters:
|
* layer - {<OpenLayers.Layer.Vector>} The new source layer layer. If
|
* null, a temporary sketch layer will be created.
|
*/
|
setSource: function(layer) {
|
if(this.active) {
|
this.deactivate();
|
if(this.handler) {
|
this.handler.destroy();
|
delete this.handler;
|
}
|
this.source = layer;
|
this.activate();
|
} else {
|
this.source = layer;
|
}
|
},
|
|
/**
|
* APIMethod: activate
|
* Activate the control. Activating the control registers listeners for
|
* editing related events so that during feature creation and
|
* modification, features in the target will be considered for
|
* splitting.
|
*/
|
activate: function() {
|
var activated = OpenLayers.Control.prototype.activate.call(this);
|
if(activated) {
|
if(!this.source) {
|
if(!this.handler) {
|
this.handler = new OpenLayers.Handler.Path(this,
|
{done: function(geometry) {
|
this.onSketchComplete({
|
feature: new OpenLayers.Feature.Vector(geometry)
|
});
|
}},
|
{layerOptions: this.sourceOptions}
|
);
|
}
|
this.handler.activate();
|
} else if(this.source.events) {
|
this.source.events.on({
|
sketchcomplete: this.onSketchComplete,
|
afterfeaturemodified: this.afterFeatureModified,
|
scope: this
|
});
|
}
|
}
|
return activated;
|
},
|
|
/**
|
* APIMethod: deactivate
|
* Deactivate the control. Deactivating the control unregisters listeners
|
* so feature editing may proceed without engaging the split agent.
|
*/
|
deactivate: function() {
|
var deactivated = OpenLayers.Control.prototype.deactivate.call(this);
|
if(deactivated) {
|
if(this.source && this.source.events) {
|
this.source.events.un({
|
sketchcomplete: this.onSketchComplete,
|
afterfeaturemodified: this.afterFeatureModified,
|
scope: this
|
});
|
}
|
}
|
return deactivated;
|
},
|
|
/**
|
* Method: onSketchComplete
|
* Registered as a listener for the sketchcomplete event on the editable
|
* layer.
|
*
|
* Parameters:
|
* event - {Object} The sketch complete event.
|
*
|
* Returns:
|
* {Boolean} Stop the sketch from being added to the layer (it has been
|
* split).
|
*/
|
onSketchComplete: function(event) {
|
this.feature = null;
|
return !this.considerSplit(event.feature);
|
},
|
|
/**
|
* Method: afterFeatureModified
|
* Registered as a listener for the afterfeaturemodified event on the
|
* editable layer.
|
*
|
* Parameters:
|
* event - {Object} The after feature modified event.
|
*/
|
afterFeatureModified: function(event) {
|
if(event.modified) {
|
var feature = event.feature;
|
if (typeof feature.geometry.split === "function") {
|
this.feature = event.feature;
|
this.considerSplit(event.feature);
|
}
|
}
|
},
|
|
/**
|
* Method: removeByGeometry
|
* Remove a feature from a list based on the given geometry.
|
*
|
* Parameters:
|
* features - {Array(<OpenLayers.Feature.Vector>)} A list of features.
|
* geometry - {<OpenLayers.Geometry>} A geometry.
|
*/
|
removeByGeometry: function(features, geometry) {
|
for(var i=0, len=features.length; i<len; ++i) {
|
if(features[i].geometry === geometry) {
|
features.splice(i, 1);
|
break;
|
}
|
}
|
},
|
|
/**
|
* Method: isEligible
|
* Test if a target feature is eligible for splitting.
|
*
|
* Parameters:
|
* target - {<OpenLayers.Feature.Vector>} The target feature.
|
*
|
* Returns:
|
* {Boolean} The target is eligible for splitting.
|
*/
|
isEligible: function(target) {
|
if (!target.geometry) {
|
return false;
|
} else {
|
return (
|
target.state !== OpenLayers.State.DELETE
|
) && (
|
typeof target.geometry.split === "function"
|
) && (
|
this.feature !== target
|
) && (
|
!this.targetFilter ||
|
this.targetFilter.evaluate(target.attributes)
|
);
|
}
|
},
|
|
/**
|
* Method: considerSplit
|
* Decide whether or not to split target features with the supplied
|
* feature. If <mutual> is true, both the source and target features
|
* will be split if eligible.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>} The newly created or modified
|
* feature.
|
*
|
* Returns:
|
* {Boolean} The supplied feature was split (and destroyed).
|
*/
|
considerSplit: function(feature) {
|
var sourceSplit = false;
|
var targetSplit = false;
|
if(!this.sourceFilter ||
|
this.sourceFilter.evaluate(feature.attributes)) {
|
var features = this.layer && this.layer.features || [];
|
var target, results, proceed;
|
var additions = [], removals = [];
|
var mutual = (this.layer === this.source) && this.mutual;
|
var options = {
|
edge: this.edge,
|
tolerance: this.tolerance,
|
mutual: mutual
|
};
|
var sourceParts = [feature.geometry];
|
var targetFeature, targetParts;
|
var source, parts;
|
for(var i=0, len=features.length; i<len; ++i) {
|
targetFeature = features[i];
|
if(this.isEligible(targetFeature)) {
|
targetParts = [targetFeature.geometry];
|
// work through source geoms - this array may change
|
for(var j=0; j<sourceParts.length; ++j) {
|
source = sourceParts[j];
|
// work through target parts - this array may change
|
for(var k=0; k<targetParts.length; ++k) {
|
target = targetParts[k];
|
if(source.getBounds().intersectsBounds(target.getBounds())) {
|
results = source.split(target, options);
|
if(results) {
|
proceed = this.events.triggerEvent(
|
"beforesplit", {source: feature, target: targetFeature}
|
);
|
if(proceed !== false) {
|
if(mutual) {
|
parts = results[0];
|
// handle parts that result from source splitting
|
if(parts.length > 1) {
|
// splice in new source parts
|
parts.unshift(j, 1); // add args for splice below
|
Array.prototype.splice.apply(sourceParts, parts);
|
j += parts.length - 3;
|
}
|
results = results[1];
|
}
|
// handle parts that result from target splitting
|
if(results.length > 1) {
|
// splice in new target parts
|
results.unshift(k, 1); // add args for splice below
|
Array.prototype.splice.apply(targetParts, results);
|
k += results.length - 3;
|
}
|
}
|
}
|
}
|
}
|
}
|
if(targetParts && targetParts.length > 1) {
|
this.geomsToFeatures(targetFeature, targetParts);
|
this.events.triggerEvent("split", {
|
original: targetFeature,
|
features: targetParts
|
});
|
Array.prototype.push.apply(additions, targetParts);
|
removals.push(targetFeature);
|
targetSplit = true;
|
}
|
}
|
}
|
if(sourceParts && sourceParts.length > 1) {
|
this.geomsToFeatures(feature, sourceParts);
|
this.events.triggerEvent("split", {
|
original: feature,
|
features: sourceParts
|
});
|
Array.prototype.push.apply(additions, sourceParts);
|
removals.push(feature);
|
sourceSplit = true;
|
}
|
if(sourceSplit || targetSplit) {
|
// remove and add feature events are suppressed
|
// listen for split event on this control instead
|
if(this.deferDelete) {
|
// Set state instead of removing. Take care to avoid
|
// setting delete for features that have not yet been
|
// inserted - those should be destroyed immediately.
|
var feat, destroys = [];
|
for(var i=0, len=removals.length; i<len; ++i) {
|
feat = removals[i];
|
if(feat.state === OpenLayers.State.INSERT) {
|
destroys.push(feat);
|
} else {
|
feat.state = OpenLayers.State.DELETE;
|
this.layer.drawFeature(feat);
|
}
|
}
|
this.layer.destroyFeatures(destroys, {silent: true});
|
for(var i=0, len=additions.length; i<len; ++i) {
|
additions[i].state = OpenLayers.State.INSERT;
|
}
|
} else {
|
this.layer.destroyFeatures(removals, {silent: true});
|
}
|
this.layer.addFeatures(additions, {silent: true});
|
this.events.triggerEvent("aftersplit", {
|
source: feature,
|
features: additions
|
});
|
}
|
}
|
return sourceSplit;
|
},
|
|
/**
|
* Method: geomsToFeatures
|
* Create new features given a template feature and a list of geometries.
|
* The list of geometries is modified in place. The result will be
|
* a list of new features.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>} The feature to be cloned.
|
* geoms - {Array(<OpenLayers.Geometry>)} List of goemetries. This will
|
* become a list of new features.
|
*/
|
geomsToFeatures: function(feature, geoms) {
|
var clone = feature.clone();
|
delete clone.geometry;
|
var newFeature;
|
for(var i=0, len=geoms.length; i<len; ++i) {
|
// turn results list from geoms to features
|
newFeature = clone.clone();
|
newFeature.geometry = geoms[i];
|
newFeature.state = OpenLayers.State.INSERT;
|
geoms[i] = newFeature;
|
}
|
},
|
|
/**
|
* Method: destroy
|
* Clean up the control.
|
*/
|
destroy: function() {
|
if(this.active) {
|
this.deactivate(); // TODO: this should be handled by the super
|
}
|
OpenLayers.Control.prototype.destroy.call(this);
|
},
|
|
CLASS_NAME: "OpenLayers.Control.Split"
|
});
|
/* ======================================================================
|
OpenLayers/Layer/WMTS.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Layer/Grid.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.WMTS
|
* Instances of the WMTS class allow viewing of tiles from a service that
|
* implements the OGC WMTS specification version 1.0.0.
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer.Grid>
|
*/
|
OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, {
|
|
/**
|
* APIProperty: isBaseLayer
|
* {Boolean} The layer will be considered a base layer. Default is true.
|
*/
|
isBaseLayer: true,
|
|
/**
|
* Property: version
|
* {String} WMTS version. Default is "1.0.0".
|
*/
|
version: "1.0.0",
|
|
/**
|
* APIProperty: requestEncoding
|
* {String} Request encoding. Can be "REST" or "KVP". Default is "KVP".
|
*/
|
requestEncoding: "KVP",
|
|
/**
|
* APIProperty: url
|
* {String|Array(String)} The base URL or request URL template for the WMTS
|
* service. Must be provided. Array is only supported for base URLs, not
|
* for request URL templates. URL templates are only supported for
|
* REST <requestEncoding>.
|
*/
|
url: null,
|
|
/**
|
* APIProperty: layer
|
* {String} The layer identifier advertised by the WMTS service. Must be
|
* provided.
|
*/
|
layer: null,
|
|
/**
|
* APIProperty: matrixSet
|
* {String} One of the advertised matrix set identifiers. Must be provided.
|
*/
|
matrixSet: null,
|
|
/**
|
* APIProperty: style
|
* {String} One of the advertised layer styles. Must be provided.
|
*/
|
style: null,
|
|
/**
|
* APIProperty: format
|
* {String} The image MIME type. Default is "image/jpeg".
|
*/
|
format: "image/jpeg",
|
|
/**
|
* APIProperty: tileOrigin
|
* {<OpenLayers.LonLat>} The top-left corner of the tile matrix in map
|
* units. If the tile origin for each matrix in a set is different,
|
* the <matrixIds> should include a topLeftCorner property. If
|
* not provided, the tile origin will default to the top left corner
|
* of the layer <maxExtent>.
|
*/
|
tileOrigin: null,
|
|
/**
|
* APIProperty: tileFullExtent
|
* {<OpenLayers.Bounds>} The full extent of the tile set. If not supplied,
|
* the layer's <maxExtent> property will be used.
|
*/
|
tileFullExtent: null,
|
|
/**
|
* APIProperty: formatSuffix
|
* {String} For REST request encoding, an image format suffix must be
|
* included in the request. If not provided, the suffix will be derived
|
* from the <format> property.
|
*/
|
formatSuffix: null,
|
|
/**
|
* APIProperty: matrixIds
|
* {Array} A list of tile matrix identifiers. If not provided, the matrix
|
* identifiers will be assumed to be integers corresponding to the
|
* map zoom level. If a list of strings is provided, each item should
|
* be the matrix identifier that corresponds to the map zoom level.
|
* Additionally, a list of objects can be provided. Each object should
|
* describe the matrix as presented in the WMTS capabilities. These
|
* objects should have the propertes shown below.
|
*
|
* Matrix properties:
|
* identifier - {String} The matrix identifier (required).
|
* scaleDenominator - {Number} The matrix scale denominator.
|
* topLeftCorner - {<OpenLayers.LonLat>} The top left corner of the
|
* matrix. Must be provided if different than the layer <tileOrigin>.
|
* tileWidth - {Number} The tile width for the matrix. Must be provided
|
* if different than the width given in the layer <tileSize>.
|
* tileHeight - {Number} The tile height for the matrix. Must be provided
|
* if different than the height given in the layer <tileSize>.
|
*/
|
matrixIds: null,
|
|
/**
|
* APIProperty: dimensions
|
* {Array} For RESTful request encoding, extra dimensions may be specified.
|
* Items in this list should be property names in the <params> object.
|
* Values of extra dimensions will be determined from the corresponding
|
* values in the <params> object.
|
*/
|
dimensions: null,
|
|
/**
|
* APIProperty: params
|
* {Object} Extra parameters to include in tile requests. For KVP
|
* <requestEncoding>, these properties will be encoded in the request
|
* query string. For REST <requestEncoding>, these properties will
|
* become part of the request path, with order determined by the
|
* <dimensions> list.
|
*/
|
params: null,
|
|
/**
|
* APIProperty: zoomOffset
|
* {Number} If your cache has more levels than you want to provide
|
* access to with this layer, supply a zoomOffset. This zoom offset
|
* is added to the current map zoom level to determine the level
|
* for a requested tile. For example, if you supply a zoomOffset
|
* of 3, when the map is at the zoom 0, tiles will be requested from
|
* level 3 of your cache. Default is 0 (assumes cache level and map
|
* zoom are equivalent). Additionally, if this layer is to be used
|
* as an overlay and the cache has fewer zoom levels than the base
|
* layer, you can supply a negative zoomOffset. For example, if a
|
* map zoom level of 1 corresponds to your cache level zero, you would
|
* supply a -1 zoomOffset (and set the maxResolution of the layer
|
* appropriately). The zoomOffset value has no effect if complete
|
* matrix definitions (including scaleDenominator) are supplied in
|
* the <matrixIds> property. Defaults to 0 (no zoom offset).
|
*/
|
zoomOffset: 0,
|
|
/**
|
* APIProperty: serverResolutions
|
* {Array} A list of all resolutions available on the server. Only set this
|
* property if the map resolutions differ from the server. This
|
* property serves two purposes. (a) <serverResolutions> can include
|
* resolutions that the server supports and that you don't want to
|
* provide with this layer; you can also look at <zoomOffset>, which is
|
* an alternative to <serverResolutions> for that specific purpose.
|
* (b) The map can work with resolutions that aren't supported by
|
* the server, i.e. that aren't in <serverResolutions>. When the
|
* map is displayed in such a resolution data for the closest
|
* server-supported resolution is loaded and the layer div is
|
* stretched as necessary.
|
*/
|
serverResolutions: null,
|
|
/**
|
* Property: formatSuffixMap
|
* {Object} a map between WMTS 'format' request parameter and tile image file suffix
|
*/
|
formatSuffixMap: {
|
"image/png": "png",
|
"image/png8": "png",
|
"image/png24": "png",
|
"image/png32": "png",
|
"png": "png",
|
"image/jpeg": "jpg",
|
"image/jpg": "jpg",
|
"jpeg": "jpg",
|
"jpg": "jpg"
|
},
|
|
/**
|
* Property: matrix
|
* {Object} Matrix definition for the current map resolution. Updated by
|
* the <updateMatrixProperties> method.
|
*/
|
matrix: null,
|
|
/**
|
* Constructor: OpenLayers.Layer.WMTS
|
* Create a new WMTS layer.
|
*
|
* Example:
|
* (code)
|
* var wmts = new OpenLayers.Layer.WMTS({
|
* name: "My WMTS Layer",
|
* url: "http://example.com/wmts",
|
* layer: "layer_id",
|
* style: "default",
|
* matrixSet: "matrix_id"
|
* });
|
* (end)
|
*
|
* Parameters:
|
* config - {Object} Configuration properties for the layer.
|
*
|
* Required configuration properties:
|
* url - {String} The base url for the service. See the <url> property.
|
* layer - {String} The layer identifier. See the <layer> property.
|
* style - {String} The layer style identifier. See the <style> property.
|
* matrixSet - {String} The tile matrix set identifier. See the <matrixSet>
|
* property.
|
*
|
* Any other documented layer properties can be provided in the config object.
|
*/
|
initialize: function(config) {
|
|
// confirm required properties are supplied
|
var required = {
|
url: true,
|
layer: true,
|
style: true,
|
matrixSet: true
|
};
|
for (var prop in required) {
|
if (!(prop in config)) {
|
throw new Error("Missing property '" + prop + "' in layer configuration.");
|
}
|
}
|
|
config.params = OpenLayers.Util.upperCaseObject(config.params);
|
var args = [config.name, config.url, config.params, config];
|
OpenLayers.Layer.Grid.prototype.initialize.apply(this, args);
|
|
|
// determine format suffix (for REST)
|
if (!this.formatSuffix) {
|
this.formatSuffix = this.formatSuffixMap[this.format] || this.format.split("/").pop();
|
}
|
|
// expand matrixIds (may be array of string or array of object)
|
if (this.matrixIds) {
|
var len = this.matrixIds.length;
|
if (len && typeof this.matrixIds[0] === "string") {
|
var ids = this.matrixIds;
|
this.matrixIds = new Array(len);
|
for (var i=0; i<len; ++i) {
|
this.matrixIds[i] = {identifier: ids[i]};
|
}
|
}
|
}
|
|
},
|
|
/**
|
* Method: setMap
|
*/
|
setMap: function() {
|
OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);
|
},
|
|
/**
|
* Method: updateMatrixProperties
|
* Called when map resolution changes to update matrix related properties.
|
*/
|
updateMatrixProperties: function() {
|
this.matrix = this.getMatrix();
|
if (this.matrix) {
|
if (this.matrix.topLeftCorner) {
|
this.tileOrigin = this.matrix.topLeftCorner;
|
}
|
if (this.matrix.tileWidth && this.matrix.tileHeight) {
|
this.tileSize = new OpenLayers.Size(
|
this.matrix.tileWidth, this.matrix.tileHeight
|
);
|
}
|
if (!this.tileOrigin) {
|
this.tileOrigin = new OpenLayers.LonLat(
|
this.maxExtent.left, this.maxExtent.top
|
);
|
}
|
if (!this.tileFullExtent) {
|
this.tileFullExtent = this.maxExtent;
|
}
|
}
|
},
|
|
/**
|
* Method: moveTo
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
* zoomChanged - {Boolean} Tells when zoom has changed, as layers have to
|
* do some init work in that case.
|
* dragging - {Boolean}
|
*/
|
moveTo:function(bounds, zoomChanged, dragging) {
|
if (zoomChanged || !this.matrix) {
|
this.updateMatrixProperties();
|
}
|
return OpenLayers.Layer.Grid.prototype.moveTo.apply(this, arguments);
|
},
|
|
/**
|
* APIMethod: clone
|
*
|
* Parameters:
|
* obj - {Object}
|
*
|
* Returns:
|
* {<OpenLayers.Layer.WMTS>} An exact clone of this <OpenLayers.Layer.WMTS>
|
*/
|
clone: function(obj) {
|
if (obj == null) {
|
obj = new OpenLayers.Layer.WMTS(this.options);
|
}
|
//get all additions from superclasses
|
obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
|
// copy/set any non-init, non-simple values here
|
return obj;
|
},
|
|
/**
|
* Method: getIdentifier
|
* Get the current index in the matrixIds array.
|
*/
|
getIdentifier: function() {
|
return this.getServerZoom();
|
},
|
|
/**
|
* Method: getMatrix
|
* Get the appropriate matrix definition for the current map resolution.
|
*/
|
getMatrix: function() {
|
var matrix;
|
if (!this.matrixIds || this.matrixIds.length === 0) {
|
matrix = {identifier: this.getIdentifier()};
|
} else {
|
// get appropriate matrix given the map scale if possible
|
if ("scaleDenominator" in this.matrixIds[0]) {
|
// scale denominator calculation based on WMTS spec
|
var denom =
|
OpenLayers.METERS_PER_INCH *
|
OpenLayers.INCHES_PER_UNIT[this.units] *
|
this.getServerResolution() / 0.28E-3;
|
var diff = Number.POSITIVE_INFINITY;
|
var delta;
|
for (var i=0, ii=this.matrixIds.length; i<ii; ++i) {
|
delta = Math.abs(1 - (this.matrixIds[i].scaleDenominator / denom));
|
if (delta < diff) {
|
diff = delta;
|
matrix = this.matrixIds[i];
|
}
|
}
|
} else {
|
// fall back on zoom as index
|
matrix = this.matrixIds[this.getIdentifier()];
|
}
|
}
|
return matrix;
|
},
|
|
/**
|
* Method: getTileInfo
|
* Get tile information for a given location at the current map resolution.
|
*
|
* Parameters:
|
* loc - {<OpenLayers.LonLat} A location in map coordinates.
|
*
|
* Returns:
|
* {Object} An object with "col", "row", "i", and "j" properties. The col
|
* and row values are zero based tile indexes from the top left. The
|
* i and j values are the number of pixels to the left and top
|
* (respectively) of the given location within the target tile.
|
*/
|
getTileInfo: function(loc) {
|
var res = this.getServerResolution();
|
|
var fx = (loc.lon - this.tileOrigin.lon) / (res * this.tileSize.w);
|
var fy = (this.tileOrigin.lat - loc.lat) / (res * this.tileSize.h);
|
|
var col = Math.floor(fx);
|
var row = Math.floor(fy);
|
|
return {
|
col: col,
|
row: row,
|
i: Math.floor((fx - col) * this.tileSize.w),
|
j: Math.floor((fy - row) * this.tileSize.h)
|
};
|
},
|
|
/**
|
* Method: getURL
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
*
|
* Returns:
|
* {String} A URL for the tile corresponding to the given bounds.
|
*/
|
getURL: function(bounds) {
|
bounds = this.adjustBounds(bounds);
|
var url = "";
|
if (!this.tileFullExtent || this.tileFullExtent.intersectsBounds(bounds)) {
|
|
var center = bounds.getCenterLonLat();
|
var info = this.getTileInfo(center);
|
var matrixId = this.matrix.identifier;
|
var dimensions = this.dimensions, params;
|
|
if (OpenLayers.Util.isArray(this.url)) {
|
url = this.selectUrl([
|
this.version, this.style, this.matrixSet,
|
this.matrix.identifier, info.row, info.col
|
].join(","), this.url);
|
} else {
|
url = this.url;
|
}
|
|
if (this.requestEncoding.toUpperCase() === "REST") {
|
params = this.params;
|
if (url.indexOf("{") !== -1) {
|
var template = url.replace(/\{/g, "${");
|
var context = {
|
// spec does not make clear if capital S or not
|
style: this.style, Style: this.style,
|
TileMatrixSet: this.matrixSet,
|
TileMatrix: this.matrix.identifier,
|
TileRow: info.row,
|
TileCol: info.col
|
};
|
if (dimensions) {
|
var dimension, i;
|
for (i=dimensions.length-1; i>=0; --i) {
|
dimension = dimensions[i];
|
context[dimension] = params[dimension.toUpperCase()];
|
}
|
}
|
url = OpenLayers.String.format(template, context);
|
} else {
|
// include 'version', 'layer' and 'style' in tile resource url
|
var path = this.version + "/" + this.layer + "/" + this.style + "/";
|
|
// append optional dimension path elements
|
if (dimensions) {
|
for (var i=0; i<dimensions.length; i++) {
|
if (params[dimensions[i]]) {
|
path = path + params[dimensions[i]] + "/";
|
}
|
}
|
}
|
|
// append other required path elements
|
path = path + this.matrixSet + "/" + this.matrix.identifier +
|
"/" + info.row + "/" + info.col + "." + this.formatSuffix;
|
|
if (!url.match(/\/$/)) {
|
url = url + "/";
|
}
|
url = url + path;
|
}
|
} else if (this.requestEncoding.toUpperCase() === "KVP") {
|
|
// assemble all required parameters
|
params = {
|
SERVICE: "WMTS",
|
REQUEST: "GetTile",
|
VERSION: this.version,
|
LAYER: this.layer,
|
STYLE: this.style,
|
TILEMATRIXSET: this.matrixSet,
|
TILEMATRIX: this.matrix.identifier,
|
TILEROW: info.row,
|
TILECOL: info.col,
|
FORMAT: this.format
|
};
|
url = OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this, [params]);
|
|
}
|
}
|
return url;
|
},
|
|
/**
|
* APIMethod: mergeNewParams
|
* Extend the existing layer <params> with new properties. Tiles will be
|
* reloaded with updated params in the request.
|
*
|
* Parameters:
|
* newParams - {Object} Properties to extend to existing <params>.
|
*/
|
mergeNewParams: function(newParams) {
|
if (this.requestEncoding.toUpperCase() === "KVP") {
|
return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(
|
this, [OpenLayers.Util.upperCaseObject(newParams)]
|
);
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.WMTS"
|
});
|
/* ======================================================================
|
OpenLayers/Protocol/SOS/v1_0_0.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Protocol/SOS.js
|
* @requires OpenLayers/Format/SOSGetFeatureOfInterest.js
|
*/
|
|
/**
|
* Class: OpenLayers.Protocol.SOS.v1_0_0
|
* An SOS v1.0.0 Protocol for vector layers. Create a new instance with the
|
* <OpenLayers.Protocol.SOS.v1_0_0> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Protocol>
|
*/
|
OpenLayers.Protocol.SOS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol, {
|
|
/**
|
* APIProperty: fois
|
* {Array(String)} Array of features of interest (foi)
|
*/
|
fois: null,
|
|
/**
|
* Property: formatOptions
|
* {Object} Optional options for the format. If a format is not provided,
|
* this property can be used to extend the default format options.
|
*/
|
formatOptions: null,
|
|
/**
|
* Constructor: OpenLayers.Protocol.SOS
|
* A class for giving layers an SOS protocol.
|
*
|
* Parameters:
|
* options - {Object} Optional object whose properties will be set on the
|
* instance.
|
*
|
* Valid options properties:
|
* url - {String} URL to send requests to (required).
|
* fois - {Array} The features of interest (required).
|
*/
|
initialize: function(options) {
|
OpenLayers.Protocol.prototype.initialize.apply(this, [options]);
|
if(!options.format) {
|
this.format = new OpenLayers.Format.SOSGetFeatureOfInterest(
|
this.formatOptions);
|
}
|
},
|
|
/**
|
* APIMethod: destroy
|
* Clean up the protocol.
|
*/
|
destroy: function() {
|
if(this.options && !this.options.format) {
|
this.format.destroy();
|
}
|
this.format = null;
|
OpenLayers.Protocol.prototype.destroy.apply(this);
|
},
|
|
/**
|
* APIMethod: read
|
* Construct a request for reading new sensor positions. This is done by
|
* issuing one GetFeatureOfInterest request.
|
*/
|
read: function(options) {
|
options = OpenLayers.Util.extend({}, options);
|
OpenLayers.Util.applyDefaults(options, this.options || {});
|
var response = new OpenLayers.Protocol.Response({requestType: "read"});
|
var format = this.format;
|
var data = OpenLayers.Format.XML.prototype.write.apply(format,
|
[format.writeNode("sos:GetFeatureOfInterest", {fois: this.fois})]
|
);
|
response.priv = OpenLayers.Request.POST({
|
url: options.url,
|
callback: this.createCallback(this.handleRead, response, options),
|
data: data
|
});
|
return response;
|
},
|
|
/**
|
* Method: handleRead
|
* Deal with response from the read request.
|
*
|
* Parameters:
|
* response - {<OpenLayers.Protocol.Response>} The response object to pass
|
* to the user callback.
|
* options - {Object} The user options passed to the read call.
|
*/
|
handleRead: function(response, options) {
|
if(options.callback) {
|
var request = response.priv;
|
if(request.status >= 200 && request.status < 300) {
|
// success
|
response.features = this.parseFeatures(request);
|
response.code = OpenLayers.Protocol.Response.SUCCESS;
|
} else {
|
// failure
|
response.code = OpenLayers.Protocol.Response.FAILURE;
|
}
|
options.callback.call(options.scope, response);
|
}
|
},
|
|
/**
|
* Method: parseFeatures
|
* Read HTTP response body and return features
|
*
|
* Parameters:
|
* request - {XMLHttpRequest} The request object
|
*
|
* Returns:
|
* {Array({<OpenLayers.Feature.Vector>})} Array of features
|
*/
|
parseFeatures: function(request) {
|
var doc = request.responseXML;
|
if(!doc || !doc.documentElement) {
|
doc = request.responseText;
|
}
|
if(!doc || doc.length <= 0) {
|
return null;
|
}
|
return this.format.read(doc);
|
},
|
|
CLASS_NAME: "OpenLayers.Protocol.SOS.v1_0_0"
|
});
|
/* ======================================================================
|
OpenLayers/Layer/KaMapCache.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
|
/**
|
* @requires OpenLayers/Layer/Grid.js
|
* @requires OpenLayers/Layer/KaMap.js
|
*/
|
|
/**
|
* Class: OpenLayers.Layer.KaMapCache
|
*
|
* This class is designed to talk directly to a web-accessible ka-Map
|
* cache generated by the precache2.php script.
|
*
|
* To create a a new KaMapCache layer, you must indicate also the "i" parameter
|
* (that will be used to calculate the file extension), and another special
|
* parameter, object names "metaTileSize", with "h" (height) and "w" (width)
|
* properties.
|
*
|
* // Create a new kaMapCache layer.
|
* var kamap_base = new OpenLayers.Layer.KaMapCache(
|
* "Satellite",
|
* "http://www.example.org/web/acessible/cache",
|
* {g: "satellite", map: "world", i: 'png24', metaTileSize: {w: 5, h: 5} }
|
* );
|
*
|
* // Create an kaMapCache overlay layer (using "isBaseLayer: false").
|
* // Forces the output to be a "gif", using the "i" parameter.
|
* var kamap_overlay = new OpenLayers.Layer.KaMapCache(
|
* "Streets",
|
* "http://www.example.org/web/acessible/cache",
|
* {g: "streets", map: "world", i: "gif", metaTileSize: {w: 5, h: 5} },
|
* {isBaseLayer: false}
|
* );
|
*
|
* The cache URLs must look like:
|
* var/cache/World/50000/Group_Name/def/t-440320/l20480
|
*
|
* This means that the cache generated via tile.php will *not* work with
|
* this class, and should instead use the KaMap layer.
|
*
|
* More information is available in Ticket #1518.
|
*
|
* Inherits from:
|
* - <OpenLayers.Layer.KaMap>
|
* - <OpenLayers.Layer.Grid>
|
*/
|
OpenLayers.Layer.KaMapCache = OpenLayers.Class(OpenLayers.Layer.KaMap, {
|
|
/**
|
* Constant: IMAGE_EXTENSIONS
|
* {Object} Simple hash map to convert format to extension.
|
*/
|
IMAGE_EXTENSIONS: {
|
'jpeg': 'jpg',
|
'gif' : 'gif',
|
'png' : 'png',
|
'png8' : 'png',
|
'png24' : 'png',
|
'dithered' : 'png'
|
},
|
|
/**
|
* Constant: DEFAULT_FORMAT
|
* {Object} Simple hash map to convert format to extension.
|
*/
|
DEFAULT_FORMAT: 'jpeg',
|
|
/**
|
* Constructor: OpenLayers.Layer.KaMapCache
|
*
|
* Parameters:
|
* name - {String}
|
* url - {String}
|
* params - {Object} Parameters to be sent to the HTTP server in the
|
* query string for the tile. The format can be set via the 'i'
|
* parameter (defaults to jpg) , and the map should be set via
|
* the 'map' parameter. It has been reported that ka-Map may behave
|
* inconsistently if your format parameter does not match the format
|
* parameter configured in your config.php. (See ticket #327 for more
|
* information.)
|
* options - {Object} Additional options for the layer. Any of the
|
* APIProperties listed on this layer, and any layer types it
|
* extends, can be overridden through the options parameter.
|
*/
|
initialize: function(name, url, params, options) {
|
OpenLayers.Layer.KaMap.prototype.initialize.apply(this, arguments);
|
this.extension = this.IMAGE_EXTENSIONS[this.params.i.toLowerCase() || this.DEFAULT_FORMAT];
|
},
|
|
/**
|
* Method: getURL
|
*
|
* Parameters:
|
* bounds - {<OpenLayers.Bounds>}
|
*
|
* Returns:
|
* {String} A string with the layer's url and parameters and also the
|
* passed-in bounds and appropriate tile size specified as
|
* parameters
|
*/
|
getURL: function (bounds) {
|
bounds = this.adjustBounds(bounds);
|
var mapRes = this.map.getResolution();
|
var scale = Math.round((this.map.getScale() * 10000)) / 10000;
|
var pX = Math.round(bounds.left / mapRes);
|
var pY = -Math.round(bounds.top / mapRes);
|
|
var metaX = Math.floor(pX / this.tileSize.w / this.params.metaTileSize.w) * this.tileSize.w * this.params.metaTileSize.w;
|
var metaY = Math.floor(pY / this.tileSize.h / this.params.metaTileSize.h) * this.tileSize.h * this.params.metaTileSize.h;
|
|
var components = [
|
"/",
|
this.params.map,
|
"/",
|
scale,
|
"/",
|
this.params.g.replace(/\s/g, '_'),
|
"/def/t",
|
metaY,
|
"/l",
|
metaX,
|
"/t",
|
pY,
|
"l",
|
pX,
|
".",
|
this.extension
|
];
|
|
var url = this.url;
|
|
if (OpenLayers.Util.isArray(url)) {
|
url = this.selectUrl(components.join(''), url);
|
}
|
return url + components.join("");
|
},
|
|
CLASS_NAME: "OpenLayers.Layer.KaMapCache"
|
});
|
/* ======================================================================
|
OpenLayers/Protocol/WFS/v1_1_0.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Protocol/WFS/v1.js
|
* @requires OpenLayers/Format/WFST/v1_1_0.js
|
*/
|
|
/**
|
* Class: OpenLayers.Protocol.WFS.v1_1_0
|
* A WFS v1.1.0 protocol for vector layers. Create a new instance with the
|
* <OpenLayers.Protocol.WFS.v1_1_0> constructor.
|
*
|
* Differences from the v1.0.0 protocol:
|
* - uses Filter Encoding 1.1.0 instead of 1.0.0
|
* - uses GML 3 instead of 2 if no format is provided
|
*
|
* Inherits from:
|
* - <OpenLayers.Protocol.WFS.v1>
|
*/
|
OpenLayers.Protocol.WFS.v1_1_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, {
|
|
/**
|
* Property: version
|
* {String} WFS version number.
|
*/
|
version: "1.1.0",
|
|
/**
|
* Constructor: OpenLayers.Protocol.WFS.v1_1_0
|
* A class for giving layers WFS v1.1.0 protocol.
|
*
|
* Parameters:
|
* options - {Object} Optional object whose properties will be set on the
|
* instance.
|
*
|
* Valid options properties:
|
* featureType - {String} Local (without prefix) feature typeName (required).
|
* featureNS - {String} Feature namespace (optional).
|
* featurePrefix - {String} Feature namespace alias (optional - only used
|
* if featureNS is provided). Default is 'feature'.
|
* geometryName - {String} Name of geometry attribute. Default is 'the_geom'.
|
* outputFormat - {String} Optional output format to use for WFS GetFeature
|
* requests. This can be any format advertized by the WFS's
|
* GetCapabilities response. If set, an appropriate readFormat also
|
* has to be provided, unless outputFormat is GML3, GML2 or JSON.
|
* readFormat - {<OpenLayers.Format>} An appropriate format parser if
|
* outputFormat is none of GML3, GML2 or JSON.
|
*/
|
initialize: function(options) {
|
OpenLayers.Protocol.WFS.v1.prototype.initialize.apply(this, arguments);
|
if (this.outputFormat && !this.readFormat) {
|
if (this.outputFormat.toLowerCase() == "gml2") {
|
this.readFormat = new OpenLayers.Format.GML.v2({
|
featureType: this.featureType,
|
featureNS: this.featureNS,
|
geometryName: this.geometryName
|
});
|
} else if (this.outputFormat.toLowerCase() == "json") {
|
this.readFormat = new OpenLayers.Format.GeoJSON();
|
}
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Protocol.WFS.v1_1_0"
|
});
|
/* ======================================================================
|
OpenLayers/Format/WMSCapabilities/v1_1_1.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/WMSCapabilities/v1_1.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WMSCapabilities/v1_1_1
|
* Read WMS Capabilities version 1.1.1.
|
*
|
* Note on <ScaleHint> parsing: If the 'min' attribute is set to "0", no
|
* maxScale will be set on the layer object. If the 'max' attribute is set to
|
* "Infinity", no minScale will be set. This makes it easy to create proper
|
* {<OpenLayers.Layer.WMS>} configurations directly from the layer object
|
* literals returned by this format, because no minScale/maxScale modifications
|
* need to be made.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.WMSCapabilities.v1_1>
|
*/
|
OpenLayers.Format.WMSCapabilities.v1_1_1 = OpenLayers.Class(
|
OpenLayers.Format.WMSCapabilities.v1_1, {
|
|
/**
|
* Property: version
|
* {String} The specific parser version.
|
*/
|
version: "1.1.1",
|
|
/**
|
* Constructor: OpenLayers.Format.WMSCapabilities.v1_1_1
|
* Create a new parser for WMS capabilities version 1.1.1.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"wms": OpenLayers.Util.applyDefaults({
|
"SRS": function(node, obj) {
|
obj.srs[this.getChildValue(node)] = true;
|
}
|
}, OpenLayers.Format.WMSCapabilities.v1_1.prototype.readers["wms"])
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_1_1"
|
|
});
|
/* ======================================================================
|
OpenLayers/Format/WMSCapabilities/v1_1_1_WMSC.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/WMSCapabilities/v1_1_1.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WMSCapabilities/v1_1_1_WMSC
|
* Read WMS-C Capabilities version 1.1.1.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.WMSCapabilities.v1_1_1>
|
*/
|
OpenLayers.Format.WMSCapabilities.v1_1_1_WMSC = OpenLayers.Class(
|
OpenLayers.Format.WMSCapabilities.v1_1_1, {
|
|
/**
|
* Property: version
|
* {String} The specific parser version.
|
*/
|
version: "1.1.1",
|
|
/**
|
* Property: profile
|
* {String} The specific profile
|
*/
|
profile: "WMSC",
|
|
/**
|
* Constructor: OpenLayers.Format.WMSCapabilities.v1_1_1
|
* Create a new parser for WMS-C capabilities version 1.1.1.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"wms": OpenLayers.Util.applyDefaults({
|
"VendorSpecificCapabilities": function(node, obj) {
|
obj.vendorSpecific = {tileSets: []};
|
this.readChildNodes(node, obj.vendorSpecific);
|
},
|
"TileSet": function(node, vendorSpecific) {
|
var tileset = {srs: {}, bbox: {}, resolutions: []};
|
this.readChildNodes(node, tileset);
|
vendorSpecific.tileSets.push(tileset);
|
},
|
"Resolutions": function(node, tileset) {
|
var res = this.getChildValue(node).split(" ");
|
for (var i=0, len=res.length; i<len; i++) {
|
if (res[i] != "") {
|
tileset.resolutions.push(parseFloat(res[i]));
|
}
|
}
|
},
|
"Width": function(node, tileset) {
|
tileset.width = parseInt(this.getChildValue(node));
|
},
|
"Height": function(node, tileset) {
|
tileset.height = parseInt(this.getChildValue(node));
|
},
|
"Layers": function(node, tileset) {
|
tileset.layers = this.getChildValue(node);
|
},
|
"Styles": function(node, tileset) {
|
tileset.styles = this.getChildValue(node);
|
}
|
}, OpenLayers.Format.WMSCapabilities.v1_1_1.prototype.readers["wms"])
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_1_1_WMSC"
|
|
});
|
/* ======================================================================
|
OpenLayers/Control/LayerSwitcher.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/Lang.js
|
* @requires OpenLayers/Util.js
|
* @requires OpenLayers/Events/buttonclick.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.LayerSwitcher
|
* The LayerSwitcher control displays a table of contents for the map. This
|
* allows the user interface to switch between BaseLasyers and to show or hide
|
* Overlays. By default the switcher is shown minimized on the right edge of
|
* the map, the user may expand it by clicking on the handle.
|
*
|
* To create the LayerSwitcher outside of the map, pass the Id of a html div
|
* as the first argument to the constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.LayerSwitcher = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* Property: layerStates
|
* {Array(Object)} Basically a copy of the "state" of the map's layers
|
* the last time the control was drawn. We have this in order to avoid
|
* unnecessarily redrawing the control.
|
*/
|
layerStates: null,
|
|
// DOM Elements
|
|
/**
|
* Property: layersDiv
|
* {DOMElement}
|
*/
|
layersDiv: null,
|
|
/**
|
* Property: baseLayersDiv
|
* {DOMElement}
|
*/
|
baseLayersDiv: null,
|
|
/**
|
* Property: baseLayers
|
* {Array(Object)}
|
*/
|
baseLayers: null,
|
|
|
/**
|
* Property: dataLbl
|
* {DOMElement}
|
*/
|
dataLbl: null,
|
|
/**
|
* Property: dataLayersDiv
|
* {DOMElement}
|
*/
|
dataLayersDiv: null,
|
|
/**
|
* Property: dataLayers
|
* {Array(Object)}
|
*/
|
dataLayers: null,
|
|
|
/**
|
* Property: minimizeDiv
|
* {DOMElement}
|
*/
|
minimizeDiv: null,
|
|
/**
|
* Property: maximizeDiv
|
* {DOMElement}
|
*/
|
maximizeDiv: null,
|
|
/**
|
* APIProperty: ascending
|
* {Boolean}
|
*/
|
ascending: true,
|
|
/**
|
* Constructor: OpenLayers.Control.LayerSwitcher
|
*
|
* Parameters:
|
* options - {Object}
|
*/
|
initialize: function(options) {
|
OpenLayers.Control.prototype.initialize.apply(this, arguments);
|
this.layerStates = [];
|
},
|
|
/**
|
* APIMethod: destroy
|
*/
|
destroy: function() {
|
|
//clear out layers info and unregister their events
|
this.clearLayersArray("base");
|
this.clearLayersArray("data");
|
|
this.map.events.un({
|
buttonclick: this.onButtonClick,
|
addlayer: this.redraw,
|
changelayer: this.redraw,
|
removelayer: this.redraw,
|
changebaselayer: this.redraw,
|
scope: this
|
});
|
this.events.unregister("buttonclick", this, this.onButtonClick);
|
|
OpenLayers.Control.prototype.destroy.apply(this, arguments);
|
},
|
|
/**
|
* Method: setMap
|
*
|
* Properties:
|
* map - {<OpenLayers.Map>}
|
*/
|
setMap: function(map) {
|
OpenLayers.Control.prototype.setMap.apply(this, arguments);
|
|
this.map.events.on({
|
addlayer: this.redraw,
|
changelayer: this.redraw,
|
removelayer: this.redraw,
|
changebaselayer: this.redraw,
|
scope: this
|
});
|
if (this.outsideViewport) {
|
this.events.attachToElement(this.div);
|
this.events.register("buttonclick", this, this.onButtonClick);
|
} else {
|
this.map.events.register("buttonclick", this, this.onButtonClick);
|
}
|
},
|
|
/**
|
* Method: draw
|
*
|
* Returns:
|
* {DOMElement} A reference to the DIV DOMElement containing the
|
* switcher tabs.
|
*/
|
draw: function() {
|
OpenLayers.Control.prototype.draw.apply(this);
|
|
// create layout divs
|
this.loadContents();
|
|
// set mode to minimize
|
if(!this.outsideViewport) {
|
this.minimizeControl();
|
}
|
|
// populate div with current info
|
this.redraw();
|
|
return this.div;
|
},
|
|
/**
|
* Method: onButtonClick
|
*
|
* Parameters:
|
* evt - {Event}
|
*/
|
onButtonClick: function(evt) {
|
var button = evt.buttonElement;
|
if (button === this.minimizeDiv) {
|
this.minimizeControl();
|
} else if (button === this.maximizeDiv) {
|
this.maximizeControl();
|
} else if (button._layerSwitcher === this.id) {
|
if (button["for"]) {
|
button = document.getElementById(button["for"]);
|
}
|
if (!button.disabled) {
|
if (button.type == "radio") {
|
button.checked = true;
|
this.map.setBaseLayer(this.map.getLayer(button._layer));
|
} else {
|
button.checked = !button.checked;
|
this.updateMap();
|
}
|
}
|
}
|
},
|
|
/**
|
* Method: clearLayersArray
|
* User specifies either "base" or "data". we then clear all the
|
* corresponding listeners, the div, and reinitialize a new array.
|
*
|
* Parameters:
|
* layersType - {String}
|
*/
|
clearLayersArray: function(layersType) {
|
this[layersType + "LayersDiv"].innerHTML = "";
|
this[layersType + "Layers"] = [];
|
},
|
|
|
/**
|
* Method: checkRedraw
|
* Checks if the layer state has changed since the last redraw() call.
|
*
|
* Returns:
|
* {Boolean} The layer state changed since the last redraw() call.
|
*/
|
checkRedraw: function() {
|
if ( !this.layerStates.length ||
|
(this.map.layers.length != this.layerStates.length) ) {
|
return true;
|
}
|
|
for (var i = 0, len = this.layerStates.length; i < len; i++) {
|
var layerState = this.layerStates[i];
|
var layer = this.map.layers[i];
|
if ( (layerState.name != layer.name) ||
|
(layerState.inRange != layer.inRange) ||
|
(layerState.id != layer.id) ||
|
(layerState.visibility != layer.visibility) ) {
|
return true;
|
}
|
}
|
|
return false;
|
},
|
|
/**
|
* Method: redraw
|
* Goes through and takes the current state of the Map and rebuilds the
|
* control to display that state. Groups base layers into a
|
* radio-button group and lists each data layer with a checkbox.
|
*
|
* Returns:
|
* {DOMElement} A reference to the DIV DOMElement containing the control
|
*/
|
redraw: function() {
|
//if the state hasn't changed since last redraw, no need
|
// to do anything. Just return the existing div.
|
if (!this.checkRedraw()) {
|
return this.div;
|
}
|
|
//clear out previous layers
|
this.clearLayersArray("base");
|
this.clearLayersArray("data");
|
|
var containsOverlays = false;
|
var containsBaseLayers = false;
|
|
// Save state -- for checking layer if the map state changed.
|
// We save this before redrawing, because in the process of redrawing
|
// we will trigger more visibility changes, and we want to not redraw
|
// and enter an infinite loop.
|
var len = this.map.layers.length;
|
this.layerStates = new Array(len);
|
for (var i=0; i <len; i++) {
|
var layer = this.map.layers[i];
|
this.layerStates[i] = {
|
'name': layer.name,
|
'visibility': layer.visibility,
|
'inRange': layer.inRange,
|
'id': layer.id
|
};
|
}
|
|
var layers = this.map.layers.slice();
|
if (!this.ascending) { layers.reverse(); }
|
for(var i=0, len=layers.length; i<len; i++) {
|
var layer = layers[i];
|
var baseLayer = layer.isBaseLayer;
|
|
if (layer.displayInLayerSwitcher) {
|
|
if (baseLayer) {
|
containsBaseLayers = true;
|
} else {
|
containsOverlays = true;
|
}
|
|
// only check a baselayer if it is *the* baselayer, check data
|
// layers if they are visible
|
var checked = (baseLayer) ? (layer == this.map.baseLayer)
|
: layer.getVisibility();
|
|
// create input element
|
var inputElem = document.createElement("input"),
|
// The input shall have an id attribute so we can use
|
// labels to interact with them.
|
inputId = OpenLayers.Util.createUniqueID(
|
this.id + "_input_"
|
);
|
|
inputElem.id = inputId;
|
inputElem.name = (baseLayer) ? this.id + "_baseLayers" : layer.name;
|
inputElem.type = (baseLayer) ? "radio" : "checkbox";
|
inputElem.value = layer.name;
|
inputElem.checked = checked;
|
inputElem.defaultChecked = checked;
|
inputElem.className = "olButton";
|
inputElem._layer = layer.id;
|
inputElem._layerSwitcher = this.id;
|
|
if (!baseLayer && !layer.inRange) {
|
inputElem.disabled = true;
|
}
|
|
// create span
|
var labelSpan = document.createElement("label");
|
// this isn't the DOM attribute 'for', but an arbitrary name we
|
// use to find the appropriate input element in <onButtonClick>
|
labelSpan["for"] = inputElem.id;
|
OpenLayers.Element.addClass(labelSpan, "labelSpan olButton");
|
labelSpan._layer = layer.id;
|
labelSpan._layerSwitcher = this.id;
|
if (!baseLayer && !layer.inRange) {
|
labelSpan.style.color = "gray";
|
}
|
labelSpan.innerHTML = layer.name;
|
labelSpan.style.verticalAlign = (baseLayer) ? "bottom"
|
: "baseline";
|
// create line break
|
var br = document.createElement("br");
|
|
|
var groupArray = (baseLayer) ? this.baseLayers
|
: this.dataLayers;
|
groupArray.push({
|
'layer': layer,
|
'inputElem': inputElem,
|
'labelSpan': labelSpan
|
});
|
|
|
var groupDiv = (baseLayer) ? this.baseLayersDiv
|
: this.dataLayersDiv;
|
groupDiv.appendChild(inputElem);
|
groupDiv.appendChild(labelSpan);
|
groupDiv.appendChild(br);
|
}
|
}
|
|
// if no overlays, dont display the overlay label
|
this.dataLbl.style.display = (containsOverlays) ? "" : "none";
|
|
// if no baselayers, dont display the baselayer label
|
this.baseLbl.style.display = (containsBaseLayers) ? "" : "none";
|
|
return this.div;
|
},
|
|
/**
|
* Method: updateMap
|
* Cycles through the loaded data and base layer input arrays and makes
|
* the necessary calls to the Map object such that that the map's
|
* visual state corresponds to what the user has selected in
|
* the control.
|
*/
|
updateMap: function() {
|
|
// set the newly selected base layer
|
for(var i=0, len=this.baseLayers.length; i<len; i++) {
|
var layerEntry = this.baseLayers[i];
|
if (layerEntry.inputElem.checked) {
|
this.map.setBaseLayer(layerEntry.layer, false);
|
}
|
}
|
|
// set the correct visibilities for the overlays
|
for(var i=0, len=this.dataLayers.length; i<len; i++) {
|
var layerEntry = this.dataLayers[i];
|
layerEntry.layer.setVisibility(layerEntry.inputElem.checked);
|
}
|
|
},
|
|
/**
|
* Method: maximizeControl
|
* Set up the labels and divs for the control
|
*
|
* Parameters:
|
* e - {Event}
|
*/
|
maximizeControl: function(e) {
|
|
// set the div's width and height to empty values, so
|
// the div dimensions can be controlled by CSS
|
this.div.style.width = "";
|
this.div.style.height = "";
|
|
this.showControls(false);
|
|
if (e != null) {
|
OpenLayers.Event.stop(e);
|
}
|
},
|
|
/**
|
* Method: minimizeControl
|
* Hide all the contents of the control, shrink the size,
|
* add the maximize icon
|
*
|
* Parameters:
|
* e - {Event}
|
*/
|
minimizeControl: function(e) {
|
|
// to minimize the control we set its div's width
|
// and height to 0px, we cannot just set "display"
|
// to "none" because it would hide the maximize
|
// div
|
this.div.style.width = "0px";
|
this.div.style.height = "0px";
|
|
this.showControls(true);
|
|
if (e != null) {
|
OpenLayers.Event.stop(e);
|
}
|
},
|
|
/**
|
* Method: showControls
|
* Hide/Show all LayerSwitcher controls depending on whether we are
|
* minimized or not
|
*
|
* Parameters:
|
* minimize - {Boolean}
|
*/
|
showControls: function(minimize) {
|
|
this.maximizeDiv.style.display = minimize ? "" : "none";
|
this.minimizeDiv.style.display = minimize ? "none" : "";
|
|
this.layersDiv.style.display = minimize ? "none" : "";
|
},
|
|
/**
|
* Method: loadContents
|
* Set up the labels and divs for the control
|
*/
|
loadContents: function() {
|
|
// layers list div
|
this.layersDiv = document.createElement("div");
|
this.layersDiv.id = this.id + "_layersDiv";
|
OpenLayers.Element.addClass(this.layersDiv, "layersDiv");
|
|
this.baseLbl = document.createElement("div");
|
this.baseLbl.innerHTML = OpenLayers.i18n("Base Layer");
|
OpenLayers.Element.addClass(this.baseLbl, "baseLbl");
|
|
this.baseLayersDiv = document.createElement("div");
|
OpenLayers.Element.addClass(this.baseLayersDiv, "baseLayersDiv");
|
|
this.dataLbl = document.createElement("div");
|
this.dataLbl.innerHTML = OpenLayers.i18n("Overlays");
|
OpenLayers.Element.addClass(this.dataLbl, "dataLbl");
|
|
this.dataLayersDiv = document.createElement("div");
|
OpenLayers.Element.addClass(this.dataLayersDiv, "dataLayersDiv");
|
|
if (this.ascending) {
|
this.layersDiv.appendChild(this.baseLbl);
|
this.layersDiv.appendChild(this.baseLayersDiv);
|
this.layersDiv.appendChild(this.dataLbl);
|
this.layersDiv.appendChild(this.dataLayersDiv);
|
} else {
|
this.layersDiv.appendChild(this.dataLbl);
|
this.layersDiv.appendChild(this.dataLayersDiv);
|
this.layersDiv.appendChild(this.baseLbl);
|
this.layersDiv.appendChild(this.baseLayersDiv);
|
}
|
|
this.div.appendChild(this.layersDiv);
|
|
// maximize button div
|
var img = OpenLayers.Util.getImageLocation('layer-switcher-maximize.png');
|
this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(
|
"OpenLayers_Control_MaximizeDiv",
|
null,
|
null,
|
img,
|
"absolute");
|
OpenLayers.Element.addClass(this.maximizeDiv, "maximizeDiv olButton");
|
this.maximizeDiv.style.display = "none";
|
|
this.div.appendChild(this.maximizeDiv);
|
|
// minimize button div
|
var img = OpenLayers.Util.getImageLocation('layer-switcher-minimize.png');
|
this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(
|
"OpenLayers_Control_MinimizeDiv",
|
null,
|
null,
|
img,
|
"absolute");
|
OpenLayers.Element.addClass(this.minimizeDiv, "minimizeDiv olButton");
|
this.minimizeDiv.style.display = "none";
|
|
this.div.appendChild(this.minimizeDiv);
|
},
|
|
CLASS_NAME: "OpenLayers.Control.LayerSwitcher"
|
});
|
/* ======================================================================
|
OpenLayers/Format/Atom.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/XML.js
|
* @requires OpenLayers/Format/GML/v3.js
|
* @requires OpenLayers/Feature/Vector.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.Atom
|
* Read/write Atom feeds. Create a new instance with the
|
* <OpenLayers.Format.AtomFeed> constructor.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.XML>
|
*/
|
OpenLayers.Format.Atom = OpenLayers.Class(OpenLayers.Format.XML, {
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs. Properties
|
* of this object should not be set individually. Read-only. All
|
* XML subclasses should have their own namespaces object. Use
|
* <setNamespace> to add or set a namespace alias after construction.
|
*/
|
namespaces: {
|
atom: "http://www.w3.org/2005/Atom",
|
georss: "http://www.georss.org/georss"
|
},
|
|
/**
|
* APIProperty: feedTitle
|
* {String} Atom feed elements require a title. Default is "untitled".
|
*/
|
feedTitle: "untitled",
|
|
/**
|
* APIProperty: defaultEntryTitle
|
* {String} Atom entry elements require a title. In cases where one is
|
* not provided in the feature attributes, this will be used. Default
|
* is "untitled".
|
*/
|
defaultEntryTitle: "untitled",
|
|
/**
|
* Property: gmlParse
|
* {Object} GML Format object for parsing features
|
* Non-API and only created if necessary
|
*/
|
gmlParser: null,
|
|
/**
|
* APIProperty: xy
|
* {Boolean} Order of the GML coordinate: true:(x,y) or false:(y,x)
|
* For GeoRSS the default is (y,x), therefore: false
|
*/
|
xy: false,
|
|
/**
|
* Constructor: OpenLayers.Format.AtomEntry
|
* Create a new parser for Atom.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
|
/**
|
* APIMethod: read
|
* Return a list of features from an Atom feed or entry document.
|
|
* Parameters:
|
* doc - {Element} or {String}
|
*
|
* Returns:
|
* Array({<OpenLayers.Feature.Vector>})
|
*/
|
read: function(doc) {
|
if (typeof doc == "string") {
|
doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);
|
}
|
return this.parseFeatures(doc);
|
},
|
|
/**
|
* APIMethod: write
|
* Serialize or more feature nodes to Atom documents.
|
*
|
* Parameters:
|
* features - {<OpenLayers.Feature.Vector>} or Array({<OpenLayers.Feature.Vector>})
|
*
|
* Returns:
|
* {String} an Atom entry document if passed one feature node, or a feed
|
* document if passed an array of feature nodes.
|
*/
|
write: function(features) {
|
var doc;
|
if (OpenLayers.Util.isArray(features)) {
|
doc = this.createElementNSPlus("atom:feed");
|
doc.appendChild(
|
this.createElementNSPlus("atom:title", {
|
value: this.feedTitle
|
})
|
);
|
for (var i=0, ii=features.length; i<ii; i++) {
|
doc.appendChild(this.buildEntryNode(features[i]));
|
}
|
}
|
else {
|
doc = this.buildEntryNode(features);
|
}
|
return OpenLayers.Format.XML.prototype.write.apply(this, [doc]);
|
},
|
|
/**
|
* Method: buildContentNode
|
*
|
* Parameters:
|
* content - {Object}
|
*
|
* Returns:
|
* {DOMElement} an Atom content node.
|
*
|
* TODO: types other than text.
|
*/
|
buildContentNode: function(content) {
|
var node = this.createElementNSPlus("atom:content", {
|
attributes: {
|
type: content.type || null
|
}
|
});
|
if (content.src) {
|
node.setAttribute("src", content.src);
|
} else {
|
if (content.type == "text" || content.type == null) {
|
node.appendChild(
|
this.createTextNode(content.value)
|
);
|
} else if (content.type == "html") {
|
if (typeof content.value != "string") {
|
throw "HTML content must be in form of an escaped string";
|
}
|
node.appendChild(
|
this.createTextNode(content.value)
|
);
|
} else if (content.type == "xhtml") {
|
node.appendChild(content.value);
|
} else if (content.type == "xhtml" ||
|
content.type.match(/(\+|\/)xml$/)) {
|
node.appendChild(content.value);
|
}
|
else { // MUST be a valid Base64 encoding
|
node.appendChild(
|
this.createTextNode(content.value)
|
);
|
}
|
}
|
return node;
|
},
|
|
/**
|
* Method: buildEntryNode
|
* Build an Atom entry node from a feature object.
|
*
|
* Parameters:
|
* feature - {<OpenLayers.Feature.Vector>}
|
*
|
* Returns:
|
* {DOMElement} an Atom entry node.
|
*
|
* These entries are geared for publication using AtomPub.
|
*
|
* TODO: support extension elements
|
*/
|
buildEntryNode: function(feature) {
|
var attrib = feature.attributes;
|
var atomAttrib = attrib.atom || {};
|
var entryNode = this.createElementNSPlus("atom:entry");
|
|
// atom:author
|
if (atomAttrib.authors) {
|
var authors = OpenLayers.Util.isArray(atomAttrib.authors) ?
|
atomAttrib.authors : [atomAttrib.authors];
|
for (var i=0, ii=authors.length; i<ii; i++) {
|
entryNode.appendChild(
|
this.buildPersonConstructNode(
|
"author", authors[i]
|
)
|
);
|
}
|
}
|
|
// atom:category
|
if (atomAttrib.categories) {
|
var categories = OpenLayers.Util.isArray(atomAttrib.categories) ?
|
atomAttrib.categories : [atomAttrib.categories];
|
var category;
|
for (var i=0, ii=categories.length; i<ii; i++) {
|
category = categories[i];
|
entryNode.appendChild(
|
this.createElementNSPlus("atom:category", {
|
attributes: {
|
term: category.term,
|
scheme: category.scheme || null,
|
label: category.label || null
|
}
|
})
|
);
|
}
|
}
|
|
// atom:content
|
if (atomAttrib.content) {
|
entryNode.appendChild(this.buildContentNode(atomAttrib.content));
|
}
|
|
// atom:contributor
|
if (atomAttrib.contributors) {
|
var contributors = OpenLayers.Util.isArray(atomAttrib.contributors) ?
|
atomAttrib.contributors : [atomAttrib.contributors];
|
for (var i=0, ii=contributors.length; i<ii; i++) {
|
entryNode.appendChild(
|
this.buildPersonConstructNode(
|
"contributor",
|
contributors[i]
|
)
|
);
|
}
|
}
|
|
// atom:id
|
if (feature.fid) {
|
entryNode.appendChild(
|
this.createElementNSPlus("atom:id", {
|
value: feature.fid
|
})
|
);
|
}
|
|
// atom:link
|
if (atomAttrib.links) {
|
var links = OpenLayers.Util.isArray(atomAttrib.links) ?
|
atomAttrib.links : [atomAttrib.links];
|
var link;
|
for (var i=0, ii=links.length; i<ii; i++) {
|
link = links[i];
|
entryNode.appendChild(
|
this.createElementNSPlus("atom:link", {
|
attributes: {
|
href: link.href,
|
rel: link.rel || null,
|
type: link.type || null,
|
hreflang: link.hreflang || null,
|
title: link.title || null,
|
length: link.length || null
|
}
|
})
|
);
|
}
|
}
|
|
// atom:published
|
if (atomAttrib.published) {
|
entryNode.appendChild(
|
this.createElementNSPlus("atom:published", {
|
value: atomAttrib.published
|
})
|
);
|
}
|
|
// atom:rights
|
if (atomAttrib.rights) {
|
entryNode.appendChild(
|
this.createElementNSPlus("atom:rights", {
|
value: atomAttrib.rights
|
})
|
);
|
}
|
|
// atom:source not implemented
|
|
// atom:summary
|
if (atomAttrib.summary || attrib.description) {
|
entryNode.appendChild(
|
this.createElementNSPlus("atom:summary", {
|
value: atomAttrib.summary || attrib.description
|
})
|
);
|
}
|
|
// atom:title
|
entryNode.appendChild(
|
this.createElementNSPlus("atom:title", {
|
value: atomAttrib.title || attrib.title || this.defaultEntryTitle
|
})
|
);
|
|
// atom:updated
|
if (atomAttrib.updated) {
|
entryNode.appendChild(
|
this.createElementNSPlus("atom:updated", {
|
value: atomAttrib.updated
|
})
|
);
|
}
|
|
// georss:where
|
if (feature.geometry) {
|
var whereNode = this.createElementNSPlus("georss:where");
|
whereNode.appendChild(
|
this.buildGeometryNode(feature.geometry)
|
);
|
entryNode.appendChild(whereNode);
|
}
|
|
return entryNode;
|
},
|
|
/**
|
* Method: initGmlParser
|
* Creates a GML parser.
|
*/
|
initGmlParser: function() {
|
this.gmlParser = new OpenLayers.Format.GML.v3({
|
xy: this.xy,
|
featureNS: "http://example.com#feature",
|
internalProjection: this.internalProjection,
|
externalProjection: this.externalProjection
|
});
|
},
|
|
/**
|
* Method: buildGeometryNode
|
* builds a GeoRSS node with a given geometry
|
*
|
* Parameters:
|
* geometry - {<OpenLayers.Geometry>}
|
*
|
* Returns:
|
* {DOMElement} A gml node.
|
*/
|
buildGeometryNode: function(geometry) {
|
if (!this.gmlParser) {
|
this.initGmlParser();
|
}
|
var node = this.gmlParser.writeNode("feature:_geometry", geometry);
|
return node.firstChild;
|
},
|
|
/**
|
* Method: buildPersonConstructNode
|
*
|
* Parameters:
|
* name - {String}
|
* value - {Object}
|
*
|
* Returns:
|
* {DOMElement} an Atom person construct node.
|
*
|
* Example:
|
* >>> buildPersonConstructNode("author", {name: "John Smith"})
|
* {<author><name>John Smith</name></author>}
|
*
|
* TODO: how to specify extension elements? Add to the oNames array?
|
*/
|
buildPersonConstructNode: function(name, value) {
|
var oNames = ["uri", "email"];
|
var personNode = this.createElementNSPlus("atom:" + name);
|
personNode.appendChild(
|
this.createElementNSPlus("atom:name", {
|
value: value.name
|
})
|
);
|
for (var i=0, ii=oNames.length; i<ii; i++) {
|
if (value[oNames[i]]) {
|
personNode.appendChild(
|
this.createElementNSPlus("atom:" + oNames[i], {
|
value: value[oNames[i]]
|
})
|
);
|
}
|
}
|
return personNode;
|
},
|
|
/**
|
* Method: getFirstChildValue
|
*
|
* Parameters:
|
* node - {DOMElement}
|
* nsuri - {String} Child node namespace uri ("*" for any).
|
* name - {String} Child node name.
|
* def - {String} Optional string default to return if no child found.
|
*
|
* Returns:
|
* {String} The value of the first child with the given tag name. Returns
|
* default value or empty string if none found.
|
*/
|
getFirstChildValue: function(node, nsuri, name, def) {
|
var value;
|
var nodes = this.getElementsByTagNameNS(node, nsuri, name);
|
if (nodes && nodes.length > 0) {
|
value = this.getChildValue(nodes[0], def);
|
} else {
|
value = def;
|
}
|
return value;
|
},
|
|
/**
|
* Method: parseFeature
|
* Parse feature from an Atom entry node..
|
*
|
* Parameters:
|
* node - {DOMElement} An Atom entry or feed node.
|
*
|
* Returns:
|
* {<OpenLayers.Feature.Vector>}
|
*/
|
parseFeature: function(node) {
|
var atomAttrib = {};
|
var value = null;
|
var nodes = null;
|
var attval = null;
|
var atomns = this.namespaces.atom;
|
|
// atomAuthor*
|
this.parsePersonConstructs(node, "author", atomAttrib);
|
|
// atomCategory*
|
nodes = this.getElementsByTagNameNS(node, atomns, "category");
|
if (nodes.length > 0) {
|
atomAttrib.categories = [];
|
}
|
for (var i=0, ii=nodes.length; i<ii; i++) {
|
value = {};
|
value.term = nodes[i].getAttribute("term");
|
attval = nodes[i].getAttribute("scheme");
|
if (attval) { value.scheme = attval; }
|
attval = nodes[i].getAttribute("label");
|
if (attval) { value.label = attval; }
|
atomAttrib.categories.push(value);
|
}
|
|
// atomContent?
|
nodes = this.getElementsByTagNameNS(node, atomns, "content");
|
if (nodes.length > 0) {
|
value = {};
|
attval = nodes[0].getAttribute("type");
|
if (attval) {
|
value.type = attval;
|
}
|
attval = nodes[0].getAttribute("src");
|
if (attval) {
|
value.src = attval;
|
} else {
|
if (value.type == "text" ||
|
value.type == "html" ||
|
value.type == null ) {
|
value.value = this.getFirstChildValue(
|
node,
|
atomns,
|
"content",
|
null
|
);
|
} else if (value.type == "xhtml" ||
|
value.type.match(/(\+|\/)xml$/)) {
|
value.value = this.getChildEl(nodes[0]);
|
} else { // MUST be base64 encoded
|
value.value = this.getFirstChildValue(
|
node,
|
atomns,
|
"content",
|
null
|
);
|
}
|
atomAttrib.content = value;
|
}
|
}
|
|
// atomContributor*
|
this.parsePersonConstructs(node, "contributor", atomAttrib);
|
|
// atomId
|
atomAttrib.id = this.getFirstChildValue(node, atomns, "id", null);
|
|
// atomLink*
|
nodes = this.getElementsByTagNameNS(node, atomns, "link");
|
if (nodes.length > 0) {
|
atomAttrib.links = new Array(nodes.length);
|
}
|
var oAtts = ["rel", "type", "hreflang", "title", "length"];
|
for (var i=0, ii=nodes.length; i<ii; i++) {
|
value = {};
|
value.href = nodes[i].getAttribute("href");
|
for (var j=0, jj=oAtts.length; j<jj; j++) {
|
attval = nodes[i].getAttribute(oAtts[j]);
|
if (attval) {
|
value[oAtts[j]] = attval;
|
}
|
}
|
atomAttrib.links[i] = value;
|
}
|
|
// atomPublished?
|
value = this.getFirstChildValue(node, atomns, "published", null);
|
if (value) {
|
atomAttrib.published = value;
|
}
|
|
// atomRights?
|
value = this.getFirstChildValue(node, atomns, "rights", null);
|
if (value) {
|
atomAttrib.rights = value;
|
}
|
|
// atomSource? -- not implemented
|
|
// atomSummary?
|
value = this.getFirstChildValue(node, atomns, "summary", null);
|
if (value) {
|
atomAttrib.summary = value;
|
}
|
|
// atomTitle
|
atomAttrib.title = this.getFirstChildValue(
|
node, atomns, "title", null
|
);
|
|
// atomUpdated
|
atomAttrib.updated = this.getFirstChildValue(
|
node, atomns, "updated", null
|
);
|
|
var featureAttrib = {
|
title: atomAttrib.title,
|
description: atomAttrib.summary,
|
atom: atomAttrib
|
};
|
var geometry = this.parseLocations(node)[0];
|
var feature = new OpenLayers.Feature.Vector(geometry, featureAttrib);
|
feature.fid = atomAttrib.id;
|
return feature;
|
},
|
|
/**
|
* Method: parseFeatures
|
* Return features from an Atom entry or feed.
|
*
|
* Parameters:
|
* node - {DOMElement} An Atom entry or feed node.
|
*
|
* Returns:
|
* Array({<OpenLayers.Feature.Vector>})
|
*/
|
parseFeatures: function(node) {
|
var features = [];
|
var entries = this.getElementsByTagNameNS(
|
node, this.namespaces.atom, "entry"
|
);
|
if (entries.length == 0) {
|
entries = [node];
|
}
|
for (var i=0, ii=entries.length; i<ii; i++) {
|
features.push(this.parseFeature(entries[i]));
|
}
|
return features;
|
},
|
|
/**
|
* Method: parseLocations
|
* Parse the locations from an Atom entry or feed.
|
*
|
* Parameters:
|
* node - {DOMElement} An Atom entry or feed node.
|
*
|
* Returns:
|
* Array({<OpenLayers.Geometry>})
|
*/
|
parseLocations: function(node) {
|
var georssns = this.namespaces.georss;
|
|
var locations = {components: []};
|
var where = this.getElementsByTagNameNS(node, georssns, "where");
|
if (where && where.length > 0) {
|
if (!this.gmlParser) {
|
this.initGmlParser();
|
}
|
for (var i=0, ii=where.length; i<ii; i++) {
|
this.gmlParser.readChildNodes(where[i], locations);
|
}
|
}
|
|
var components = locations.components;
|
var point = this.getElementsByTagNameNS(node, georssns, "point");
|
if (point && point.length > 0) {
|
for (var i=0, ii=point.length; i<ii; i++) {
|
var xy = OpenLayers.String.trim(
|
point[i].firstChild.nodeValue
|
).split(/\s+/);
|
if (xy.length !=2) {
|
xy = OpenLayers.String.trim(
|
point[i].firstChild.nodeValue
|
).split(/\s*,\s*/);
|
}
|
components.push(new OpenLayers.Geometry.Point(xy[1], xy[0]));
|
}
|
}
|
|
var line = this.getElementsByTagNameNS(node, georssns, "line");
|
if (line && line.length > 0) {
|
var coords;
|
var p;
|
var points;
|
for (var i=0, ii=line.length; i<ii; i++) {
|
coords = OpenLayers.String.trim(
|
line[i].firstChild.nodeValue
|
).split(/\s+/);
|
points = [];
|
for (var j=0, jj=coords.length; j<jj; j+=2) {
|
p = new OpenLayers.Geometry.Point(coords[j+1], coords[j]);
|
points.push(p);
|
}
|
components.push(
|
new OpenLayers.Geometry.LineString(points)
|
);
|
}
|
}
|
|
var polygon = this.getElementsByTagNameNS(node, georssns, "polygon");
|
if (polygon && polygon.length > 0) {
|
var coords;
|
var p;
|
var points;
|
for (var i=0, ii=polygon.length; i<ii; i++) {
|
coords = OpenLayers.String.trim(
|
polygon[i].firstChild.nodeValue
|
).split(/\s+/);
|
points = [];
|
for (var j=0, jj=coords.length; j<jj; j+=2) {
|
p = new OpenLayers.Geometry.Point(coords[j+1], coords[j]);
|
points.push(p);
|
}
|
components.push(
|
new OpenLayers.Geometry.Polygon(
|
[new OpenLayers.Geometry.LinearRing(points)]
|
)
|
);
|
}
|
}
|
|
if (this.internalProjection && this.externalProjection) {
|
for (var i=0, ii=components.length; i<ii; i++) {
|
if (components[i]) {
|
components[i].transform(
|
this.externalProjection,
|
this.internalProjection
|
);
|
}
|
}
|
}
|
|
return components;
|
},
|
|
/**
|
* Method: parsePersonConstruct
|
* Parse Atom person constructs from an Atom entry node.
|
*
|
* Parameters:
|
* node - {DOMElement} An Atom entry or feed node.
|
* name - {String} Construcy name ("author" or "contributor")
|
* data = {Object} Object in which to put parsed persons.
|
*
|
* Returns:
|
* An {Object}.
|
*/
|
parsePersonConstructs: function(node, name, data) {
|
var persons = [];
|
var atomns = this.namespaces.atom;
|
var nodes = this.getElementsByTagNameNS(node, atomns, name);
|
var oAtts = ["uri", "email"];
|
for (var i=0, ii=nodes.length; i<ii; i++) {
|
var value = {};
|
value.name = this.getFirstChildValue(
|
nodes[i],
|
atomns,
|
"name",
|
null
|
);
|
for (var j=0, jj=oAtts.length; j<jj; j++) {
|
var attval = this.getFirstChildValue(
|
nodes[i],
|
atomns,
|
oAtts[j],
|
null);
|
if (attval) {
|
value[oAtts[j]] = attval;
|
}
|
}
|
persons.push(value);
|
}
|
if (persons.length > 0) {
|
data[name + "s"] = persons;
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Format.Atom"
|
});
|
/* ======================================================================
|
OpenLayers/Control/KeyboardDefaults.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Control.js
|
* @requires OpenLayers/Handler/Keyboard.js
|
* @requires OpenLayers/Events.js
|
*/
|
|
/**
|
* Class: OpenLayers.Control.KeyboardDefaults
|
* The KeyboardDefaults control adds panning and zooming functions, controlled
|
* with the keyboard. By default arrow keys pan, +/- keys zoom & Page Up/Page
|
* Down/Home/End scroll by three quarters of a page.
|
*
|
* This control has no visible appearance.
|
*
|
* Inherits from:
|
* - <OpenLayers.Control>
|
*/
|
OpenLayers.Control.KeyboardDefaults = OpenLayers.Class(OpenLayers.Control, {
|
|
/**
|
* APIProperty: autoActivate
|
* {Boolean} Activate the control when it is added to a map. Default is
|
* true.
|
*/
|
autoActivate: true,
|
|
/**
|
* APIProperty: slideFactor
|
* Pixels to slide by.
|
*/
|
slideFactor: 75,
|
|
/**
|
* APIProperty: observeElement
|
* {DOMelement|String} The DOM element to handle keys for. You
|
* can use the map div here, to have the navigation keys
|
* work when the map div has the focus. If undefined the
|
* document is used.
|
*/
|
observeElement: null,
|
|
/**
|
* Constructor: OpenLayers.Control.KeyboardDefaults
|
*/
|
|
/**
|
* Method: draw
|
* Create handler.
|
*/
|
draw: function() {
|
var observeElement = this.observeElement || document;
|
this.handler = new OpenLayers.Handler.Keyboard( this,
|
{"keydown": this.defaultKeyPress},
|
{observeElement: observeElement}
|
);
|
},
|
|
/**
|
* Method: defaultKeyPress
|
* When handling the key event, we only use evt.keyCode. This holds
|
* some drawbacks, though we get around them below. When interpretting
|
* the keycodes below (including the comments associated with them),
|
* consult the URL below. For instance, the Safari browser returns
|
* "IE keycodes", and so is supported by any keycode labeled "IE".
|
*
|
* Very informative URL:
|
* http://unixpapa.com/js/key.html
|
*
|
* Parameters:
|
* evt - {Event}
|
*/
|
defaultKeyPress: function (evt) {
|
var size, handled = true;
|
|
var target = OpenLayers.Event.element(evt);
|
if (target &&
|
(target.tagName == 'INPUT' ||
|
target.tagName == 'TEXTAREA' ||
|
target.tagName == 'SELECT')) {
|
return;
|
}
|
|
switch (evt.keyCode) {
|
case OpenLayers.Event.KEY_LEFT:
|
this.map.pan(-this.slideFactor, 0);
|
break;
|
case OpenLayers.Event.KEY_RIGHT:
|
this.map.pan(this.slideFactor, 0);
|
break;
|
case OpenLayers.Event.KEY_UP:
|
this.map.pan(0, -this.slideFactor);
|
break;
|
case OpenLayers.Event.KEY_DOWN:
|
this.map.pan(0, this.slideFactor);
|
break;
|
|
case 33: // Page Up. Same in all browsers.
|
size = this.map.getSize();
|
this.map.pan(0, -0.75*size.h);
|
break;
|
case 34: // Page Down. Same in all browsers.
|
size = this.map.getSize();
|
this.map.pan(0, 0.75*size.h);
|
break;
|
case 35: // End. Same in all browsers.
|
size = this.map.getSize();
|
this.map.pan(0.75*size.w, 0);
|
break;
|
case 36: // Home. Same in all browsers.
|
size = this.map.getSize();
|
this.map.pan(-0.75*size.w, 0);
|
break;
|
|
case 43: // +/= (ASCII), keypad + (ASCII, Opera)
|
case 61: // +/= (Mozilla, Opera, some ASCII)
|
case 187: // +/= (IE)
|
case 107: // keypad + (IE, Mozilla)
|
this.map.zoomIn();
|
break;
|
case 45: // -/_ (ASCII, Opera), keypad - (ASCII, Opera)
|
case 109: // -/_ (Mozilla), keypad - (Mozilla, IE)
|
case 189: // -/_ (IE)
|
case 95: // -/_ (some ASCII)
|
this.map.zoomOut();
|
break;
|
default:
|
handled = false;
|
}
|
if (handled) {
|
// prevent browser default not to move the page
|
// when moving the page with the keyboard
|
OpenLayers.Event.stop(evt);
|
}
|
},
|
|
CLASS_NAME: "OpenLayers.Control.KeyboardDefaults"
|
});
|
/* ======================================================================
|
OpenLayers/Format/WMTSCapabilities/v1_0_0.js
|
====================================================================== */
|
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
* full list of contributors). Published under the 2-clause BSD license.
|
* See license.txt in the OpenLayers distribution or repository for the
|
* full text of the license. */
|
|
/**
|
* @requires OpenLayers/Format/WMTSCapabilities.js
|
* @requires OpenLayers/Format/OWSCommon/v1_1_0.js
|
*/
|
|
/**
|
* Class: OpenLayers.Format.WMTSCapabilities.v1_0_0
|
* Read WMTS Capabilities version 1.0.0.
|
*
|
* Inherits from:
|
* - <OpenLayers.Format.WMTSCapabilities>
|
*/
|
OpenLayers.Format.WMTSCapabilities.v1_0_0 = OpenLayers.Class(
|
OpenLayers.Format.OWSCommon.v1_1_0, {
|
|
/**
|
* Property: version
|
* {String} The parser version ("1.0.0").
|
*/
|
version: "1.0.0",
|
|
/**
|
* Property: namespaces
|
* {Object} Mapping of namespace aliases to namespace URIs.
|
*/
|
namespaces: {
|
ows: "http://www.opengis.net/ows/1.1",
|
wmts: "http://www.opengis.net/wmts/1.0",
|
xlink: "http://www.w3.org/1999/xlink"
|
},
|
|
/**
|
* Property: yx
|
* {Object} Members in the yx object are used to determine if a CRS URN
|
* corresponds to a CRS with y,x axis order. Member names are CRS URNs
|
* and values are boolean. Defaults come from the
|
* <OpenLayers.Format.WMTSCapabilities> prototype.
|
*/
|
yx: null,
|
|
/**
|
* Property: defaultPrefix
|
* {String} The default namespace alias for creating element nodes.
|
*/
|
defaultPrefix: "wmts",
|
|
/**
|
* Constructor: OpenLayers.Format.WMTSCapabilities.v1_0_0
|
* Create a new parser for WMTS capabilities version 1.0.0.
|
*
|
* Parameters:
|
* options - {Object} An optional object whose properties will be set on
|
* this instance.
|
*/
|
initialize: function(options) {
|
OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
|
this.options = options;
|
var yx = OpenLayers.Util.extend(
|
{}, OpenLayers.Format.WMTSCapabilities.prototype.yx
|
);
|
this.yx = OpenLayers.Util.extend(yx, this.yx);
|
},
|
|
/**
|
* APIMethod: read
|
* Read capabilities data from a string, and return info about the WMTS.
|
*
|
* Parameters:
|
* data - {String} or {DOMElement} data to read/parse.
|
*
|
* Returns:
|
* {Object} Information about the SOS service.
|
*/
|
read: function(data) {
|
if(typeof data == "string") {
|
data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
|
}
|
if(data && data.nodeType == 9) {
|
data = data.documentElement;
|
}
|
var capabilities = {};
|
this.readNode(data, capabilities);
|
capabilities.version = this.version;
|
return capabilities;
|
},
|
|
/**
|
* Property: readers
|
* Contains public functions, grouped by namespace prefix, that will
|
* be applied when a namespaced node is found matching the function
|
* name. The function will be applied in the scope of this parser
|
* with two arguments: the node being read and a context object passed
|
* from the parent.
|
*/
|
readers: {
|
"wmts": {
|
"Capabilities": function(node, obj) {
|
this.readChildNodes(node, obj);
|
},
|
"Contents": function(node, obj) {
|
obj.contents = {};
|
obj.contents.layers = [];
|
obj.contents.tileMatrixSets = {};
|
this.readChildNodes(node, obj.contents);
|
},
|
"Layer": function(node, obj) {
|
var layer = {
|
styles: [],
|
formats: [],
|
dimensions: [],
|
tileMatrixSetLinks: []
|
};
|
layer.layers = [];
|
this.readChildNodes(node, layer);
|
obj.layers.push(layer);
|
},
|
"Style": function(node, obj) {
|
var style = {};
|
style.isDefault = (node.getAttribute("isDefault") === "true");
|
this.readChildNodes(node, style);
|
obj.styles.push(style);
|
},
|
"Format": function(node, obj) {
|
obj.formats.push(this.getChildValue(node));
|
},
|
"TileMatrixSetLink": function(node, obj) {
|
var tileMatrixSetLink = {};
|
this.readChildNodes(node, tileMatrixSetLink);
|
obj.tileMatrixSetLinks.push(tileMatrixSetLink);
|
},
|
"TileMatrixSet": function(node, obj) {
|
// node could be child of wmts:Contents or wmts:TileMatrixSetLink
|
// duck type wmts:Contents by looking for layers
|
if (obj.layers) {
|
// TileMatrixSet as object type in schema
|
var tileMatrixSet = {
|
matrixIds: []
|
};
|
this.readChildNodes(node, tileMatrixSet);
|
obj.tileMatrixSets[tileMatrixSet.identifier] = tileMatrixSet;
|
} else {
|
// TileMatrixSet as string type in schema
|
obj.tileMatrixSet = this.getChildValue(node);
|
}
|
},
|
"TileMatrix": function(node, obj) {
|
var tileMatrix = {
|
supportedCRS: obj.supportedCRS
|
};
|
this.readChildNodes(node, tileMatrix);
|
obj.matrixIds.push(tileMatrix);
|
},
|
"ScaleDenominator": function(node, obj) {
|
obj.scaleDenominator = parseFloat(this.getChildValue(node));
|
},
|
"TopLeftCorner": function(node, obj) {
|
var topLeftCorner = this.getChildValue(node);
|
var coords = topLeftCorner.split(" ");
|
// decide on axis order for the given CRS
|
var yx;
|
if (obj.supportedCRS) {
|
// extract out version from URN
|
var crs = obj.supportedCRS.replace(
|
/urn:ogc:def:crs:(\w+):.+:(\w+)$/,
|
"urn:ogc:def:crs:$1::$2"
|
);
|
yx = !!this.yx[crs];
|
}
|
if (yx) {
|
obj.topLeftCorner = new OpenLayers.LonLat(
|
coords[1], coords[0]
|
);
|
} else {
|
obj.topLeftCorner = new OpenLayers.LonLat(
|
coords[0], coords[1]
|
);
|
}
|
},
|
"TileWidth": function(node, obj) {
|
obj.tileWidth = parseInt(this.getChildValue(node));
|
},
|
"TileHeight": function(node, obj) {
|
obj.tileHeight = parseInt(this.getChildValue(node));
|
},
|
"MatrixWidth": function(node, obj) {
|
obj.matrixWidth = parseInt(this.getChildValue(node));
|
},
|
"MatrixHeight": function(node, obj) {
|
obj.matrixHeight = parseInt(this.getChildValue(node));
|
},
|
"ResourceURL": function(node, obj) {
|
obj.resourceUrl = obj.resourceUrl || {};
|
var resourceType = node.getAttribute("resourceType");
|
if (!obj.resourceUrls) {
|
obj.resourceUrls = [];
|
}
|
var resourceUrl = obj.resourceUrl[resourceType] = {
|
format: node.getAttribute("format"),
|
template: node.getAttribute("template"),
|
resourceType: resourceType
|
};
|
obj.resourceUrls.push(resourceUrl);
|
},
|
// not used for now, can be added in the future though
|
/*"Themes": function(node, obj) {
|
obj.themes = [];
|
this.readChildNodes(node, obj.themes);
|
},
|
"Theme": function(node, obj) {
|
var theme = {};
|
this.readChildNodes(node, theme);
|
obj.push(theme);
|
},*/
|
"WSDL": function(node, obj) {
|
obj.wsdl = {};
|
obj.wsdl.href = node.getAttribute("xlink:href");
|
// TODO: other attributes of <WSDL> element
|
},
|
"ServiceMetadataURL": function(node, obj) {
|
obj.serviceMetadataUrl = {};
|
obj.serviceMetadataUrl.href = node.getAttribute("xlink:href");
|
// TODO: other attributes of <ServiceMetadataURL> element
|
},
|
"LegendURL": function(node, obj) {
|
obj.legend = {};
|
obj.legend.href = node.getAttribute("xlink:href");
|
obj.legend.format = node.getAttribute("format");
|
},
|
"Dimension": function(node, obj) {
|
var dimension = {values: []};
|
this.readChildNodes(node, dimension);
|
obj.dimensions.push(dimension);
|
},
|
"Default": function(node, obj) {
|
obj["default"] = this.getChildValue(node);
|
},
|
"Value": function(node, obj) {
|
obj.values.push(this.getChildValue(node));
|
}
|
},
|
"ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"]
|
},
|
|
CLASS_NAME: "OpenLayers.Format.WMTSCapabilities.v1_0_0"
|
|
});
|