sphericalmercator.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. var SphericalMercator = (function(){
  2. // Closures including constants and other precalculated values.
  3. var cache = {},
  4. EPSLN = 1.0e-10,
  5. D2R = Math.PI / 180,
  6. R2D = 180 / Math.PI,
  7. // 900913 properties.
  8. A = 6378137.0,
  9. MAXEXTENT = 20037508.342789244;
  10. // SphericalMercator constructor: precaches calculations
  11. // for fast tile lookups.
  12. function SphericalMercator(options) {
  13. options = options || {};
  14. this.size = options.size || 256;
  15. if (!cache[this.size]) {
  16. var size = this.size;
  17. var c = cache[this.size] = {};
  18. c.Bc = [];
  19. c.Cc = [];
  20. c.zc = [];
  21. c.Ac = [];
  22. for (var d = 0; d < 30; d++) {
  23. c.Bc.push(size / 360);
  24. c.Cc.push(size / (2 * Math.PI));
  25. c.zc.push(size / 2);
  26. c.Ac.push(size);
  27. size *= 2;
  28. }
  29. }
  30. this.Bc = cache[this.size].Bc;
  31. this.Cc = cache[this.size].Cc;
  32. this.zc = cache[this.size].zc;
  33. this.Ac = cache[this.size].Ac;
  34. };
  35. // Convert lon lat to screen pixel value
  36. //
  37. // - `ll` {Array} `[lon, lat]` array of geographic coordinates.
  38. // - `zoom` {Number} zoom level.
  39. SphericalMercator.prototype.px = function(ll, zoom) {
  40. var d = this.zc[zoom];
  41. var f = Math.min(Math.max(Math.sin(D2R * ll[1]), -0.9999), 0.9999);
  42. var x = Math.round(d + ll[0] * this.Bc[zoom]);
  43. var y = Math.round(d + 0.5 * Math.log((1 + f) / (1 - f)) * (-this.Cc[zoom]));
  44. (x > this.Ac[zoom]) && (x = this.Ac[zoom]);
  45. (y > this.Ac[zoom]) && (y = this.Ac[zoom]);
  46. //(x < 0) && (x = 0);
  47. //(y < 0) && (y = 0);
  48. return [x, y];
  49. };
  50. // Convert screen pixel value to lon lat
  51. //
  52. // - `px` {Array} `[x, y]` array of geographic coordinates.
  53. // - `zoom` {Number} zoom level.
  54. SphericalMercator.prototype.ll = function(px, zoom) {
  55. var g = (px[1] - this.zc[zoom]) / (-this.Cc[zoom]);
  56. var lon = (px[0] - this.zc[zoom]) / this.Bc[zoom];
  57. var lat = R2D * (2 * Math.atan(Math.exp(g)) - 0.5 * Math.PI);
  58. return [lon, lat];
  59. };
  60. // Convert tile xyz value to bbox of the form `[w, s, e, n]`
  61. //
  62. // - `x` {Number} x (longitude) number.
  63. // - `y` {Number} y (latitude) number.
  64. // - `zoom` {Number} zoom.
  65. // - `tms_style` {Boolean} whether to compute using tms-style.
  66. // - `srs` {String} projection for resulting bbox (WGS84|900913).
  67. // - `return` {Array} bbox array of values in form `[w, s, e, n]`.
  68. SphericalMercator.prototype.bbox = function(x, y, zoom, tms_style, srs) {
  69. // Convert xyz into bbox with srs WGS84
  70. if (tms_style) {
  71. y = (Math.pow(2, zoom) - 1) - y;
  72. }
  73. // Use +y to make sure it's a number to avoid inadvertent concatenation.
  74. var ll = [x * this.size, (+y + 1) * this.size]; // lower left
  75. // Use +x to make sure it's a number to avoid inadvertent concatenation.
  76. var ur = [(+x + 1) * this.size, y * this.size]; // upper right
  77. var bbox = this.ll(ll, zoom).concat(this.ll(ur, zoom));
  78. // If web mercator requested reproject to 900913.
  79. if (srs === '900913') {
  80. return this.convert(bbox, '900913');
  81. } else {
  82. return bbox;
  83. }
  84. };
  85. // Convert bbox to xyx bounds
  86. //
  87. // - `bbox` {Number} bbox in the form `[w, s, e, n]`.
  88. // - `zoom` {Number} zoom.
  89. // - `tms_style` {Boolean} whether to compute using tms-style.
  90. // - `srs` {String} projection of input bbox (WGS84|900913).
  91. // - `@return` {Object} XYZ bounds containing minX, maxX, minY, maxY properties.
  92. SphericalMercator.prototype.xyz = function(bbox, zoom, tms_style, srs) {
  93. // If web mercator provided reproject to WGS84.
  94. if (srs === '900913') {
  95. bbox = this.convert(bbox, 'WGS84');
  96. }
  97. var ll = [bbox[0], bbox[1]]; // lower left
  98. var ur = [bbox[2], bbox[3]]; // upper right
  99. var px_ll = this.px(ll, zoom);
  100. var px_ur = this.px(ur, zoom);
  101. // Y = 0 for XYZ is the top hence minY uses px_ur[1].
  102. var x = [ Math.floor(px_ll[0] / this.size), Math.floor((px_ur[0] - 1) / this.size) ];
  103. var y = [ Math.floor(px_ur[1] / this.size), Math.floor((px_ll[1] - 1) / this.size) ];
  104. var bounds = {
  105. minX: Math.min.apply(Math, x) < 0 ? 0 : Math.min.apply(Math, x),
  106. minY: Math.min.apply(Math, y) < 0 ? 0 : Math.min.apply(Math, y),
  107. maxX: Math.max.apply(Math, x),
  108. maxY: Math.max.apply(Math, y)
  109. };
  110. if (tms_style) {
  111. var tms = {
  112. minY: (Math.pow(2, zoom) - 1) - bounds.maxY,
  113. maxY: (Math.pow(2, zoom) - 1) - bounds.minY
  114. };
  115. bounds.minY = tms.minY;
  116. bounds.maxY = tms.maxY;
  117. }
  118. return bounds;
  119. };
  120. // Convert projection of given bbox.
  121. //
  122. // - `bbox` {Number} bbox in the form `[w, s, e, n]`.
  123. // - `to` {String} projection of output bbox (WGS84|900913). Input bbox
  124. // assumed to be the "other" projection.
  125. // - `@return` {Object} bbox with reprojected coordinates.
  126. SphericalMercator.prototype.convert = function(bbox, to) {
  127. if (to === '900913') {
  128. return this.forward(bbox.slice(0, 2)).concat(this.forward(bbox.slice(2,4)));
  129. } else {
  130. return this.inverse(bbox.slice(0, 2)).concat(this.inverse(bbox.slice(2,4)));
  131. }
  132. };
  133. // Convert lon/lat values to 900913 x/y.
  134. SphericalMercator.prototype.forward = function(ll) {
  135. var xy = [
  136. A * ll[0] * D2R,
  137. A * Math.log(Math.tan((Math.PI*0.25) + (0.5 * ll[1] * D2R)))
  138. ];
  139. // if xy value is beyond maxextent (e.g. poles), return maxextent.
  140. (xy[0] > MAXEXTENT) && (xy[0] = MAXEXTENT);
  141. (xy[0] < -MAXEXTENT) && (xy[0] = -MAXEXTENT);
  142. (xy[1] > MAXEXTENT) && (xy[1] = MAXEXTENT);
  143. (xy[1] < -MAXEXTENT) && (xy[1] = -MAXEXTENT);
  144. return xy;
  145. };
  146. // Convert 900913 x/y values to lon/lat.
  147. SphericalMercator.prototype.inverse = function(xy) {
  148. return [
  149. (xy[0] * R2D / A),
  150. ((Math.PI*0.5) - 2.0 * Math.atan(Math.exp(-xy[1] / A))) * R2D
  151. ];
  152. };
  153. return SphericalMercator;
  154. })();
  155. if (typeof module !== 'undefined' && typeof exports !== 'undefined') {
  156. module.exports = exports = SphericalMercator;
  157. }