DataSourceDisplay.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. import ApproximateTerrainHeights from "../Core/ApproximateTerrainHeights.js";
  2. import BoundingSphere from "../Core/BoundingSphere.js";
  3. import Check from "../Core/Check.js";
  4. import defaultValue from "../Core/defaultValue.js";
  5. import defined from "../Core/defined.js";
  6. import destroyObject from "../Core/destroyObject.js";
  7. import EventHelper from "../Core/EventHelper.js";
  8. import GroundPolylinePrimitive from "../Scene/GroundPolylinePrimitive.js";
  9. import GroundPrimitive from "../Scene/GroundPrimitive.js";
  10. import OrderedGroundPrimitiveCollection from "../Scene/OrderedGroundPrimitiveCollection.js";
  11. import PrimitiveCollection from "../Scene/PrimitiveCollection.js";
  12. import BillboardVisualizer from "./BillboardVisualizer.js";
  13. import BoundingSphereState from "./BoundingSphereState.js";
  14. import CustomDataSource from "./CustomDataSource.js";
  15. import GeometryVisualizer from "./GeometryVisualizer.js";
  16. import LabelVisualizer from "./LabelVisualizer.js";
  17. import ModelVisualizer from "./ModelVisualizer.js";
  18. import Cesium3DTilesetVisualizer from "./Cesium3DTilesetVisualizer.js";
  19. import PathVisualizer from "./PathVisualizer.js";
  20. import PointVisualizer from "./PointVisualizer.js";
  21. import PolylineVisualizer from "./PolylineVisualizer.js";
  22. /**
  23. * Visualizes a collection of {@link DataSource} instances.
  24. * @alias DataSourceDisplay
  25. * @constructor
  26. *
  27. * @param {Object} options Object with the following properties:
  28. * @param {Scene} options.scene The scene in which to display the data.
  29. * @param {DataSourceCollection} options.dataSourceCollection The data sources to display.
  30. * @param {DataSourceDisplay.VisualizersCallback} [options.visualizersCallback=DataSourceDisplay.defaultVisualizersCallback]
  31. * A function which creates an array of visualizers used for visualization.
  32. * If undefined, all standard visualizers are used.
  33. */
  34. function DataSourceDisplay(options) {
  35. //>>includeStart('debug', pragmas.debug);
  36. Check.typeOf.object("options", options);
  37. Check.typeOf.object("options.scene", options.scene);
  38. Check.typeOf.object(
  39. "options.dataSourceCollection",
  40. options.dataSourceCollection
  41. );
  42. //>>includeEnd('debug');
  43. GroundPrimitive.initializeTerrainHeights();
  44. GroundPolylinePrimitive.initializeTerrainHeights();
  45. const scene = options.scene;
  46. const dataSourceCollection = options.dataSourceCollection;
  47. this._eventHelper = new EventHelper();
  48. this._eventHelper.add(
  49. dataSourceCollection.dataSourceAdded,
  50. this._onDataSourceAdded,
  51. this
  52. );
  53. this._eventHelper.add(
  54. dataSourceCollection.dataSourceRemoved,
  55. this._onDataSourceRemoved,
  56. this
  57. );
  58. this._eventHelper.add(
  59. dataSourceCollection.dataSourceMoved,
  60. this._onDataSourceMoved,
  61. this
  62. );
  63. this._eventHelper.add(scene.postRender, this._postRender, this);
  64. this._dataSourceCollection = dataSourceCollection;
  65. this._scene = scene;
  66. this._visualizersCallback = defaultValue(
  67. options.visualizersCallback,
  68. DataSourceDisplay.defaultVisualizersCallback
  69. );
  70. let primitivesAdded = false;
  71. const primitives = new PrimitiveCollection();
  72. const groundPrimitives = new PrimitiveCollection();
  73. if (dataSourceCollection.length > 0) {
  74. scene.primitives.add(primitives);
  75. scene.groundPrimitives.add(groundPrimitives);
  76. primitivesAdded = true;
  77. }
  78. this._primitives = primitives;
  79. this._groundPrimitives = groundPrimitives;
  80. for (let i = 0, len = dataSourceCollection.length; i < len; i++) {
  81. this._onDataSourceAdded(dataSourceCollection, dataSourceCollection.get(i));
  82. }
  83. const defaultDataSource = new CustomDataSource();
  84. this._onDataSourceAdded(undefined, defaultDataSource);
  85. this._defaultDataSource = defaultDataSource;
  86. let removeDefaultDataSourceListener;
  87. let removeDataSourceCollectionListener;
  88. if (!primitivesAdded) {
  89. const that = this;
  90. const addPrimitives = function () {
  91. scene.primitives.add(primitives);
  92. scene.groundPrimitives.add(groundPrimitives);
  93. removeDefaultDataSourceListener();
  94. removeDataSourceCollectionListener();
  95. that._removeDefaultDataSourceListener = undefined;
  96. that._removeDataSourceCollectionListener = undefined;
  97. };
  98. removeDefaultDataSourceListener = defaultDataSource.entities.collectionChanged.addEventListener(
  99. addPrimitives
  100. );
  101. removeDataSourceCollectionListener = dataSourceCollection.dataSourceAdded.addEventListener(
  102. addPrimitives
  103. );
  104. }
  105. this._removeDefaultDataSourceListener = removeDefaultDataSourceListener;
  106. this._removeDataSourceCollectionListener = removeDataSourceCollectionListener;
  107. this._ready = false;
  108. }
  109. /**
  110. * Gets or sets the default function which creates an array of visualizers used for visualization.
  111. * By default, this function uses all standard visualizers.
  112. *
  113. * @type {DataSourceDisplay.VisualizersCallback}
  114. */
  115. DataSourceDisplay.defaultVisualizersCallback = function (
  116. scene,
  117. entityCluster,
  118. dataSource
  119. ) {
  120. const entities = dataSource.entities;
  121. return [
  122. new BillboardVisualizer(entityCluster, entities),
  123. new GeometryVisualizer(
  124. scene,
  125. entities,
  126. dataSource._primitives,
  127. dataSource._groundPrimitives
  128. ),
  129. new LabelVisualizer(entityCluster, entities),
  130. new ModelVisualizer(scene, entities),
  131. new Cesium3DTilesetVisualizer(scene, entities),
  132. new PointVisualizer(entityCluster, entities),
  133. new PathVisualizer(scene, entities),
  134. new PolylineVisualizer(
  135. scene,
  136. entities,
  137. dataSource._primitives,
  138. dataSource._groundPrimitives
  139. ),
  140. ];
  141. };
  142. Object.defineProperties(DataSourceDisplay.prototype, {
  143. /**
  144. * Gets the scene associated with this display.
  145. * @memberof DataSourceDisplay.prototype
  146. * @type {Scene}
  147. */
  148. scene: {
  149. get: function () {
  150. return this._scene;
  151. },
  152. },
  153. /**
  154. * Gets the collection of data sources to display.
  155. * @memberof DataSourceDisplay.prototype
  156. * @type {DataSourceCollection}
  157. */
  158. dataSources: {
  159. get: function () {
  160. return this._dataSourceCollection;
  161. },
  162. },
  163. /**
  164. * Gets the default data source instance which can be used to
  165. * manually create and visualize entities not tied to
  166. * a specific data source. This instance is always available
  167. * and does not appear in the list dataSources collection.
  168. * @memberof DataSourceDisplay.prototype
  169. * @type {CustomDataSource}
  170. */
  171. defaultDataSource: {
  172. get: function () {
  173. return this._defaultDataSource;
  174. },
  175. },
  176. /**
  177. * Gets a value indicating whether or not all entities in the data source are ready
  178. * @memberof DataSourceDisplay.prototype
  179. * @type {Boolean}
  180. * @readonly
  181. */
  182. ready: {
  183. get: function () {
  184. return this._ready;
  185. },
  186. },
  187. });
  188. /**
  189. * Returns true if this object was destroyed; otherwise, false.
  190. * <br /><br />
  191. * If this object was destroyed, it should not be used; calling any function other than
  192. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  193. *
  194. * @returns {Boolean} True if this object was destroyed; otherwise, false.
  195. *
  196. * @see DataSourceDisplay#destroy
  197. */
  198. DataSourceDisplay.prototype.isDestroyed = function () {
  199. return false;
  200. };
  201. /**
  202. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  203. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  204. * <br /><br />
  205. * Once an object is destroyed, it should not be used; calling any function other than
  206. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  207. * assign the return value (<code>undefined</code>) to the object as done in the example.
  208. *
  209. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  210. *
  211. *
  212. * @example
  213. * dataSourceDisplay = dataSourceDisplay.destroy();
  214. *
  215. * @see DataSourceDisplay#isDestroyed
  216. */
  217. DataSourceDisplay.prototype.destroy = function () {
  218. this._eventHelper.removeAll();
  219. const dataSourceCollection = this._dataSourceCollection;
  220. for (let i = 0, length = dataSourceCollection.length; i < length; ++i) {
  221. this._onDataSourceRemoved(
  222. this._dataSourceCollection,
  223. dataSourceCollection.get(i)
  224. );
  225. }
  226. this._onDataSourceRemoved(undefined, this._defaultDataSource);
  227. if (defined(this._removeDefaultDataSourceListener)) {
  228. this._removeDefaultDataSourceListener();
  229. this._removeDataSourceCollectionListener();
  230. } else {
  231. this._scene.primitives.remove(this._primitives);
  232. this._scene.groundPrimitives.remove(this._groundPrimitives);
  233. }
  234. return destroyObject(this);
  235. };
  236. /**
  237. * Updates the display to the provided time.
  238. *
  239. * @param {JulianDate} time The simulation time.
  240. * @returns {Boolean} True if all data sources are ready to be displayed, false otherwise.
  241. */
  242. DataSourceDisplay.prototype.update = function (time) {
  243. //>>includeStart('debug', pragmas.debug);
  244. Check.defined("time", time);
  245. //>>includeEnd('debug');
  246. if (!ApproximateTerrainHeights.initialized) {
  247. this._ready = false;
  248. return false;
  249. }
  250. let result = true;
  251. let i;
  252. let x;
  253. let visualizers;
  254. let vLength;
  255. const dataSources = this._dataSourceCollection;
  256. const length = dataSources.length;
  257. for (i = 0; i < length; i++) {
  258. const dataSource = dataSources.get(i);
  259. if (defined(dataSource.update)) {
  260. result = dataSource.update(time) && result;
  261. }
  262. visualizers = dataSource._visualizers;
  263. vLength = visualizers.length;
  264. for (x = 0; x < vLength; x++) {
  265. result = visualizers[x].update(time) && result;
  266. }
  267. }
  268. visualizers = this._defaultDataSource._visualizers;
  269. vLength = visualizers.length;
  270. for (x = 0; x < vLength; x++) {
  271. result = visualizers[x].update(time) && result;
  272. }
  273. this._ready = result;
  274. return result;
  275. };
  276. DataSourceDisplay.prototype._postRender = function () {
  277. // Adds credits for all datasources
  278. const frameState = this._scene.frameState;
  279. const dataSources = this._dataSourceCollection;
  280. const length = dataSources.length;
  281. for (let i = 0; i < length; i++) {
  282. const dataSource = dataSources.get(i);
  283. const credit = dataSource.credit;
  284. if (defined(credit)) {
  285. frameState.creditDisplay.addCredit(credit);
  286. }
  287. // Credits from the resource that the user can't remove
  288. const credits = dataSource._resourceCredits;
  289. if (defined(credits)) {
  290. const creditCount = credits.length;
  291. for (let c = 0; c < creditCount; c++) {
  292. frameState.creditDisplay.addCredit(credits[c]);
  293. }
  294. }
  295. }
  296. };
  297. const getBoundingSphereArrayScratch = [];
  298. const getBoundingSphereBoundingSphereScratch = new BoundingSphere();
  299. /**
  300. * Computes a bounding sphere which encloses the visualization produced for the specified entity.
  301. * The bounding sphere is in the fixed frame of the scene's globe.
  302. *
  303. * @param {Entity} entity The entity whose bounding sphere to compute.
  304. * @param {Boolean} allowPartial If true, pending bounding spheres are ignored and an answer will be returned from the currently available data.
  305. * If false, the the function will halt and return pending if any of the bounding spheres are pending.
  306. * @param {BoundingSphere} result The bounding sphere onto which to store the result.
  307. * @returns {BoundingSphereState} BoundingSphereState.DONE if the result contains the bounding sphere,
  308. * BoundingSphereState.PENDING if the result is still being computed, or
  309. * BoundingSphereState.FAILED if the entity has no visualization in the current scene.
  310. * @private
  311. */
  312. DataSourceDisplay.prototype.getBoundingSphere = function (
  313. entity,
  314. allowPartial,
  315. result
  316. ) {
  317. //>>includeStart('debug', pragmas.debug);
  318. Check.defined("entity", entity);
  319. Check.typeOf.bool("allowPartial", allowPartial);
  320. Check.defined("result", result);
  321. //>>includeEnd('debug');
  322. if (!this._ready) {
  323. return BoundingSphereState.PENDING;
  324. }
  325. let i;
  326. let length;
  327. let dataSource = this._defaultDataSource;
  328. if (!dataSource.entities.contains(entity)) {
  329. dataSource = undefined;
  330. const dataSources = this._dataSourceCollection;
  331. length = dataSources.length;
  332. for (i = 0; i < length; i++) {
  333. const d = dataSources.get(i);
  334. if (d.entities.contains(entity)) {
  335. dataSource = d;
  336. break;
  337. }
  338. }
  339. }
  340. if (!defined(dataSource)) {
  341. return BoundingSphereState.FAILED;
  342. }
  343. const boundingSpheres = getBoundingSphereArrayScratch;
  344. const tmp = getBoundingSphereBoundingSphereScratch;
  345. let count = 0;
  346. let state = BoundingSphereState.DONE;
  347. const visualizers = dataSource._visualizers;
  348. const visualizersLength = visualizers.length;
  349. for (i = 0; i < visualizersLength; i++) {
  350. const visualizer = visualizers[i];
  351. if (defined(visualizer.getBoundingSphere)) {
  352. state = visualizers[i].getBoundingSphere(entity, tmp);
  353. if (!allowPartial && state === BoundingSphereState.PENDING) {
  354. return BoundingSphereState.PENDING;
  355. } else if (state === BoundingSphereState.DONE) {
  356. boundingSpheres[count] = BoundingSphere.clone(
  357. tmp,
  358. boundingSpheres[count]
  359. );
  360. count++;
  361. }
  362. }
  363. }
  364. if (count === 0) {
  365. return BoundingSphereState.FAILED;
  366. }
  367. boundingSpheres.length = count;
  368. BoundingSphere.fromBoundingSpheres(boundingSpheres, result);
  369. return BoundingSphereState.DONE;
  370. };
  371. DataSourceDisplay.prototype._onDataSourceAdded = function (
  372. dataSourceCollection,
  373. dataSource
  374. ) {
  375. const scene = this._scene;
  376. const displayPrimitives = this._primitives;
  377. const displayGroundPrimitives = this._groundPrimitives;
  378. const primitives = displayPrimitives.add(new PrimitiveCollection());
  379. const groundPrimitives = displayGroundPrimitives.add(
  380. new OrderedGroundPrimitiveCollection()
  381. );
  382. dataSource._primitives = primitives;
  383. dataSource._groundPrimitives = groundPrimitives;
  384. const entityCluster = dataSource.clustering;
  385. entityCluster._initialize(scene);
  386. primitives.add(entityCluster);
  387. dataSource._visualizers = this._visualizersCallback(
  388. scene,
  389. entityCluster,
  390. dataSource
  391. );
  392. };
  393. DataSourceDisplay.prototype._onDataSourceRemoved = function (
  394. dataSourceCollection,
  395. dataSource
  396. ) {
  397. const displayPrimitives = this._primitives;
  398. const displayGroundPrimitives = this._groundPrimitives;
  399. const primitives = dataSource._primitives;
  400. const groundPrimitives = dataSource._groundPrimitives;
  401. const entityCluster = dataSource.clustering;
  402. primitives.remove(entityCluster);
  403. const visualizers = dataSource._visualizers;
  404. const length = visualizers.length;
  405. for (let i = 0; i < length; i++) {
  406. visualizers[i].destroy();
  407. }
  408. displayPrimitives.remove(primitives);
  409. displayGroundPrimitives.remove(groundPrimitives);
  410. dataSource._visualizers = undefined;
  411. };
  412. DataSourceDisplay.prototype._onDataSourceMoved = function (
  413. dataSource,
  414. newIndex,
  415. oldIndex
  416. ) {
  417. const displayPrimitives = this._primitives;
  418. const displayGroundPrimitives = this._groundPrimitives;
  419. const primitives = dataSource._primitives;
  420. const groundPrimitives = dataSource._groundPrimitives;
  421. if (newIndex === oldIndex + 1) {
  422. displayPrimitives.raise(primitives);
  423. displayGroundPrimitives.raise(groundPrimitives);
  424. } else if (newIndex === oldIndex - 1) {
  425. displayPrimitives.lower(primitives);
  426. displayGroundPrimitives.lower(groundPrimitives);
  427. } else if (newIndex === 0) {
  428. displayPrimitives.lowerToBottom(primitives);
  429. displayGroundPrimitives.lowerToBottom(groundPrimitives);
  430. displayPrimitives.raise(primitives); // keep defaultDataSource primitives at index 0 since it's not in the collection
  431. displayGroundPrimitives.raise(groundPrimitives);
  432. } else {
  433. displayPrimitives.raiseToTop(primitives);
  434. displayGroundPrimitives.raiseToTop(groundPrimitives);
  435. }
  436. };
  437. /**
  438. * A function which creates an array of visualizers used for visualization.
  439. * @callback DataSourceDisplay.VisualizersCallback
  440. *
  441. * @param {Scene} scene The scene to create visualizers for.
  442. * @param {DataSource} dataSource The data source to create visualizers for.
  443. * @returns {Visualizer[]} An array of visualizers used for visualization.
  444. *
  445. * @example
  446. * function createVisualizers(scene, dataSource) {
  447. * return [new Cesium.BillboardVisualizer(scene, dataSource.entities)];
  448. * }
  449. */
  450. export default DataSourceDisplay;