CesiumWidget.js 27 KB

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