1 /* Copyright (c) 2006 MetaCarta, Inc., published under the BSD license.
2 * See http://svn.openlayers.org/trunk/openlayers/license.txt for the full
3 * text of the license. */
7 OpenLayers.Util = new Object();
13 * @class This class represents a screen coordinate, in x and y coordinates
15 OpenLayers.Pixel = Class.create();
16 OpenLayers.Pixel.prototype = {
30 initialize: function(x, y) {
36 * @return string representation of Pixel. ex: "x=200.4,y=242.2"
40 return ("x=" + this.x + ",y=" + this.y);
44 * @type OpenLayers.Pixel
47 return new OpenLayers.Pixel(this.x, this.y);
51 * @param {OpenLayers.Pixel} px
53 * @return whether or not the point passed in as parameter is equal to this
54 * note that if px passed in is null, returns false
60 equals = ((this.x == px.x) && (this.y == px.y));
69 * @return a new Pixel with this pixel's x&y augmented by the
71 * @type OpenLayers.Pixel
74 return new OpenLayers.Pixel(this.x + x, this.y + y);
78 * @param {OpenLayers.Pixel} px
80 * @return a new Pixel with this pixel's x&y augmented by the
81 * x&y values of the pixel passed in.
82 * @type OpenLayers.Pixel
85 return this.add(px.x, px.y);
88 /** @final @type str */
89 CLASS_NAME: "OpenLayers.Pixel"
94 * @class This class represents a width and height pair
96 OpenLayers.Size = Class.create();
97 OpenLayers.Size.prototype = {
112 initialize: function(w, h) {
118 * @return String representation of OpenLayers.Size object.
119 * (ex. <i>"w=55,h=66"</i>)
122 toString:function() {
123 return ("w=" + this.w + ",h=" + this.h);
127 * @return New OpenLayers.Size object with the same w and h values
128 * @type OpenLayers.Size
131 return new OpenLayers.Size(this.w, this.h);
135 * @param {OpenLayers.Size} sz
136 * @returns Boolean value indicating whether the passed-in OpenLayers.Size
137 * object has the same w and h components as this
138 * note that if sz passed in is null, returns false
142 equals:function(sz) {
145 equals = ((this.w == sz.w) && (this.h == sz.h));
150 /** @final @type String */
151 CLASS_NAME: "OpenLayers.Size"
155 * @class This class represents a longitude and latitude pair
157 OpenLayers.LonLat = Class.create();
158 OpenLayers.LonLat.prototype = {
172 initialize: function(lon, lat) {
178 * @return String representation of OpenLayers.LonLat object.
179 * (ex. <i>"lon=5,lat=42"</i>)
182 toString:function() {
183 return ("lon=" + this.lon + ",lat=" + this.lat);
187 * @return Shortened String representation of OpenLayers.LonLat object.
188 * (ex. <i>"5, 42"</i>)
191 toShortString:function() {
192 return (this.lon + ", " + this.lat);
196 * @return New OpenLayers.LonLat object with the same lon and lat values
197 * @type OpenLayers.LonLat
200 return new OpenLayers.LonLat(this.lon, this.lat);
207 * @return A new OpenLayers.LonLat object with the lon and lat passed-in
209 * @type OpenLayers.LonLat
211 add:function(lon, lat) {
212 return new OpenLayers.LonLat(this.lon + lon, this.lat + lat);
216 * @param {OpenLayers.LonLat} ll
217 * @returns Boolean value indicating whether the passed-in OpenLayers.LonLat
218 * object has the same lon and lat components as this
219 * note that if ll passed in is null, returns false
223 equals:function(ll) {
226 equals = ((this.lon == ll.lon) && (this.lat == ll.lat));
231 /** @final @type String */
232 CLASS_NAME: "OpenLayers.LonLat"
235 /** Alternative constructor that builds a new OpenLayers.LonLat from a
240 * @param {String} str Comma-separated Lon,Lat coordinate string.
241 * (ex. <i>"5,40"</i>)
243 * @returns New OpenLayers.LonLat object built from the passed-in String.
244 * @type OpenLayers.LonLat
246 OpenLayers.LonLat.fromString = function(str) {
247 var pair = str.split(",");
248 return new OpenLayers.LonLat(parseFloat(pair[0]),
249 parseFloat(pair[1]));
256 * @class This class represents a bounding box.
257 * Data stored as left, bottom, right, top floats
259 OpenLayers.Bounds = Class.create();
260 OpenLayers.Bounds.prototype = {
277 * @param {float} left
278 * @param {float} bottom
279 * @param {float} right
283 initialize: function(left, bottom, right, top) {
285 this.bottom = bottom;
291 * @returns A fresh copy of the bounds
292 * @type OpenLayers.Bounds
295 return new OpenLayers.Bounds(this.left, this.bottom,
296 this.right, this.top);
300 * @param {OpenLayers.Bounds} bounds
301 * @returns Boolean value indicating whether the passed-in OpenLayers.Bounds
302 * object has the same left, right, top, bottom components as this
303 * note that if bounds passed in is null, returns false
307 equals:function(bounds) {
309 if (bounds != null) {
310 equals = ((this.left == bounds.left) &&
311 (this.right == bounds.right) &&
312 (this.top == bounds.top) &&
313 (this.bottom == bounds.bottom));
319 * @return String representation of OpenLayers.Bounds object.
320 * (ex.<i>"left-bottom=(5,42) right-top=(10,45)"</i>)
324 return ( "left-bottom=(" + this.left + "," + this.bottom + ")"
325 + " right-top=(" + this.right + "," + this.top + ")" );
329 * @return Simple String representation of OpenLayers.Bounds object.
330 * (ex. <i>"5,42,10,45"</i>)
334 return (this.left + "," + this.bottom + ","
335 + this.right + "," + this.top);
339 * @returns The width of the bounds
342 getWidth:function() {
343 return (this.right - this.left);
347 * @returns The height of the bounds
350 getHeight:function() {
351 return (this.top - this.bottom);
355 * @returns An OpenLayers.Size which represents the size of the box
356 * @type OpenLayers.Size
359 return new OpenLayers.Size(this.getWidth(), this.getHeight());
363 * @returns An OpenLayers.Pixel which represents the center of the bounds
364 * @type OpenLayers.Pixel
366 getCenterPixel:function() {
367 return new OpenLayers.Pixel( (this.left + this.right) / 2,
368 (this.bottom + this.top) / 2);
372 * @returns An OpenLayers.LonLat which represents the center of the bounds
373 * @type OpenLayers.LonLat
375 getCenterLonLat:function() {
376 return new OpenLayers.LonLat( (this.left + this.right) / 2,
377 (this.bottom + this.top) / 2);
384 * @returns A new OpenLayers.Bounds whose coordinates are the same as this,
385 * but shifted by the passed-in x and y values
386 * @type OpenLayers.Bounds
389 return new OpenLayers.Box(this.left + x, this.bottom + y,
390 this.right + x, this.top + y);
396 * @param {Boolean} inclusive Whether or not to include the border.
399 * @return Whether or not the passed-in coordinates are within this bounds
402 contains:function(x, y, inclusive) {
405 if (inclusive == null) {
409 var contains = false;
411 contains = ((x >= this.left) && (x <= this.right) &&
412 (y >= this.bottom) && (y <= this.top));
414 contains = ((x > this.left) && (x < this.right) &&
415 (y > this.bottom) && (y < this.top));
421 * @param {OpenLayers.Bounds} bounds
422 * @param {Boolean} partial If true, only part of passed-in
423 * OpenLayers.Bounds needs be within this bounds.
424 * If false, the entire passed-in bounds must be
425 * within. Default is false
426 * @param {Boolean} inclusive Whether or not to include the border.
429 * @return Whether or not the passed-in OpenLayers.Bounds object is
430 * contained within this bounds.
433 containsBounds:function(bounds, partial, inclusive) {
436 if (partial == null) {
439 if (inclusive == null) {
449 inLeft = (bounds.left >= this.left) && (bounds.left <= this.right);
450 inTop = (bounds.top >= this.bottom) && (bounds.top <= this.top);
451 inRight= (bounds.right >= this.left) && (bounds.right <= this.right);
452 inBottom = (bounds.bottom >= this.bottom) && (bounds.bottom <= this.top);
454 inLeft = (bounds.left > this.left) && (bounds.left < this.right);
455 inTop = (bounds.top > this.bottom) && (bounds.top < this.top);
456 inRight= (bounds.right > this.left) && (bounds.right < this.right);
457 inBottom = (bounds.bottom > this.bottom) && (bounds.bottom < this.top);
460 return (partial) ? (inTop || inBottom) && (inLeft || inRight )
461 : (inTop && inLeft && inBottom && inRight);
465 * @param {OpenLayers.LonLat} lonlat
467 * @returns The quadrant ("br" "tr" "tl" "bl") of the bounds in which
468 * the coordinate lies.
471 determineQuadrant: function(lonlat) {
474 var center = this.getCenterLonLat();
476 quadrant += (lonlat.lat < center.lat) ? "b" : "t";
477 quadrant += (lonlat.lon < center.lon) ? "l" : "r";
482 /** @final @type String */
483 CLASS_NAME: "OpenLayers.Bounds"
486 /** Alternative constructor that builds a new OpenLayers.Bounds from a
491 * @param {String} str Comma-separated bounds string. (ex. <i>"5,42,10,45"</i>)
493 * @returns New OpenLayers.Bounds object built from the passed-in String.
494 * @type OpenLayers.Bounds
496 OpenLayers.Bounds.fromString = function(str) {
497 var bounds = str.split(",");
498 return OpenLayers.Bounds.fromArray(bounds);
501 /** Alternative constructor that builds a new OpenLayers.Bounds
506 * @param {Array} bbox Array of bounds values (ex. <i>[5,42,10,45]</i>)
508 * @returns New OpenLayers.Bounds object built from the passed-in Array.
509 * @type OpenLayers.Bounds
511 OpenLayers.Bounds.fromArray = function(bbox) {
512 return new OpenLayers.Bounds(parseFloat(bbox[0]),
515 parseFloat(bbox[3]));
518 /** Alternative constructor that builds a new OpenLayers.Bounds
519 * from an OpenLayers.Size
523 * @param {OpenLayers.Size} size
525 * @returns New OpenLayers.Bounds object built with top and left set to 0 and
526 * bottom right taken from the passed-in OpenLayers.Size.
527 * @type OpenLayers.Bounds
529 OpenLayers.Bounds.fromSize = function(size) {
530 return new OpenLayers.Bounds(0,
536 * @param {String} quadrant
538 * @returns The opposing quadrant ("br" "tr" "tl" "bl"). For Example, if
539 * you pass in "bl" it returns "tr", if you pass in "br" it
543 OpenLayers.Bounds.oppositeQuadrant = function(quadrant) {
546 opp += (quadrant.charAt(0) == 't') ? 'b' : 't';
547 opp += (quadrant.charAt(1) == 'l') ? 'r' : 'l';
552 // Some other helpful things
555 * @param {String} sStart
557 * @returns Whether or not this string starts with the string passed in.
560 String.prototype.startsWith = function(sStart){
561 return (this.substr(0,sStart.length) == sStart);
565 * @returns A trimmed version of the string - all leading and
566 * trailing spaces removed
569 String.prototype.trim = function() {
572 while(this.substr(b,1) == " ") {
576 var e = this.length - 1;
577 while(this.substr(e,1) == " ") {
581 return this.substring(b, e+1);
584 /** Remove an object from an array. Iterates through the array
585 * to find the item, then removes it.
587 * @param {Object} item
589 * @returns A reference to the array
592 Array.prototype.remove = function(item) {
593 for(var i=0; i < this.length; i++) {
594 if(this[i] == item) {
596 //break;more than once??
603 * @returns A fresh copy of the array
606 Array.prototype.copyOf = function() {
607 var copy = new Array();
608 for (var i = 0; i < this.length; i++) {
615 * @param {Object} item
617 Array.prototype.prepend = function(item) {
618 this.splice(0, 0, item);
622 * @param {Object} item
624 Array.prototype.append = function(item){
625 this[this.length] = item;
630 Array.prototype.clear = function() {
635 * @param {Object} element
637 * @returns The first index of the element in the array if found. Else returns -1
640 Array.prototype.indexOf = function(element) {
642 for(var i=0; i < this.length; i++) {
643 if (this[i] == element) {
653 * @param {OpenLayers.Pixel} px
654 * @param {OpenLayers.Size} sz
655 * @param {String} position
656 * @param {String} border
657 * @param {String} overflow
659 OpenLayers.Util.modifyDOMElement = function(element, id, px, sz, position,
666 element.style.left = px.x + "px";
667 element.style.top = px.y + "px";
670 element.style.width = sz.w + "px";
671 element.style.height = sz.h + "px";
674 element.style.position = position;
677 element.style.border = border;
680 element.style.overflow = overflow;
688 * @param {OpenLayers.Pixel} px
689 * @param {OpenLayers.Size} sz
690 * @param {String} imgURL
691 * @param {String} position
692 * @param {String} border
693 * @param {String} overflow
695 * @returns A DOM Div created with the specified attributes.
698 OpenLayers.Util.createDiv = function(id, px, sz, imgURL, position,
701 var dom = document.createElement('div');
703 //set specific properties
704 dom.style.padding = "0";
705 dom.style.margin = "0";
707 dom.style.backgroundImage = 'url(' + imgURL + ')';
710 //set generic properties
712 id = "OpenLayersDiv" + (Math.random() * 10000 % 10000);
715 position = "absolute";
717 OpenLayers.Util.modifyDOMElement(dom, id, px, sz,
718 position, border, overflow);
725 * @param {OpenLayers.Pixel} px
726 * @param {OpenLayers.Size} sz
727 * @param {String} imgURL
728 * @param {String} position
729 * @param {String} border
731 * @returns A DOM Image created with the specified attributes.
734 OpenLayers.Util.createImage = function(id, px, sz, imgURL, position, border) {
736 image = document.createElement("img");
738 //set special properties
739 image.style.alt = id;
740 image.galleryImg = "no";
745 //set generic properties
747 id = "OpenLayersDiv" + (Math.random() * 10000 % 10000);
750 position = "relative";
752 OpenLayers.Util.modifyDOMElement(image, id, px, sz, position, border);
757 OpenLayers.Util.alphaHack = function() {
758 var arVersion = navigator.appVersion.split("MSIE");
759 var version = parseFloat(arVersion[1]);
761 return ( (document.body.filters) &&
762 (version >= 5.5) && (version < 7) );
766 * @param {DOMElement} div Div containing Alpha-adjusted Image
768 * @param {OpenLayers.Pixel} px
769 * @param {OpenLayers.Size} sz
770 * @param {String} imgURL
771 * @param {String} position
772 * @param {String} border
773 * @param {String} sizing 'crop', 'scale', or 'image'. Default is "scale"
775 OpenLayers.Util.modifyAlphaImageDiv = function(div, id, px, sz, imgURL,
776 position, border, sizing) {
778 OpenLayers.Util.modifyDOMElement(div, id, px, sz);
780 var img = div.childNodes[0];
785 OpenLayers.Util.modifyDOMElement(img, div.id + "_innerImage", null, sz,
788 if (OpenLayers.Util.alphaHack()) {
789 div.style.display = "inline-block";
790 if (sizing == null) {
793 div.style.filter = "progid:DXImageTransform.Microsoft" +
794 ".AlphaImageLoader(src='" + img.src + "', " +
795 "sizingMethod='" + sizing + "')";
796 img.style.filter = "progid:DXImageTransform.Microsoft" +
803 * @param {OpenLayers.Pixel} px
804 * @param {OpenLayers.Size} sz
805 * @param {String} imgURL
806 * @param {String} position
807 * @param {String} border
808 * @param {String} sizing 'crop', 'scale', or 'image'. Default is "scale"
810 * @returns A DOM Div created with a DOM Image inside it. If the hack is
811 * needed for transparency in IE, it is added.
814 OpenLayers.Util.createAlphaImageDiv = function(id, px, sz, imgURL,
815 position, border, sizing) {
817 var div = OpenLayers.Util.createDiv();
818 var img = OpenLayers.Util.createImage();
819 div.appendChild(img);
821 OpenLayers.Util.modifyAlphaImageDiv(div, id, px, sz, imgURL,
822 position, border, sizing);
828 /** Creates a new hash and copies over all the keys from the
829 * passed-in object, but storing them under an uppercased
830 * version of the key at which they were stored.
832 * @param {Object} object
834 * @returns A new Object with all the same keys but uppercased
837 OpenLayers.Util.upperCaseObject = function (object) {
838 var uObject = new Object();
839 for (var key in object) {
840 uObject[key.toUpperCase()] = object[key];
845 /** Takes a hash and copies any keys that don't exist from
846 * another hash, by analogy with Object.extend() from
850 * @param {Object} from
854 OpenLayers.Util.applyDefaults = function (to, from) {
855 for (var key in from) {
856 if (to[key] == null) {
864 * @param {Object} params
866 * @returns a concatenation of the properties of an object in
867 * http parameter notation.
868 * (ex. <i>"key1=value1&key2=value2&key3=value3"</i>)
871 OpenLayers.Util.getParameterString = function(params) {
872 paramsArray = new Array();
874 for (var key in params) {
875 var value = params[key];
877 if (typeof value == 'function') continue;
879 paramsArray.push(key + "=" + value);
882 return paramsArray.join("&");
886 * @returns The fully formatted image location string
889 OpenLayers.Util.getImagesLocation = function() {
890 return OpenLayers._getScriptLocation() + "img/";
895 /** These could/should be made namespace aware?
898 * @param {str} tagName
902 OpenLayers.Util.getNodes=function(p, tagName) {
903 var nodes = Try.these(
905 return OpenLayers.Util._getNodes(p.documentElement.childNodes,
909 return OpenLayers.Util._getNodes(p.childNodes, tagName);
916 * @param {Array} nodes
917 * @param {str} tagName
921 OpenLayers.Util._getNodes=function(nodes, tagName) {
922 var retArray = new Array();
923 for (var i=0;i<nodes.length;i++) {
924 if (nodes[i].nodeName==tagName) {
925 retArray.push(nodes[i]);
941 OpenLayers.Util.getTagText = function (parent, item, index) {
942 var result = OpenLayers.Util.getNodes(parent, item);
943 if (result && (result.length > 0))
948 if (result[index].childNodes.length > 1) {
949 return result.childNodes[1].nodeValue;
951 else if (result[index].childNodes.length == 1) {
952 return result[index].firstChild.nodeValue;
961 * @param {HTMLDivElement} div
965 OpenLayers.Util.mouseLeft = function (evt, div) {
966 // start with the element to which the mouse has moved
967 var target = (evt.relatedTarget) ? evt.relatedTarget : evt.toElement;
968 // walk up the DOM tree.
969 while (target != div && target != null) {
970 target = target.parentNode;
972 // if the target we stop at isn't the div, then we've left the div.
973 return (target != div);
976 OpenLayers.Util.rad = function(x) {return x*Math.PI/180;};
977 OpenLayers.Util.distVincenty=function(p1, p2) {
978 var a = 6378137, b = 6356752.3142, f = 1/298.257223563;
979 var L = OpenLayers.Util.rad(p2.lon - p1.lon);
980 var U1 = Math.atan((1-f) * Math.tan(OpenLayers.Util.rad(p1.lat)));
981 var U2 = Math.atan((1-f) * Math.tan(OpenLayers.Util.rad(p2.lat)));
982 var sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
983 var sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
984 var lambda = L, lambdaP = 2*Math.PI;
986 while (Math.abs(lambda-lambdaP) > 1e-12 && --iterLimit>0) {
987 var sinLambda = Math.sin(lambda), cosLambda = Math.cos(lambda);
988 var sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) +
989 (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda));
990 if (sinSigma==0) return 0; // co-incident points
991 var cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda;
992 var sigma = Math.atan2(sinSigma, cosSigma);
993 var alpha = Math.asin(cosU1 * cosU2 * sinLambda / sinSigma);
994 var cosSqAlpha = Math.cos(alpha) * Math.cos(alpha);
995 var cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha;
996 var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
998 lambda = L + (1-C) * f * Math.sin(alpha) *
999 (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
1001 if (iterLimit==0) return NaN // formula failed to converge
1002 var uSq = cosSqAlpha * (a*a - b*b) / (b*b);
1003 var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
1004 var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
1005 var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
1006 B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
1007 var s = b*A*(sigma-deltaSigma);
1008 var d = s.toFixed(3)/1000; // round to 1mm precision