WebMercatorTilingScheme.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. import Cartesian2 from './Cartesian2.js';
  2. import defaultValue from './defaultValue.js';
  3. import defined from './defined.js';
  4. import defineProperties from './defineProperties.js';
  5. import Ellipsoid from './Ellipsoid.js';
  6. import Rectangle from './Rectangle.js';
  7. import WebMercatorProjection from './WebMercatorProjection.js';
  8. /**
  9. * A tiling scheme for geometry referenced to a {@link WebMercatorProjection}, EPSG:3857. This is
  10. * the tiling scheme used by Google Maps, Microsoft Bing Maps, and most of ESRI ArcGIS Online.
  11. *
  12. * @alias WebMercatorTilingScheme
  13. * @constructor
  14. *
  15. * @param {Object} [options] Object with the following properties:
  16. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid whose surface is being tiled. Defaults to
  17. * the WGS84 ellipsoid.
  18. * @param {Number} [options.numberOfLevelZeroTilesX=1] The number of tiles in the X direction at level zero of
  19. * the tile tree.
  20. * @param {Number} [options.numberOfLevelZeroTilesY=1] The number of tiles in the Y direction at level zero of
  21. * the tile tree.
  22. * @param {Cartesian2} [options.rectangleSouthwestInMeters] The southwest corner of the rectangle covered by the
  23. * tiling scheme, in meters. If this parameter or rectangleNortheastInMeters is not specified, the entire
  24. * globe is covered in the longitude direction and an equal distance is covered in the latitude
  25. * direction, resulting in a square projection.
  26. * @param {Cartesian2} [options.rectangleNortheastInMeters] The northeast corner of the rectangle covered by the
  27. * tiling scheme, in meters. If this parameter or rectangleSouthwestInMeters is not specified, the entire
  28. * globe is covered in the longitude direction and an equal distance is covered in the latitude
  29. * direction, resulting in a square projection.
  30. */
  31. function WebMercatorTilingScheme(options) {
  32. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  33. this._ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84);
  34. this._numberOfLevelZeroTilesX = defaultValue(options.numberOfLevelZeroTilesX, 1);
  35. this._numberOfLevelZeroTilesY = defaultValue(options.numberOfLevelZeroTilesY, 1);
  36. this._projection = new WebMercatorProjection(this._ellipsoid);
  37. if (defined(options.rectangleSouthwestInMeters) &&
  38. defined(options.rectangleNortheastInMeters)) {
  39. this._rectangleSouthwestInMeters = options.rectangleSouthwestInMeters;
  40. this._rectangleNortheastInMeters = options.rectangleNortheastInMeters;
  41. } else {
  42. var semimajorAxisTimesPi = this._ellipsoid.maximumRadius * Math.PI;
  43. this._rectangleSouthwestInMeters = new Cartesian2(-semimajorAxisTimesPi, -semimajorAxisTimesPi);
  44. this._rectangleNortheastInMeters = new Cartesian2(semimajorAxisTimesPi, semimajorAxisTimesPi);
  45. }
  46. var southwest = this._projection.unproject(this._rectangleSouthwestInMeters);
  47. var northeast = this._projection.unproject(this._rectangleNortheastInMeters);
  48. this._rectangle = new Rectangle(southwest.longitude, southwest.latitude,
  49. northeast.longitude, northeast.latitude);
  50. }
  51. defineProperties(WebMercatorTilingScheme.prototype, {
  52. /**
  53. * Gets the ellipsoid that is tiled by this tiling scheme.
  54. * @memberof WebMercatorTilingScheme.prototype
  55. * @type {Ellipsoid}
  56. */
  57. ellipsoid : {
  58. get : function() {
  59. return this._ellipsoid;
  60. }
  61. },
  62. /**
  63. * Gets the rectangle, in radians, covered by this tiling scheme.
  64. * @memberof WebMercatorTilingScheme.prototype
  65. * @type {Rectangle}
  66. */
  67. rectangle : {
  68. get : function() {
  69. return this._rectangle;
  70. }
  71. },
  72. /**
  73. * Gets the map projection used by this tiling scheme.
  74. * @memberof WebMercatorTilingScheme.prototype
  75. * @type {MapProjection}
  76. */
  77. projection : {
  78. get : function() {
  79. return this._projection;
  80. }
  81. }
  82. });
  83. /**
  84. * Gets the total number of tiles in the X direction at a specified level-of-detail.
  85. *
  86. * @param {Number} level The level-of-detail.
  87. * @returns {Number} The number of tiles in the X direction at the given level.
  88. */
  89. WebMercatorTilingScheme.prototype.getNumberOfXTilesAtLevel = function(level) {
  90. return this._numberOfLevelZeroTilesX << level;
  91. };
  92. /**
  93. * Gets the total number of tiles in the Y direction at a specified level-of-detail.
  94. *
  95. * @param {Number} level The level-of-detail.
  96. * @returns {Number} The number of tiles in the Y direction at the given level.
  97. */
  98. WebMercatorTilingScheme.prototype.getNumberOfYTilesAtLevel = function(level) {
  99. return this._numberOfLevelZeroTilesY << level;
  100. };
  101. /**
  102. * Transforms a rectangle specified in geodetic radians to the native coordinate system
  103. * of this tiling scheme.
  104. *
  105. * @param {Rectangle} rectangle The rectangle to transform.
  106. * @param {Rectangle} [result] The instance to which to copy the result, or undefined if a new instance
  107. * should be created.
  108. * @returns {Rectangle} The specified 'result', or a new object containing the native rectangle if 'result'
  109. * is undefined.
  110. */
  111. WebMercatorTilingScheme.prototype.rectangleToNativeRectangle = function(rectangle, result) {
  112. var projection = this._projection;
  113. var southwest = projection.project(Rectangle.southwest(rectangle));
  114. var northeast = projection.project(Rectangle.northeast(rectangle));
  115. if (!defined(result)) {
  116. return new Rectangle(southwest.x, southwest.y, northeast.x, northeast.y);
  117. }
  118. result.west = southwest.x;
  119. result.south = southwest.y;
  120. result.east = northeast.x;
  121. result.north = northeast.y;
  122. return result;
  123. };
  124. /**
  125. * Converts tile x, y coordinates and level to a rectangle expressed in the native coordinates
  126. * of the tiling scheme.
  127. *
  128. * @param {Number} x The integer x coordinate of the tile.
  129. * @param {Number} y The integer y coordinate of the tile.
  130. * @param {Number} level The tile level-of-detail. Zero is the least detailed.
  131. * @param {Object} [result] The instance to which to copy the result, or undefined if a new instance
  132. * should be created.
  133. * @returns {Rectangle} The specified 'result', or a new object containing the rectangle
  134. * if 'result' is undefined.
  135. */
  136. WebMercatorTilingScheme.prototype.tileXYToNativeRectangle = function(x, y, level, result) {
  137. var xTiles = this.getNumberOfXTilesAtLevel(level);
  138. var yTiles = this.getNumberOfYTilesAtLevel(level);
  139. var xTileWidth = (this._rectangleNortheastInMeters.x - this._rectangleSouthwestInMeters.x) / xTiles;
  140. var west = this._rectangleSouthwestInMeters.x + x * xTileWidth;
  141. var east = this._rectangleSouthwestInMeters.x + (x + 1) * xTileWidth;
  142. var yTileHeight = (this._rectangleNortheastInMeters.y - this._rectangleSouthwestInMeters.y) / yTiles;
  143. var north = this._rectangleNortheastInMeters.y - y * yTileHeight;
  144. var south = this._rectangleNortheastInMeters.y - (y + 1) * yTileHeight;
  145. if (!defined(result)) {
  146. return new Rectangle(west, south, east, north);
  147. }
  148. result.west = west;
  149. result.south = south;
  150. result.east = east;
  151. result.north = north;
  152. return result;
  153. };
  154. /**
  155. * Converts tile x, y coordinates and level to a cartographic rectangle in radians.
  156. *
  157. * @param {Number} x The integer x coordinate of the tile.
  158. * @param {Number} y The integer y coordinate of the tile.
  159. * @param {Number} level The tile level-of-detail. Zero is the least detailed.
  160. * @param {Object} [result] The instance to which to copy the result, or undefined if a new instance
  161. * should be created.
  162. * @returns {Rectangle} The specified 'result', or a new object containing the rectangle
  163. * if 'result' is undefined.
  164. */
  165. WebMercatorTilingScheme.prototype.tileXYToRectangle = function(x, y, level, result) {
  166. var nativeRectangle = this.tileXYToNativeRectangle(x, y, level, result);
  167. var projection = this._projection;
  168. var southwest = projection.unproject(new Cartesian2(nativeRectangle.west, nativeRectangle.south));
  169. var northeast = projection.unproject(new Cartesian2(nativeRectangle.east, nativeRectangle.north));
  170. nativeRectangle.west = southwest.longitude;
  171. nativeRectangle.south = southwest.latitude;
  172. nativeRectangle.east = northeast.longitude;
  173. nativeRectangle.north = northeast.latitude;
  174. return nativeRectangle;
  175. };
  176. /**
  177. * Calculates the tile x, y coordinates of the tile containing
  178. * a given cartographic position.
  179. *
  180. * @param {Cartographic} position The position.
  181. * @param {Number} level The tile level-of-detail. Zero is the least detailed.
  182. * @param {Cartesian2} [result] The instance to which to copy the result, or undefined if a new instance
  183. * should be created.
  184. * @returns {Cartesian2} The specified 'result', or a new object containing the tile x, y coordinates
  185. * if 'result' is undefined.
  186. */
  187. WebMercatorTilingScheme.prototype.positionToTileXY = function(position, level, result) {
  188. var rectangle = this._rectangle;
  189. if (!Rectangle.contains(rectangle, position)) {
  190. // outside the bounds of the tiling scheme
  191. return undefined;
  192. }
  193. var xTiles = this.getNumberOfXTilesAtLevel(level);
  194. var yTiles = this.getNumberOfYTilesAtLevel(level);
  195. var overallWidth = this._rectangleNortheastInMeters.x - this._rectangleSouthwestInMeters.x;
  196. var xTileWidth = overallWidth / xTiles;
  197. var overallHeight = this._rectangleNortheastInMeters.y - this._rectangleSouthwestInMeters.y;
  198. var yTileHeight = overallHeight / yTiles;
  199. var projection = this._projection;
  200. var webMercatorPosition = projection.project(position);
  201. var distanceFromWest = webMercatorPosition.x - this._rectangleSouthwestInMeters.x;
  202. var distanceFromNorth = this._rectangleNortheastInMeters.y - webMercatorPosition.y;
  203. var xTileCoordinate = distanceFromWest / xTileWidth | 0;
  204. if (xTileCoordinate >= xTiles) {
  205. xTileCoordinate = xTiles - 1;
  206. }
  207. var yTileCoordinate = distanceFromNorth / yTileHeight | 0;
  208. if (yTileCoordinate >= yTiles) {
  209. yTileCoordinate = yTiles - 1;
  210. }
  211. if (!defined(result)) {
  212. return new Cartesian2(xTileCoordinate, yTileCoordinate);
  213. }
  214. result.x = xTileCoordinate;
  215. result.y = yTileCoordinate;
  216. return result;
  217. };
  218. export default WebMercatorTilingScheme;