CesiumWidget.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  1. import buildModuleUrl from '../../Core/buildModuleUrl.js';
  2. import Cartesian3 from '../../Core/Cartesian3.js';
  3. import Clock from '../../Core/Clock.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 DeveloperError from '../../Core/DeveloperError.js';
  9. import Ellipsoid from '../../Core/Ellipsoid.js';
  10. import FeatureDetection from '../../Core/FeatureDetection.js';
  11. import formatError from '../../Core/formatError.js';
  12. import requestAnimationFrame from '../../Core/requestAnimationFrame.js';
  13. import ScreenSpaceEventHandler from '../../Core/ScreenSpaceEventHandler.js';
  14. import createWorldImagery from '../../Scene/createWorldImagery.js';
  15. import Globe from '../../Scene/Globe.js';
  16. import Moon from '../../Scene/Moon.js';
  17. import Scene from '../../Scene/Scene.js';
  18. import SceneMode from '../../Scene/SceneMode.js';
  19. import ShadowMode from '../../Scene/ShadowMode.js';
  20. import SkyAtmosphere from '../../Scene/SkyAtmosphere.js';
  21. import SkyBox from '../../Scene/SkyBox.js';
  22. import Sun from '../../Scene/Sun.js';
  23. import getElement from '../getElement.js';
  24. function getDefaultSkyBoxUrl(suffix) {
  25. return buildModuleUrl('Assets/Textures/SkyBox/tycho2t3_80_' + suffix + '.jpg');
  26. }
  27. function startRenderLoop(widget) {
  28. widget._renderLoopRunning = true;
  29. var lastFrameTime = 0;
  30. function render(frameTime) {
  31. if (widget.isDestroyed()) {
  32. return;
  33. }
  34. if (widget._useDefaultRenderLoop) {
  35. try {
  36. var targetFrameRate = widget._targetFrameRate;
  37. if (!defined(targetFrameRate)) {
  38. widget.resize();
  39. widget.render();
  40. requestAnimationFrame(render);
  41. } else {
  42. var interval = 1000.0 / targetFrameRate;
  43. var delta = frameTime - lastFrameTime;
  44. if (delta > interval) {
  45. widget.resize();
  46. widget.render();
  47. lastFrameTime = frameTime - (delta % interval);
  48. }
  49. requestAnimationFrame(render);
  50. }
  51. } catch (error) {
  52. widget._useDefaultRenderLoop = false;
  53. widget._renderLoopRunning = false;
  54. if (widget._showRenderLoopErrors) {
  55. var title = 'An error occurred while rendering. Rendering has stopped.';
  56. widget.showErrorPanel(title, undefined, error);
  57. }
  58. }
  59. } else {
  60. widget._renderLoopRunning = false;
  61. }
  62. }
  63. requestAnimationFrame(render);
  64. }
  65. function configurePixelRatio(widget) {
  66. var pixelRatio = widget._useBrowserRecommendedResolution ? 1.0 : window.devicePixelRatio;
  67. pixelRatio *= widget._resolutionScale;
  68. if (defined(widget._scene)) {
  69. widget._scene.pixelRatio = pixelRatio;
  70. }
  71. return pixelRatio;
  72. }
  73. function configureCanvasSize(widget) {
  74. var canvas = widget._canvas;
  75. var width = canvas.clientWidth;
  76. var height = canvas.clientHeight;
  77. var pixelRatio = configurePixelRatio(widget);
  78. widget._canvasClientWidth = width;
  79. widget._canvasClientHeight = height;
  80. width *= pixelRatio;
  81. height *= pixelRatio;
  82. canvas.width = width;
  83. canvas.height = height;
  84. widget._canRender = width !== 0 && height !== 0;
  85. widget._lastDevicePixelRatio = window.devicePixelRatio;
  86. }
  87. function configureCameraFrustum(widget) {
  88. var canvas = widget._canvas;
  89. var width = canvas.width;
  90. var height = canvas.height;
  91. if (width !== 0 && height !== 0) {
  92. var frustum = widget._scene.camera.frustum;
  93. if (defined(frustum.aspectRatio)) {
  94. frustum.aspectRatio = width / height;
  95. } else {
  96. frustum.top = frustum.right * (height / width);
  97. frustum.bottom = -frustum.top;
  98. }
  99. }
  100. }
  101. /**
  102. * A widget containing a Cesium scene.
  103. *
  104. * @alias CesiumWidget
  105. * @constructor
  106. *
  107. * @param {Element|String} container The DOM element or ID that will contain the widget.
  108. * @param {Object} [options] Object with the following properties:
  109. * @param {Clock} [options.clock=new Clock()] The clock to use to control current time.
  110. * @param {ImageryProvider} [options.imageryProvider=createWorldImagery()] The imagery provider to serve as the base layer. If set to <code>false</code>, no imagery provider will be added.
  111. * @param {TerrainProvider} [options.terrainProvider=new EllipsoidTerrainProvider] The terrain provider.
  112. * @param {SkyBox} [options.skyBox] The skybox used to render the stars. When <code>undefined</code>, the default stars are used. If set to <code>false</code>, no skyBox, Sun, or Moon will be added.
  113. * @param {SkyAtmosphere} [options.skyAtmosphere] Blue sky, and the glow around the Earth's limb. Set to <code>false</code> to turn it off.
  114. * @param {SceneMode} [options.sceneMode=SceneMode.SCENE3D] The initial scene mode.
  115. * @param {Boolean} [options.scene3DOnly=false] When <code>true</code>, each geometry instance will only be rendered in 3D to save GPU memory.
  116. * @param {Boolean} [options.orderIndependentTranslucency=true] If true and the configuration supports it, use order independent translucency.
  117. * @param {MapProjection} [options.mapProjection=new GeographicProjection()] The map projection to use in 2D and Columbus View modes.
  118. * @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.
  119. * @param {Boolean} [options.useDefaultRenderLoop=true] True if this widget should control the render loop, false otherwise.
  120. * @param {Boolean} [options.useBrowserRecommendedResolution=false] If true, render at the browser's recommended resolution and ignore <code>window.devicePixelRatio</code>.
  121. * @param {Number} [options.targetFrameRate] The target frame rate when using the default render loop.
  122. * @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.
  123. * @param {Object} [options.contextOptions] Context and WebGL creation properties corresponding to <code>options</code> passed to {@link Scene}.
  124. * @param {Element|String} [options.creditContainer] The DOM element or ID that will contain the {@link CreditDisplay}. If not specified, the credits are added
  125. * to the bottom of the widget itself.
  126. * @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.
  127. * @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.
  128. * @param {Boolean} [options.shadows=false] Determines if shadows are cast by the sun.
  129. * @param {ShadowMode} [options.terrainShadows=ShadowMode.RECEIVE_ONLY] Determines if the terrain casts or receives shadows from the sun.
  130. * @param {MapMode2D} [options.mapMode2D=MapMode2D.INFINITE_SCROLL] Determines if the 2D map is rotatable or can be scrolled infinitely in the horizontal direction.
  131. * @param {Boolean} [options.requestRenderMode=false] If true, rendering a frame will only occur when needed as determined by changes within the scene. Enabling improves performance of the application, 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}.
  132. * @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}.
  133. *
  134. * @exception {DeveloperError} Element with id "container" does not exist in the document.
  135. *
  136. * @demo {@link https://sandcastle.cesium.com/index.html?src=Cesium%20Widget.html|Cesium Sandcastle Cesium Widget Demo}
  137. *
  138. * @example
  139. * // For each example, include a link to CesiumWidget.css stylesheet in HTML head,
  140. * // and in the body, include: <div id="cesiumContainer"></div>
  141. *
  142. * //Widget with no terrain and default Bing Maps imagery provider.
  143. * var widget = new Cesium.CesiumWidget('cesiumContainer');
  144. *
  145. * //Widget with ion imagery and Cesium World Terrain.
  146. * var widget = new Cesium.CesiumWidget('cesiumContainer', {
  147. * imageryProvider : Cesium.createWorldImagery(),
  148. * terrainProvider : Cesium.createWorldTerrain(),
  149. * // Use high-res stars downloaded from https://github.com/AnalyticalGraphicsInc/cesium-assets
  150. * skyBox : new Cesium.SkyBox({
  151. * sources : {
  152. * positiveX : 'stars/TychoSkymapII.t3_08192x04096_80_px.jpg',
  153. * negativeX : 'stars/TychoSkymapII.t3_08192x04096_80_mx.jpg',
  154. * positiveY : 'stars/TychoSkymapII.t3_08192x04096_80_py.jpg',
  155. * negativeY : 'stars/TychoSkymapII.t3_08192x04096_80_my.jpg',
  156. * positiveZ : 'stars/TychoSkymapII.t3_08192x04096_80_pz.jpg',
  157. * negativeZ : 'stars/TychoSkymapII.t3_08192x04096_80_mz.jpg'
  158. * }
  159. * }),
  160. * // Show Columbus View map with Web Mercator projection
  161. * sceneMode : Cesium.SceneMode.COLUMBUS_VIEW,
  162. * mapProjection : new Cesium.WebMercatorProjection()
  163. * });
  164. */
  165. function CesiumWidget(container, options) {
  166. //>>includeStart('debug', pragmas.debug);
  167. if (!defined(container)) {
  168. throw new DeveloperError('container is required.');
  169. }
  170. //>>includeEnd('debug');
  171. container = getElement(container);
  172. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  173. //Configure the widget DOM elements
  174. var element = document.createElement('div');
  175. element.className = 'cesium-widget';
  176. container.appendChild(element);
  177. var canvas = document.createElement('canvas');
  178. var supportsImageRenderingPixelated = FeatureDetection.supportsImageRenderingPixelated();
  179. this._supportsImageRenderingPixelated = supportsImageRenderingPixelated;
  180. if (supportsImageRenderingPixelated) {
  181. canvas.style.imageRendering = FeatureDetection.imageRenderingValue();
  182. }
  183. canvas.oncontextmenu = function() {
  184. return false;
  185. };
  186. canvas.onselectstart = function() {
  187. return false;
  188. };
  189. element.appendChild(canvas);
  190. var innerCreditContainer = document.createElement('div');
  191. innerCreditContainer.className = 'cesium-widget-credits';
  192. var creditContainer = defined(options.creditContainer) ? getElement(options.creditContainer) : element;
  193. creditContainer.appendChild(innerCreditContainer);
  194. var creditViewport = defined(options.creditViewport) ? getElement(options.creditViewport) : element;
  195. var showRenderLoopErrors = defaultValue(options.showRenderLoopErrors, true);
  196. var useBrowserRecommendedResolution = defaultValue(options.useBrowserRecommendedResolution, false);
  197. this._element = element;
  198. this._container = container;
  199. this._canvas = canvas;
  200. this._canvasClientWidth = 0;
  201. this._canvasClientHeight = 0;
  202. this._lastDevicePixelRatio = 0;
  203. this._creditViewport = creditViewport;
  204. this._creditContainer = creditContainer;
  205. this._innerCreditContainer = innerCreditContainer;
  206. this._canRender = false;
  207. this._renderLoopRunning = false;
  208. this._showRenderLoopErrors = showRenderLoopErrors;
  209. this._resolutionScale = 1.0;
  210. this._useBrowserRecommendedResolution = useBrowserRecommendedResolution;
  211. this._forceResize = false;
  212. this._clock = defined(options.clock) ? options.clock : new Clock();
  213. configureCanvasSize(this);
  214. try {
  215. var scene = new Scene({
  216. canvas : canvas,
  217. contextOptions : options.contextOptions,
  218. creditContainer : innerCreditContainer,
  219. creditViewport: creditViewport,
  220. mapProjection : options.mapProjection,
  221. orderIndependentTranslucency : options.orderIndependentTranslucency,
  222. scene3DOnly : defaultValue(options.scene3DOnly, false),
  223. terrainExaggeration : options.terrainExaggeration,
  224. shadows : options.shadows,
  225. mapMode2D : options.mapMode2D,
  226. requestRenderMode : options.requestRenderMode,
  227. maximumRenderTimeChange : options.maximumRenderTimeChange
  228. });
  229. this._scene = scene;
  230. scene.camera.constrainedAxis = Cartesian3.UNIT_Z;
  231. configurePixelRatio(this);
  232. configureCameraFrustum(this);
  233. var ellipsoid = defaultValue(scene.mapProjection.ellipsoid, Ellipsoid.WGS84);
  234. var globe = options.globe;
  235. if (!defined(globe)) {
  236. globe = new Globe(ellipsoid);
  237. }
  238. if (globe !== false) {
  239. scene.globe = globe;
  240. scene.globe.shadows = defaultValue(options.terrainShadows, ShadowMode.RECEIVE_ONLY);
  241. }
  242. var skyBox = options.skyBox;
  243. if (!defined(skyBox)) {
  244. skyBox = new SkyBox({
  245. sources : {
  246. positiveX : getDefaultSkyBoxUrl('px'),
  247. negativeX : getDefaultSkyBoxUrl('mx'),
  248. positiveY : getDefaultSkyBoxUrl('py'),
  249. negativeY : getDefaultSkyBoxUrl('my'),
  250. positiveZ : getDefaultSkyBoxUrl('pz'),
  251. negativeZ : getDefaultSkyBoxUrl('mz')
  252. }
  253. });
  254. }
  255. if (skyBox !== false) {
  256. scene.skyBox = skyBox;
  257. scene.sun = new Sun();
  258. scene.moon = new Moon();
  259. }
  260. // Blue sky, and the glow around the Earth's limb.
  261. var skyAtmosphere = options.skyAtmosphere;
  262. if (!defined(skyAtmosphere)) {
  263. skyAtmosphere = new SkyAtmosphere(ellipsoid);
  264. }
  265. if (skyAtmosphere !== false) {
  266. scene.skyAtmosphere = skyAtmosphere;
  267. }
  268. //Set the base imagery layer
  269. var imageryProvider = (options.globe === false) ? false : options.imageryProvider;
  270. if (!defined(imageryProvider)) {
  271. imageryProvider = createWorldImagery();
  272. }
  273. if (imageryProvider !== false) {
  274. scene.imageryLayers.addImageryProvider(imageryProvider);
  275. }
  276. //Set the terrain provider if one is provided.
  277. if (defined(options.terrainProvider) && options.globe !== false) {
  278. scene.terrainProvider = options.terrainProvider;
  279. }
  280. this._screenSpaceEventHandler = new ScreenSpaceEventHandler(canvas);
  281. if (defined(options.sceneMode)) {
  282. if (options.sceneMode === SceneMode.SCENE2D) {
  283. this._scene.morphTo2D(0);
  284. }
  285. if (options.sceneMode === SceneMode.COLUMBUS_VIEW) {
  286. this._scene.morphToColumbusView(0);
  287. }
  288. }
  289. this._useDefaultRenderLoop = undefined;
  290. this.useDefaultRenderLoop = defaultValue(options.useDefaultRenderLoop, true);
  291. this._targetFrameRate = undefined;
  292. this.targetFrameRate = options.targetFrameRate;
  293. var that = this;
  294. scene.renderError.addEventListener(function(scene, error) {
  295. that._useDefaultRenderLoop = false;
  296. that._renderLoopRunning = false;
  297. if (that._showRenderLoopErrors) {
  298. var title = 'An error occurred while rendering. Rendering has stopped.';
  299. that.showErrorPanel(title, undefined, error);
  300. }
  301. });
  302. } catch (error) {
  303. if (showRenderLoopErrors) {
  304. var title = 'Error constructing CesiumWidget.';
  305. var message = 'Visit <a href="http://get.webgl.org">http://get.webgl.org</a> to verify that your web browser and hardware support WebGL. Consider trying a different web browser or updating your video drivers. Detailed error information is below:';
  306. this.showErrorPanel(title, message, error);
  307. }
  308. throw error;
  309. }
  310. }
  311. defineProperties(CesiumWidget.prototype, {
  312. /**
  313. * Gets the parent container.
  314. * @memberof CesiumWidget.prototype
  315. *
  316. * @type {Element}
  317. */
  318. container : {
  319. get : function() {
  320. return this._container;
  321. }
  322. },
  323. /**
  324. * Gets the canvas.
  325. * @memberof CesiumWidget.prototype
  326. *
  327. * @type {Canvas}
  328. */
  329. canvas : {
  330. get : function() {
  331. return this._canvas;
  332. }
  333. },
  334. /**
  335. * Gets the credit container.
  336. * @memberof CesiumWidget.prototype
  337. *
  338. * @type {Element}
  339. */
  340. creditContainer: {
  341. get : function() {
  342. return this._creditContainer;
  343. }
  344. },
  345. /**
  346. * Gets the credit viewport
  347. * @memberof CesiumWidget.prototype
  348. *
  349. * @type {Element}
  350. */
  351. creditViewport: {
  352. get: function() {
  353. return this._creditViewport;
  354. }
  355. },
  356. /**
  357. * Gets the scene.
  358. * @memberof CesiumWidget.prototype
  359. *
  360. * @type {Scene}
  361. */
  362. scene : {
  363. get : function() {
  364. return this._scene;
  365. }
  366. },
  367. /**
  368. * Gets the collection of image layers that will be rendered on the globe.
  369. * @memberof CesiumWidget.prototype
  370. *
  371. * @type {ImageryLayerCollection}
  372. * @readonly
  373. */
  374. imageryLayers : {
  375. get : function() {
  376. return this._scene.imageryLayers;
  377. }
  378. },
  379. /**
  380. * The terrain provider providing surface geometry for the globe.
  381. * @memberof CesiumWidget.prototype
  382. *
  383. * @type {TerrainProvider}
  384. */
  385. terrainProvider : {
  386. get : function() {
  387. return this._scene.terrainProvider;
  388. },
  389. set : function(terrainProvider) {
  390. this._scene.terrainProvider = terrainProvider;
  391. }
  392. },
  393. /**
  394. * Gets the camera.
  395. * @memberof CesiumWidget.prototype
  396. *
  397. * @type {Camera}
  398. * @readonly
  399. */
  400. camera : {
  401. get : function() {
  402. return this._scene.camera;
  403. }
  404. },
  405. /**
  406. * Gets the clock.
  407. * @memberof CesiumWidget.prototype
  408. *
  409. * @type {Clock}
  410. */
  411. clock : {
  412. get : function() {
  413. return this._clock;
  414. }
  415. },
  416. /**
  417. * Gets the screen space event handler.
  418. * @memberof CesiumWidget.prototype
  419. *
  420. * @type {ScreenSpaceEventHandler}
  421. */
  422. screenSpaceEventHandler : {
  423. get : function() {
  424. return this._screenSpaceEventHandler;
  425. }
  426. },
  427. /**
  428. * Gets or sets the target frame rate of the widget when <code>useDefaultRenderLoop</code>
  429. * is true. If undefined, the browser's {@link requestAnimationFrame} implementation
  430. * determines the frame rate. If defined, this value must be greater than 0. A value higher
  431. * than the underlying requestAnimationFrame implementation will have no effect.
  432. * @memberof CesiumWidget.prototype
  433. *
  434. * @type {Number}
  435. */
  436. targetFrameRate : {
  437. get : function() {
  438. return this._targetFrameRate;
  439. },
  440. set : function(value) {
  441. //>>includeStart('debug', pragmas.debug);
  442. if (value <= 0) {
  443. throw new DeveloperError('targetFrameRate must be greater than 0, or undefined.');
  444. }
  445. //>>includeEnd('debug');
  446. this._targetFrameRate = value;
  447. }
  448. },
  449. /**
  450. * Gets or sets whether or not this widget should control the render loop.
  451. * If set to true the widget will use {@link requestAnimationFrame} to
  452. * perform rendering and resizing of the widget, as well as drive the
  453. * simulation clock. If set to false, you must manually call the
  454. * <code>resize</code>, <code>render</code> methods as part of a custom
  455. * render loop. If an error occurs during rendering, {@link Scene}'s
  456. * <code>renderError</code> event will be raised and this property
  457. * will be set to false. It must be set back to true to continue rendering
  458. * after the error.
  459. * @memberof CesiumWidget.prototype
  460. *
  461. * @type {Boolean}
  462. */
  463. useDefaultRenderLoop : {
  464. get : function() {
  465. return this._useDefaultRenderLoop;
  466. },
  467. set : function(value) {
  468. if (this._useDefaultRenderLoop !== value) {
  469. this._useDefaultRenderLoop = value;
  470. if (value && !this._renderLoopRunning) {
  471. startRenderLoop(this);
  472. }
  473. }
  474. }
  475. },
  476. /**
  477. * Gets or sets a scaling factor for rendering resolution. Values less than 1.0 can improve
  478. * performance on less powerful devices while values greater than 1.0 will render at a higher
  479. * resolution and then scale down, resulting in improved visual fidelity.
  480. * For example, if the widget is laid out at a size of 640x480, setting this value to 0.5
  481. * will cause the scene to be rendered at 320x240 and then scaled up while setting
  482. * it to 2.0 will cause the scene to be rendered at 1280x960 and then scaled down.
  483. * @memberof CesiumWidget.prototype
  484. *
  485. * @type {Number}
  486. * @default 1.0
  487. */
  488. resolutionScale : {
  489. get : function() {
  490. return this._resolutionScale;
  491. },
  492. set : function(value) {
  493. //>>includeStart('debug', pragmas.debug);
  494. if (value <= 0) {
  495. throw new DeveloperError('resolutionScale must be greater than 0.');
  496. }
  497. //>>includeEnd('debug');
  498. if (this._resolutionScale !== value) {
  499. this._resolutionScale = value;
  500. this._forceResize = true;
  501. }
  502. }
  503. },
  504. /**
  505. * Boolean flag indicating if the browser's recommended resolution is used.
  506. * If true, the browser's device pixel ratio is ignored and 1.0 is used instead,
  507. * effectively rendering based on CSS pixels instead of device pixels. This can improve
  508. * performance on less powerful devices that have high pixel density. When false, rendering
  509. * will be in device pixels. {@link CesiumWidget#resolutionScale} will still take effect whether
  510. * this flag is true or false.
  511. * @memberof CesiumWidget.prototype
  512. *
  513. * @type {Boolean}
  514. * @default false
  515. */
  516. useBrowserRecommendedResolution : {
  517. get : function() {
  518. return this._useBrowserRecommendedResolution;
  519. },
  520. set : function(value) {
  521. if (this._useBrowserRecommendedResolution !== value) {
  522. this._useBrowserRecommendedResolution = value;
  523. this._forceResize = true;
  524. }
  525. }
  526. }
  527. });
  528. /**
  529. * Show an error panel to the user containing a title and a longer error message,
  530. * which can be dismissed using an OK button. This panel is displayed automatically
  531. * when a render loop error occurs, if showRenderLoopErrors was not false when the
  532. * widget was constructed.
  533. *
  534. * @param {String} title The title to be displayed on the error panel. This string is interpreted as text.
  535. * @param {String} message A helpful, user-facing message to display prior to the detailed error information. This string is interpreted as HTML.
  536. * @param {String} [error] The error to be displayed on the error panel. This string is formatted using {@link formatError} and then displayed as text.
  537. */
  538. CesiumWidget.prototype.showErrorPanel = function(title, message, error) {
  539. var element = this._element;
  540. var overlay = document.createElement('div');
  541. overlay.className = 'cesium-widget-errorPanel';
  542. var content = document.createElement('div');
  543. content.className = 'cesium-widget-errorPanel-content';
  544. overlay.appendChild(content);
  545. var errorHeader = document.createElement('div');
  546. errorHeader.className = 'cesium-widget-errorPanel-header';
  547. errorHeader.appendChild(document.createTextNode(title));
  548. content.appendChild(errorHeader);
  549. var errorPanelScroller = document.createElement('div');
  550. errorPanelScroller.className = 'cesium-widget-errorPanel-scroll';
  551. content.appendChild(errorPanelScroller);
  552. function resizeCallback() {
  553. errorPanelScroller.style.maxHeight = Math.max(Math.round(element.clientHeight * 0.9 - 100), 30) + 'px';
  554. }
  555. resizeCallback();
  556. if (defined(window.addEventListener)) {
  557. window.addEventListener('resize', resizeCallback, false);
  558. }
  559. if (defined(message)) {
  560. var errorMessage = document.createElement('div');
  561. errorMessage.className = 'cesium-widget-errorPanel-message';
  562. errorMessage.innerHTML = '<p>' + message + '</p>';
  563. errorPanelScroller.appendChild(errorMessage);
  564. }
  565. var errorDetails = '(no error details available)';
  566. if (defined(error)) {
  567. errorDetails = formatError(error);
  568. }
  569. var errorMessageDetails = document.createElement('div');
  570. errorMessageDetails.className = 'cesium-widget-errorPanel-message';
  571. errorMessageDetails.appendChild(document.createTextNode(errorDetails));
  572. errorPanelScroller.appendChild(errorMessageDetails);
  573. var buttonPanel = document.createElement('div');
  574. buttonPanel.className = 'cesium-widget-errorPanel-buttonPanel';
  575. content.appendChild(buttonPanel);
  576. var okButton = document.createElement('button');
  577. okButton.setAttribute('type', 'button');
  578. okButton.className = 'cesium-button';
  579. okButton.appendChild(document.createTextNode('OK'));
  580. okButton.onclick = function() {
  581. if (defined(resizeCallback) && defined(window.removeEventListener)) {
  582. window.removeEventListener('resize', resizeCallback, false);
  583. }
  584. element.removeChild(overlay);
  585. };
  586. buttonPanel.appendChild(okButton);
  587. element.appendChild(overlay);
  588. //IE8 does not have a console object unless the dev tools are open.
  589. if (typeof console !== 'undefined') {
  590. console.error(title + '\n' + message + '\n' + errorDetails);
  591. }
  592. };
  593. /**
  594. * @returns {Boolean} true if the object has been destroyed, false otherwise.
  595. */
  596. CesiumWidget.prototype.isDestroyed = function() {
  597. return false;
  598. };
  599. /**
  600. * Destroys the widget. Should be called if permanently
  601. * removing the widget from layout.
  602. */
  603. CesiumWidget.prototype.destroy = function() {
  604. this._scene = this._scene && this._scene.destroy();
  605. this._container.removeChild(this._element);
  606. this._creditContainer.removeChild(this._innerCreditContainer);
  607. destroyObject(this);
  608. };
  609. /**
  610. * Updates the canvas size, camera aspect ratio, and viewport size.
  611. * This function is called automatically as needed unless
  612. * <code>useDefaultRenderLoop</code> is set to false.
  613. */
  614. CesiumWidget.prototype.resize = function() {
  615. var canvas = this._canvas;
  616. if (!this._forceResize && this._canvasClientWidth === canvas.clientWidth && this._canvasClientHeight === canvas.clientHeight && this._lastDevicePixelRatio === window.devicePixelRatio) {
  617. return;
  618. }
  619. this._forceResize = false;
  620. configureCanvasSize(this);
  621. configureCameraFrustum(this);
  622. this._scene.requestRender();
  623. };
  624. /**
  625. * Renders the scene. This function is called automatically
  626. * unless <code>useDefaultRenderLoop</code> is set to false;
  627. */
  628. CesiumWidget.prototype.render = function() {
  629. if (this._canRender) {
  630. this._scene.initializeFrame();
  631. var currentTime = this._clock.tick();
  632. this._scene.render(currentTime);
  633. } else {
  634. this._clock.tick();
  635. }
  636. };
  637. export default CesiumWidget;