QuadtreeTile.js 15 KB

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