UNPKG

cesium

Version:

CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.

1,138 lines (1,035 loc) 86.7 kB
import BoundingSphere from '../../Core/BoundingSphere.js'; import Cartesian3 from '../../Core/Cartesian3.js'; import Cartographic from '../../Core/Cartographic.js'; import Clock from '../../Core/Clock.js'; import defaultValue from '../../Core/defaultValue.js'; import defined from '../../Core/defined.js'; import destroyObject from '../../Core/destroyObject.js'; import DeveloperError from '../../Core/DeveloperError.js'; import Event from '../../Core/Event.js'; import EventHelper from '../../Core/EventHelper.js'; import HeadingPitchRange from '../../Core/HeadingPitchRange.js'; import Matrix4 from '../../Core/Matrix4.js'; import ScreenSpaceEventType from '../../Core/ScreenSpaceEventType.js'; import BoundingSphereState from '../../DataSources/BoundingSphereState.js'; import ConstantPositionProperty from '../../DataSources/ConstantPositionProperty.js'; import DataSourceCollection from '../../DataSources/DataSourceCollection.js'; import DataSourceDisplay from '../../DataSources/DataSourceDisplay.js'; import Entity from '../../DataSources/Entity.js'; import EntityView from '../../DataSources/EntityView.js'; import Property from '../../DataSources/Property.js'; import Cesium3DTileset from '../../Scene/Cesium3DTileset.js'; import computeFlyToLocationForRectangle from '../../Scene/computeFlyToLocationForRectangle.js'; import ImageryLayer from '../../Scene/ImageryLayer.js'; import SceneMode from '../../Scene/SceneMode.js'; import TimeDynamicPointCloud from '../../Scene/TimeDynamicPointCloud.js'; import knockout from '../../ThirdParty/knockout.js'; import when from '../../ThirdParty/when.js'; import Animation from '../Animation/Animation.js'; import AnimationViewModel from '../Animation/AnimationViewModel.js'; import BaseLayerPicker from '../BaseLayerPicker/BaseLayerPicker.js'; import createDefaultImageryProviderViewModels from '../BaseLayerPicker/createDefaultImageryProviderViewModels.js'; import createDefaultTerrainProviderViewModels from '../BaseLayerPicker/createDefaultTerrainProviderViewModels.js'; import CesiumWidget from '../CesiumWidget/CesiumWidget.js'; import ClockViewModel from '../ClockViewModel.js'; import FullscreenButton from '../FullscreenButton/FullscreenButton.js'; import Geocoder from '../Geocoder/Geocoder.js'; import getElement from '../getElement.js'; import HomeButton from '../HomeButton/HomeButton.js'; import InfoBox from '../InfoBox/InfoBox.js'; import NavigationHelpButton from '../NavigationHelpButton/NavigationHelpButton.js'; import ProjectionPicker from '../ProjectionPicker/ProjectionPicker.js'; import SceneModePicker from '../SceneModePicker/SceneModePicker.js'; import SelectionIndicator from '../SelectionIndicator/SelectionIndicator.js'; import subscribeAndEvaluate from '../subscribeAndEvaluate.js'; import Timeline from '../Timeline/Timeline.js'; import VRButton from '../VRButton/VRButton.js'; var boundingSphereScratch = new BoundingSphere(); function onTimelineScrubfunction(e) { var clock = e.clock; clock.currentTime = e.timeJulian; clock.shouldAnimate = false; } function pickEntity(viewer, e) { var picked = viewer.scene.pick(e.position); if (defined(picked)) { var id = defaultValue(picked.id, picked.primitive.id); if (id instanceof Entity) { return id; } } // No regular entity picked. Try picking features from imagery layers. if (defined(viewer.scene.globe)) { return pickImageryLayerFeature(viewer, e.position); } } function trackDataSourceClock(timeline, clock, dataSource) { if (defined(dataSource)) { var dataSourceClock = dataSource.clock; if (defined(dataSourceClock)) { dataSourceClock.getValue(clock); if (defined(timeline)) { timeline.updateFromClock(); timeline.zoomTo(dataSourceClock.startTime, dataSourceClock.stopTime); } } } } var cartesian3Scratch = new Cartesian3(); function pickImageryLayerFeature(viewer, windowPosition) { var scene = viewer.scene; var pickRay = scene.camera.getPickRay(windowPosition); var imageryLayerFeaturePromise = scene.imageryLayers.pickImageryLayerFeatures(pickRay, scene); if (!defined(imageryLayerFeaturePromise)) { return; } // Imagery layer feature picking is asynchronous, so put up a message while loading. var loadingMessage = new Entity({ id : 'Loading...', description : 'Loading feature information...' }); when(imageryLayerFeaturePromise, function(features) { // Has this async pick been superseded by a later one? if (viewer.selectedEntity !== loadingMessage) { return; } if (!defined(features) || features.length === 0) { viewer.selectedEntity = createNoFeaturesEntity(); return; } // Select the first feature. var feature = features[0]; var entity = new Entity({ id : feature.name, description : feature.description }); if (defined(feature.position)) { var ecfPosition = viewer.scene.globe.ellipsoid.cartographicToCartesian(feature.position, cartesian3Scratch); entity.position = new ConstantPositionProperty(ecfPosition); } viewer.selectedEntity = entity; }, function() { // Has this async pick been superseded by a later one? if (viewer.selectedEntity !== loadingMessage) { return; } viewer.selectedEntity = createNoFeaturesEntity(); }); return loadingMessage; } function createNoFeaturesEntity() { return new Entity({ id : 'None', description : 'No features found.' }); } function enableVRUI(viewer, enabled) { var geocoder = viewer._geocoder; var homeButton = viewer._homeButton; var sceneModePicker = viewer._sceneModePicker; var projectionPicker = viewer._projectionPicker; var baseLayerPicker = viewer._baseLayerPicker; var animation = viewer._animation; var timeline = viewer._timeline; var fullscreenButton = viewer._fullscreenButton; var infoBox = viewer._infoBox; var selectionIndicator = viewer._selectionIndicator; var visibility = enabled ? 'hidden' : 'visible'; if (defined(geocoder)) { geocoder.container.style.visibility = visibility; } if (defined(homeButton)) { homeButton.container.style.visibility = visibility; } if (defined(sceneModePicker)) { sceneModePicker.container.style.visibility = visibility; } if (defined(projectionPicker)) { projectionPicker.container.style.visibility = visibility; } if (defined(baseLayerPicker)) { baseLayerPicker.container.style.visibility = visibility; } if (defined(animation)) { animation.container.style.visibility = visibility; } if (defined(timeline)) { timeline.container.style.visibility = visibility; } if (defined(fullscreenButton) && fullscreenButton.viewModel.isFullscreenEnabled) { fullscreenButton.container.style.visibility = visibility; } if (defined(infoBox)) { infoBox.container.style.visibility = visibility; } if (defined(selectionIndicator)) { selectionIndicator.container.style.visibility = visibility; } if (viewer._container) { var right = enabled || !defined(fullscreenButton) ? 0 : fullscreenButton.container.clientWidth; viewer._vrButton.container.style.right = right + 'px'; viewer.forceResize(); } } /** * A base widget for building applications. It composites all of the standard Cesium widgets into one reusable package. * The widget can always be extended by using mixins, which add functionality useful for a variety of applications. * * @alias Viewer * @constructor * * @param {Element|String} container The DOM element or ID that will contain the widget. * @param {Object} [options] Object with the following properties: * @param {Boolean} [options.animation=true] If set to false, the Animation widget will not be created. * @param {Boolean} [options.baseLayerPicker=true] If set to false, the BaseLayerPicker widget will not be created. * @param {Boolean} [options.fullscreenButton=true] If set to false, the FullscreenButton widget will not be created. * @param {Boolean} [options.vrButton=false] If set to true, the VRButton widget will be created. * @param {Boolean|GeocoderService[]} [options.geocoder=true] If set to false, the Geocoder widget will not be created. * @param {Boolean} [options.homeButton=true] If set to false, the HomeButton widget will not be created. * @param {Boolean} [options.infoBox=true] If set to false, the InfoBox widget will not be created. * @param {Boolean} [options.sceneModePicker=true] If set to false, the SceneModePicker widget will not be created. * @param {Boolean} [options.selectionIndicator=true] If set to false, the SelectionIndicator widget will not be created. * @param {Boolean} [options.timeline=true] If set to false, the Timeline widget will not be created. * @param {Boolean} [options.navigationHelpButton=true] If set to false, the navigation help button will not be created. * @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. * @param {Boolean} [options.scene3DOnly=false] When <code>true</code>, each geometry instance will only be rendered in 3D to save GPU memory. * @param {Boolean} [options.shouldAnimate=false] <code>true</code> if the clock should attempt to advance simulation time by default, <code>false</code> otherwise. This option takes precedence over setting {@link Viewer#clockViewModel}. * @param {ClockViewModel} [options.clockViewModel=new ClockViewModel(options.clock)] The clock view model to use to control current time. * @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. * @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. * @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. * @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. * @param {ImageryProvider} [options.imageryProvider=createWorldImagery()] The imagery provider to use. This value is only valid if options.baseLayerPicker is set to false. * @param {TerrainProvider} [options.terrainProvider=new EllipsoidTerrainProvider()] The terrain provider to use * @param {SkyBox} [options.skyBox] The skybox used to render the stars. When <code>undefined</code>, the default stars are used. * @param {SkyAtmosphere} [options.skyAtmosphere] Blue sky, and the glow around the Earth's limb. Set to <code>false</code> to turn it off. * @param {Element|String} [options.fullscreenElement=document.body] The element or id to be placed into fullscreen mode when the full screen button is pressed. * @param {Boolean} [options.useDefaultRenderLoop=true] True if this widget should control the render loop, false otherwise. * @param {Number} [options.targetFrameRate] The target frame rate when using the default render loop. * @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. * @param {Boolean} [options.useBrowserRecommendedResolution=true] If true, render at the browser's recommended resolution and ignore <code>window.devicePixelRatio</code>. * @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. * @param {Object} [options.contextOptions] Context and WebGL creation properties corresponding to <code>options</code> passed to {@link Scene}. * @param {SceneMode} [options.sceneMode=SceneMode.SCENE3D] The initial scene mode. * @param {MapProjection} [options.mapProjection=new GeographicProjection()] The map projection to use in 2D and Columbus View modes. * @param {Globe} [options.globe=new Globe(mapProjection.ellipsoid)] The globe to use in the scene. If set to <code>false</code>, no globe will be added. * @param {Boolean} [options.orderIndependentTranslucency=true] If true and the configuration supports it, use order independent translucency. * @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. * @param {Element|String} [options.creditViewport] The DOM element or ID that will contain the credit pop up created by the {@link CreditDisplay}. If not specified, it will appear over the widget itself. * @param {DataSourceCollection} [options.dataSources=new DataSourceCollection()] The collection of data sources visualized by the widget. If this parameter is provided, * the instance is assumed to be owned by the caller and will not be destroyed when the viewer is destroyed. * @param {Number} [options.terrainExaggeration=1.0] A scalar used to exaggerate the terrain. Note that terrain exaggeration will not modify any other primitive as they are positioned relative to the ellipsoid. * @param {Boolean} [options.shadows=false] Determines if shadows are cast by light sources. * @param {ShadowMode} [options.terrainShadows=ShadowMode.RECEIVE_ONLY] Determines if the terrain casts or receives shadows from light sources. * @param {MapMode2D} [options.mapMode2D=MapMode2D.INFINITE_SCROLL] Determines if the 2D map is rotatable or can be scrolled infinitely in the horizontal direction. * @param {Boolean} [options.projectionPicker=false] If set to true, the ProjectionPicker widget will be created. * @param {Boolean} [options.requestRenderMode=false] If true, rendering a frame will only occur when needed as determined by changes within the scene. Enabling reduces the CPU/GPU usage of your application and uses less battery on mobile, but requires using {@link Scene#requestRender} to render a new frame explicitly in this mode. This will be necessary in many cases after making changes to the scene in other parts of the API. See {@link https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/|Improving Performance with Explicit Rendering}. * @param {Number} [options.maximumRenderTimeChange=0.0] If requestRenderMode is true, this value defines the maximum change in simulation time allowed before a render is requested. See {@link https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/|Improving Performance with Explicit Rendering}. * * @exception {DeveloperError} Element with id "container" does not exist in the document. * @exception {DeveloperError} options.selectedImageryProviderViewModel is not available when not using the BaseLayerPicker widget, specify options.imageryProvider instead. * @exception {DeveloperError} options.selectedTerrainProviderViewModel is not available when not using the BaseLayerPicker widget, specify options.terrainProvider instead. * * @see Animation * @see BaseLayerPicker * @see CesiumWidget * @see FullscreenButton * @see HomeButton * @see SceneModePicker * @see Timeline * @see viewerDragDropMixin * * @demo {@link https://sandcastle.cesium.com/index.html?src=Hello%20World.html|Cesium Sandcastle Hello World Demo} * * @example * //Initialize the viewer widget with several custom options and mixins. * var viewer = new Cesium.Viewer('cesiumContainer', { * //Start in Columbus Viewer * sceneMode : Cesium.SceneMode.COLUMBUS_VIEW, * //Use Cesium World Terrain * terrainProvider : Cesium.createWorldTerrain(), * //Hide the base layer picker * baseLayerPicker : false, * //Use OpenStreetMaps * imageryProvider : new Cesium.OpenStreetMapImageryProvider({ * url : 'https://a.tile.openstreetmap.org/' * }), * skyBox : new Cesium.SkyBox({ * sources : { * positiveX : 'stars/TychoSkymapII.t3_08192x04096_80_px.jpg', * negativeX : 'stars/TychoSkymapII.t3_08192x04096_80_mx.jpg', * positiveY : 'stars/TychoSkymapII.t3_08192x04096_80_py.jpg', * negativeY : 'stars/TychoSkymapII.t3_08192x04096_80_my.jpg', * positiveZ : 'stars/TychoSkymapII.t3_08192x04096_80_pz.jpg', * negativeZ : 'stars/TychoSkymapII.t3_08192x04096_80_mz.jpg' * } * }), * // Show Columbus View map with Web Mercator projection * mapProjection : new Cesium.WebMercatorProjection() * }); * * //Add basic drag and drop functionality * viewer.extend(Cesium.viewerDragDropMixin); * * //Show a pop-up alert if we encounter an error when processing a dropped file * viewer.dropError.addEventListener(function(dropHandler, name, error) { * console.log(error); * window.alert(error); * }); */ function Viewer(container, options) { //>>includeStart('debug', pragmas.debug); if (!defined(container)) { throw new DeveloperError('container is required.'); } //>>includeEnd('debug'); container = getElement(container); options = defaultValue(options, defaultValue.EMPTY_OBJECT); var createBaseLayerPicker = (!defined(options.globe) || options.globe !== false) && (!defined(options.baseLayerPicker) || options.baseLayerPicker !== false); //>>includeStart('debug', pragmas.debug); // If not using BaseLayerPicker, selectedImageryProviderViewModel is an invalid option if (!createBaseLayerPicker && defined(options.selectedImageryProviderViewModel)) { throw new DeveloperError('options.selectedImageryProviderViewModel is not available when not using the BaseLayerPicker widget. \ Either specify options.imageryProvider instead or set options.baseLayerPicker to true.'); } // If not using BaseLayerPicker, selectedTerrainProviderViewModel is an invalid option if (!createBaseLayerPicker && defined(options.selectedTerrainProviderViewModel)) { throw new DeveloperError('options.selectedTerrainProviderViewModel is not available when not using the BaseLayerPicker widget. \ Either specify options.terrainProvider instead or set options.baseLayerPicker to true.'); } //>>includeEnd('debug') var that = this; var viewerContainer = document.createElement('div'); viewerContainer.className = 'cesium-viewer'; container.appendChild(viewerContainer); // Cesium widget container var cesiumWidgetContainer = document.createElement('div'); cesiumWidgetContainer.className = 'cesium-viewer-cesiumWidgetContainer'; viewerContainer.appendChild(cesiumWidgetContainer); // Bottom container var bottomContainer = document.createElement('div'); bottomContainer.className = 'cesium-viewer-bottom'; viewerContainer.appendChild(bottomContainer); var scene3DOnly = defaultValue(options.scene3DOnly, false); var clock; var clockViewModel; var destroyClockViewModel = false; if (defined(options.clockViewModel)) { clockViewModel = options.clockViewModel; clock = clockViewModel.clock; } else { clock = new Clock(); clockViewModel = new ClockViewModel(clock); destroyClockViewModel = true; } if (defined(options.shouldAnimate)) { clock.shouldAnimate = options.shouldAnimate; } // Cesium widget var cesiumWidget = new CesiumWidget(cesiumWidgetContainer, { imageryProvider: createBaseLayerPicker || defined(options.imageryProvider) ? false : undefined, clock : clock, skyBox : options.skyBox, skyAtmosphere : options.skyAtmosphere, sceneMode : options.sceneMode, mapProjection : options.mapProjection, globe : options.globe, orderIndependentTranslucency : options.orderIndependentTranslucency, contextOptions : options.contextOptions, useDefaultRenderLoop : options.useDefaultRenderLoop, targetFrameRate : options.targetFrameRate, showRenderLoopErrors : options.showRenderLoopErrors, useBrowserRecommendedResolution : options.useBrowserRecommendedResolution, creditContainer : defined(options.creditContainer) ? options.creditContainer : bottomContainer, creditViewport : options.creditViewport, scene3DOnly : scene3DOnly, terrainExaggeration : options.terrainExaggeration, shadows : options.shadows, terrainShadows : options.terrainShadows, mapMode2D : options.mapMode2D, requestRenderMode : options.requestRenderMode, maximumRenderTimeChange : options.maximumRenderTimeChange }); var dataSourceCollection = options.dataSources; var destroyDataSourceCollection = false; if (!defined(dataSourceCollection)) { dataSourceCollection = new DataSourceCollection(); destroyDataSourceCollection = true; } var scene = cesiumWidget.scene; var dataSourceDisplay = new DataSourceDisplay({ scene : scene, dataSourceCollection : dataSourceCollection }); var eventHelper = new EventHelper(); eventHelper.add(clock.onTick, Viewer.prototype._onTick, this); eventHelper.add(scene.morphStart, Viewer.prototype._clearTrackedObject, this); // Selection Indicator var selectionIndicator; if (!defined(options.selectionIndicator) || options.selectionIndicator !== false) { var selectionIndicatorContainer = document.createElement('div'); selectionIndicatorContainer.className = 'cesium-viewer-selectionIndicatorContainer'; viewerContainer.appendChild(selectionIndicatorContainer); selectionIndicator = new SelectionIndicator(selectionIndicatorContainer, scene); } // Info Box var infoBox; if (!defined(options.infoBox) || options.infoBox !== false) { var infoBoxContainer = document.createElement('div'); infoBoxContainer.className = 'cesium-viewer-infoBoxContainer'; viewerContainer.appendChild(infoBoxContainer); infoBox = new InfoBox(infoBoxContainer); var infoBoxViewModel = infoBox.viewModel; eventHelper.add(infoBoxViewModel.cameraClicked, Viewer.prototype._onInfoBoxCameraClicked, this); eventHelper.add(infoBoxViewModel.closeClicked, Viewer.prototype._onInfoBoxClockClicked, this); } // Main Toolbar var toolbar = document.createElement('div'); toolbar.className = 'cesium-viewer-toolbar'; viewerContainer.appendChild(toolbar); // Geocoder var geocoder; if (!defined(options.geocoder) || options.geocoder !== false) { var geocoderContainer = document.createElement('div'); geocoderContainer.className = 'cesium-viewer-geocoderContainer'; toolbar.appendChild(geocoderContainer); var geocoderService; if (defined(options.geocoder) && typeof options.geocoder !== 'boolean') { geocoderService = Array.isArray(options.geocoder) ? options.geocoder : [options.geocoder]; } geocoder = new Geocoder({ container : geocoderContainer, geocoderServices : geocoderService, scene : scene }); // Subscribe to search so that we can clear the trackedEntity when it is clicked. eventHelper.add(geocoder.viewModel.search.beforeExecute, Viewer.prototype._clearObjects, this); } // HomeButton var homeButton; if (!defined(options.homeButton) || options.homeButton !== false) { homeButton = new HomeButton(toolbar, scene); if (defined(geocoder)) { eventHelper.add(homeButton.viewModel.command.afterExecute, function() { var viewModel = geocoder.viewModel; viewModel.searchText = ''; if (viewModel.isSearchInProgress) { viewModel.search(); } }); } // Subscribe to the home button beforeExecute event so that we can clear the trackedEntity. eventHelper.add(homeButton.viewModel.command.beforeExecute, Viewer.prototype._clearTrackedObject, this); } // SceneModePicker // By default, we silently disable the scene mode picker if scene3DOnly is true, // but if sceneModePicker is explicitly set to true, throw an error. //>>includeStart('debug', pragmas.debug); if ((options.sceneModePicker === true) && scene3DOnly) { throw new DeveloperError('options.sceneModePicker is not available when options.scene3DOnly is set to true.'); } //>>includeEnd('debug'); var sceneModePicker; if (!scene3DOnly && (!defined(options.sceneModePicker) || options.sceneModePicker !== false)) { sceneModePicker = new SceneModePicker(toolbar, scene); } var projectionPicker; if (options.projectionPicker) { projectionPicker = new ProjectionPicker(toolbar, scene); } // BaseLayerPicker var baseLayerPicker; var baseLayerPickerDropDown; if (createBaseLayerPicker) { var imageryProviderViewModels = defaultValue(options.imageryProviderViewModels, createDefaultImageryProviderViewModels()); var terrainProviderViewModels = defaultValue(options.terrainProviderViewModels, createDefaultTerrainProviderViewModels()); baseLayerPicker = new BaseLayerPicker(toolbar, { globe : scene.globe, imageryProviderViewModels : imageryProviderViewModels, selectedImageryProviderViewModel : options.selectedImageryProviderViewModel, terrainProviderViewModels : terrainProviderViewModels, selectedTerrainProviderViewModel : options.selectedTerrainProviderViewModel }); //Grab the dropdown for resize code. var elements = toolbar.getElementsByClassName('cesium-baseLayerPicker-dropDown'); baseLayerPickerDropDown = elements[0]; } // These need to be set after the BaseLayerPicker is created in order to take effect if (defined(options.imageryProvider) && options.imageryProvider !== false) { if (createBaseLayerPicker) { baseLayerPicker.viewModel.selectedImagery = undefined; } scene.imageryLayers.removeAll(); scene.imageryLayers.addImageryProvider(options.imageryProvider); } if (defined(options.terrainProvider)) { if (createBaseLayerPicker) { baseLayerPicker.viewModel.selectedTerrain = undefined; } scene.terrainProvider = options.terrainProvider; } // Navigation Help Button var navigationHelpButton; if (!defined(options.navigationHelpButton) || options.navigationHelpButton !== false) { var showNavHelp = true; try { //window.localStorage is null if disabled in Firefox or undefined in browsers with implementation if (defined(window.localStorage)) { var hasSeenNavHelp = window.localStorage.getItem('cesium-hasSeenNavHelp'); if (defined(hasSeenNavHelp) && Boolean(hasSeenNavHelp)) { showNavHelp = false; } else { window.localStorage.setItem('cesium-hasSeenNavHelp', 'true'); } } } catch (e) { //Accessing window.localStorage throws if disabled in Chrome //window.localStorage.setItem throws if in Safari private browsing mode or in any browser if we are over quota. } navigationHelpButton = new NavigationHelpButton({ container : toolbar, instructionsInitiallyVisible : defaultValue(options.navigationInstructionsInitiallyVisible, showNavHelp) }); } // Animation var animation; if (!defined(options.animation) || options.animation !== false) { var animationContainer = document.createElement('div'); animationContainer.className = 'cesium-viewer-animationContainer'; viewerContainer.appendChild(animationContainer); animation = new Animation(animationContainer, new AnimationViewModel(clockViewModel)); } // Timeline var timeline; if (!defined(options.timeline) || options.timeline !== false) { var timelineContainer = document.createElement('div'); timelineContainer.className = 'cesium-viewer-timelineContainer'; viewerContainer.appendChild(timelineContainer); timeline = new Timeline(timelineContainer, clock); timeline.addEventListener('settime', onTimelineScrubfunction, false); timeline.zoomTo(clock.startTime, clock.stopTime); } // Fullscreen var fullscreenButton; var fullscreenSubscription; var fullscreenContainer; if (!defined(options.fullscreenButton) || options.fullscreenButton !== false) { fullscreenContainer = document.createElement('div'); fullscreenContainer.className = 'cesium-viewer-fullscreenContainer'; viewerContainer.appendChild(fullscreenContainer); fullscreenButton = new FullscreenButton(fullscreenContainer, options.fullscreenElement); //Subscribe to fullscreenButton.viewModel.isFullscreenEnabled so //that we can hide/show the button as well as size the timeline. fullscreenSubscription = subscribeAndEvaluate(fullscreenButton.viewModel, 'isFullscreenEnabled', function(isFullscreenEnabled) { fullscreenContainer.style.display = isFullscreenEnabled ? 'block' : 'none'; if (defined(timeline)) { timeline.container.style.right = fullscreenContainer.clientWidth + 'px'; timeline.resize(); } }); } // VR var vrButton; var vrSubscription; var vrModeSubscription; if (options.vrButton) { var vrContainer = document.createElement('div'); vrContainer.className = 'cesium-viewer-vrContainer'; viewerContainer.appendChild(vrContainer); vrButton = new VRButton(vrContainer, scene, options.fullScreenElement); vrSubscription = subscribeAndEvaluate(vrButton.viewModel, 'isVREnabled', function(isVREnabled) { vrContainer.style.display = isVREnabled ? 'block' : 'none'; if (defined(fullscreenButton)) { vrContainer.style.right = fullscreenContainer.clientWidth + 'px'; } if (defined(timeline)) { timeline.container.style.right = vrContainer.clientWidth + 'px'; timeline.resize(); } }); vrModeSubscription = subscribeAndEvaluate(vrButton.viewModel, 'isVRMode', function(isVRMode) { enableVRUI(that, isVRMode); }); } //Assign all properties to this instance. No "this" assignments should //take place above this line. this._baseLayerPickerDropDown = baseLayerPickerDropDown; this._fullscreenSubscription = fullscreenSubscription; this._vrSubscription = vrSubscription; this._vrModeSubscription = vrModeSubscription; this._dataSourceChangedListeners = {}; this._automaticallyTrackDataSourceClocks = defaultValue(options.automaticallyTrackDataSourceClocks, true); this._container = container; this._bottomContainer = bottomContainer; this._element = viewerContainer; this._cesiumWidget = cesiumWidget; this._selectionIndicator = selectionIndicator; this._infoBox = infoBox; this._dataSourceCollection = dataSourceCollection; this._destroyDataSourceCollection = destroyDataSourceCollection; this._dataSourceDisplay = dataSourceDisplay; this._clockViewModel = clockViewModel; this._destroyClockViewModel = destroyClockViewModel; this._toolbar = toolbar; this._homeButton = homeButton; this._sceneModePicker = sceneModePicker; this._projectionPicker = projectionPicker; this._baseLayerPicker = baseLayerPicker; this._navigationHelpButton = navigationHelpButton; this._animation = animation; this._timeline = timeline; this._fullscreenButton = fullscreenButton; this._vrButton = vrButton; this._geocoder = geocoder; this._eventHelper = eventHelper; this._lastWidth = 0; this._lastHeight = 0; this._allowDataSourcesToSuspendAnimation = true; this._entityView = undefined; this._enableInfoOrSelection = defined(infoBox) || defined(selectionIndicator); this._clockTrackedDataSource = undefined; this._trackedEntity = undefined; this._needTrackedEntityUpdate = false; this._selectedEntity = undefined; this._clockTrackedDataSource = undefined; this._zoomIsFlight = false; this._zoomTarget = undefined; this._zoomPromise = undefined; this._zoomOptions = undefined; this._selectedEntityChanged = new Event(); this._trackedEntityChanged = new Event(); knockout.track(this, ['_trackedEntity', '_selectedEntity', '_clockTrackedDataSource']); //Listen to data source events in order to track clock changes. eventHelper.add(dataSourceCollection.dataSourceAdded, Viewer.prototype._onDataSourceAdded, this); eventHelper.add(dataSourceCollection.dataSourceRemoved, Viewer.prototype._onDataSourceRemoved, this); // Prior to each render, check if anything needs to be resized. eventHelper.add(scene.postUpdate, Viewer.prototype.resize, this); eventHelper.add(scene.postRender, Viewer.prototype._postRender, this); // We need to subscribe to the data sources and collections so that we can clear the // tracked object when it is removed from the scene. // Subscribe to current data sources var dataSourceLength = dataSourceCollection.length; for (var i = 0; i < dataSourceLength; i++) { this._dataSourceAdded(dataSourceCollection, dataSourceCollection.get(i)); } this._dataSourceAdded(undefined, dataSourceDisplay.defaultDataSource); // Hook up events so that we can subscribe to future sources. eventHelper.add(dataSourceCollection.dataSourceAdded, Viewer.prototype._dataSourceAdded, this); eventHelper.add(dataSourceCollection.dataSourceRemoved, Viewer.prototype._dataSourceRemoved, this); // Subscribe to left clicks and zoom to the picked object. function pickAndTrackObject(e) { var entity = pickEntity(that, e); if (defined(entity)) { //Only track the entity if it has a valid position at the current time. if (Property.getValueOrUndefined(entity.position, that.clock.currentTime)) { that.trackedEntity = entity; } else { that.zoomTo(entity); } } else if (defined(that.trackedEntity)) { that.trackedEntity = undefined; } } function pickAndSelectObject(e) { that.selectedEntity = pickEntity(that, e); } cesiumWidget.screenSpaceEventHandler.setInputAction(pickAndSelectObject, ScreenSpaceEventType.LEFT_CLICK); cesiumWidget.screenSpaceEventHandler.setInputAction(pickAndTrackObject, ScreenSpaceEventType.LEFT_DOUBLE_CLICK); } Object.defineProperties(Viewer.prototype, { /** * Gets the parent container. * @memberof Viewer.prototype * @type {Element} * @readonly */ container : { get : function() { return this._container; } }, /** * Gets the DOM element for the area at the bottom of the window containing the * {@link CreditDisplay} and potentially other things. * @memberof Viewer.prototype * @type {Element} * @readonly */ bottomContainer : { get : function() { return this._bottomContainer; } }, /** * Gets the CesiumWidget. * @memberof Viewer.prototype * @type {CesiumWidget} * @readonly */ cesiumWidget : { get : function() { return this._cesiumWidget; } }, /** * Gets the selection indicator. * @memberof Viewer.prototype * @type {SelectionIndicator} * @readonly */ selectionIndicator : { get : function() { return this._selectionIndicator; } }, /** * Gets the info box. * @memberof Viewer.prototype * @type {InfoBox} * @readonly */ infoBox : { get : function() { return this._infoBox; } }, /** * Gets the Geocoder. * @memberof Viewer.prototype * @type {Geocoder} * @readonly */ geocoder : { get : function() { return this._geocoder; } }, /** * Gets the HomeButton. * @memberof Viewer.prototype * @type {HomeButton} * @readonly */ homeButton : { get : function() { return this._homeButton; } }, /** * Gets the SceneModePicker. * @memberof Viewer.prototype * @type {SceneModePicker} * @readonly */ sceneModePicker : { get : function() { return this._sceneModePicker; } }, /** * Gets the ProjectionPicker. * @memberof Viewer.prototype * @type {ProjectionPicker} * @readonly */ projectionPicker : { get : function() { return this._projectionPicker; } }, /** * Gets the BaseLayerPicker. * @memberof Viewer.prototype * @type {BaseLayerPicker} * @readonly */ baseLayerPicker : { get : function() { return this._baseLayerPicker; } }, /** * Gets the NavigationHelpButton. * @memberof Viewer.prototype * @type {NavigationHelpButton} * @readonly */ navigationHelpButton : { get : function() { return this._navigationHelpButton; } }, /** * Gets the Animation widget. * @memberof Viewer.prototype * @type {Animation} * @readonly */ animation : { get : function() { return this._animation; } }, /** * Gets the Timeline widget. * @memberof Viewer.prototype * @type {Timeline} * @readonly */ timeline : { get : function() { return this._timeline; } }, /** * Gets the FullscreenButton. * @memberof Viewer.prototype * @type {FullscreenButton} * @readonly */ fullscreenButton : { get : function() { return this._fullscreenButton; } }, /** * Gets the VRButton. * @memberof Viewer.prototype * @type {VRButton} * @readonly */ vrButton : { get : function() { return this._vrButton; } }, /** * Gets the display used for {@link DataSource} visualization. * @memberof Viewer.prototype * @type {DataSourceDisplay} * @readonly */ dataSourceDisplay : { get : function() { return this._dataSourceDisplay; } }, /** * Gets the collection of entities not tied to a particular data source. * This is a shortcut to [dataSourceDisplay.defaultDataSource.entities]{@link Viewer#dataSourceDisplay}. * @memberof Viewer.prototype * @type {EntityCollection} * @readonly */ entities : { get : function() { return this._dataSourceDisplay.defaultDataSource.entities; } }, /** * Gets the set of {@link DataSource} instances to be visualized. * @memberof Viewer.prototype * @type {DataSourceCollection} * @readonly */ dataSources : { get : function() { return this._dataSourceCollection; } }, /** * Gets the canvas. * @memberof Viewer.prototype * @type {Canvas} * @readonly */ canvas : { get : function() { return this._cesiumWidget.canvas; } }, /** * Gets the scene. * @memberof Viewer.prototype * @type {Scene} * @readonly */ scene : { get : function() { return this._cesiumWidget.scene; } }, /** * Determines if shadows are cast by light sources. * @memberof Viewer.prototype * @type {Boolean} */ shadows : { get : function() { return this.scene.shadowMap.enabled; }, set : function(value) { this.scene.shadowMap.enabled = value; } }, /** * Determines if the terrain casts or shadows from light sources. * @memberof Viewer.prototype * @type {ShadowMode} */ terrainShadows : { get : function() { return this.scene.globe.shadows; }, set : function(value) { this.scene.globe.shadows = value; } }, /** * Get the scene's shadow map * @memberof Viewer.prototype * @type {ShadowMap} * @readonly */ shadowMap : { get : function() { return this.scene.shadowMap; } }, /** * Gets the collection of image layers that will be rendered on the globe. * @memberof Viewer.prototype * * @type {ImageryLayerCollection} * @readonly */ imageryLayers : { get : function() { return this.scene.imageryLayers; } }, /** * The terrain provider providing surface geometry for the globe. * @memberof Viewer.prototype * * @type {TerrainProvider} */ terrainProvider : { get : function() { return this.scene.terrainProvider; }, set : function(terrainProvider) { this.scene.terrainProvider = terrainProvider; } }, /** * Gets the camera. * @memberof Viewer.prototype * * @type {Camera} * @readonly */ camera : { get : function() { return this.scene.camera; } }, /** * Gets the post-process stages. * @memberof Viewer.prototype * * @type {PostProcessStageCollection} * @readonly */ postProcessStages : { get : function() { return this.scene.postProcessStages; } }, /** * Gets the clock. * @memberof Viewer.prototype * @type {Clock} * @readonly */ clock : { get : function() { return this._clockViewModel.clock; } }, /** * Gets the clock view model. * @memberof Viewer.prototype * @type {ClockViewModel} * @readonly */ clockViewModel : { get : function() { return this._clockViewModel; } }, /** * Gets the screen space event handler. * @memberof Viewer.prototype * @type {ScreenSpaceEventHandler} * @readonly */ screenSpaceEventHandler : { get : function() { return this._cesiumWidget.screenSpaceEventHandler; } }, /** * Gets or sets the target frame rate of the widget when <code>useDefaultRenderLoop</code> * is true. If undefined, the browser's {@link requestAnimationFrame} implementation * determines the frame rate. If defined, this value must be greater than 0. A value higher * than the underlying requestAnimationFrame implementation will have no effect. * @memberof Viewer.prototype * * @type {Number} */ targetFrameRate : { get : function() { return this._cesiumWidget.targetFrameRate; }, set : function(value) { this._cesiumWidget.targetFrameRate = value; } }, /** * Gets or sets whether or not this widget should control the render loop. * If set to true the widget will use {@link requestAnimationFrame} to * perform rendering and resizing of the widget, as well as drive the * simulation clock. If set to false, you must manually call the * <code>resize</code>, <code>render</code> methods * as part of a custom render loop. If an error occurs during rendering, {@link Scene}'s * <code>renderError</code> event will be raised and this property * will be set to false. It must be set back to true to continue rendering * after the error. * @memberof Viewer.prototype * * @type {Boolean} */ useDefaultRenderLoop : { get : function() { return this._cesiumWidget.useDefaultRenderLoop; }, set : function(value) { this._cesiumWidget.useDefaultRenderLoop = value; } }, /** * Gets or sets a scaling factor for rendering resolution. Values less than 1.0 can improve * performance on less powerful devices while values greater than 1.0 will render at a higher * resolution and then scale down, resulting in improved visual fidelity. * For example, if the widget is laid out at a size of 640x480, setting this value to 0.5 * will cause the scene to be rendered at 320x240 and then scaled up while setting * it to 2.0 will cause the scene to be rendered at 1280x960 and then scaled down. * @memberof Viewer.prototype * * @type {Number} * @default 1.0 */ resolutionScale : { get : functi