/Source/Widgets/Viewer/Viewer.js

https://github.com/snarayan/cesium · JavaScript · 967 lines · 564 code · 99 blank · 304 comment · 75 complexity · 6c0935cfd8a1273d843460fc9ef6c95e MD5 · raw file

  1. /*global define*/
  2. define([
  3. '../../Core/clone',
  4. '../../Core/defaultValue',
  5. '../../Core/defined',
  6. '../../Core/defineProperties',
  7. '../../Core/destroyObject',
  8. '../../Core/DeveloperError',
  9. '../../Core/EventHelper',
  10. '../../DynamicScene/DataSourceCollection',
  11. '../../DynamicScene/DataSourceDisplay',
  12. '../../ThirdParty/knockout',
  13. '../Animation/Animation',
  14. '../Animation/AnimationViewModel',
  15. '../BaseLayerPicker/BaseLayerPicker',
  16. '../BaseLayerPicker/createDefaultImageryProviderViewModels',
  17. '../BaseLayerPicker/createDefaultTerrainProviderViewModels',
  18. '../CesiumWidget/CesiumWidget',
  19. '../ClockViewModel',
  20. '../FullscreenButton/FullscreenButton',
  21. '../Geocoder/Geocoder',
  22. '../getElement',
  23. '../HomeButton/HomeButton',
  24. '../InfoBox/InfoBox',
  25. '../NavigationHelpButton/NavigationHelpButton',
  26. '../SceneModePicker/SceneModePicker',
  27. '../SelectionIndicator/SelectionIndicator',
  28. '../subscribeAndEvaluate',
  29. '../Timeline/Timeline'
  30. ], function(
  31. clone,
  32. defaultValue,
  33. defined,
  34. defineProperties,
  35. destroyObject,
  36. DeveloperError,
  37. EventHelper,
  38. DataSourceCollection,
  39. DataSourceDisplay,
  40. knockout,
  41. Animation,
  42. AnimationViewModel,
  43. BaseLayerPicker,
  44. createDefaultImageryProviderViewModels,
  45. createDefaultTerrainProviderViewModels,
  46. CesiumWidget,
  47. ClockViewModel,
  48. FullscreenButton,
  49. Geocoder,
  50. getElement,
  51. HomeButton,
  52. InfoBox,
  53. NavigationHelpButton,
  54. SceneModePicker,
  55. SelectionIndicator,
  56. subscribeAndEvaluate,
  57. Timeline) {
  58. "use strict";
  59. function onTimelineScrubfunction(e) {
  60. var clock = e.clock;
  61. clock.currentTime = e.timeJulian;
  62. clock.shouldAnimate = false;
  63. }
  64. /**
  65. * A base widget for building applications. It composites all of the standard Cesium widgets into one reusable package.
  66. * The widget can always be extended by using mixins, which add functionality useful for a variety of applications.
  67. *
  68. * @alias Viewer
  69. * @constructor
  70. *
  71. * @param {Element|String} container The DOM element or ID that will contain the widget.
  72. * @param {Object} [options] Object with the following properties:
  73. * @param {Boolean} [options.animation=true] If set to false, the Animation widget will not be created.
  74. * @param {Boolean} [options.baseLayerPicker=true] If set to false, the BaseLayerPicker widget will not be created.
  75. * @param {Boolean} [options.fullscreenButton=true] If set to false, the FullscreenButton widget will not be created.
  76. * @param {Boolean} [options.geocoder=true] If set to false, the Geocoder widget will not be created.
  77. * @param {Boolean} [options.homeButton=true] If set to false, the HomeButton widget will not be created.
  78. * @param {Boolean} [options.infoBox=true] If set to false, the InfoBox widget will not be created.
  79. * @param {Boolean} [options.sceneModePicker=true] If set to false, the SceneModePicker widget will not be created.
  80. * @param {Boolean} [options.selectionIndicator=true] If set to false, the SelectionIndicator widget will not be created.
  81. * @param {Boolean} [options.timeline=true] If set to false, the Timeline widget will not be created.
  82. * @param {Boolean} [options.navigationHelpButton=true] If set to the false, the navigation help button will not be created.
  83. * @param {Boolean} [options.navigationInstructionsInitiallyVisible=true] True if the navigation instructions should initially be visible, or false if the should not be shown until the user explicitly clicks the button.
  84. * @param {ProviderViewModel} [options.selectedImageryProviderViewModel] The view model for the current base imagery layer, if not supplied the first available base layer is used. This value is only valid if options.baseLayerPicker is set to true.
  85. * @param {ProviderViewModel[]} [options.imageryProviderViewModels=createDefaultImageryProviderViewModels()] The array of ProviderViewModels to be selectable from the BaseLayerPicker. This value is only valid if options.baseLayerPicker is set to true.
  86. * @param {ProviderViewModel} [options.selectedTerrainProviderViewModel] The view model for the current base terrain layer, if not supplied the first available base layer is used. This value is only valid if options.baseLayerPicker is set to true.
  87. * @param {ProviderViewModel[]} [options.terrainProviderViewModels=createDefaultTerrainProviderViewModels()] The array of ProviderViewModels to be selectable from the BaseLayerPicker. This value is only valid if options.baseLayerPicker is set to true.
  88. * @param {ImageryProvider} [options.imageryProvider=new BingMapsImageryProvider()] The imagery provider to use. This value is only valid if options.baseLayerPicker is set to false.
  89. * @param {TerrainProvider} [options.terrainProvider=new EllipsoidTerrainProvider()] The terrain provider to use
  90. * @param {SkyBox} [options.skyBox] The skybox used to render the stars. When <code>undefined</code>, the default stars are used.
  91. * @param {Element} [options.fullscreenElement=document.body] The element to make full screen when the full screen button is pressed.
  92. * @param {Boolean} [options.useDefaultRenderLoop=true] True if this widget should control the render loop, false otherwise.
  93. * @param {Number} [options.targetFrameRate] The target frame rate when using the default render loop.
  94. * @param {Boolean} [options.showRenderLoopErrors=true] If true, this widget will automatically display an HTML panel to the user containing the error, if a render loop error occurs.
  95. * @param {Boolean} [options.automaticallyTrackDataSourceClocks=true] If true, this widget will automatically track the clock settings of newly added DataSources, updating if the DataSource's clock changes. Set this to false if you want to configure the clock independently.
  96. * @param {Object} [options.contextOptions] Context and WebGL creation properties corresponding to <code>options</code> passed to {@link Scene}.
  97. * @param {SceneMode} [options.sceneMode=SceneMode.SCENE3D] The initial scene mode.
  98. * @param {MapProjection} [options.mapProjection=new GeographicProjection()] The map projection to use in 2D and Columbus View modes.
  99. * @param {Element|String} [options.creditContainer] The DOM element or ID that will contain the {@link CreditDisplay}. If not specified, the credits are added to the bottom of the widget itself.
  100. * @param {DataSourceCollection} [options.dataSources=new DataSourceCollection()] The collection of data sources visualized by the widget. If this parameter is provided,
  101. the instance is assumed to be owned by the caller and will not be destroyed when the viewer is destroyed.
  102. *
  103. * @exception {DeveloperError} Element with id "container" does not exist in the document.
  104. * @exception {DeveloperError} options.imageryProvider is not available when using the BaseLayerPicker widget, specify options.selectedImageryProviderViewModel instead.
  105. * @exception {DeveloperError} options.terrainProvider is not available when using the BaseLayerPicker widget, specify options.selectedTerrainProviderViewModel instead.
  106. * @exception {DeveloperError} options.selectedImageryProviderViewModel is not available when not using the BaseLayerPicker widget, specify options.imageryProvider instead.
  107. * @exception {DeveloperError} options.selectedTerrainProviderViewModel is not available when not using the BaseLayerPicker widget, specify options.terrainProvider instead.
  108. *
  109. * @see Animation
  110. * @see BaseLayerPicker
  111. * @see CesiumWidget
  112. * @see FullscreenButton
  113. * @see HomeButton
  114. * @see SceneModePicker
  115. * @see Timeline
  116. * @see viewerDragDropMixin
  117. * @see viewerDynamicObjectMixin
  118. *
  119. * @example
  120. * //Initialize the viewer widget with several custom options and mixins.
  121. * var viewer = new Cesium.Viewer('cesiumContainer', {
  122. * //Start in Columbus Viewer
  123. * sceneMode : Cesium.SceneMode.COLUMBUS_VIEW,
  124. * //Use standard Cesium terrain
  125. * terrainProvider : new Cesium.CesiumTerrainProvider({
  126. * url : '//cesiumjs.org/smallterrain',
  127. * credit : 'Terrain data courtesy Analytical Graphics, Inc.'
  128. * }),
  129. * //Hide the base layer picker
  130. * baseLayerPicker : false,
  131. * //Use OpenStreetMaps
  132. * imageryProvider : new Cesium.OpenStreetMapImageryProvider({
  133. * url : '//a.tile.openstreetmap.org/'
  134. * }),
  135. * // Use high-res stars downloaded from https://github.com/AnalyticalGraphicsInc/cesium-assets
  136. * skyBox : new Cesium.SkyBox({
  137. * sources : {
  138. * positiveX : 'stars/TychoSkymapII.t3_08192x04096_80_px.jpg',
  139. * negativeX : 'stars/TychoSkymapII.t3_08192x04096_80_mx.jpg',
  140. * positiveY : 'stars/TychoSkymapII.t3_08192x04096_80_py.jpg',
  141. * negativeY : 'stars/TychoSkymapII.t3_08192x04096_80_my.jpg',
  142. * positiveZ : 'stars/TychoSkymapII.t3_08192x04096_80_pz.jpg',
  143. * negativeZ : 'stars/TychoSkymapII.t3_08192x04096_80_mz.jpg'
  144. * }
  145. * }),
  146. * // Show Columbus View map with Web Mercator projection
  147. * sceneMode : Cesium.SceneMode.COLUMBUS_VIEW,
  148. * mapProjection : new Cesium.WebMercatorProjection()
  149. * });
  150. *
  151. * //Add basic drag and drop functionality
  152. * viewer.extend(Cesium.viewerDragDropMixin);
  153. *
  154. * //Allow users to zoom and follow objects loaded from CZML by clicking on it.
  155. * viewer.extend(Cesium.viewerDynamicObjectMixin);
  156. *
  157. * //Show a pop-up alert if we encounter an error when processing a dropped file
  158. * viewer.dropError.addEventListener(function(dropHandler, name, error) {
  159. * console.log(error);
  160. * window.alert(error);
  161. * });
  162. */
  163. var Viewer = function(container, options) {
  164. //>>includeStart('debug', pragmas.debug);
  165. if (!defined(container)) {
  166. throw new DeveloperError('container is required.');
  167. }
  168. //>>includeEnd('debug');
  169. container = getElement(container);
  170. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  171. var createBaseLayerPicker = !defined(options.baseLayerPicker) || options.baseLayerPicker !== false;
  172. //>>includeStart('debug', pragmas.debug);
  173. // If using BaseLayerPicker, imageryProvider is an invalid option
  174. if (createBaseLayerPicker && defined(options.imageryProvider)) {
  175. throw new DeveloperError('options.imageryProvider is not available when using the BaseLayerPicker widget. \
  176. Either specify options.selectedImageryProviderViewModel instead or set options.baseLayerPicker to false.');
  177. }
  178. // If not using BaseLayerPicker, selectedImageryProviderViewModel is an invalid option
  179. if (!createBaseLayerPicker && defined(options.selectedImageryProviderViewModel)) {
  180. throw new DeveloperError('options.selectedImageryProviderViewModel is not available when not using the BaseLayerPicker widget. \
  181. Either specify options.imageryProvider instead or set options.baseLayerPicker to true.');
  182. }
  183. // If using BaseLayerPicker, terrainProvider is an invalid option
  184. if (createBaseLayerPicker && defined(options.terrainProvider)) {
  185. throw new DeveloperError('options.terrainProvider is not available when using the BaseLayerPicker widget. \
  186. Either specify options.selectedTerrainProviderViewModel instead or set options.baseLayerPicker to false.');
  187. }
  188. // If not using BaseLayerPicker, selectedTerrainProviderViewModel is an invalid option
  189. if (!createBaseLayerPicker && defined(options.selectedTerrainProviderViewModel)) {
  190. throw new DeveloperError('options.selectedTerrainProviderViewModel is not available when not using the BaseLayerPicker widget. \
  191. Either specify options.terrainProvider instead or set options.baseLayerPicker to true.');
  192. }
  193. //>>includeEnd('debug')
  194. var viewerContainer = document.createElement('div');
  195. viewerContainer.className = 'cesium-viewer';
  196. container.appendChild(viewerContainer);
  197. // Cesium widget container
  198. var cesiumWidgetContainer = document.createElement('div');
  199. cesiumWidgetContainer.className = 'cesium-viewer-cesiumWidgetContainer';
  200. viewerContainer.appendChild(cesiumWidgetContainer);
  201. // Bottom container
  202. var bottomContainer = document.createElement('div');
  203. bottomContainer.className = 'cesium-viewer-bottom';
  204. viewerContainer.appendChild(bottomContainer);
  205. // Cesium widget
  206. var cesiumWidget = new CesiumWidget(cesiumWidgetContainer, {
  207. terrainProvider : options.terrainProvider,
  208. imageryProvider : createBaseLayerPicker ? false : options.imageryProvider,
  209. skyBox : options.skyBox,
  210. sceneMode : options.sceneMode,
  211. mapProjection : options.mapProjection,
  212. contextOptions : options.contextOptions,
  213. useDefaultRenderLoop : options.useDefaultRenderLoop,
  214. targetFrameRate : options.targetFrameRate,
  215. showRenderLoopErrors : options.showRenderLoopErrors,
  216. creditContainer : defined(options.creditContainer) ? options.creditContainer : bottomContainer
  217. });
  218. var dataSourceCollection = options.dataSources;
  219. var destroyDataSourceCollection = false;
  220. if (!defined(dataSourceCollection)) {
  221. dataSourceCollection = new DataSourceCollection();
  222. destroyDataSourceCollection = true;
  223. }
  224. var dataSourceDisplay = new DataSourceDisplay(cesiumWidget.scene, dataSourceCollection);
  225. var clock = cesiumWidget.clock;
  226. var clockViewModel = new ClockViewModel(clock);
  227. var eventHelper = new EventHelper();
  228. var that = this;
  229. eventHelper.add(clock.onTick, function(clock) {
  230. var isUpdated = dataSourceDisplay.update(clock.currentTime);
  231. if (that._allowDataSourcesToSuspendAnimation) {
  232. clockViewModel.canAnimate = isUpdated;
  233. }
  234. });
  235. // Selection Indicator
  236. var selectionIndicator;
  237. if (!defined(options.selectionIndicator) || options.selectionIndicator !== false) {
  238. var selectionIndicatorContainer = document.createElement('div');
  239. selectionIndicatorContainer.className = 'cesium-viewer-selectionIndicatorContainer';
  240. viewerContainer.appendChild(selectionIndicatorContainer);
  241. selectionIndicator = new SelectionIndicator(selectionIndicatorContainer, cesiumWidget.scene);
  242. }
  243. // Info Box
  244. var infoBox;
  245. if (!defined(options.infoBox) || options.infoBox !== false) {
  246. var infoBoxContainer = document.createElement('div');
  247. infoBoxContainer.className = 'cesium-viewer-infoBoxContainer';
  248. viewerContainer.appendChild(infoBoxContainer);
  249. infoBox = new InfoBox(infoBoxContainer);
  250. }
  251. // Main Toolbar
  252. var toolbar = document.createElement('div');
  253. toolbar.className = 'cesium-viewer-toolbar';
  254. viewerContainer.appendChild(toolbar);
  255. // Geocoder
  256. var geocoder;
  257. if (!defined(options.geocoder) || options.geocoder !== false) {
  258. var geocoderContainer = document.createElement('div');
  259. geocoderContainer.className = 'cesium-viewer-geocoderContainer';
  260. toolbar.appendChild(geocoderContainer);
  261. geocoder = new Geocoder({
  262. container : geocoderContainer,
  263. scene : cesiumWidget.scene,
  264. ellipsoid : cesiumWidget.scene.globe.ellipsoid
  265. });
  266. }
  267. // HomeButton
  268. var homeButton;
  269. if (!defined(options.homeButton) || options.homeButton !== false) {
  270. homeButton = new HomeButton(toolbar, cesiumWidget.scene, cesiumWidget.scene.globe.ellipsoid);
  271. if (defined(geocoder)) {
  272. eventHelper.add(homeButton.viewModel.command.afterExecute, function() {
  273. var viewModel = geocoder.viewModel;
  274. viewModel.searchText = '';
  275. if (viewModel.isSearchInProgress) {
  276. viewModel.search();
  277. }
  278. });
  279. }
  280. }
  281. // SceneModePicker
  282. var sceneModePicker;
  283. if (!defined(options.sceneModePicker) || options.sceneModePicker !== false) {
  284. sceneModePicker = new SceneModePicker(toolbar, cesiumWidget.scene);
  285. }
  286. // BaseLayerPicker
  287. var baseLayerPicker;
  288. if (createBaseLayerPicker) {
  289. var imageryProviderViewModels = defaultValue(options.imageryProviderViewModels, createDefaultImageryProviderViewModels());
  290. var terrainProviderViewModels = defaultValue(options.terrainProviderViewModels, createDefaultTerrainProviderViewModels());
  291. baseLayerPicker = new BaseLayerPicker(toolbar, {
  292. globe : cesiumWidget.scene.globe,
  293. imageryProviderViewModels : imageryProviderViewModels,
  294. selectedImageryProviderViewModel : options.selectedImageryProviderViewModel,
  295. terrainProviderViewModels : terrainProviderViewModels,
  296. selectedTerrainProviderViewModel : options.selectedTerrainProviderViewModel
  297. });
  298. //Grab the dropdown for resize code.
  299. var elements = toolbar.getElementsByClassName('cesium-baseLayerPicker-dropDown');
  300. this._baseLayerPickerDropDown = elements[0];
  301. }
  302. // Navigation Help Button
  303. var navigationHelpButton;
  304. if (!defined(options.navigationHelpButton) || options.navigationHelpButton !== false) {
  305. navigationHelpButton = new NavigationHelpButton({
  306. container : toolbar,
  307. instructionsInitiallyVisible : defaultValue(options.navigationInstructionsInitiallyVisible, true)
  308. });
  309. }
  310. // Animation
  311. var animation;
  312. if (!defined(options.animation) || options.animation !== false) {
  313. var animationContainer = document.createElement('div');
  314. animationContainer.className = 'cesium-viewer-animationContainer';
  315. viewerContainer.appendChild(animationContainer);
  316. animation = new Animation(animationContainer, new AnimationViewModel(clockViewModel));
  317. }
  318. // Timeline
  319. var timeline;
  320. if (!defined(options.timeline) || options.timeline !== false) {
  321. var timelineContainer = document.createElement('div');
  322. timelineContainer.className = 'cesium-viewer-timelineContainer';
  323. viewerContainer.appendChild(timelineContainer);
  324. timeline = new Timeline(timelineContainer, clock);
  325. timeline.addEventListener('settime', onTimelineScrubfunction, false);
  326. timeline.zoomTo(clock.startTime, clock.stopTime);
  327. }
  328. // Fullscreen
  329. var fullscreenButton;
  330. if (!defined(options.fullscreenButton) || options.fullscreenButton !== false) {
  331. var fullscreenContainer = document.createElement('div');
  332. fullscreenContainer.className = 'cesium-viewer-fullscreenContainer';
  333. viewerContainer.appendChild(fullscreenContainer);
  334. fullscreenButton = new FullscreenButton(fullscreenContainer, options.fullscreenElement);
  335. //Subscribe to fullscreenButton.viewModel.isFullscreenEnabled so
  336. //that we can hide/show the button as well as size the timeline.
  337. this._fullscreenSubscription = subscribeAndEvaluate(fullscreenButton.viewModel, 'isFullscreenEnabled', function(isFullscreenEnabled) {
  338. fullscreenContainer.style.display = isFullscreenEnabled ? 'block' : 'none';
  339. if (defined(timeline)) {
  340. timeline.container.style.right = fullscreenContainer.clientWidth + 'px';
  341. timeline.resize();
  342. }
  343. });
  344. } else if (defined(timeline)) {
  345. timeline.container.style.right = 0;
  346. }
  347. /**
  348. * Gets or sets the data source to track with the viewer's clock.
  349. * @type {DataSource}
  350. */
  351. this.clockTrackedDataSource = undefined;
  352. knockout.track(this, ['clockTrackedDataSource']);
  353. this._dataSourceChangedListeners = {};
  354. this._knockoutSubscriptions = [];
  355. var automaticallyTrackDataSourceClocks = defaultValue(options.automaticallyTrackDataSourceClocks, true);
  356. function trackDataSourceClock(dataSource) {
  357. if (defined(dataSource)) {
  358. var dataSourceClock = dataSource.clock;
  359. if (defined(dataSourceClock)) {
  360. dataSourceClock.getValue(clock);
  361. if (defined(timeline)) {
  362. timeline.updateFromClock();
  363. timeline.zoomTo(dataSourceClock.startTime, dataSourceClock.stopTime);
  364. }
  365. }
  366. }
  367. }
  368. this._knockoutSubscriptions.push(subscribeAndEvaluate(this, 'clockTrackedDataSource', function(value) {
  369. trackDataSourceClock(value);
  370. }));
  371. var onDataSourceChanged = function(dataSource) {
  372. if (that.clockTrackedDataSource === dataSource) {
  373. trackDataSourceClock(dataSource);
  374. }
  375. };
  376. var onDataSourceAdded = function(dataSourceCollection, dataSource) {
  377. if (automaticallyTrackDataSourceClocks) {
  378. that.clockTrackedDataSource = dataSource;
  379. }
  380. var id = dataSource.dynamicObjects.id;
  381. var removalFunc = eventHelper.add(dataSource.changedEvent, onDataSourceChanged);
  382. that._dataSourceChangedListeners[id] = removalFunc;
  383. };
  384. var onDataSourceRemoved = function(dataSourceCollection, dataSource) {
  385. var resetClock = (that.clockTrackedDataSource === dataSource);
  386. var id = dataSource.dynamicObjects.id;
  387. that._dataSourceChangedListeners[id]();
  388. that._dataSourceChangedListeners[id] = undefined;
  389. if (resetClock) {
  390. var numDataSources = dataSourceCollection.length;
  391. if (automaticallyTrackDataSourceClocks && numDataSources > 0) {
  392. that.clockTrackedDataSource = dataSourceCollection.get(numDataSources - 1);
  393. } else {
  394. that.clockTrackedDataSource = undefined;
  395. }
  396. }
  397. };
  398. eventHelper.add(dataSourceCollection.dataSourceAdded, onDataSourceAdded);
  399. eventHelper.add(dataSourceCollection.dataSourceRemoved, onDataSourceRemoved);
  400. this._container = container;
  401. this._bottomContainer = bottomContainer;
  402. this._element = viewerContainer;
  403. this._cesiumWidget = cesiumWidget;
  404. this._selectionIndicator = selectionIndicator;
  405. this._infoBox = infoBox;
  406. this._dataSourceCollection = dataSourceCollection;
  407. this._destroyDataSourceCollection = destroyDataSourceCollection;
  408. this._dataSourceDisplay = dataSourceDisplay;
  409. this._clockViewModel = clockViewModel;
  410. this._toolbar = toolbar;
  411. this._homeButton = homeButton;
  412. this._sceneModePicker = sceneModePicker;
  413. this._baseLayerPicker = baseLayerPicker;
  414. this._animation = animation;
  415. this._timeline = timeline;
  416. this._fullscreenButton = fullscreenButton;
  417. this._geocoder = geocoder;
  418. this._eventHelper = eventHelper;
  419. this._lastWidth = 0;
  420. this._lastHeight = 0;
  421. this._allowDataSourcesToSuspendAnimation = true;
  422. // Prior to each render, check if anything needs to be resized.
  423. cesiumWidget.scene.preRender.addEventListener(function(scene, time) {
  424. resizeViewer(that);
  425. });
  426. };
  427. defineProperties(Viewer.prototype, {
  428. /**
  429. * Gets the parent container.
  430. * @memberof Viewer.prototype
  431. * @type {Element}
  432. */
  433. container : {
  434. get : function() {
  435. return this._container;
  436. }
  437. },
  438. /**
  439. * Gets the DOM element for the area at the bottom of the window containing the
  440. * {@link CreditDisplay} and potentially other things.
  441. * @memberof Viewer.prototype
  442. * @type {Element}
  443. */
  444. bottomContainer : {
  445. get : function() {
  446. return this._bottomContainer;
  447. }
  448. },
  449. /**
  450. * Gets the CesiumWidget.
  451. * @memberof Viewer.prototype
  452. * @type {CesiumWidget}
  453. */
  454. cesiumWidget : {
  455. get : function() {
  456. return this._cesiumWidget;
  457. }
  458. },
  459. /**
  460. * Gets the selection indicator.
  461. * @memberof Viewer.prototype
  462. * @type {SelectionIndicator}
  463. */
  464. selectionIndicator : {
  465. get : function() {
  466. return this._selectionIndicator;
  467. }
  468. },
  469. /**
  470. * Gets the info box.
  471. * @memberof Viewer.prototype
  472. * @type {InfoBox}
  473. */
  474. infoBox : {
  475. get : function() {
  476. return this._infoBox;
  477. }
  478. },
  479. /**
  480. * Gets the Geocoder.
  481. * @memberof Viewer.prototype
  482. * @type {Geocoder}
  483. */
  484. geocoder : {
  485. get : function() {
  486. return this._geocoder;
  487. }
  488. },
  489. /**
  490. * Gets the HomeButton.
  491. * @memberof Viewer.prototype
  492. * @type {HomeButton}
  493. */
  494. homeButton : {
  495. get : function() {
  496. return this._homeButton;
  497. }
  498. },
  499. /**
  500. * Gets the SceneModePicker.
  501. * @memberof Viewer.prototype
  502. * @type {SceneModePicker}
  503. */
  504. sceneModePicker : {
  505. get : function() {
  506. return this._sceneModePicker;
  507. }
  508. },
  509. /**
  510. * Gets the BaseLayerPicker.
  511. * @memberof Viewer.prototype
  512. * @type {BaseLayerPicker}
  513. */
  514. baseLayerPicker : {
  515. get : function() {
  516. return this._baseLayerPicker;
  517. }
  518. },
  519. /**
  520. * Gets the Animation widget.
  521. * @memberof Viewer.prototype
  522. * @type {Animation}
  523. */
  524. animation : {
  525. get : function() {
  526. return this._animation;
  527. }
  528. },
  529. /**
  530. * Gets the Timeline widget.
  531. * @memberof Viewer.prototype
  532. * @type {Timeline}
  533. */
  534. timeline : {
  535. get : function() {
  536. return this._timeline;
  537. }
  538. },
  539. /**
  540. * Gets the FullscreenButton.
  541. * @memberof Viewer.prototype
  542. * @type {FullscreenButton}
  543. */
  544. fullscreenButton : {
  545. get : function() {
  546. return this._fullscreenButton;
  547. }
  548. },
  549. /**
  550. * Gets the display used for {@link DataSource} visualization.
  551. * @memberof Viewer.prototype
  552. * @type {DataSourceDisplay}
  553. */
  554. dataSourceDisplay : {
  555. get : function() {
  556. return this._dataSourceDisplay;
  557. }
  558. },
  559. /**
  560. * Gets the set of {@link DataSource} instances to be visualized.
  561. * @memberof Viewer.prototype
  562. * @type {DataSourceCollection}
  563. */
  564. dataSources : {
  565. get : function() {
  566. return this._dataSourceCollection;
  567. }
  568. },
  569. /**
  570. * Gets the canvas.
  571. * @memberof Viewer.prototype
  572. * @type {Canvas}
  573. */
  574. canvas : {
  575. get : function() {
  576. return this._cesiumWidget.canvas;
  577. }
  578. },
  579. /**
  580. * Gets the Cesium logo element.
  581. * @memberof Viewer.prototype
  582. * @type {Element}
  583. */
  584. cesiumLogo : {
  585. get : function() {
  586. return this._cesiumWidget.cesiumLogo;
  587. }
  588. },
  589. /**
  590. * Gets the scene.
  591. * @memberof Viewer.prototype
  592. * @type {Scene}
  593. */
  594. scene : {
  595. get : function() {
  596. return this._cesiumWidget.scene;
  597. }
  598. },
  599. /**
  600. * Gets the clock.
  601. * @memberof Viewer.prototype
  602. * @type {Clock}
  603. */
  604. clock : {
  605. get : function() {
  606. return this._cesiumWidget.clock;
  607. }
  608. },
  609. /**
  610. * Gets the screen space event handler.
  611. * @memberof Viewer.prototype
  612. * @type {ScreenSpaceEventHandler}
  613. */
  614. screenSpaceEventHandler : {
  615. get : function() {
  616. return this._cesiumWidget.screenSpaceEventHandler;
  617. }
  618. },
  619. /**
  620. * Gets or sets the target frame rate of the widget when <code>useDefaultRenderLoop</code>
  621. * is true. If undefined, the browser's {@link requestAnimationFrame} implementation
  622. * determines the frame rate. This value must be greater than 0 and a value higher than
  623. * the underlying requestAnimationFrame implementatin will have no effect.
  624. * @memberof Viewer.prototype
  625. *
  626. * @type {Number}
  627. */
  628. targetFrameRate : {
  629. get : function() {
  630. return this._cesiumWidget.targetFrameRate;
  631. },
  632. set : function(value) {
  633. this._cesiumWidget.targetFrameRate = value;
  634. }
  635. },
  636. /**
  637. * Gets or sets whether or not this widget should control the render loop.
  638. * If set to true the widget will use {@link requestAnimationFrame} to
  639. * perform rendering and resizing of the widget, as well as drive the
  640. * simulation clock. If set to false, you must manually call the
  641. * <code>resize</code>, <code>render</code> methods
  642. * as part of a custom render loop. If an error occurs during rendering, {@link Scene}'s
  643. * <code>renderError</code> event will be raised and this property
  644. * will be set to false. It must be set back to true to continue rendering
  645. * after the error.
  646. * @memberof Viewer.prototype
  647. *
  648. * @type {Boolean}
  649. */
  650. useDefaultRenderLoop : {
  651. get : function() {
  652. return this._cesiumWidget.useDefaultRenderLoop;
  653. },
  654. set : function(value) {
  655. this._cesiumWidget.useDefaultRenderLoop = value;
  656. }
  657. },
  658. /**
  659. * Gets or sets a scaling factor for rendering resolution. Values less than 1.0 can improve
  660. * performance on less powerful devices while values greater than 1.0 will render at a higher
  661. * resolution and then scale down, resulting in improved visual fidelity.
  662. * For example, if the widget is laid out at a size of 640x480, setting this value to 0.5
  663. * will cause the scene to be rendered at 320x240 and then scaled up while setting
  664. * it to 2.0 will cause the scene to be rendered at 1280x960 and then scaled down.
  665. * @memberof Viewer.prototype
  666. *
  667. * @type {Number}
  668. * @default 1.0
  669. */
  670. resolutionScale : {
  671. get : function() {
  672. return this._cesiumWidget.resolutionScale;
  673. },
  674. set : function(value) {
  675. this._cesiumWidget.resolutionScale = value;
  676. }
  677. },
  678. /**
  679. * Gets or sets whether or not data sources can temporarily pause
  680. * animation in order to avoid showing an incomplete picture to the user.
  681. * For example, if asynchronous primitives are being processed in the
  682. * background, the clock will not advance until the geometry is ready.
  683. *
  684. * @memberof Viewer.prototype
  685. *
  686. * @type {Boolean}
  687. */
  688. allowDataSourcesToSuspendAnimation : {
  689. get : function() {
  690. return this._allowDataSourcesToSuspendAnimation;
  691. },
  692. set : function(value) {
  693. this._allowDataSourcesToSuspendAnimation = value;
  694. }
  695. }
  696. });
  697. /**
  698. * Extends the base viewer functionality with the provided mixin.
  699. * A mixin may add additional properties, functions, or other behavior
  700. * to the provided viewer instance.
  701. *
  702. * @param {Function} mixin The Viewer mixin to add to this instance.
  703. * @param {Object} options The options object to be passed to the mixin function.
  704. *
  705. * @see viewerDragDropMixin
  706. * @see viewerDynamicObjectMixin
  707. */
  708. Viewer.prototype.extend = function(mixin, options) {
  709. //>>includeStart('debug', pragmas.debug);
  710. if (!defined(mixin)) {
  711. throw new DeveloperError('mixin is required.');
  712. }
  713. //>>includeEnd('debug')
  714. mixin(this, options);
  715. };
  716. /**
  717. * Resizes the widget to match the container size.
  718. * This function is called automatically as needed unless
  719. * <code>useDefaultRenderLoop</code> is set to false.
  720. */
  721. Viewer.prototype.resize = function() {
  722. var cesiumWidget = this._cesiumWidget;
  723. cesiumWidget.resize();
  724. resizeViewer(this);
  725. };
  726. /**
  727. * This forces the widget to re-think its layout, including
  728. * widget sizes and credit placement.
  729. */
  730. Viewer.prototype.forceResize = function() {
  731. this._lastWidth = 0;
  732. this.resize();
  733. };
  734. /**
  735. * Renders the scene. This function is called automatically
  736. * unless <code>useDefaultRenderLoop</code> is set to false;
  737. */
  738. Viewer.prototype.render = function() {
  739. this._cesiumWidget.render();
  740. };
  741. /**
  742. * @returns {Boolean} true if the object has been destroyed, false otherwise.
  743. */
  744. Viewer.prototype.isDestroyed = function() {
  745. return false;
  746. };
  747. /**
  748. * Destroys the widget. Should be called if permanently
  749. * removing the widget from layout.
  750. */
  751. Viewer.prototype.destroy = function() {
  752. var i;
  753. var numSubscriptions = this._knockoutSubscriptions.length;
  754. for (i = 0; i < numSubscriptions; i++) {
  755. this._knockoutSubscriptions[i].dispose();
  756. }
  757. this._container.removeChild(this._element);
  758. this._element.removeChild(this._toolbar);
  759. this._eventHelper.removeAll();
  760. if (defined(this._geocoder)) {
  761. this._geocoder = this._geocoder.destroy();
  762. }
  763. if (defined(this._homeButton)) {
  764. this._homeButton = this._homeButton.destroy();
  765. }
  766. if (defined(this._sceneModePicker)) {
  767. this._sceneModePicker = this._sceneModePicker.destroy();
  768. }
  769. if (defined(this._baseLayerPicker)) {
  770. this._baseLayerPicker = this._baseLayerPicker.destroy();
  771. }
  772. if (defined(this._animation)) {
  773. this._element.removeChild(this._animation.container);
  774. this._animation = this._animation.destroy();
  775. }
  776. if (defined(this._timeline)) {
  777. this._timeline.removeEventListener('settime', onTimelineScrubfunction, false);
  778. this._element.removeChild(this._timeline.container);
  779. this._timeline = this._timeline.destroy();
  780. }
  781. if (defined(this._fullscreenButton)) {
  782. this._fullscreenSubscription.dispose();
  783. this._element.removeChild(this._fullscreenButton.container);
  784. this._fullscreenButton = this._fullscreenButton.destroy();
  785. }
  786. if (defined(this._infoBox)) {
  787. this._element.removeChild(this._infoBox.container);
  788. this._infoBox = this._infoBox.destroy();
  789. }
  790. if (defined(this._selectionIndicator)) {
  791. this._element.removeChild(this._selectionIndicator.container);
  792. this._selectionIndicator = this._selectionIndicator.destroy();
  793. }
  794. this._clockViewModel = this._clockViewModel.destroy();
  795. this._dataSourceDisplay = this._dataSourceDisplay.destroy();
  796. this._cesiumWidget = this._cesiumWidget.destroy();
  797. if (this._destroyDataSourceCollection) {
  798. this._dataSourceCollection = this._dataSourceCollection.destroy();
  799. }
  800. return destroyObject(this);
  801. };
  802. function resizeViewer(viewer) {
  803. var container = viewer._container;
  804. var width = container.clientWidth;
  805. var height = container.clientHeight;
  806. var animationExists = defined(viewer._animation);
  807. var timelineExists = defined(viewer._timeline);
  808. if (width === viewer._lastWidth && height === viewer._lastHeight) {
  809. return;
  810. }
  811. var panelMaxHeight = height - 125;
  812. var baseLayerPickerDropDown = viewer._baseLayerPickerDropDown;
  813. if (defined(baseLayerPickerDropDown)) {
  814. baseLayerPickerDropDown.style.maxHeight = panelMaxHeight + 'px';
  815. }
  816. if (defined(viewer._infoBox)) {
  817. viewer._infoBox.viewModel.maxHeight = panelMaxHeight;
  818. }
  819. var timeline = viewer._timeline;
  820. var animationContainer;
  821. var animationWidth = 0;
  822. var creditLeft = 0;
  823. var creditBottom = 0;
  824. if (animationExists && window.getComputedStyle(viewer._animation.container).visibility !== 'hidden') {
  825. var lastWidth = viewer._lastWidth;
  826. animationContainer = viewer._animation.container;
  827. if (width > 900) {
  828. animationWidth = 169;
  829. if (lastWidth <= 900) {
  830. animationContainer.style.width = '169px';
  831. animationContainer.style.height = '112px';
  832. viewer._animation.resize();
  833. }
  834. } else if (width >= 600) {
  835. animationWidth = 136;
  836. if (lastWidth < 600 || lastWidth > 900) {
  837. animationContainer.style.width = '136px';
  838. animationContainer.style.height = '90px';
  839. viewer._animation.resize();
  840. }
  841. } else {
  842. animationWidth = 106;
  843. if (lastWidth > 600 || lastWidth === 0) {
  844. animationContainer.style.width = '106px';
  845. animationContainer.style.height = '70px';
  846. viewer._animation.resize();
  847. }
  848. }
  849. creditLeft = animationWidth + 5;
  850. }
  851. if (timelineExists && window.getComputedStyle(viewer._timeline.container).visibility !== 'hidden') {
  852. var fullscreenButton = viewer._fullscreenButton;
  853. var timelineContainer = timeline.container;
  854. var timelineStyle = timelineContainer.style;
  855. creditBottom = timelineContainer.clientHeight + 3;
  856. timelineStyle.left = animationWidth + 'px';
  857. if (defined(fullscreenButton)) {
  858. timelineStyle.right = fullscreenButton.container.clientWidth + 'px';
  859. }
  860. timeline.resize();
  861. }
  862. viewer._bottomContainer.style.left = creditLeft + 'px';
  863. viewer._bottomContainer.style.bottom = creditBottom + 'px';
  864. viewer._lastWidth = width;
  865. viewer._lastHeight = height;
  866. }
  867. return Viewer;
  868. });