| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 | import Cartesian2 from "./Cartesian2.js";import defaultValue from "./defaultValue.js";import defined from "./defined.js";import Ellipsoid from "./Ellipsoid.js";import Rectangle from "./Rectangle.js";import WebMercatorProjection from "./WebMercatorProjection.js";/** * A tiling scheme for geometry referenced to a {@link WebMercatorProjection}, EPSG:3857.  This is * the tiling scheme used by Google Maps, Microsoft Bing Maps, and most of ESRI ArcGIS Online. * * @alias WebMercatorTilingScheme * @constructor * * @param {Object} [options] Object with the following properties: * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid whose surface is being tiled. Defaults to * the WGS84 ellipsoid. * @param {Number} [options.numberOfLevelZeroTilesX=1] The number of tiles in the X direction at level zero of *        the tile tree. * @param {Number} [options.numberOfLevelZeroTilesY=1] The number of tiles in the Y direction at level zero of *        the tile tree. * @param {Cartesian2} [options.rectangleSouthwestInMeters] The southwest corner of the rectangle covered by the *        tiling scheme, in meters.  If this parameter or rectangleNortheastInMeters is not specified, the entire *        globe is covered in the longitude direction and an equal distance is covered in the latitude *        direction, resulting in a square projection. * @param {Cartesian2} [options.rectangleNortheastInMeters] The northeast corner of the rectangle covered by the *        tiling scheme, in meters.  If this parameter or rectangleSouthwestInMeters is not specified, the entire *        globe is covered in the longitude direction and an equal distance is covered in the latitude *        direction, resulting in a square projection. */function WebMercatorTilingScheme(options) {  options = defaultValue(options, defaultValue.EMPTY_OBJECT);  this._ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84);  this._numberOfLevelZeroTilesX = defaultValue(    options.numberOfLevelZeroTilesX,    1  );  this._numberOfLevelZeroTilesY = defaultValue(    options.numberOfLevelZeroTilesY,    1  );  this._projection = new WebMercatorProjection(this._ellipsoid);  if (    defined(options.rectangleSouthwestInMeters) &&    defined(options.rectangleNortheastInMeters)  ) {    this._rectangleSouthwestInMeters = options.rectangleSouthwestInMeters;    this._rectangleNortheastInMeters = options.rectangleNortheastInMeters;  } else {    const semimajorAxisTimesPi = this._ellipsoid.maximumRadius * Math.PI;    this._rectangleSouthwestInMeters = new Cartesian2(      -semimajorAxisTimesPi,      -semimajorAxisTimesPi    );    this._rectangleNortheastInMeters = new Cartesian2(      semimajorAxisTimesPi,      semimajorAxisTimesPi    );  }  const southwest = this._projection.unproject(    this._rectangleSouthwestInMeters  );  const northeast = this._projection.unproject(    this._rectangleNortheastInMeters  );  this._rectangle = new Rectangle(    southwest.longitude,    southwest.latitude,    northeast.longitude,    northeast.latitude  );}Object.defineProperties(WebMercatorTilingScheme.prototype, {  /**   * Gets the ellipsoid that is tiled by this tiling scheme.   * @memberof WebMercatorTilingScheme.prototype   * @type {Ellipsoid}   */  ellipsoid: {    get: function () {      return this._ellipsoid;    },  },  /**   * Gets the rectangle, in radians, covered by this tiling scheme.   * @memberof WebMercatorTilingScheme.prototype   * @type {Rectangle}   */  rectangle: {    get: function () {      return this._rectangle;    },  },  /**   * Gets the map projection used by this tiling scheme.   * @memberof WebMercatorTilingScheme.prototype   * @type {MapProjection}   */  projection: {    get: function () {      return this._projection;    },  },});/** * Gets the total number of tiles in the X direction at a specified level-of-detail. * * @param {Number} level The level-of-detail. * @returns {Number} The number of tiles in the X direction at the given level. */WebMercatorTilingScheme.prototype.getNumberOfXTilesAtLevel = function (level) {  return this._numberOfLevelZeroTilesX << level;};/** * Gets the total number of tiles in the Y direction at a specified level-of-detail. * * @param {Number} level The level-of-detail. * @returns {Number} The number of tiles in the Y direction at the given level. */WebMercatorTilingScheme.prototype.getNumberOfYTilesAtLevel = function (level) {  return this._numberOfLevelZeroTilesY << level;};/** * Transforms a rectangle specified in geodetic radians to the native coordinate system * of this tiling scheme. * * @param {Rectangle} rectangle The rectangle to transform. * @param {Rectangle} [result] The instance to which to copy the result, or undefined if a new instance *        should be created. * @returns {Rectangle} The specified 'result', or a new object containing the native rectangle if 'result' *          is undefined. */WebMercatorTilingScheme.prototype.rectangleToNativeRectangle = function (  rectangle,  result) {  const projection = this._projection;  const southwest = projection.project(Rectangle.southwest(rectangle));  const northeast = projection.project(Rectangle.northeast(rectangle));  if (!defined(result)) {    return new Rectangle(southwest.x, southwest.y, northeast.x, northeast.y);  }  result.west = southwest.x;  result.south = southwest.y;  result.east = northeast.x;  result.north = northeast.y;  return result;};/** * Converts tile x, y coordinates and level to a rectangle expressed in the native coordinates * of the tiling scheme. * * @param {Number} x The integer x coordinate of the tile. * @param {Number} y The integer y coordinate of the tile. * @param {Number} level The tile level-of-detail.  Zero is the least detailed. * @param {Object} [result] The instance to which to copy the result, or undefined if a new instance *        should be created. * @returns {Rectangle} The specified 'result', or a new object containing the rectangle *          if 'result' is undefined. */WebMercatorTilingScheme.prototype.tileXYToNativeRectangle = function (  x,  y,  level,  result) {  const xTiles = this.getNumberOfXTilesAtLevel(level);  const yTiles = this.getNumberOfYTilesAtLevel(level);  const xTileWidth =    (this._rectangleNortheastInMeters.x - this._rectangleSouthwestInMeters.x) /    xTiles;  const west = this._rectangleSouthwestInMeters.x + x * xTileWidth;  const east = this._rectangleSouthwestInMeters.x + (x + 1) * xTileWidth;  const yTileHeight =    (this._rectangleNortheastInMeters.y - this._rectangleSouthwestInMeters.y) /    yTiles;  const north = this._rectangleNortheastInMeters.y - y * yTileHeight;  const south = this._rectangleNortheastInMeters.y - (y + 1) * yTileHeight;  if (!defined(result)) {    return new Rectangle(west, south, east, north);  }  result.west = west;  result.south = south;  result.east = east;  result.north = north;  return result;};/** * Converts tile x, y coordinates and level to a cartographic rectangle in radians. * * @param {Number} x The integer x coordinate of the tile. * @param {Number} y The integer y coordinate of the tile. * @param {Number} level The tile level-of-detail.  Zero is the least detailed. * @param {Object} [result] The instance to which to copy the result, or undefined if a new instance *        should be created. * @returns {Rectangle} The specified 'result', or a new object containing the rectangle *          if 'result' is undefined. */WebMercatorTilingScheme.prototype.tileXYToRectangle = function (  x,  y,  level,  result) {  const nativeRectangle = this.tileXYToNativeRectangle(x, y, level, result);  const projection = this._projection;  const southwest = projection.unproject(    new Cartesian2(nativeRectangle.west, nativeRectangle.south)  );  const northeast = projection.unproject(    new Cartesian2(nativeRectangle.east, nativeRectangle.north)  );  nativeRectangle.west = southwest.longitude;  nativeRectangle.south = southwest.latitude;  nativeRectangle.east = northeast.longitude;  nativeRectangle.north = northeast.latitude;  return nativeRectangle;};/** * Calculates the tile x, y coordinates of the tile containing * a given cartographic position. * * @param {Cartographic} position The position. * @param {Number} level The tile level-of-detail.  Zero is the least detailed. * @param {Cartesian2} [result] The instance to which to copy the result, or undefined if a new instance *        should be created. * @returns {Cartesian2} The specified 'result', or a new object containing the tile x, y coordinates *          if 'result' is undefined. */WebMercatorTilingScheme.prototype.positionToTileXY = function (  position,  level,  result) {  const rectangle = this._rectangle;  if (!Rectangle.contains(rectangle, position)) {    // outside the bounds of the tiling scheme    return undefined;  }  const xTiles = this.getNumberOfXTilesAtLevel(level);  const yTiles = this.getNumberOfYTilesAtLevel(level);  const overallWidth =    this._rectangleNortheastInMeters.x - this._rectangleSouthwestInMeters.x;  const xTileWidth = overallWidth / xTiles;  const overallHeight =    this._rectangleNortheastInMeters.y - this._rectangleSouthwestInMeters.y;  const yTileHeight = overallHeight / yTiles;  const projection = this._projection;  const webMercatorPosition = projection.project(position);  const distanceFromWest =    webMercatorPosition.x - this._rectangleSouthwestInMeters.x;  const distanceFromNorth =    this._rectangleNortheastInMeters.y - webMercatorPosition.y;  let xTileCoordinate = (distanceFromWest / xTileWidth) | 0;  if (xTileCoordinate >= xTiles) {    xTileCoordinate = xTiles - 1;  }  let yTileCoordinate = (distanceFromNorth / yTileHeight) | 0;  if (yTileCoordinate >= yTiles) {    yTileCoordinate = yTiles - 1;  }  if (!defined(result)) {    return new Cartesian2(xTileCoordinate, yTileCoordinate);  }  result.x = xTileCoordinate;  result.y = yTileCoordinate;  return result;};export default WebMercatorTilingScheme;
 |