Util.js 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773
  1. /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
  2. * full list of contributors). Published under the 2-clause BSD license.
  3. * See license.txt in the OpenLayers distribution or repository for the
  4. * full text of the license. */
  5. /**
  6. * @requires OpenLayers/BaseTypes.js
  7. * @requires OpenLayers/BaseTypes/Bounds.js
  8. * @requires OpenLayers/BaseTypes/Element.js
  9. * @requires OpenLayers/BaseTypes/LonLat.js
  10. * @requires OpenLayers/BaseTypes/Pixel.js
  11. * @requires OpenLayers/BaseTypes/Size.js
  12. * @requires OpenLayers/Lang.js
  13. */
  14. /**
  15. * Namespace: Util
  16. */
  17. OpenLayers.Util = OpenLayers.Util || {};
  18. /**
  19. * Function: getElement
  20. * This is the old $() from prototype
  21. *
  22. * Parameters:
  23. * e - {String or DOMElement or Window}
  24. *
  25. * Returns:
  26. * {Array(DOMElement) or DOMElement}
  27. */
  28. OpenLayers.Util.getElement = function() {
  29. var elements = [];
  30. for (var i=0, len=arguments.length; i<len; i++) {
  31. var element = arguments[i];
  32. if (typeof element == 'string') {
  33. element = document.getElementById(element);
  34. }
  35. if (arguments.length == 1) {
  36. return element;
  37. }
  38. elements.push(element);
  39. }
  40. return elements;
  41. };
  42. /**
  43. * Function: isElement
  44. * A cross-browser implementation of "e instanceof Element".
  45. *
  46. * Parameters:
  47. * o - {Object} The object to test.
  48. *
  49. * Returns:
  50. * {Boolean}
  51. */
  52. OpenLayers.Util.isElement = function(o) {
  53. return !!(o && o.nodeType === 1);
  54. };
  55. /**
  56. * Function: isArray
  57. * Tests that the provided object is an array.
  58. * This test handles the cross-IFRAME case not caught
  59. * by "a instanceof Array" and should be used instead.
  60. *
  61. * Parameters:
  62. * a - {Object} the object test.
  63. *
  64. * Returns:
  65. * {Boolean} true if the object is an array.
  66. */
  67. OpenLayers.Util.isArray = function(a) {
  68. return (Object.prototype.toString.call(a) === '[object Array]');
  69. };
  70. /**
  71. * Function: removeItem
  72. * Remove an object from an array. Iterates through the array
  73. * to find the item, then removes it.
  74. *
  75. * Parameters:
  76. * array - {Array}
  77. * item - {Object}
  78. *
  79. * Returns:
  80. * {Array} A reference to the array
  81. */
  82. OpenLayers.Util.removeItem = function(array, item) {
  83. for(var i = array.length - 1; i >= 0; i--) {
  84. if(array[i] == item) {
  85. array.splice(i,1);
  86. //break;more than once??
  87. }
  88. }
  89. return array;
  90. };
  91. /**
  92. * Function: indexOf
  93. * Seems to exist already in FF, but not in MOZ.
  94. *
  95. * Parameters:
  96. * array - {Array}
  97. * obj - {*}
  98. *
  99. * Returns:
  100. * {Integer} The index at which the first object was found in the array.
  101. * If not found, returns -1.
  102. */
  103. OpenLayers.Util.indexOf = function(array, obj) {
  104. // use the build-in function if available.
  105. if (typeof array.indexOf == "function") {
  106. return array.indexOf(obj);
  107. } else {
  108. for (var i = 0, len = array.length; i < len; i++) {
  109. if (array[i] == obj) {
  110. return i;
  111. }
  112. }
  113. return -1;
  114. }
  115. };
  116. /**
  117. * Property: dotless
  118. * {RegExp}
  119. * Compiled regular expression to match dots ("."). This is used for replacing
  120. * dots in identifiers. Because object identifiers are frequently used for
  121. * DOM element identifiers by the library, we avoid using dots to make for
  122. * more sensible CSS selectors.
  123. *
  124. * TODO: Use a module pattern to avoid bloating the API with stuff like this.
  125. */
  126. OpenLayers.Util.dotless = /\./g;
  127. /**
  128. * Function: modifyDOMElement
  129. *
  130. * Modifies many properties of a DOM element all at once. Passing in
  131. * null to an individual parameter will avoid setting the attribute.
  132. *
  133. * Parameters:
  134. * element - {DOMElement} DOM element to modify.
  135. * id - {String} The element id attribute to set. Note that dots (".") will be
  136. * replaced with underscore ("_") in setting the element id.
  137. * px - {<OpenLayers.Pixel>|Object} The element left and top position,
  138. * OpenLayers.Pixel or an object with
  139. * a 'x' and 'y' properties.
  140. * sz - {<OpenLayers.Size>|Object} The element width and height,
  141. * OpenLayers.Size or an object with a
  142. * 'w' and 'h' properties.
  143. * position - {String} The position attribute. eg: absolute,
  144. * relative, etc.
  145. * border - {String} The style.border attribute. eg:
  146. * solid black 2px
  147. * overflow - {String} The style.overview attribute.
  148. * opacity - {Float} Fractional value (0.0 - 1.0)
  149. */
  150. OpenLayers.Util.modifyDOMElement = function(element, id, px, sz, position,
  151. border, overflow, opacity) {
  152. if (id) {
  153. element.id = id.replace(OpenLayers.Util.dotless, "_");
  154. }
  155. if (px) {
  156. element.style.left = px.x + "px";
  157. element.style.top = px.y + "px";
  158. }
  159. if (sz) {
  160. element.style.width = sz.w + "px";
  161. element.style.height = sz.h + "px";
  162. }
  163. if (position) {
  164. element.style.position = position;
  165. }
  166. if (border) {
  167. element.style.border = border;
  168. }
  169. if (overflow) {
  170. element.style.overflow = overflow;
  171. }
  172. if (parseFloat(opacity) >= 0.0 && parseFloat(opacity) < 1.0) {
  173. element.style.filter = 'alpha(opacity=' + (opacity * 100) + ')';
  174. element.style.opacity = opacity;
  175. } else if (parseFloat(opacity) == 1.0) {
  176. element.style.filter = '';
  177. element.style.opacity = '';
  178. }
  179. };
  180. /**
  181. * Function: createDiv
  182. * Creates a new div and optionally set some standard attributes.
  183. * Null may be passed to each parameter if you do not wish to
  184. * set a particular attribute.
  185. * Note - zIndex is NOT set on the resulting div.
  186. *
  187. * Parameters:
  188. * id - {String} An identifier for this element. If no id is
  189. * passed an identifier will be created
  190. * automatically. Note that dots (".") will be replaced with
  191. * underscore ("_") when generating ids.
  192. * px - {<OpenLayers.Pixel>|Object} The element left and top position,
  193. * OpenLayers.Pixel or an object with
  194. * a 'x' and 'y' properties.
  195. * sz - {<OpenLayers.Size>|Object} The element width and height,
  196. * OpenLayers.Size or an object with a
  197. * 'w' and 'h' properties.
  198. * imgURL - {String} A url pointing to an image to use as a
  199. * background image.
  200. * position - {String} The style.position value. eg: absolute,
  201. * relative etc.
  202. * border - {String} The the style.border value.
  203. * eg: 2px solid black
  204. * overflow - {String} The style.overflow value. Eg. hidden
  205. * opacity - {Float} Fractional value (0.0 - 1.0)
  206. *
  207. * Returns:
  208. * {DOMElement} A DOM Div created with the specified attributes.
  209. */
  210. OpenLayers.Util.createDiv = function(id, px, sz, imgURL, position,
  211. border, overflow, opacity) {
  212. var dom = document.createElement('div');
  213. if (imgURL) {
  214. dom.style.backgroundImage = 'url(' + imgURL + ')';
  215. }
  216. //set generic properties
  217. if (!id) {
  218. id = OpenLayers.Util.createUniqueID("OpenLayersDiv");
  219. }
  220. if (!position) {
  221. position = "absolute";
  222. }
  223. OpenLayers.Util.modifyDOMElement(dom, id, px, sz, position,
  224. border, overflow, opacity);
  225. return dom;
  226. };
  227. /**
  228. * Function: createImage
  229. * Creates an img element with specific attribute values.
  230. *
  231. * Parameters:
  232. * id - {String} The id field for the img. If none assigned one will be
  233. * automatically generated.
  234. * px - {<OpenLayers.Pixel>|Object} The element left and top position,
  235. * OpenLayers.Pixel or an object with
  236. * a 'x' and 'y' properties.
  237. * sz - {<OpenLayers.Size>|Object} The element width and height,
  238. * OpenLayers.Size or an object with a
  239. * 'w' and 'h' properties.
  240. * imgURL - {String} The url to use as the image source.
  241. * position - {String} The style.position value.
  242. * border - {String} The border to place around the image.
  243. * opacity - {Float} Fractional value (0.0 - 1.0)
  244. * delayDisplay - {Boolean} If true waits until the image has been
  245. * loaded.
  246. *
  247. * Returns:
  248. * {DOMElement} A DOM Image created with the specified attributes.
  249. */
  250. OpenLayers.Util.createImage = function(id, px, sz, imgURL, position, border,
  251. opacity, delayDisplay) {
  252. var image = document.createElement("img");
  253. //set generic properties
  254. if (!id) {
  255. id = OpenLayers.Util.createUniqueID("OpenLayersDiv");
  256. }
  257. if (!position) {
  258. position = "relative";
  259. }
  260. OpenLayers.Util.modifyDOMElement(image, id, px, sz, position,
  261. border, null, opacity);
  262. if (delayDisplay) {
  263. image.style.display = "none";
  264. function display() {
  265. image.style.display = "";
  266. OpenLayers.Event.stopObservingElement(image);
  267. }
  268. OpenLayers.Event.observe(image, "load", display);
  269. OpenLayers.Event.observe(image, "error", display);
  270. }
  271. //set special properties
  272. image.style.alt = id;
  273. image.galleryImg = "no";
  274. if (imgURL) {
  275. image.src = imgURL;
  276. }
  277. return image;
  278. };
  279. /**
  280. * Property: IMAGE_RELOAD_ATTEMPTS
  281. * {Integer} How many times should we try to reload an image before giving up?
  282. * Default is 0
  283. */
  284. OpenLayers.IMAGE_RELOAD_ATTEMPTS = 0;
  285. /**
  286. * Property: alphaHackNeeded
  287. * {Boolean} true if the png alpha hack is necessary and possible, false otherwise.
  288. */
  289. OpenLayers.Util.alphaHackNeeded = null;
  290. /**
  291. * Function: alphaHack
  292. * Checks whether it's necessary (and possible) to use the png alpha
  293. * hack which allows alpha transparency for png images under Internet
  294. * Explorer.
  295. *
  296. * Returns:
  297. * {Boolean} true if the png alpha hack is necessary and possible, false otherwise.
  298. */
  299. OpenLayers.Util.alphaHack = function() {
  300. if (OpenLayers.Util.alphaHackNeeded == null) {
  301. var arVersion = navigator.appVersion.split("MSIE");
  302. var version = parseFloat(arVersion[1]);
  303. var filter = false;
  304. // IEs4Lin dies when trying to access document.body.filters, because
  305. // the property is there, but requires a DLL that can't be provided. This
  306. // means that we need to wrap this in a try/catch so that this can
  307. // continue.
  308. try {
  309. filter = !!(document.body.filters);
  310. } catch (e) {}
  311. OpenLayers.Util.alphaHackNeeded = (filter &&
  312. (version >= 5.5) && (version < 7));
  313. }
  314. return OpenLayers.Util.alphaHackNeeded;
  315. };
  316. /**
  317. * Function: modifyAlphaImageDiv
  318. *
  319. * Parameters:
  320. * div - {DOMElement} Div containing Alpha-adjusted Image
  321. * id - {String}
  322. * px - {<OpenLayers.Pixel>|Object} OpenLayers.Pixel or an object with
  323. * a 'x' and 'y' properties.
  324. * sz - {<OpenLayers.Size>|Object} OpenLayers.Size or an object with
  325. * a 'w' and 'h' properties.
  326. * imgURL - {String}
  327. * position - {String}
  328. * border - {String}
  329. * sizing - {String} 'crop', 'scale', or 'image'. Default is "scale"
  330. * opacity - {Float} Fractional value (0.0 - 1.0)
  331. */
  332. OpenLayers.Util.modifyAlphaImageDiv = function(div, id, px, sz, imgURL,
  333. position, border, sizing,
  334. opacity) {
  335. OpenLayers.Util.modifyDOMElement(div, id, px, sz, position,
  336. null, null, opacity);
  337. var img = div.childNodes[0];
  338. if (imgURL) {
  339. img.src = imgURL;
  340. }
  341. OpenLayers.Util.modifyDOMElement(img, div.id + "_innerImage", null, sz,
  342. "relative", border);
  343. if (OpenLayers.Util.alphaHack()) {
  344. if(div.style.display != "none") {
  345. div.style.display = "inline-block";
  346. }
  347. if (sizing == null) {
  348. sizing = "scale";
  349. }
  350. div.style.filter = "progid:DXImageTransform.Microsoft" +
  351. ".AlphaImageLoader(src='" + img.src + "', " +
  352. "sizingMethod='" + sizing + "')";
  353. if (parseFloat(div.style.opacity) >= 0.0 &&
  354. parseFloat(div.style.opacity) < 1.0) {
  355. div.style.filter += " alpha(opacity=" + div.style.opacity * 100 + ")";
  356. }
  357. img.style.filter = "alpha(opacity=0)";
  358. }
  359. };
  360. /**
  361. * Function: createAlphaImageDiv
  362. *
  363. * Parameters:
  364. * id - {String}
  365. * px - {<OpenLayers.Pixel>|Object} OpenLayers.Pixel or an object with
  366. * a 'x' and 'y' properties.
  367. * sz - {<OpenLayers.Size>|Object} OpenLayers.Size or an object with
  368. * a 'w' and 'h' properties.
  369. * imgURL - {String}
  370. * position - {String}
  371. * border - {String}
  372. * sizing - {String} 'crop', 'scale', or 'image'. Default is "scale"
  373. * opacity - {Float} Fractional value (0.0 - 1.0)
  374. * delayDisplay - {Boolean} If true waits until the image has been
  375. * loaded.
  376. *
  377. * Returns:
  378. * {DOMElement} A DOM Div created with a DOM Image inside it. If the hack is
  379. * needed for transparency in IE, it is added.
  380. */
  381. OpenLayers.Util.createAlphaImageDiv = function(id, px, sz, imgURL,
  382. position, border, sizing,
  383. opacity, delayDisplay) {
  384. var div = OpenLayers.Util.createDiv();
  385. var img = OpenLayers.Util.createImage(null, null, null, null, null, null,
  386. null, delayDisplay);
  387. img.className = "olAlphaImg";
  388. div.appendChild(img);
  389. OpenLayers.Util.modifyAlphaImageDiv(div, id, px, sz, imgURL, position,
  390. border, sizing, opacity);
  391. return div;
  392. };
  393. /**
  394. * Function: upperCaseObject
  395. * Creates a new hashtable and copies over all the keys from the
  396. * passed-in object, but storing them under an uppercased
  397. * version of the key at which they were stored.
  398. *
  399. * Parameters:
  400. * object - {Object}
  401. *
  402. * Returns:
  403. * {Object} A new Object with all the same keys but uppercased
  404. */
  405. OpenLayers.Util.upperCaseObject = function (object) {
  406. var uObject = {};
  407. for (var key in object) {
  408. uObject[key.toUpperCase()] = object[key];
  409. }
  410. return uObject;
  411. };
  412. /**
  413. * Function: applyDefaults
  414. * Takes an object and copies any properties that don't exist from
  415. * another properties, by analogy with OpenLayers.Util.extend() from
  416. * Prototype.js.
  417. *
  418. * Parameters:
  419. * to - {Object} The destination object.
  420. * from - {Object} The source object. Any properties of this object that
  421. * are undefined in the to object will be set on the to object.
  422. *
  423. * Returns:
  424. * {Object} A reference to the to object. Note that the to argument is modified
  425. * in place and returned by this function.
  426. */
  427. OpenLayers.Util.applyDefaults = function (to, from) {
  428. to = to || {};
  429. /*
  430. * FF/Windows < 2.0.0.13 reports "Illegal operation on WrappedNative
  431. * prototype object" when calling hawOwnProperty if the source object is an
  432. * instance of window.Event.
  433. */
  434. var fromIsEvt = typeof window.Event == "function"
  435. && from instanceof window.Event;
  436. for (var key in from) {
  437. if (to[key] === undefined ||
  438. (!fromIsEvt && from.hasOwnProperty
  439. && from.hasOwnProperty(key) && !to.hasOwnProperty(key))) {
  440. to[key] = from[key];
  441. }
  442. }
  443. /**
  444. * IE doesn't include the toString property when iterating over an object's
  445. * properties with the for(property in object) syntax. Explicitly check if
  446. * the source has its own toString property.
  447. */
  448. if(!fromIsEvt && from && from.hasOwnProperty
  449. && from.hasOwnProperty('toString') && !to.hasOwnProperty('toString')) {
  450. to.toString = from.toString;
  451. }
  452. return to;
  453. };
  454. /**
  455. * Function: getParameterString
  456. *
  457. * Parameters:
  458. * params - {Object}
  459. *
  460. * Returns:
  461. * {String} A concatenation of the properties of an object in
  462. * http parameter notation.
  463. * (ex. <i>"key1=value1&key2=value2&key3=value3"</i>)
  464. * If a parameter is actually a list, that parameter will then
  465. * be set to a comma-seperated list of values (foo,bar) instead
  466. * of being URL escaped (foo%3Abar).
  467. */
  468. OpenLayers.Util.getParameterString = function(params) {
  469. var paramsArray = [];
  470. for (var key in params) {
  471. var value = params[key];
  472. if ((value != null) && (typeof value != 'function')) {
  473. var encodedValue;
  474. if (typeof value == 'object' && value.constructor == Array) {
  475. /* value is an array; encode items and separate with "," */
  476. var encodedItemArray = [];
  477. var item;
  478. for (var itemIndex=0, len=value.length; itemIndex<len; itemIndex++) {
  479. item = value[itemIndex];
  480. encodedItemArray.push(encodeURIComponent(
  481. (item === null || item === undefined) ? "" : item)
  482. );
  483. }
  484. encodedValue = encodedItemArray.join(",");
  485. }
  486. else {
  487. /* value is a string; simply encode */
  488. encodedValue = encodeURIComponent(value);
  489. }
  490. paramsArray.push(encodeURIComponent(key) + "=" + encodedValue);
  491. }
  492. }
  493. return paramsArray.join("&");
  494. };
  495. /**
  496. * Function: urlAppend
  497. * Appends a parameter string to a url. This function includes the logic for
  498. * using the appropriate character (none, & or ?) to append to the url before
  499. * appending the param string.
  500. *
  501. * Parameters:
  502. * url - {String} The url to append to
  503. * paramStr - {String} The param string to append
  504. *
  505. * Returns:
  506. * {String} The new url
  507. */
  508. OpenLayers.Util.urlAppend = function(url, paramStr) {
  509. var newUrl = url;
  510. if(paramStr) {
  511. var parts = (url + " ").split(/[?&]/);
  512. newUrl += (parts.pop() === " " ?
  513. paramStr :
  514. parts.length ? "&" + paramStr : "?" + paramStr);
  515. }
  516. return newUrl;
  517. };
  518. /**
  519. * Function: getImagesLocation
  520. *
  521. * Returns:
  522. * {String} The fully formatted image location string
  523. */
  524. OpenLayers.Util.getImagesLocation = function() {
  525. return OpenLayers.ImgPath || (OpenLayers._getScriptLocation() + "img/");
  526. };
  527. /**
  528. * Function: getImageLocation
  529. *
  530. * Returns:
  531. * {String} The fully formatted location string for a specified image
  532. */
  533. OpenLayers.Util.getImageLocation = function(image) {
  534. return OpenLayers.Util.getImagesLocation() + image;
  535. };
  536. /**
  537. * Function: Try
  538. * Execute functions until one of them doesn't throw an error.
  539. * Capitalized because "try" is a reserved word in JavaScript.
  540. * Taken directly from OpenLayers.Util.Try()
  541. *
  542. * Parameters:
  543. * [*] - {Function} Any number of parameters may be passed to Try()
  544. * It will attempt to execute each of them until one of them
  545. * successfully executes.
  546. * If none executes successfully, returns null.
  547. *
  548. * Returns:
  549. * {*} The value returned by the first successfully executed function.
  550. */
  551. OpenLayers.Util.Try = function() {
  552. var returnValue = null;
  553. for (var i=0, len=arguments.length; i<len; i++) {
  554. var lambda = arguments[i];
  555. try {
  556. returnValue = lambda();
  557. break;
  558. } catch (e) {}
  559. }
  560. return returnValue;
  561. };
  562. /**
  563. * Function: getXmlNodeValue
  564. *
  565. * Parameters:
  566. * node - {XMLNode}
  567. *
  568. * Returns:
  569. * {String} The text value of the given node, without breaking in firefox or IE
  570. */
  571. OpenLayers.Util.getXmlNodeValue = function(node) {
  572. var val = null;
  573. OpenLayers.Util.Try(
  574. function() {
  575. val = node.text;
  576. if (!val) {
  577. val = node.textContent;
  578. }
  579. if (!val) {
  580. val = node.firstChild.nodeValue;
  581. }
  582. },
  583. function() {
  584. val = node.textContent;
  585. });
  586. return val;
  587. };
  588. /**
  589. * Function: mouseLeft
  590. *
  591. * Parameters:
  592. * evt - {Event}
  593. * div - {HTMLDivElement}
  594. *
  595. * Returns:
  596. * {Boolean}
  597. */
  598. OpenLayers.Util.mouseLeft = function (evt, div) {
  599. // start with the element to which the mouse has moved
  600. var target = (evt.relatedTarget) ? evt.relatedTarget : evt.toElement;
  601. // walk up the DOM tree.
  602. while (target != div && target != null) {
  603. target = target.parentNode;
  604. }
  605. // if the target we stop at isn't the div, then we've left the div.
  606. return (target != div);
  607. };
  608. /**
  609. * Property: precision
  610. * {Number} The number of significant digits to retain to avoid
  611. * floating point precision errors.
  612. *
  613. * We use 14 as a "safe" default because, although IEEE 754 double floats
  614. * (standard on most modern operating systems) support up to about 16
  615. * significant digits, 14 significant digits are sufficient to represent
  616. * sub-millimeter accuracy in any coordinate system that anyone is likely to
  617. * use with OpenLayers.
  618. *
  619. * If DEFAULT_PRECISION is set to 0, the original non-truncating behavior
  620. * of OpenLayers <2.8 is preserved. Be aware that this will cause problems
  621. * with certain projections, e.g. spherical Mercator.
  622. *
  623. */
  624. OpenLayers.Util.DEFAULT_PRECISION = 14;
  625. /**
  626. * Function: toFloat
  627. * Convenience method to cast an object to a Number, rounded to the
  628. * desired floating point precision.
  629. *
  630. * Parameters:
  631. * number - {Number} The number to cast and round.
  632. * precision - {Number} An integer suitable for use with
  633. * Number.toPrecision(). Defaults to OpenLayers.Util.DEFAULT_PRECISION.
  634. * If set to 0, no rounding is performed.
  635. *
  636. * Returns:
  637. * {Number} The cast, rounded number.
  638. */
  639. OpenLayers.Util.toFloat = function (number, precision) {
  640. if (precision == null) {
  641. precision = OpenLayers.Util.DEFAULT_PRECISION;
  642. }
  643. if (typeof number !== "number") {
  644. number = parseFloat(number);
  645. }
  646. return precision === 0 ? number :
  647. parseFloat(number.toPrecision(precision));
  648. };
  649. /**
  650. * Function: rad
  651. *
  652. * Parameters:
  653. * x - {Float}
  654. *
  655. * Returns:
  656. * {Float}
  657. */
  658. OpenLayers.Util.rad = function(x) {return x*Math.PI/180;};
  659. /**
  660. * Function: deg
  661. *
  662. * Parameters:
  663. * x - {Float}
  664. *
  665. * Returns:
  666. * {Float}
  667. */
  668. OpenLayers.Util.deg = function(x) {return x*180/Math.PI;};
  669. /**
  670. * Property: VincentyConstants
  671. * {Object} Constants for Vincenty functions.
  672. */
  673. OpenLayers.Util.VincentyConstants = {
  674. a: 6378137,
  675. b: 6356752.3142,
  676. f: 1/298.257223563
  677. };
  678. /**
  679. * APIFunction: distVincenty
  680. * Given two objects representing points with geographic coordinates, this
  681. * calculates the distance between those points on the surface of an
  682. * ellipsoid.
  683. *
  684. * Parameters:
  685. * p1 - {<OpenLayers.LonLat>} (or any object with both .lat, .lon properties)
  686. * p2 - {<OpenLayers.LonLat>} (or any object with both .lat, .lon properties)
  687. *
  688. * Returns:
  689. * {Float} The distance (in km) between the two input points as measured on an
  690. * ellipsoid. Note that the input point objects must be in geographic
  691. * coordinates (decimal degrees) and the return distance is in kilometers.
  692. */
  693. OpenLayers.Util.distVincenty = function(p1, p2) {
  694. var ct = OpenLayers.Util.VincentyConstants;
  695. var a = ct.a, b = ct.b, f = ct.f;
  696. var L = OpenLayers.Util.rad(p2.lon - p1.lon);
  697. var U1 = Math.atan((1-f) * Math.tan(OpenLayers.Util.rad(p1.lat)));
  698. var U2 = Math.atan((1-f) * Math.tan(OpenLayers.Util.rad(p2.lat)));
  699. var sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
  700. var sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
  701. var lambda = L, lambdaP = 2*Math.PI;
  702. var iterLimit = 20;
  703. while (Math.abs(lambda-lambdaP) > 1e-12 && --iterLimit>0) {
  704. var sinLambda = Math.sin(lambda), cosLambda = Math.cos(lambda);
  705. var sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) +
  706. (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda));
  707. if (sinSigma==0) {
  708. return 0; // co-incident points
  709. }
  710. var cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda;
  711. var sigma = Math.atan2(sinSigma, cosSigma);
  712. var alpha = Math.asin(cosU1 * cosU2 * sinLambda / sinSigma);
  713. var cosSqAlpha = Math.cos(alpha) * Math.cos(alpha);
  714. var cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha;
  715. var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
  716. lambdaP = lambda;
  717. lambda = L + (1-C) * f * Math.sin(alpha) *
  718. (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
  719. }
  720. if (iterLimit==0) {
  721. return NaN; // formula failed to converge
  722. }
  723. var uSq = cosSqAlpha * (a*a - b*b) / (b*b);
  724. var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
  725. var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
  726. var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
  727. B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
  728. var s = b*A*(sigma-deltaSigma);
  729. var d = s.toFixed(3)/1000; // round to 1mm precision
  730. return d;
  731. };
  732. /**
  733. * APIFunction: destinationVincenty
  734. * Calculate destination point given start point lat/long (numeric degrees),
  735. * bearing (numeric degrees) & distance (in m).
  736. * Adapted from Chris Veness work, see
  737. * http://www.movable-type.co.uk/scripts/latlong-vincenty-direct.html
  738. *
  739. * Parameters:
  740. * lonlat - {<OpenLayers.LonLat>} (or any object with both .lat, .lon
  741. * properties) The start point.
  742. * brng - {Float} The bearing (degrees).
  743. * dist - {Float} The ground distance (meters).
  744. *
  745. * Returns:
  746. * {<OpenLayers.LonLat>} The destination point.
  747. */
  748. OpenLayers.Util.destinationVincenty = function(lonlat, brng, dist) {
  749. var u = OpenLayers.Util;
  750. var ct = u.VincentyConstants;
  751. var a = ct.a, b = ct.b, f = ct.f;
  752. var lon1 = lonlat.lon;
  753. var lat1 = lonlat.lat;
  754. var s = dist;
  755. var alpha1 = u.rad(brng);
  756. var sinAlpha1 = Math.sin(alpha1);
  757. var cosAlpha1 = Math.cos(alpha1);
  758. var tanU1 = (1-f) * Math.tan(u.rad(lat1));
  759. var cosU1 = 1 / Math.sqrt((1 + tanU1*tanU1)), sinU1 = tanU1*cosU1;
  760. var sigma1 = Math.atan2(tanU1, cosAlpha1);
  761. var sinAlpha = cosU1 * sinAlpha1;
  762. var cosSqAlpha = 1 - sinAlpha*sinAlpha;
  763. var uSq = cosSqAlpha * (a*a - b*b) / (b*b);
  764. var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
  765. var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
  766. var sigma = s / (b*A), sigmaP = 2*Math.PI;
  767. while (Math.abs(sigma-sigmaP) > 1e-12) {
  768. var cos2SigmaM = Math.cos(2*sigma1 + sigma);
  769. var sinSigma = Math.sin(sigma);
  770. var cosSigma = Math.cos(sigma);
  771. var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
  772. B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
  773. sigmaP = sigma;
  774. sigma = s / (b*A) + deltaSigma;
  775. }
  776. var tmp = sinU1*sinSigma - cosU1*cosSigma*cosAlpha1;
  777. var lat2 = Math.atan2(sinU1*cosSigma + cosU1*sinSigma*cosAlpha1,
  778. (1-f)*Math.sqrt(sinAlpha*sinAlpha + tmp*tmp));
  779. var lambda = Math.atan2(sinSigma*sinAlpha1, cosU1*cosSigma - sinU1*sinSigma*cosAlpha1);
  780. var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
  781. var L = lambda - (1-C) * f * sinAlpha *
  782. (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
  783. var revAz = Math.atan2(sinAlpha, -tmp); // final bearing
  784. return new OpenLayers.LonLat(lon1+u.deg(L), u.deg(lat2));
  785. };
  786. /**
  787. * Function: getParameters
  788. * Parse the parameters from a URL or from the current page itself into a
  789. * JavaScript Object. Note that parameter values with commas are separated
  790. * out into an Array.
  791. *
  792. * Parameters:
  793. * url - {String} Optional url used to extract the query string.
  794. * If url is null or is not supplied, query string is taken
  795. * from the page location.
  796. * options - {Object} Additional options. Optional.
  797. *
  798. * Valid options:
  799. * splitArgs - {Boolean} Split comma delimited params into arrays? Default is
  800. * true.
  801. *
  802. * Returns:
  803. * {Object} An object of key/value pairs from the query string.
  804. */
  805. OpenLayers.Util.getParameters = function(url, options) {
  806. options = options || {};
  807. // if no url specified, take it from the location bar
  808. url = (url === null || url === undefined) ? window.location.href : url;
  809. //parse out parameters portion of url string
  810. var paramsString = "";
  811. if (OpenLayers.String.contains(url, '?')) {
  812. var start = url.indexOf('?') + 1;
  813. var end = OpenLayers.String.contains(url, "#") ?
  814. url.indexOf('#') : url.length;
  815. paramsString = url.substring(start, end);
  816. }
  817. var parameters = {};
  818. var pairs = paramsString.split(/[&;]/);
  819. for(var i=0, len=pairs.length; i<len; ++i) {
  820. var keyValue = pairs[i].split('=');
  821. if (keyValue[0]) {
  822. var key = keyValue[0];
  823. try {
  824. key = decodeURIComponent(key);
  825. } catch (err) {
  826. key = unescape(key);
  827. }
  828. // being liberal by replacing "+" with " "
  829. var value = (keyValue[1] || '').replace(/\+/g, " ");
  830. try {
  831. value = decodeURIComponent(value);
  832. } catch (err) {
  833. value = unescape(value);
  834. }
  835. // follow OGC convention of comma delimited values
  836. if (options.splitArgs !== false) {
  837. value = value.split(",");
  838. }
  839. //if there's only one value, do not return as array
  840. if (value.length == 1) {
  841. value = value[0];
  842. }
  843. parameters[key] = value;
  844. }
  845. }
  846. return parameters;
  847. };
  848. /**
  849. * Property: lastSeqID
  850. * {Integer} The ever-incrementing count variable.
  851. * Used for generating unique ids.
  852. */
  853. OpenLayers.Util.lastSeqID = 0;
  854. /**
  855. * Function: createUniqueID
  856. * Create a unique identifier for this session. Each time this function
  857. * is called, a counter is incremented. The return will be the optional
  858. * prefix (defaults to "id_") appended with the counter value.
  859. *
  860. * Parameters:
  861. * prefix - {String} Optional string to prefix unique id. Default is "id_".
  862. * Note that dots (".") in the prefix will be replaced with underscore ("_").
  863. *
  864. * Returns:
  865. * {String} A unique id string, built on the passed in prefix.
  866. */
  867. OpenLayers.Util.createUniqueID = function(prefix) {
  868. if (prefix == null) {
  869. prefix = "id_";
  870. } else {
  871. prefix = prefix.replace(OpenLayers.Util.dotless, "_");
  872. }
  873. OpenLayers.Util.lastSeqID += 1;
  874. return prefix + OpenLayers.Util.lastSeqID;
  875. };
  876. /**
  877. * Constant: INCHES_PER_UNIT
  878. * {Object} Constant inches per unit -- borrowed from MapServer mapscale.c
  879. * derivation of nautical miles from http://en.wikipedia.org/wiki/Nautical_mile
  880. * Includes the full set of units supported by CS-MAP (http://trac.osgeo.org/csmap/)
  881. * and PROJ.4 (http://trac.osgeo.org/proj/)
  882. * The hardcoded table is maintain in a CS-MAP source code module named CSdataU.c
  883. * The hardcoded table of PROJ.4 units are in pj_units.c.
  884. */
  885. OpenLayers.INCHES_PER_UNIT = {
  886. 'inches': 1.0,
  887. 'ft': 12.0,
  888. 'mi': 63360.0,
  889. 'm': 39.37,
  890. 'km': 39370,
  891. 'dd': 4374754,
  892. 'yd': 36
  893. };
  894. OpenLayers.INCHES_PER_UNIT["in"]= OpenLayers.INCHES_PER_UNIT.inches;
  895. OpenLayers.INCHES_PER_UNIT["degrees"] = OpenLayers.INCHES_PER_UNIT.dd;
  896. OpenLayers.INCHES_PER_UNIT["nmi"] = 1852 * OpenLayers.INCHES_PER_UNIT.m;
  897. // Units from CS-Map
  898. OpenLayers.METERS_PER_INCH = 0.02540005080010160020;
  899. OpenLayers.Util.extend(OpenLayers.INCHES_PER_UNIT, {
  900. "Inch": OpenLayers.INCHES_PER_UNIT.inches,
  901. "Meter": 1.0 / OpenLayers.METERS_PER_INCH, //EPSG:9001
  902. "Foot": 0.30480060960121920243 / OpenLayers.METERS_PER_INCH, //EPSG:9003
  903. "IFoot": 0.30480000000000000000 / OpenLayers.METERS_PER_INCH, //EPSG:9002
  904. "ClarkeFoot": 0.3047972651151 / OpenLayers.METERS_PER_INCH, //EPSG:9005
  905. "SearsFoot": 0.30479947153867624624 / OpenLayers.METERS_PER_INCH, //EPSG:9041
  906. "GoldCoastFoot": 0.30479971018150881758 / OpenLayers.METERS_PER_INCH, //EPSG:9094
  907. "IInch": 0.02540000000000000000 / OpenLayers.METERS_PER_INCH,
  908. "MicroInch": 0.00002540000000000000 / OpenLayers.METERS_PER_INCH,
  909. "Mil": 0.00000002540000000000 / OpenLayers.METERS_PER_INCH,
  910. "Centimeter": 0.01000000000000000000 / OpenLayers.METERS_PER_INCH,
  911. "Kilometer": 1000.00000000000000000000 / OpenLayers.METERS_PER_INCH, //EPSG:9036
  912. "Yard": 0.91440182880365760731 / OpenLayers.METERS_PER_INCH,
  913. "SearsYard": 0.914398414616029 / OpenLayers.METERS_PER_INCH, //EPSG:9040
  914. "IndianYard": 0.91439853074444079983 / OpenLayers.METERS_PER_INCH, //EPSG:9084
  915. "IndianYd37": 0.91439523 / OpenLayers.METERS_PER_INCH, //EPSG:9085
  916. "IndianYd62": 0.9143988 / OpenLayers.METERS_PER_INCH, //EPSG:9086
  917. "IndianYd75": 0.9143985 / OpenLayers.METERS_PER_INCH, //EPSG:9087
  918. "IndianFoot": 0.30479951 / OpenLayers.METERS_PER_INCH, //EPSG:9080
  919. "IndianFt37": 0.30479841 / OpenLayers.METERS_PER_INCH, //EPSG:9081
  920. "IndianFt62": 0.3047996 / OpenLayers.METERS_PER_INCH, //EPSG:9082
  921. "IndianFt75": 0.3047995 / OpenLayers.METERS_PER_INCH, //EPSG:9083
  922. "Mile": 1609.34721869443738887477 / OpenLayers.METERS_PER_INCH,
  923. "IYard": 0.91440000000000000000 / OpenLayers.METERS_PER_INCH, //EPSG:9096
  924. "IMile": 1609.34400000000000000000 / OpenLayers.METERS_PER_INCH, //EPSG:9093
  925. "NautM": 1852.00000000000000000000 / OpenLayers.METERS_PER_INCH, //EPSG:9030
  926. "Lat-66": 110943.316488932731 / OpenLayers.METERS_PER_INCH,
  927. "Lat-83": 110946.25736872234125 / OpenLayers.METERS_PER_INCH,
  928. "Decimeter": 0.10000000000000000000 / OpenLayers.METERS_PER_INCH,
  929. "Millimeter": 0.00100000000000000000 / OpenLayers.METERS_PER_INCH,
  930. "Dekameter": 10.00000000000000000000 / OpenLayers.METERS_PER_INCH,
  931. "Decameter": 10.00000000000000000000 / OpenLayers.METERS_PER_INCH,
  932. "Hectometer": 100.00000000000000000000 / OpenLayers.METERS_PER_INCH,
  933. "GermanMeter": 1.0000135965 / OpenLayers.METERS_PER_INCH, //EPSG:9031
  934. "CaGrid": 0.999738 / OpenLayers.METERS_PER_INCH,
  935. "ClarkeChain": 20.1166194976 / OpenLayers.METERS_PER_INCH, //EPSG:9038
  936. "GunterChain": 20.11684023368047 / OpenLayers.METERS_PER_INCH, //EPSG:9033
  937. "BenoitChain": 20.116782494375872 / OpenLayers.METERS_PER_INCH, //EPSG:9062
  938. "SearsChain": 20.11676512155 / OpenLayers.METERS_PER_INCH, //EPSG:9042
  939. "ClarkeLink": 0.201166194976 / OpenLayers.METERS_PER_INCH, //EPSG:9039
  940. "GunterLink": 0.2011684023368047 / OpenLayers.METERS_PER_INCH, //EPSG:9034
  941. "BenoitLink": 0.20116782494375872 / OpenLayers.METERS_PER_INCH, //EPSG:9063
  942. "SearsLink": 0.2011676512155 / OpenLayers.METERS_PER_INCH, //EPSG:9043
  943. "Rod": 5.02921005842012 / OpenLayers.METERS_PER_INCH,
  944. "IntnlChain": 20.1168 / OpenLayers.METERS_PER_INCH, //EPSG:9097
  945. "IntnlLink": 0.201168 / OpenLayers.METERS_PER_INCH, //EPSG:9098
  946. "Perch": 5.02921005842012 / OpenLayers.METERS_PER_INCH,
  947. "Pole": 5.02921005842012 / OpenLayers.METERS_PER_INCH,
  948. "Furlong": 201.1684023368046 / OpenLayers.METERS_PER_INCH,
  949. "Rood": 3.778266898 / OpenLayers.METERS_PER_INCH,
  950. "CapeFoot": 0.3047972615 / OpenLayers.METERS_PER_INCH,
  951. "Brealey": 375.00000000000000000000 / OpenLayers.METERS_PER_INCH,
  952. "ModAmFt": 0.304812252984505969011938 / OpenLayers.METERS_PER_INCH,
  953. "Fathom": 1.8288 / OpenLayers.METERS_PER_INCH,
  954. "NautM-UK": 1853.184 / OpenLayers.METERS_PER_INCH,
  955. "50kilometers": 50000.0 / OpenLayers.METERS_PER_INCH,
  956. "150kilometers": 150000.0 / OpenLayers.METERS_PER_INCH
  957. });
  958. //unit abbreviations supported by PROJ.4
  959. OpenLayers.Util.extend(OpenLayers.INCHES_PER_UNIT, {
  960. "mm": OpenLayers.INCHES_PER_UNIT["Meter"] / 1000.0,
  961. "cm": OpenLayers.INCHES_PER_UNIT["Meter"] / 100.0,
  962. "dm": OpenLayers.INCHES_PER_UNIT["Meter"] * 100.0,
  963. "km": OpenLayers.INCHES_PER_UNIT["Meter"] * 1000.0,
  964. "kmi": OpenLayers.INCHES_PER_UNIT["nmi"], //International Nautical Mile
  965. "fath": OpenLayers.INCHES_PER_UNIT["Fathom"], //International Fathom
  966. "ch": OpenLayers.INCHES_PER_UNIT["IntnlChain"], //International Chain
  967. "link": OpenLayers.INCHES_PER_UNIT["IntnlLink"], //International Link
  968. "us-in": OpenLayers.INCHES_PER_UNIT["inches"], //U.S. Surveyor's Inch
  969. "us-ft": OpenLayers.INCHES_PER_UNIT["Foot"], //U.S. Surveyor's Foot
  970. "us-yd": OpenLayers.INCHES_PER_UNIT["Yard"], //U.S. Surveyor's Yard
  971. "us-ch": OpenLayers.INCHES_PER_UNIT["GunterChain"], //U.S. Surveyor's Chain
  972. "us-mi": OpenLayers.INCHES_PER_UNIT["Mile"], //U.S. Surveyor's Statute Mile
  973. "ind-yd": OpenLayers.INCHES_PER_UNIT["IndianYd37"], //Indian Yard
  974. "ind-ft": OpenLayers.INCHES_PER_UNIT["IndianFt37"], //Indian Foot
  975. "ind-ch": 20.11669506 / OpenLayers.METERS_PER_INCH //Indian Chain
  976. });
  977. /**
  978. * Constant: DOTS_PER_INCH
  979. * {Integer} 72 (A sensible default)
  980. */
  981. OpenLayers.DOTS_PER_INCH = 72;
  982. /**
  983. * Function: normalizeScale
  984. *
  985. * Parameters:
  986. * scale - {float}
  987. *
  988. * Returns:
  989. * {Float} A normalized scale value, in 1 / X format.
  990. * This means that if a value less than one ( already 1/x) is passed
  991. * in, it just returns scale directly. Otherwise, it returns
  992. * 1 / scale
  993. */
  994. OpenLayers.Util.normalizeScale = function (scale) {
  995. var normScale = (scale > 1.0) ? (1.0 / scale)
  996. : scale;
  997. return normScale;
  998. };
  999. /**
  1000. * Function: getResolutionFromScale
  1001. *
  1002. * Parameters:
  1003. * scale - {Float}
  1004. * units - {String} Index into OpenLayers.INCHES_PER_UNIT hashtable.
  1005. * Default is degrees
  1006. *
  1007. * Returns:
  1008. * {Float} The corresponding resolution given passed-in scale and unit
  1009. * parameters. If the given scale is falsey, the returned resolution will
  1010. * be undefined.
  1011. */
  1012. OpenLayers.Util.getResolutionFromScale = function (scale, units) {
  1013. var resolution;
  1014. if (scale) {
  1015. if (units == null) {
  1016. units = "degrees";
  1017. }
  1018. var normScale = OpenLayers.Util.normalizeScale(scale);
  1019. resolution = 1 / (normScale * OpenLayers.INCHES_PER_UNIT[units]
  1020. * OpenLayers.DOTS_PER_INCH);
  1021. }
  1022. return resolution;
  1023. };
  1024. /**
  1025. * Function: getScaleFromResolution
  1026. *
  1027. * Parameters:
  1028. * resolution - {Float}
  1029. * units - {String} Index into OpenLayers.INCHES_PER_UNIT hashtable.
  1030. * Default is degrees
  1031. *
  1032. * Returns:
  1033. * {Float} The corresponding scale given passed-in resolution and unit
  1034. * parameters.
  1035. */
  1036. OpenLayers.Util.getScaleFromResolution = function (resolution, units) {
  1037. if (units == null) {
  1038. units = "degrees";
  1039. }
  1040. var scale = resolution * OpenLayers.INCHES_PER_UNIT[units] *
  1041. OpenLayers.DOTS_PER_INCH;
  1042. return scale;
  1043. };
  1044. /**
  1045. * Function: pagePosition
  1046. * Calculates the position of an element on the page (see
  1047. * http://code.google.com/p/doctype/wiki/ArticlePageOffset)
  1048. *
  1049. * OpenLayers.Util.pagePosition is based on Yahoo's getXY method, which is
  1050. * Copyright (c) 2006, Yahoo! Inc.
  1051. * All rights reserved.
  1052. *
  1053. * Redistribution and use of this software in source and binary forms, with or
  1054. * without modification, are permitted provided that the following conditions
  1055. * are met:
  1056. *
  1057. * * Redistributions of source code must retain the above copyright notice,
  1058. * this list of conditions and the following disclaimer.
  1059. *
  1060. * * Redistributions in binary form must reproduce the above copyright notice,
  1061. * this list of conditions and the following disclaimer in the documentation
  1062. * and/or other materials provided with the distribution.
  1063. *
  1064. * * Neither the name of Yahoo! Inc. nor the names of its contributors may be
  1065. * used to endorse or promote products derived from this software without
  1066. * specific prior written permission of Yahoo! Inc.
  1067. *
  1068. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  1069. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  1070. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  1071. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  1072. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  1073. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  1074. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  1075. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  1076. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  1077. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  1078. * POSSIBILITY OF SUCH DAMAGE.
  1079. *
  1080. * Parameters:
  1081. * forElement - {DOMElement}
  1082. *
  1083. * Returns:
  1084. * {Array} two item array, Left value then Top value.
  1085. */
  1086. OpenLayers.Util.pagePosition = function(forElement) {
  1087. // NOTE: If element is hidden (display none or disconnected or any the
  1088. // ancestors are hidden) we get (0,0) by default but we still do the
  1089. // accumulation of scroll position.
  1090. var pos = [0, 0];
  1091. var viewportElement = OpenLayers.Util.getViewportElement();
  1092. if (!forElement || forElement == window || forElement == viewportElement) {
  1093. // viewport is always at 0,0 as that defined the coordinate system for
  1094. // this function - this avoids special case checks in the code below
  1095. return pos;
  1096. }
  1097. // Gecko browsers normally use getBoxObjectFor to calculate the position.
  1098. // When invoked for an element with an implicit absolute position though it
  1099. // can be off by one. Therefore the recursive implementation is used in
  1100. // those (relatively rare) cases.
  1101. var BUGGY_GECKO_BOX_OBJECT =
  1102. OpenLayers.IS_GECKO && document.getBoxObjectFor &&
  1103. OpenLayers.Element.getStyle(forElement, 'position') == 'absolute' &&
  1104. (forElement.style.top == '' || forElement.style.left == '');
  1105. var parent = null;
  1106. var box;
  1107. if (forElement.getBoundingClientRect) { // IE
  1108. box = forElement.getBoundingClientRect();
  1109. var scrollTop = window.pageYOffset || viewportElement.scrollTop;
  1110. var scrollLeft = window.pageXOffset || viewportElement.scrollLeft;
  1111. pos[0] = box.left + scrollLeft;
  1112. pos[1] = box.top + scrollTop;
  1113. } else if (document.getBoxObjectFor && !BUGGY_GECKO_BOX_OBJECT) { // gecko
  1114. // Gecko ignores the scroll values for ancestors, up to 1.9. See:
  1115. // https://bugzilla.mozilla.org/show_bug.cgi?id=328881 and
  1116. // https://bugzilla.mozilla.org/show_bug.cgi?id=330619
  1117. box = document.getBoxObjectFor(forElement);
  1118. var vpBox = document.getBoxObjectFor(viewportElement);
  1119. pos[0] = box.screenX - vpBox.screenX;
  1120. pos[1] = box.screenY - vpBox.screenY;
  1121. } else { // safari/opera
  1122. pos[0] = forElement.offsetLeft;
  1123. pos[1] = forElement.offsetTop;
  1124. parent = forElement.offsetParent;
  1125. if (parent != forElement) {
  1126. while (parent) {
  1127. pos[0] += parent.offsetLeft;
  1128. pos[1] += parent.offsetTop;
  1129. parent = parent.offsetParent;
  1130. }
  1131. }
  1132. var browser = OpenLayers.BROWSER_NAME;
  1133. // opera & (safari absolute) incorrectly account for body offsetTop
  1134. if (browser == "opera" || (browser == "safari" &&
  1135. OpenLayers.Element.getStyle(forElement, 'position') == 'absolute')) {
  1136. pos[1] -= document.body.offsetTop;
  1137. }
  1138. // accumulate the scroll positions for everything but the body element
  1139. parent = forElement.offsetParent;
  1140. while (parent && parent != document.body) {
  1141. pos[0] -= parent.scrollLeft;
  1142. // see https://bugs.opera.com/show_bug.cgi?id=249965
  1143. if (browser != "opera" || parent.tagName != 'TR') {
  1144. pos[1] -= parent.scrollTop;
  1145. }
  1146. parent = parent.offsetParent;
  1147. }
  1148. }
  1149. return pos;
  1150. };
  1151. /**
  1152. * Function: getViewportElement
  1153. * Returns die viewport element of the document. The viewport element is
  1154. * usually document.documentElement, except in IE,where it is either
  1155. * document.body or document.documentElement, depending on the document's
  1156. * compatibility mode (see
  1157. * http://code.google.com/p/doctype/wiki/ArticleClientViewportElement)
  1158. *
  1159. * Returns:
  1160. * {DOMElement}
  1161. */
  1162. OpenLayers.Util.getViewportElement = function() {
  1163. var viewportElement = arguments.callee.viewportElement;
  1164. if (viewportElement == undefined) {
  1165. viewportElement = (OpenLayers.BROWSER_NAME == "msie" &&
  1166. document.compatMode != 'CSS1Compat') ? document.body :
  1167. document.documentElement;
  1168. arguments.callee.viewportElement = viewportElement;
  1169. }
  1170. return viewportElement;
  1171. };
  1172. /**
  1173. * Function: isEquivalentUrl
  1174. * Test two URLs for equivalence.
  1175. *
  1176. * Setting 'ignoreCase' allows for case-independent comparison.
  1177. *
  1178. * Comparison is based on:
  1179. * - Protocol
  1180. * - Host (evaluated without the port)
  1181. * - Port (set 'ignorePort80' to ignore "80" values)
  1182. * - Hash ( set 'ignoreHash' to disable)
  1183. * - Pathname (for relative <-> absolute comparison)
  1184. * - Arguments (so they can be out of order)
  1185. *
  1186. * Parameters:
  1187. * url1 - {String}
  1188. * url2 - {String}
  1189. * options - {Object} Allows for customization of comparison:
  1190. * 'ignoreCase' - Default is True
  1191. * 'ignorePort80' - Default is True
  1192. * 'ignoreHash' - Default is True
  1193. *
  1194. * Returns:
  1195. * {Boolean} Whether or not the two URLs are equivalent
  1196. */
  1197. OpenLayers.Util.isEquivalentUrl = function(url1, url2, options) {
  1198. options = options || {};
  1199. OpenLayers.Util.applyDefaults(options, {
  1200. ignoreCase: true,
  1201. ignorePort80: true,
  1202. ignoreHash: true,
  1203. splitArgs: false
  1204. });
  1205. var urlObj1 = OpenLayers.Util.createUrlObject(url1, options);
  1206. var urlObj2 = OpenLayers.Util.createUrlObject(url2, options);
  1207. //compare all keys except for "args" (treated below)
  1208. for(var key in urlObj1) {
  1209. if(key !== "args") {
  1210. if(urlObj1[key] != urlObj2[key]) {
  1211. return false;
  1212. }
  1213. }
  1214. }
  1215. // compare search args - irrespective of order
  1216. for(var key in urlObj1.args) {
  1217. if(urlObj1.args[key] != urlObj2.args[key]) {
  1218. return false;
  1219. }
  1220. delete urlObj2.args[key];
  1221. }
  1222. // urlObj2 shouldn't have any args left
  1223. for(var key in urlObj2.args) {
  1224. return false;
  1225. }
  1226. return true;
  1227. };
  1228. /**
  1229. * Function: createUrlObject
  1230. *
  1231. * Parameters:
  1232. * url - {String}
  1233. * options - {Object} A hash of options.
  1234. *
  1235. * Valid options:
  1236. * ignoreCase - {Boolean} lowercase url,
  1237. * ignorePort80 - {Boolean} don't include explicit port if port is 80,
  1238. * ignoreHash - {Boolean} Don't include part of url after the hash (#).
  1239. * splitArgs - {Boolean} Split comma delimited params into arrays? Default is
  1240. * true.
  1241. *
  1242. * Returns:
  1243. * {Object} An object with separate url, a, port, host, and args parsed out
  1244. * and ready for comparison
  1245. */
  1246. OpenLayers.Util.createUrlObject = function(url, options) {
  1247. options = options || {};
  1248. // deal with relative urls first
  1249. if(!(/^\w+:\/\//).test(url)) {
  1250. var loc = window.location;
  1251. var port = loc.port ? ":" + loc.port : "";
  1252. var fullUrl = loc.protocol + "//" + loc.host.split(":").shift() + port;
  1253. if(url.indexOf("/") === 0) {
  1254. // full pathname
  1255. url = fullUrl + url;
  1256. } else {
  1257. // relative to current path
  1258. var parts = loc.pathname.split("/");
  1259. parts.pop();
  1260. url = fullUrl + parts.join("/") + "/" + url;
  1261. }
  1262. }
  1263. if (options.ignoreCase) {
  1264. url = url.toLowerCase();
  1265. }
  1266. var a = document.createElement('a');
  1267. a.href = url;
  1268. var urlObject = {};
  1269. //host (without port)
  1270. urlObject.host = a.host.split(":").shift();
  1271. //protocol
  1272. urlObject.protocol = a.protocol;
  1273. //port (get uniform browser behavior with port 80 here)
  1274. if(options.ignorePort80) {
  1275. urlObject.port = (a.port == "80" || a.port == "0") ? "" : a.port;
  1276. } else {
  1277. urlObject.port = (a.port == "" || a.port == "0") ? "80" : a.port;
  1278. }
  1279. //hash
  1280. urlObject.hash = (options.ignoreHash || a.hash === "#") ? "" : a.hash;
  1281. //args
  1282. var queryString = a.search;
  1283. if (!queryString) {
  1284. var qMark = url.indexOf("?");
  1285. queryString = (qMark != -1) ? url.substr(qMark) : "";
  1286. }
  1287. urlObject.args = OpenLayers.Util.getParameters(queryString,
  1288. {splitArgs: options.splitArgs});
  1289. // pathname
  1290. //
  1291. // This is a workaround for Internet Explorer where
  1292. // window.location.pathname has a leading "/", but
  1293. // a.pathname has no leading "/".
  1294. urlObject.pathname = (a.pathname.charAt(0) == "/") ? a.pathname : "/" + a.pathname;
  1295. return urlObject;
  1296. };
  1297. /**
  1298. * Function: removeTail
  1299. * Takes a url and removes everything after the ? and #
  1300. *
  1301. * Parameters:
  1302. * url - {String} The url to process
  1303. *
  1304. * Returns:
  1305. * {String} The string with all queryString and Hash removed
  1306. */
  1307. OpenLayers.Util.removeTail = function(url) {
  1308. var head = null;
  1309. var qMark = url.indexOf("?");
  1310. var hashMark = url.indexOf("#");
  1311. if (qMark == -1) {
  1312. head = (hashMark != -1) ? url.substr(0,hashMark) : url;
  1313. } else {
  1314. head = (hashMark != -1) ? url.substr(0,Math.min(qMark, hashMark))
  1315. : url.substr(0, qMark);
  1316. }
  1317. return head;
  1318. };
  1319. /**
  1320. * Constant: IS_GECKO
  1321. * {Boolean} True if the userAgent reports the browser to use the Gecko engine
  1322. */
  1323. OpenLayers.IS_GECKO = (function() {
  1324. var ua = navigator.userAgent.toLowerCase();
  1325. return ua.indexOf("webkit") == -1 && ua.indexOf("gecko") != -1;
  1326. })();
  1327. /**
  1328. * Constant: CANVAS_SUPPORTED
  1329. * {Boolean} True if canvas 2d is supported.
  1330. */
  1331. OpenLayers.CANVAS_SUPPORTED = (function() {
  1332. var elem = document.createElement('canvas');
  1333. return !!(elem.getContext && elem.getContext('2d'));
  1334. })();
  1335. /**
  1336. * Constant: BROWSER_NAME
  1337. * {String}
  1338. * A substring of the navigator.userAgent property. Depending on the userAgent
  1339. * property, this will be the empty string or one of the following:
  1340. * * "opera" -- Opera
  1341. * * "msie" -- Internet Explorer
  1342. * * "safari" -- Safari
  1343. * * "firefox" -- Firefox
  1344. * * "mozilla" -- Mozilla
  1345. */
  1346. OpenLayers.BROWSER_NAME = (function() {
  1347. var name = "";
  1348. var ua = navigator.userAgent.toLowerCase();
  1349. if (ua.indexOf("opera") != -1) {
  1350. name = "opera";
  1351. } else if (ua.indexOf("msie") != -1) {
  1352. name = "msie";
  1353. } else if (ua.indexOf("safari") != -1) {
  1354. name = "safari";
  1355. } else if (ua.indexOf("mozilla") != -1) {
  1356. if (ua.indexOf("firefox") != -1) {
  1357. name = "firefox";
  1358. } else {
  1359. name = "mozilla";
  1360. }
  1361. }
  1362. return name;
  1363. })();
  1364. /**
  1365. * Function: getBrowserName
  1366. *
  1367. * Returns:
  1368. * {String} A string which specifies which is the current
  1369. * browser in which we are running.
  1370. *
  1371. * Currently-supported browser detection and codes:
  1372. * * 'opera' -- Opera
  1373. * * 'msie' -- Internet Explorer
  1374. * * 'safari' -- Safari
  1375. * * 'firefox' -- Firefox
  1376. * * 'mozilla' -- Mozilla
  1377. *
  1378. * If we are unable to property identify the browser, we
  1379. * return an empty string.
  1380. */
  1381. OpenLayers.Util.getBrowserName = function() {
  1382. return OpenLayers.BROWSER_NAME;
  1383. };
  1384. /**
  1385. * Method: getRenderedDimensions
  1386. * Renders the contentHTML offscreen to determine actual dimensions for
  1387. * popup sizing. As we need layout to determine dimensions the content
  1388. * is rendered -9999px to the left and absolute to ensure the
  1389. * scrollbars do not flicker
  1390. *
  1391. * Parameters:
  1392. * contentHTML
  1393. * size - {<OpenLayers.Size>} If either the 'w' or 'h' properties is
  1394. * specified, we fix that dimension of the div to be measured. This is
  1395. * useful in the case where we have a limit in one dimension and must
  1396. * therefore meaure the flow in the other dimension.
  1397. * options - {Object}
  1398. *
  1399. * Allowed Options:
  1400. * displayClass - {String} Optional parameter. A CSS class name(s) string
  1401. * to provide the CSS context of the rendered content.
  1402. * containerElement - {DOMElement} Optional parameter. Insert the HTML to
  1403. * this node instead of the body root when calculating dimensions.
  1404. *
  1405. * Returns:
  1406. * {<OpenLayers.Size>}
  1407. */
  1408. OpenLayers.Util.getRenderedDimensions = function(contentHTML, size, options) {
  1409. var w, h;
  1410. // create temp container div with restricted size
  1411. var container = document.createElement("div");
  1412. container.style.visibility = "hidden";
  1413. var containerElement = (options && options.containerElement)
  1414. ? options.containerElement : document.body;
  1415. // Opera and IE7 can't handle a node with position:aboslute if it inherits
  1416. // position:absolute from a parent.
  1417. var parentHasPositionAbsolute = false;
  1418. var superContainer = null;
  1419. var parent = containerElement;
  1420. while (parent && parent.tagName.toLowerCase()!="body") {
  1421. var parentPosition = OpenLayers.Element.getStyle(parent, "position");
  1422. if(parentPosition == "absolute") {
  1423. parentHasPositionAbsolute = true;
  1424. break;
  1425. } else if (parentPosition && parentPosition != "static") {
  1426. break;
  1427. }
  1428. parent = parent.parentNode;
  1429. }
  1430. if(parentHasPositionAbsolute && (containerElement.clientHeight === 0 ||
  1431. containerElement.clientWidth === 0) ){
  1432. superContainer = document.createElement("div");
  1433. superContainer.style.visibility = "hidden";
  1434. superContainer.style.position = "absolute";
  1435. superContainer.style.overflow = "visible";
  1436. superContainer.style.width = document.body.clientWidth + "px";
  1437. superContainer.style.height = document.body.clientHeight + "px";
  1438. superContainer.appendChild(container);
  1439. }
  1440. container.style.position = "absolute";
  1441. //fix a dimension, if specified.
  1442. if (size) {
  1443. if (size.w) {
  1444. w = size.w;
  1445. container.style.width = w + "px";
  1446. } else if (size.h) {
  1447. h = size.h;
  1448. container.style.height = h + "px";
  1449. }
  1450. }
  1451. //add css classes, if specified
  1452. if (options && options.displayClass) {
  1453. container.className = options.displayClass;
  1454. }
  1455. // create temp content div and assign content
  1456. var content = document.createElement("div");
  1457. content.innerHTML = contentHTML;
  1458. // we need overflow visible when calculating the size
  1459. content.style.overflow = "visible";
  1460. if (content.childNodes) {
  1461. for (var i=0, l=content.childNodes.length; i<l; i++) {
  1462. if (!content.childNodes[i].style) continue;
  1463. content.childNodes[i].style.overflow = "visible";
  1464. }
  1465. }
  1466. // add content to restricted container
  1467. container.appendChild(content);
  1468. // append container to body for rendering
  1469. if (superContainer) {
  1470. containerElement.appendChild(superContainer);
  1471. } else {
  1472. containerElement.appendChild(container);
  1473. }
  1474. // calculate scroll width of content and add corners and shadow width
  1475. if (!w) {
  1476. w = parseInt(content.scrollWidth);
  1477. // update container width to allow height to adjust
  1478. container.style.width = w + "px";
  1479. }
  1480. // capture height and add shadow and corner image widths
  1481. if (!h) {
  1482. h = parseInt(content.scrollHeight);
  1483. }
  1484. // remove elements
  1485. container.removeChild(content);
  1486. if (superContainer) {
  1487. superContainer.removeChild(container);
  1488. containerElement.removeChild(superContainer);
  1489. } else {
  1490. containerElement.removeChild(container);
  1491. }
  1492. return new OpenLayers.Size(w, h);
  1493. };
  1494. /**
  1495. * APIFunction: getScrollbarWidth
  1496. * This function has been modified by the OpenLayers from the original version,
  1497. * written by Matthew Eernisse and released under the Apache 2
  1498. * license here:
  1499. *
  1500. * http://www.fleegix.org/articles/2006/05/30/getting-the-scrollbar-width-in-pixels
  1501. *
  1502. * It has been modified simply to cache its value, since it is physically
  1503. * impossible that this code could ever run in more than one browser at
  1504. * once.
  1505. *
  1506. * Returns:
  1507. * {Integer}
  1508. */
  1509. OpenLayers.Util.getScrollbarWidth = function() {
  1510. var scrollbarWidth = OpenLayers.Util._scrollbarWidth;
  1511. if (scrollbarWidth == null) {
  1512. var scr = null;
  1513. var inn = null;
  1514. var wNoScroll = 0;
  1515. var wScroll = 0;
  1516. // Outer scrolling div
  1517. scr = document.createElement('div');
  1518. scr.style.position = 'absolute';
  1519. scr.style.top = '-1000px';
  1520. scr.style.left = '-1000px';
  1521. scr.style.width = '100px';
  1522. scr.style.height = '50px';
  1523. // Start with no scrollbar
  1524. scr.style.overflow = 'hidden';
  1525. // Inner content div
  1526. inn = document.createElement('div');
  1527. inn.style.width = '100%';
  1528. inn.style.height = '200px';
  1529. // Put the inner div in the scrolling div
  1530. scr.appendChild(inn);
  1531. // Append the scrolling div to the doc
  1532. document.body.appendChild(scr);
  1533. // Width of the inner div sans scrollbar
  1534. wNoScroll = inn.offsetWidth;
  1535. // Add the scrollbar
  1536. scr.style.overflow = 'scroll';
  1537. // Width of the inner div width scrollbar
  1538. wScroll = inn.offsetWidth;
  1539. // Remove the scrolling div from the doc
  1540. document.body.removeChild(document.body.lastChild);
  1541. // Pixel width of the scroller
  1542. OpenLayers.Util._scrollbarWidth = (wNoScroll - wScroll);
  1543. scrollbarWidth = OpenLayers.Util._scrollbarWidth;
  1544. }
  1545. return scrollbarWidth;
  1546. };
  1547. /**
  1548. * APIFunction: getFormattedLonLat
  1549. * This function will return latitude or longitude value formatted as
  1550. *
  1551. * Parameters:
  1552. * coordinate - {Float} the coordinate value to be formatted
  1553. * axis - {String} value of either 'lat' or 'lon' to indicate which axis is to
  1554. * to be formatted (default = lat)
  1555. * dmsOption - {String} specify the precision of the output can be one of:
  1556. * 'dms' show degrees minutes and seconds
  1557. * 'dm' show only degrees and minutes
  1558. * 'd' show only degrees
  1559. *
  1560. * Returns:
  1561. * {String} the coordinate value formatted as a string
  1562. */
  1563. OpenLayers.Util.getFormattedLonLat = function(coordinate, axis, dmsOption) {
  1564. if (!dmsOption) {
  1565. dmsOption = 'dms'; //default to show degree, minutes, seconds
  1566. }
  1567. coordinate = (coordinate+540)%360 - 180; // normalize for sphere being round
  1568. var abscoordinate = Math.abs(coordinate);
  1569. var coordinatedegrees = Math.floor(abscoordinate);
  1570. var coordinateminutes = (abscoordinate - coordinatedegrees)/(1/60);
  1571. var tempcoordinateminutes = coordinateminutes;
  1572. coordinateminutes = Math.floor(coordinateminutes);
  1573. var coordinateseconds = (tempcoordinateminutes - coordinateminutes)/(1/60);
  1574. coordinateseconds = Math.round(coordinateseconds*10);
  1575. coordinateseconds /= 10;
  1576. if( coordinateseconds >= 60) {
  1577. coordinateseconds -= 60;
  1578. coordinateminutes += 1;
  1579. if( coordinateminutes >= 60) {
  1580. coordinateminutes -= 60;
  1581. coordinatedegrees += 1;
  1582. }
  1583. }
  1584. if( coordinatedegrees < 10 ) {
  1585. coordinatedegrees = "0" + coordinatedegrees;
  1586. }
  1587. var str = coordinatedegrees + "\u00B0";
  1588. if (dmsOption.indexOf('dm') >= 0) {
  1589. if( coordinateminutes < 10 ) {
  1590. coordinateminutes = "0" + coordinateminutes;
  1591. }
  1592. str += coordinateminutes + "'";
  1593. if (dmsOption.indexOf('dms') >= 0) {
  1594. if( coordinateseconds < 10 ) {
  1595. coordinateseconds = "0" + coordinateseconds;
  1596. }
  1597. str += coordinateseconds + '"';
  1598. }
  1599. }
  1600. if (axis == "lon") {
  1601. str += coordinate < 0 ? OpenLayers.i18n("W") : OpenLayers.i18n("E");
  1602. } else {
  1603. str += coordinate < 0 ? OpenLayers.i18n("S") : OpenLayers.i18n("N");
  1604. }
  1605. return str;
  1606. };