openstreetmap-website/public/lib/OpenLayers/Util.js
2006-08-27 00:45:43 +00:00

1010 lines
26 KiB
JavaScript

/* 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. <i>"w=55,h=66"</i>)
* @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. <i>"lon=5,lat=42"</i>)
* @type String
*/
toString:function() {
return ("lon=" + this.lon + ",lat=" + this.lat);
},
/**
* @return Shortened String representation of OpenLayers.LonLat object.
* (ex. <i>"5, 42"</i>)
* @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. <i>"5,40"</i>)
*
* @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.<i>"left-bottom=(5,42) right-top=(10,45)"</i>)
* @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. <i>"5,42,10,45"</i>)
* @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. <i>"5,42,10,45"</i>)
*
* @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. <i>[5,42,10,45]</i>)
*
* @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. <i>"key1=value1&key2=value2&key3=value3"</i>)
* @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<nodes.length;i++) {
if (nodes[i].nodeName==tagName) {
retArray.push(nodes[i]);
}
}
return retArray;
};
/**
* @param {} parent
* @param {str} item
* @param {int} index
*
* @return {str}
*/
OpenLayers.Util.getTagText = function (parent, item, index) {
var result = OpenLayers.Util.getNodes(parent, item);
if (result && (result.length > 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;
};