/* Copyright (c) 2006 MetaCarta, Inc., published under the BSD license. * See http://svn.openlayers.org/trunk/openlayers/license.txt for the full * text of the license. */ /** * @class */ OpenLayers.Util = new Object(); /** * @class This class represents a screen coordinate, in x and y coordinates */ OpenLayers.Pixel = Class.create(); OpenLayers.Pixel.prototype = { /** @type float */ x: 0.0, /** @type float */ y: 0.0, /** * @constructor * * @param {float} x * @param {float} y */ initialize: function(x, y) { this.x = x; this.y = y; }, /** * @return string representation of Pixel. ex: "x=200.4,y=242.2" * @type str */ toString:function() { return ("x=" + this.x + ",y=" + this.y); }, /** * @type OpenLayers.Pixel */ copyOf:function() { return new OpenLayers.Pixel(this.x, this.y); }, /** * @param {OpenLayers.Pixel} px * * @return whether or not the point passed in as parameter is equal to this * note that if px passed in is null, returns false * @type bool */ equals:function(px) { var equals = false; if (px != null) { equals = ((this.x == px.x) && (this.y == px.y)); } return equals; }, /** * @param {int} x * @param {int} y * * @return a new Pixel with this pixel's x&y augmented by the * values passed in. * @type OpenLayers.Pixel */ add:function(x, y) { return new OpenLayers.Pixel(this.x + x, this.y + y); }, /** * @param {OpenLayers.Pixel} px * * @return a new Pixel with this pixel's x&y augmented by the * x&y values of the pixel passed in. * @type OpenLayers.Pixel */ offset:function(px) { return this.add(px.x, px.y); }, /** @final @type str */ CLASS_NAME: "OpenLayers.Pixel" }; /** * @class This class represents a width and height pair */ OpenLayers.Size = Class.create(); OpenLayers.Size.prototype = { /** @type float */ w: 0.0, /** @type float */ h: 0.0, /** * @constructor * * @param {float} w * @param {float} h */ initialize: function(w, h) { this.w = w; this.h = h; }, /** * @return String representation of OpenLayers.Size object. * (ex. "w=55,h=66") * @type String */ toString:function() { return ("w=" + this.w + ",h=" + this.h); }, /** * @return New OpenLayers.Size object with the same w and h values * @type OpenLayers.Size */ copyOf:function() { return new OpenLayers.Size(this.w, this.h); }, /** * @param {OpenLayers.Size} sz * @returns Boolean value indicating whether the passed-in OpenLayers.Size * object has the same w and h components as this * note that if sz passed in is null, returns false * * @type bool */ equals:function(sz) { var equals = false; if (sz != null) { equals = ((this.w == sz.w) && (this.h == sz.h)); } return equals; }, /** @final @type String */ CLASS_NAME: "OpenLayers.Size" }; /** * @class This class represents a longitude and latitude pair */ OpenLayers.LonLat = Class.create(); OpenLayers.LonLat.prototype = { /** @type float */ lon: 0.0, /** @type float */ lat: 0.0, /** * @constructor * * @param {float} lon * @param {float} lat */ initialize: function(lon, lat) { this.lon = lon; this.lat = lat; }, /** * @return String representation of OpenLayers.LonLat object. * (ex. "lon=5,lat=42") * @type String */ toString:function() { return ("lon=" + this.lon + ",lat=" + this.lat); }, /** * @return Shortened String representation of OpenLayers.LonLat object. * (ex. "5, 42") * @type String */ toShortString:function() { return (this.lon + ", " + this.lat); }, /** * @return New OpenLayers.LonLat object with the same lon and lat values * @type OpenLayers.LonLat */ copyOf:function() { return new OpenLayers.LonLat(this.lon, this.lat); }, /** * @param {float} lon * @param {float} lat * * @return A new OpenLayers.LonLat object with the lon and lat passed-in * added to this's. * @type OpenLayers.LonLat */ add:function(lon, lat) { return new OpenLayers.LonLat(this.lon + lon, this.lat + lat); }, /** * @param {OpenLayers.LonLat} ll * @returns Boolean value indicating whether the passed-in OpenLayers.LonLat * object has the same lon and lat components as this * note that if ll passed in is null, returns false * * @type bool */ equals:function(ll) { var equals = false; if (ll != null) { equals = ((this.lon == ll.lon) && (this.lat == ll.lat)); } return equals; }, /** @final @type String */ CLASS_NAME: "OpenLayers.LonLat" }; /** Alternative constructor that builds a new OpenLayers.LonLat from a * parameter string * * @constructor * * @param {String} str Comma-separated Lon,Lat coordinate string. * (ex. "5,40") * * @returns New OpenLayers.LonLat object built from the passed-in String. * @type OpenLayers.LonLat */ OpenLayers.LonLat.fromString = function(str) { var pair = str.split(","); return new OpenLayers.LonLat(parseFloat(pair[0]), parseFloat(pair[1])); }; /** * @class This class represents a bounding box. * Data stored as left, bottom, right, top floats */ OpenLayers.Bounds = Class.create(); OpenLayers.Bounds.prototype = { /** @type float */ left: 0.0, /** @type float */ bottom: 0.0, /** @type float */ right: 0.0, /** @type float */ top: 0.0, /** * @constructor * * @param {float} left * @param {float} bottom * @param {float} right * @param {float} top * */ initialize: function(left, bottom, right, top) { this.left = left; this.bottom = bottom; this.right = right; this.top = top; }, /** * @returns A fresh copy of the bounds * @type OpenLayers.Bounds */ copyOf:function() { return new OpenLayers.Bounds(this.left, this.bottom, this.right, this.top); }, /** * @param {OpenLayers.Bounds} bounds * @returns Boolean value indicating whether the passed-in OpenLayers.Bounds * object has the same left, right, top, bottom components as this * note that if bounds passed in is null, returns false * * @type bool */ 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; }, /** * @return String representation of OpenLayers.Bounds object. * (ex."left-bottom=(5,42) right-top=(10,45)") * @type String */ toString:function(){ return ( "left-bottom=(" + this.left + "," + this.bottom + ")" + " right-top=(" + this.right + "," + this.top + ")" ); }, /** * @return Simple String representation of OpenLayers.Bounds object. * (ex. "5,42,10,45") * @type String */ toBBOX:function() { return (this.left + "," + this.bottom + "," + this.right + "," + this.top); }, /** * @returns The width of the bounds * @type float */ getWidth:function() { return (this.right - this.left); }, /** * @returns The height of the bounds * @type float */ getHeight:function() { return (this.top - this.bottom); }, /** * @returns An OpenLayers.Size which represents the size of the box * @type OpenLayers.Size */ getSize:function() { return new OpenLayers.Size(this.getWidth(), this.getHeight()); }, /** * @returns An OpenLayers.Pixel which represents the center of the bounds * @type OpenLayers.Pixel */ getCenterPixel:function() { return new OpenLayers.Pixel( (this.left + this.right) / 2, (this.bottom + this.top) / 2); }, /** * @returns An OpenLayers.LonLat which represents the center of the bounds * @type OpenLayers.LonLat */ getCenterLonLat:function() { return new OpenLayers.LonLat( (this.left + this.right) / 2, (this.bottom + this.top) / 2); }, /** * @param {float} x * @param {float} y * * @returns A new OpenLayers.Bounds whose coordinates are the same as this, * but shifted by the passed-in x and y values * @type OpenLayers.Bounds */ add:function(x, y){ return new OpenLayers.Box(this.left + x, this.bottom + y, this.right + x, this.top + y); }, /** * @param {float} x * @param {float} y * @param {Boolean} inclusive Whether or not to include the border. * Default is true * * @return Whether or not the passed-in coordinates are within this bounds * @type Boolean */ contains:function(x, y, inclusive) { //set default if (inclusive == null) { inclusive = true; } 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; }, /** * @param {OpenLayers.Bounds} bounds * @param {Boolean} partial If true, only part of passed-in * OpenLayers.Bounds needs be within this bounds. * If false, the entire passed-in bounds must be * within. Default is false * @param {Boolean} inclusive Whether or not to include the border. * Default is true * * @return Whether or not the passed-in OpenLayers.Bounds object is * contained within this bounds. * @type Boolean */ containsBounds:function(bounds, partial, inclusive) { //set defaults if (partial == null) { partial = false; } if (inclusive == null) { inclusive = true; } var inLeft; var inTop; var inRight; var inBottom; if (inclusive) { inLeft = (bounds.left >= this.left) && (bounds.left <= this.right); inTop = (bounds.top >= this.bottom) && (bounds.top <= this.top); inRight= (bounds.right >= this.left) && (bounds.right <= this.right); inBottom = (bounds.bottom >= this.bottom) && (bounds.bottom <= this.top); } else { inLeft = (bounds.left > this.left) && (bounds.left < this.right); inTop = (bounds.top > this.bottom) && (bounds.top < this.top); inRight= (bounds.right > this.left) && (bounds.right < this.right); inBottom = (bounds.bottom > this.bottom) && (bounds.bottom < this.top); } return (partial) ? (inTop || inBottom) && (inLeft || inRight ) : (inTop && inLeft && inBottom && inRight); }, /** * @param {OpenLayers.LonLat} lonlat * * @returns The quadrant ("br" "tr" "tl" "bl") of the bounds in which * the coordinate lies. * @type String */ 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; }, /** @final @type String */ CLASS_NAME: "OpenLayers.Bounds" }; /** Alternative constructor that builds a new OpenLayers.Bounds from a * parameter string * * @constructor * * @param {String} str Comma-separated bounds string. (ex. "5,42,10,45") * * @returns New OpenLayers.Bounds object built from the passed-in String. * @type OpenLayers.Bounds */ OpenLayers.Bounds.fromString = function(str) { var bounds = str.split(","); return OpenLayers.Bounds.fromArray(bounds); }; /** Alternative constructor that builds a new OpenLayers.Bounds * from an array * * @constructor * * @param {Array} bbox Array of bounds values (ex. [5,42,10,45]) * * @returns New OpenLayers.Bounds object built from the passed-in Array. * @type OpenLayers.Bounds */ OpenLayers.Bounds.fromArray = function(bbox) { return new OpenLayers.Bounds(parseFloat(bbox[0]), parseFloat(bbox[1]), parseFloat(bbox[2]), parseFloat(bbox[3])); }; /** Alternative constructor that builds a new OpenLayers.Bounds * from an OpenLayers.Size * * @constructor * * @param {OpenLayers.Size} size * * @returns New OpenLayers.Bounds object built with top and left set to 0 and * bottom right taken from the passed-in OpenLayers.Size. * @type OpenLayers.Bounds */ OpenLayers.Bounds.fromSize = function(size) { return new OpenLayers.Bounds(0, size.h, size.w, 0); }; /** * @param {String} quadrant * * @returns 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. * @type String */ OpenLayers.Bounds.oppositeQuadrant = function(quadrant) { var opp = ""; opp += (quadrant.charAt(0) == 't') ? 'b' : 't'; opp += (quadrant.charAt(1) == 'l') ? 'r' : 'l'; return opp; }; // Some other helpful things /** * @param {String} sStart * * @returns Whether or not this string starts with the string passed in. * @type Boolean */ String.prototype.startsWith = function(sStart){ return (this.substr(0,sStart.length) == sStart); }; /** * @returns A trimmed version of the string - all leading and * trailing spaces removed * @type String */ String.prototype.trim = function() { var b = 0; while(this.substr(b,1) == " ") { b++; } var e = this.length - 1; while(this.substr(e,1) == " ") { e--; } return this.substring(b, e+1); }; /** Remove an object from an array. Iterates through the array * to find the item, then removes it. * * @param {Object} item * * @returns A reference to the array * @type Array */ Array.prototype.remove = function(item) { for(var i=0; i < this.length; i++) { if(this[i] == item) { this.splice(i,1); //break;more than once?? } } return this; } /** * @returns A fresh copy of the array * @type Array */ Array.prototype.copyOf = function() { var copy = new Array(); for (var i = 0; i < this.length; i++) { copy[i] = this[i]; } return copy; }; /** * @param {Object} item */ Array.prototype.prepend = function(item) { this.splice(0, 0, item); }; /** * @param {Object} item */ Array.prototype.append = function(item){ this[this.length] = item; }; /** */ Array.prototype.clear = function() { this.length = 0; }; /** * @param {Object} element * * @returns The first index of the element in the array if found. Else returns -1 * @type int */ Array.prototype.indexOf = function(element) { var index = -1; for(var i=0; i < this.length; i++) { if (this[i] == element) { index = i; break; } } return index; } /** * @param {String} id * @param {OpenLayers.Pixel} px * @param {OpenLayers.Size} sz * @param {String} position * @param {String} border * @param {String} overflow */ OpenLayers.Util.modifyDOMElement = function(element, id, px, sz, position, border, overflow) { if (id) { element.id = id; } 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; } }; /** * zIndex is NOT set * * @param {String} id * @param {OpenLayers.Pixel} px * @param {OpenLayers.Size} sz * @param {String} imgURL * @param {String} position * @param {String} border * @param {String} overflow * * @returns A DOM Div created with the specified attributes. * @type DOMElement */ OpenLayers.Util.createDiv = function(id, px, sz, imgURL, position, border, overflow) { var dom = document.createElement('div'); //set specific properties dom.style.padding = "0"; dom.style.margin = "0"; if (imgURL) { dom.style.backgroundImage = 'url(' + imgURL + ')'; } //set generic properties if (!id) { id = "OpenLayersDiv" + (Math.random() * 10000 % 10000); } if (!position) { position = "absolute"; } OpenLayers.Util.modifyDOMElement(dom, id, px, sz, position, border, overflow); return dom; }; /** * @param {String} id * @param {OpenLayers.Pixel} px * @param {OpenLayers.Size} sz * @param {String} imgURL * @param {String} position * @param {String} border * * @returns A DOM Image created with the specified attributes. * @type DOMElement */ OpenLayers.Util.createImage = function(id, px, sz, imgURL, position, border) { image = document.createElement("img"); //set special properties image.style.alt = id; image.galleryImg = "no"; if (imgURL) { image.src = imgURL; } //set generic properties if (!id) { id = "OpenLayersDiv" + (Math.random() * 10000 % 10000); } if (!position) { position = "relative"; } OpenLayers.Util.modifyDOMElement(image, id, px, sz, position, border); return image; }; OpenLayers.Util.alphaHack = function() { var arVersion = navigator.appVersion.split("MSIE"); var version = parseFloat(arVersion[1]); return ( (document.body.filters) && (version >= 5.5) && (version < 7) ); } /** * @param {DOMElement} div Div containing Alpha-adjusted Image * @param {String} id * @param {OpenLayers.Pixel} px * @param {OpenLayers.Size} sz * @param {String} imgURL * @param {String} position * @param {String} border * @param {String} sizing 'crop', 'scale', or 'image'. Default is "scale" */ OpenLayers.Util.modifyAlphaImageDiv = function(div, id, px, sz, imgURL, position, border, sizing) { OpenLayers.Util.modifyDOMElement(div, id, px, sz); 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()) { div.style.display = "inline-block"; if (sizing == null) { sizing = "scale"; } div.style.filter = "progid:DXImageTransform.Microsoft" + ".AlphaImageLoader(src='" + img.src + "', " + "sizingMethod='" + sizing + "')"; img.style.filter = "progid:DXImageTransform.Microsoft" + ".Alpha(opacity=0)"; } }; /** * @param {String} id * @param {OpenLayers.Pixel} px * @param {OpenLayers.Size} sz * @param {String} imgURL * @param {String} position * @param {String} border * @param {String} sizing 'crop', 'scale', or 'image'. Default is "scale" * * @returns A DOM Div created with a DOM Image inside it. If the hack is * needed for transparency in IE, it is added. * @type DOMElement */ OpenLayers.Util.createAlphaImageDiv = function(id, px, sz, imgURL, position, border, sizing) { var div = OpenLayers.Util.createDiv(); var img = OpenLayers.Util.createImage(); div.appendChild(img); OpenLayers.Util.modifyAlphaImageDiv(div, id, px, sz, imgURL, position, border, sizing); return div; }; /** Creates a new hash 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. * * @param {Object} object * * @returns A new Object with all the same keys but uppercased * @type Object */ OpenLayers.Util.upperCaseObject = function (object) { var uObject = new Object(); for (var key in object) { uObject[key.toUpperCase()] = object[key]; } return uObject; }; /** Takes a hash and copies any keys that don't exist from * another hash, by analogy with Object.extend() from * Prototype.js. * * @param {Object} to * @param {Object} from * * @type Object */ OpenLayers.Util.applyDefaults = function (to, from) { for (var key in from) { if (to[key] == null) { to[key] = from[key]; } } return to; }; /** * @param {Object} params * * @returns a concatenation of the properties of an object in * http parameter notation. * (ex. "key1=value1&key2=value2&key3=value3") * @type String */ OpenLayers.Util.getParameterString = function(params) { paramsArray = new Array(); for (var key in params) { var value = params[key]; //skip functions if (typeof value == 'function') continue; paramsArray.push(key + "=" + value); } return paramsArray.join("&"); }; /** * @returns The fully formatted image location string * @type String */ OpenLayers.Util.getImagesLocation = function() { return OpenLayers._getScriptLocation() + "img/"; }; /** These could/should be made namespace aware? * * @param {} p * @param {str} tagName * * @return {Array} */ OpenLayers.Util.getNodes=function(p, tagName) { var nodes = Try.these( function () { return OpenLayers.Util._getNodes(p.documentElement.childNodes, tagName); }, function () { return OpenLayers.Util._getNodes(p.childNodes, tagName); } ); return nodes; }; /** * @param {Array} nodes * @param {str} tagName * * @return {Array} */ OpenLayers.Util._getNodes=function(nodes, tagName) { var retArray = new Array(); for (var i=0;i 0)) { if (!index) { index=0; } if (result[index].childNodes.length > 1) { return result.childNodes[1].nodeValue; } else if (result[index].childNodes.length == 1) { return result[index].firstChild.nodeValue; } } else { return ""; } }; /** * @param {Event} evt * @param {HTMLDivElement} div * * @return {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); }; OpenLayers.Util.rad = function(x) {return x*Math.PI/180;}; OpenLayers.Util.distVincenty=function(p1, p2) { var a = 6378137, b = 6356752.3142, f = 1/298.257223563; 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; };