DataSourceDisplay.js 18 KB

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