QuadtreeTile.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. import defined from '../Core/defined.js';
  2. import defineProperties from '../Core/defineProperties.js';
  3. import DeveloperError from '../Core/DeveloperError.js';
  4. import Rectangle from '../Core/Rectangle.js';
  5. import QuadtreeTileLoadState from './QuadtreeTileLoadState.js';
  6. import TileSelectionResult from './TileSelectionResult.js';
  7. /**
  8. * A single tile in a {@link QuadtreePrimitive}.
  9. *
  10. * @alias QuadtreeTile
  11. * @constructor
  12. * @private
  13. *
  14. * @param {Number} options.level The level of the tile in the quadtree.
  15. * @param {Number} options.x The X coordinate of the tile in the quadtree. 0 is the westernmost tile.
  16. * @param {Number} options.y The Y coordinate of the tile in the quadtree. 0 is the northernmost tile.
  17. * @param {TilingScheme} options.tilingScheme The tiling scheme in which this tile exists.
  18. * @param {QuadtreeTile} [options.parent] This tile's parent, or undefined if this is a root tile.
  19. */
  20. function QuadtreeTile(options) {
  21. //>>includeStart('debug', pragmas.debug);
  22. if (!defined(options)) {
  23. throw new DeveloperError('options is required.');
  24. }
  25. if (!defined(options.x)) {
  26. throw new DeveloperError('options.x is required.');
  27. } else if (!defined(options.y)) {
  28. throw new DeveloperError('options.y is required.');
  29. } else if (options.x < 0 || options.y < 0) {
  30. throw new DeveloperError('options.x and options.y must be greater than or equal to zero.');
  31. }
  32. if (!defined(options.level)) {
  33. throw new DeveloperError('options.level is required and must be greater than or equal to zero.');
  34. }
  35. if (!defined(options.tilingScheme)) {
  36. throw new DeveloperError('options.tilingScheme is required.');
  37. }
  38. //>>includeEnd('debug');
  39. this._tilingScheme = options.tilingScheme;
  40. this._x = options.x;
  41. this._y = options.y;
  42. this._level = options.level;
  43. this._parent = options.parent;
  44. this._rectangle = this._tilingScheme.tileXYToRectangle(this._x, this._y, this._level);
  45. this._southwestChild = undefined;
  46. this._southeastChild = undefined;
  47. this._northwestChild = undefined;
  48. this._northeastChild = undefined;
  49. // TileReplacementQueue gets/sets these private properties.
  50. this.replacementPrevious = undefined;
  51. this.replacementNext = undefined;
  52. // The distance from the camera to this tile, updated when the tile is selected
  53. // for rendering. We can get rid of this if we have a better way to sort by
  54. // distance - for example, by using the natural ordering of a quadtree.
  55. // QuadtreePrimitive gets/sets this private property.
  56. this._distance = 0.0;
  57. this._loadPriority = 0.0;
  58. this._customData = [];
  59. this._frameUpdated = undefined;
  60. this._lastSelectionResult = TileSelectionResult.NONE;
  61. this._lastSelectionResultFrame = undefined;
  62. this._loadedCallbacks = {};
  63. /**
  64. * Gets or sets the current state of the tile in the tile load pipeline.
  65. * @type {QuadtreeTileLoadState}
  66. * @default {@link QuadtreeTileLoadState.START}
  67. */
  68. this.state = QuadtreeTileLoadState.START;
  69. /**
  70. * Gets or sets a value indicating whether or not the tile is currently renderable.
  71. * @type {Boolean}
  72. * @default false
  73. */
  74. this.renderable = false;
  75. /**
  76. * Gets or set a value indicating whether or not the tile was entirely upsampled from its
  77. * parent tile. If all four children of a parent tile were upsampled from the parent,
  78. * we will render the parent instead of the children even if the LOD indicates that
  79. * the children would be preferable.
  80. * @type {Boolean}
  81. * @default false
  82. */
  83. this.upsampledFromParent = false;
  84. /**
  85. * Gets or sets the additional data associated with this tile. The exact content is specific to the
  86. * {@link QuadtreeTileProvider}.
  87. * @type {Object}
  88. * @default undefined
  89. */
  90. this.data = undefined;
  91. }
  92. /**
  93. * Creates a rectangular set of tiles for level of detail zero, the coarsest, least detailed level.
  94. *
  95. * @memberof QuadtreeTile
  96. *
  97. * @param {TilingScheme} tilingScheme The tiling scheme for which the tiles are to be created.
  98. * @returns {QuadtreeTile[]} An array containing the tiles at level of detail zero, starting with the
  99. * tile in the northwest corner and followed by the tile (if any) to its east.
  100. */
  101. QuadtreeTile.createLevelZeroTiles = function(tilingScheme) {
  102. //>>includeStart('debug', pragmas.debug);
  103. if (!defined(tilingScheme)) {
  104. throw new DeveloperError('tilingScheme is required.');
  105. }
  106. //>>includeEnd('debug');
  107. var numberOfLevelZeroTilesX = tilingScheme.getNumberOfXTilesAtLevel(0);
  108. var numberOfLevelZeroTilesY = tilingScheme.getNumberOfYTilesAtLevel(0);
  109. var result = new Array(numberOfLevelZeroTilesX * numberOfLevelZeroTilesY);
  110. var index = 0;
  111. for (var y = 0; y < numberOfLevelZeroTilesY; ++y) {
  112. for (var x = 0; x < numberOfLevelZeroTilesX; ++x) {
  113. result[index++] = new QuadtreeTile({
  114. tilingScheme : tilingScheme,
  115. x : x,
  116. y : y,
  117. level : 0
  118. });
  119. }
  120. }
  121. return result;
  122. };
  123. QuadtreeTile.prototype._updateCustomData = function(frameNumber, added, removed) {
  124. var customData = this.customData;
  125. var i;
  126. var data;
  127. var rectangle;
  128. if (defined(added) && defined(removed)) {
  129. customData = customData.filter(function(value) {
  130. return removed.indexOf(value) === -1;
  131. });
  132. this._customData = customData;
  133. rectangle = this._rectangle;
  134. for (i = 0; i < added.length; ++i) {
  135. data = added[i];
  136. if (Rectangle.contains(rectangle, data.positionCartographic)) {
  137. customData.push(data);
  138. }
  139. }
  140. this._frameUpdated = frameNumber;
  141. } else {
  142. // interior or leaf tile, update from parent
  143. var parent = this._parent;
  144. if (defined(parent) && this._frameUpdated !== parent._frameUpdated) {
  145. customData.length = 0;
  146. rectangle = this._rectangle;
  147. var parentCustomData = parent.customData;
  148. for (i = 0; i < parentCustomData.length; ++i) {
  149. data = parentCustomData[i];
  150. if (Rectangle.contains(rectangle, data.positionCartographic)) {
  151. customData.push(data);
  152. }
  153. }
  154. this._frameUpdated = parent._frameUpdated;
  155. }
  156. }
  157. };
  158. defineProperties(QuadtreeTile.prototype, {
  159. /**
  160. * Gets the tiling scheme used to tile the surface.
  161. * @memberof QuadtreeTile.prototype
  162. * @type {TilingScheme}
  163. */
  164. tilingScheme : {
  165. get : function() {
  166. return this._tilingScheme;
  167. }
  168. },
  169. /**
  170. * Gets the tile X coordinate.
  171. * @memberof QuadtreeTile.prototype
  172. * @type {Number}
  173. */
  174. x : {
  175. get : function() {
  176. return this._x;
  177. }
  178. },
  179. /**
  180. * Gets the tile Y coordinate.
  181. * @memberof QuadtreeTile.prototype
  182. * @type {Number}
  183. */
  184. y : {
  185. get : function() {
  186. return this._y;
  187. }
  188. },
  189. /**
  190. * Gets the level-of-detail, where zero is the coarsest, least-detailed.
  191. * @memberof QuadtreeTile.prototype
  192. * @type {Number}
  193. */
  194. level : {
  195. get : function() {
  196. return this._level;
  197. }
  198. },
  199. /**
  200. * Gets the parent tile of this tile.
  201. * @memberof QuadtreeTile.prototype
  202. * @type {QuadtreeTile}
  203. */
  204. parent : {
  205. get : function() {
  206. return this._parent;
  207. }
  208. },
  209. /**
  210. * Gets the cartographic rectangle of the tile, with north, south, east and
  211. * west properties in radians.
  212. * @memberof QuadtreeTile.prototype
  213. * @type {Rectangle}
  214. */
  215. rectangle : {
  216. get : function() {
  217. return this._rectangle;
  218. }
  219. },
  220. /**
  221. * An array of tiles that is at the next level of the tile tree.
  222. * @memberof QuadtreeTile.prototype
  223. * @type {QuadtreeTile[]}
  224. */
  225. children : {
  226. get : function() {
  227. return [this.northwestChild, this.northeastChild, this.southwestChild, this.southeastChild];
  228. }
  229. },
  230. /**
  231. * Gets the southwest child tile.
  232. * @memberof QuadtreeTile.prototype
  233. * @type {QuadtreeTile}
  234. */
  235. southwestChild : {
  236. get : function() {
  237. if (!defined(this._southwestChild)) {
  238. this._southwestChild = new QuadtreeTile({
  239. tilingScheme : this.tilingScheme,
  240. x : this.x * 2,
  241. y : this.y * 2 + 1,
  242. level : this.level + 1,
  243. parent : this
  244. });
  245. }
  246. return this._southwestChild;
  247. }
  248. },
  249. /**
  250. * Gets the southeast child tile.
  251. * @memberof QuadtreeTile.prototype
  252. * @type {QuadtreeTile}
  253. */
  254. southeastChild : {
  255. get : function() {
  256. if (!defined(this._southeastChild)) {
  257. this._southeastChild = new QuadtreeTile({
  258. tilingScheme : this.tilingScheme,
  259. x : this.x * 2 + 1,
  260. y : this.y * 2 + 1,
  261. level : this.level + 1,
  262. parent : this
  263. });
  264. }
  265. return this._southeastChild;
  266. }
  267. },
  268. /**
  269. * Gets the northwest child tile.
  270. * @memberof QuadtreeTile.prototype
  271. * @type {QuadtreeTile}
  272. */
  273. northwestChild : {
  274. get : function() {
  275. if (!defined(this._northwestChild)) {
  276. this._northwestChild = new QuadtreeTile({
  277. tilingScheme : this.tilingScheme,
  278. x : this.x * 2,
  279. y : this.y * 2,
  280. level : this.level + 1,
  281. parent : this
  282. });
  283. }
  284. return this._northwestChild;
  285. }
  286. },
  287. /**
  288. * Gets the northeast child tile.
  289. * @memberof QuadtreeTile.prototype
  290. * @type {QuadtreeTile}
  291. */
  292. northeastChild : {
  293. get : function() {
  294. if (!defined(this._northeastChild)) {
  295. this._northeastChild = new QuadtreeTile({
  296. tilingScheme : this.tilingScheme,
  297. x : this.x * 2 + 1,
  298. y : this.y * 2,
  299. level : this.level + 1,
  300. parent : this
  301. });
  302. }
  303. return this._northeastChild;
  304. }
  305. },
  306. /**
  307. * An array of objects associated with this tile.
  308. * @memberof QuadtreeTile.prototype
  309. * @type {Array}
  310. */
  311. customData : {
  312. get : function() {
  313. return this._customData;
  314. }
  315. },
  316. /**
  317. * Gets a value indicating whether or not this tile needs further loading.
  318. * This property will return true if the {@link QuadtreeTile#state} is
  319. * <code>START</code> or <code>LOADING</code>.
  320. * @memberof QuadtreeTile.prototype
  321. * @type {Boolean}
  322. */
  323. needsLoading : {
  324. get : function() {
  325. return this.state < QuadtreeTileLoadState.DONE;
  326. }
  327. },
  328. /**
  329. * Gets a value indicating whether or not this tile is eligible to be unloaded.
  330. * Typically, a tile is ineligible to be unloaded while an asynchronous operation,
  331. * such as a request for data, is in progress on it. A tile will never be
  332. * unloaded while it is needed for rendering, regardless of the value of this
  333. * property. If {@link QuadtreeTile#data} is defined and has an
  334. * <code>eligibleForUnloading</code> property, the value of that property is returned.
  335. * Otherwise, this property returns true.
  336. * @memberof QuadtreeTile.prototype
  337. * @type {Boolean}
  338. */
  339. eligibleForUnloading : {
  340. get : function() {
  341. var result = true;
  342. if (defined(this.data)) {
  343. result = this.data.eligibleForUnloading;
  344. if (!defined(result)) {
  345. result = true;
  346. }
  347. }
  348. return result;
  349. }
  350. }
  351. });
  352. QuadtreeTile.prototype.findLevelZeroTile = function(levelZeroTiles, x, y) {
  353. var xTiles = this.tilingScheme.getNumberOfXTilesAtLevel(0);
  354. if (x < 0) {
  355. x += xTiles;
  356. } else if (x >= xTiles) {
  357. x -= xTiles;
  358. }
  359. if (y < 0 || y >= this.tilingScheme.getNumberOfYTilesAtLevel(0)) {
  360. return undefined;
  361. }
  362. return levelZeroTiles.filter(function(tile) {
  363. return tile.x === x && tile.y === y;
  364. })[0];
  365. };
  366. QuadtreeTile.prototype.findTileToWest = function(levelZeroTiles) {
  367. var parent = this.parent;
  368. if (parent === undefined) {
  369. return this.findLevelZeroTile(levelZeroTiles, this.x - 1, this.y);
  370. }
  371. if (parent.southeastChild === this) {
  372. return parent.southwestChild;
  373. } else if (parent.northeastChild === this) {
  374. return parent.northwestChild;
  375. }
  376. var westOfParent = parent.findTileToWest(levelZeroTiles);
  377. if (westOfParent === undefined) {
  378. return undefined;
  379. } else if (parent.southwestChild === this) {
  380. return westOfParent.southeastChild;
  381. }
  382. return westOfParent.northeastChild;
  383. };
  384. QuadtreeTile.prototype.findTileToEast = function(levelZeroTiles) {
  385. var parent = this.parent;
  386. if (parent === undefined) {
  387. return this.findLevelZeroTile(levelZeroTiles, this.x + 1, this.y);
  388. }
  389. if (parent.southwestChild === this) {
  390. return parent.southeastChild;
  391. } else if (parent.northwestChild === this) {
  392. return parent.northeastChild;
  393. }
  394. var eastOfParent = parent.findTileToEast(levelZeroTiles);
  395. if (eastOfParent === undefined) {
  396. return undefined;
  397. } else if (parent.southeastChild === this) {
  398. return eastOfParent.southwestChild;
  399. }
  400. return eastOfParent.northwestChild;
  401. };
  402. QuadtreeTile.prototype.findTileToSouth = function(levelZeroTiles) {
  403. var parent = this.parent;
  404. if (parent === undefined) {
  405. return this.findLevelZeroTile(levelZeroTiles, this.x, this.y + 1);
  406. }
  407. if (parent.northwestChild === this) {
  408. return parent.southwestChild;
  409. } else if (parent.northeastChild === this) {
  410. return parent.southeastChild;
  411. }
  412. var southOfParent = parent.findTileToSouth(levelZeroTiles);
  413. if (southOfParent === undefined) {
  414. return undefined;
  415. } else if (parent.southwestChild === this) {
  416. return southOfParent.northwestChild;
  417. }
  418. return southOfParent.northeastChild;
  419. };
  420. QuadtreeTile.prototype.findTileToNorth = function(levelZeroTiles) {
  421. var parent = this.parent;
  422. if (parent === undefined) {
  423. return this.findLevelZeroTile(levelZeroTiles, this.x, this.y - 1);
  424. }
  425. if (parent.southwestChild === this) {
  426. return parent.northwestChild;
  427. } else if (parent.southeastChild === this) {
  428. return parent.northeastChild;
  429. }
  430. var northOfParent = parent.findTileToNorth(levelZeroTiles);
  431. if (northOfParent === undefined) {
  432. return undefined;
  433. } else if (parent.northwestChild === this) {
  434. return northOfParent.southwestChild;
  435. }
  436. return northOfParent.southeastChild;
  437. };
  438. /**
  439. * Frees the resources associated with this tile and returns it to the <code>START</code>
  440. * {@link QuadtreeTileLoadState}. If the {@link QuadtreeTile#data} property is defined and it
  441. * has a <code>freeResources</code> method, the method will be invoked.
  442. *
  443. * @memberof QuadtreeTile
  444. */
  445. QuadtreeTile.prototype.freeResources = function() {
  446. this.state = QuadtreeTileLoadState.START;
  447. this.renderable = false;
  448. this.upsampledFromParent = false;
  449. if (defined(this.data) && defined(this.data.freeResources)) {
  450. this.data.freeResources();
  451. }
  452. freeTile(this._southwestChild);
  453. this._southwestChild = undefined;
  454. freeTile(this._southeastChild);
  455. this._southeastChild = undefined;
  456. freeTile(this._northwestChild);
  457. this._northwestChild = undefined;
  458. freeTile(this._northeastChild);
  459. this._northeastChild = undefined;
  460. };
  461. function freeTile(tile) {
  462. if (defined(tile)) {
  463. tile.freeResources();
  464. }
  465. }
  466. export default QuadtreeTile;