智慧水务管理系统 - 精河县供水工程综合管理平台

Viewer.js 66KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033
  1. import {
  2. BoundingSphere,
  3. BoundingSphereState,
  4. Cartesian3,
  5. CesiumWidget,
  6. Cesium3DTileFeature,
  7. Cesium3DTileVectorFeature,
  8. Clock,
  9. ConstantPositionProperty,
  10. Frozen,
  11. defined,
  12. destroyObject,
  13. DeveloperError,
  14. Entity,
  15. Event,
  16. EventHelper,
  17. getElement,
  18. JulianDate,
  19. Math as CesiumMath,
  20. Property,
  21. ScreenSpaceEventType,
  22. IonGeocoderService,
  23. } from "@cesium/engine";
  24. import Animation from "../Animation/Animation.js";
  25. import AnimationViewModel from "../Animation/AnimationViewModel.js";
  26. import BaseLayerPicker from "../BaseLayerPicker/BaseLayerPicker.js";
  27. import createDefaultImageryProviderViewModels from "../BaseLayerPicker/createDefaultImageryProviderViewModels.js";
  28. import createDefaultTerrainProviderViewModels from "../BaseLayerPicker/createDefaultTerrainProviderViewModels.js";
  29. import ClockViewModel from "../ClockViewModel.js";
  30. import FullscreenButton from "../FullscreenButton/FullscreenButton.js";
  31. import Geocoder from "../Geocoder/Geocoder.js";
  32. import HomeButton from "../HomeButton/HomeButton.js";
  33. import InfoBox from "../InfoBox/InfoBox.js";
  34. import NavigationHelpButton from "../NavigationHelpButton/NavigationHelpButton.js";
  35. import ProjectionPicker from "../ProjectionPicker/ProjectionPicker.js";
  36. import SceneModePicker from "../SceneModePicker/SceneModePicker.js";
  37. import SelectionIndicator from "../SelectionIndicator/SelectionIndicator.js";
  38. import subscribeAndEvaluate from "../subscribeAndEvaluate.js";
  39. import Timeline from "../Timeline/Timeline.js";
  40. import VRButton from "../VRButton/VRButton.js";
  41. const boundingSphereScratch = new BoundingSphere();
  42. function onTimelineScrubfunction(e) {
  43. const clock = e.clock;
  44. clock.currentTime = e.timeJulian;
  45. clock.shouldAnimate = false;
  46. }
  47. function getCesium3DTileFeatureDescription(feature) {
  48. const propertyIds = feature.getPropertyIds();
  49. let html = "";
  50. propertyIds.forEach(function (propertyId) {
  51. const value = feature.getProperty(propertyId);
  52. if (defined(value)) {
  53. html += `<tr><th>${propertyId}</th><td>${value}</td></tr>`;
  54. }
  55. });
  56. if (html.length > 0) {
  57. html = `<table class="cesium-infoBox-defaultTable"><tbody>${html}</tbody></table>`;
  58. }
  59. return html;
  60. }
  61. function getCesium3DTileFeatureName(feature) {
  62. // We need to iterate all property IDs to find potential
  63. // candidates, but since we prefer some property IDs
  64. // over others, we store them in an indexed array
  65. // and then use the first defined element in the array
  66. // as the preferred choice.
  67. let i;
  68. const possibleIds = [];
  69. const propertyIds = feature.getPropertyIds();
  70. for (i = 0; i < propertyIds.length; i++) {
  71. const propertyId = propertyIds[i];
  72. if (/^name$/i.test(propertyId)) {
  73. possibleIds[0] = feature.getProperty(propertyId);
  74. } else if (/name/i.test(propertyId)) {
  75. possibleIds[1] = feature.getProperty(propertyId);
  76. } else if (/^title$/i.test(propertyId)) {
  77. possibleIds[2] = feature.getProperty(propertyId);
  78. } else if (/^(id|identifier)$/i.test(propertyId)) {
  79. possibleIds[3] = feature.getProperty(propertyId);
  80. } else if (/element/i.test(propertyId)) {
  81. possibleIds[4] = feature.getProperty(propertyId);
  82. } else if (/(id|identifier)$/i.test(propertyId)) {
  83. possibleIds[5] = feature.getProperty(propertyId);
  84. }
  85. }
  86. const length = possibleIds.length;
  87. for (i = 0; i < length; i++) {
  88. const item = possibleIds[i];
  89. if (defined(item) && item !== "") {
  90. return item;
  91. }
  92. }
  93. return "Unnamed Feature";
  94. }
  95. function pickEntity(viewer, e) {
  96. const picked = viewer.scene.pick(e.position);
  97. if (defined(picked)) {
  98. const id = picked.id ?? picked.primitive.id;
  99. if (id instanceof Entity) {
  100. return id;
  101. }
  102. if (
  103. picked instanceof Cesium3DTileFeature ||
  104. picked instanceof Cesium3DTileVectorFeature
  105. ) {
  106. return new Entity({
  107. name: getCesium3DTileFeatureName(picked),
  108. description: getCesium3DTileFeatureDescription(picked),
  109. feature: picked,
  110. });
  111. }
  112. }
  113. // No regular entity picked. Try picking features from imagery layers.
  114. if (defined(viewer.scene.globe)) {
  115. return pickImageryLayerFeature(viewer, e.position);
  116. }
  117. }
  118. const scratchStopTime = new JulianDate();
  119. function linkTimelineToDataSourceClock(timeline, dataSource) {
  120. if (defined(dataSource)) {
  121. const dataSourceClock = dataSource.clock;
  122. if (defined(dataSourceClock) && defined(timeline)) {
  123. const startTime = dataSourceClock.startTime;
  124. let stopTime = dataSourceClock.stopTime;
  125. // When the start and stop times are equal, set the timeline to the shortest interval
  126. // starting at the start time. This prevents an invalid timeline configuration.
  127. if (JulianDate.equals(startTime, stopTime)) {
  128. stopTime = JulianDate.addSeconds(
  129. startTime,
  130. CesiumMath.EPSILON2,
  131. scratchStopTime,
  132. );
  133. }
  134. timeline.updateFromClock();
  135. timeline.zoomTo(startTime, stopTime);
  136. }
  137. }
  138. }
  139. const cartesian3Scratch = new Cartesian3();
  140. function pickImageryLayerFeature(viewer, windowPosition) {
  141. const scene = viewer.scene;
  142. const pickRay = scene.camera.getPickRay(windowPosition);
  143. const imageryLayerFeaturePromise =
  144. scene.imageryLayers.pickImageryLayerFeatures(pickRay, scene);
  145. if (!defined(imageryLayerFeaturePromise)) {
  146. return;
  147. }
  148. // Imagery layer feature picking is asynchronous, so put up a message while loading.
  149. const loadingMessage = new Entity({
  150. id: "Loading...",
  151. description: "Loading feature information...",
  152. });
  153. imageryLayerFeaturePromise.then(
  154. function (features) {
  155. // Has this async pick been superseded by a later one?
  156. if (viewer.selectedEntity !== loadingMessage) {
  157. return;
  158. }
  159. if (!defined(features) || features.length === 0) {
  160. viewer.selectedEntity = createNoFeaturesEntity();
  161. return;
  162. }
  163. // Select the first feature.
  164. const feature = features[0];
  165. const entity = new Entity({
  166. id: feature.name,
  167. description: feature.description,
  168. });
  169. if (defined(feature.position)) {
  170. const ecfPosition = viewer.scene.ellipsoid.cartographicToCartesian(
  171. feature.position,
  172. cartesian3Scratch,
  173. );
  174. entity.position = new ConstantPositionProperty(ecfPosition);
  175. }
  176. viewer.selectedEntity = entity;
  177. },
  178. function () {
  179. // Has this async pick been superseded by a later one?
  180. if (viewer.selectedEntity !== loadingMessage) {
  181. return;
  182. }
  183. viewer.selectedEntity = createNoFeaturesEntity();
  184. },
  185. );
  186. return loadingMessage;
  187. }
  188. function createNoFeaturesEntity() {
  189. return new Entity({
  190. id: "None",
  191. description: "No features found.",
  192. });
  193. }
  194. function enableVRUI(viewer, enabled) {
  195. const geocoder = viewer._geocoder;
  196. const homeButton = viewer._homeButton;
  197. const sceneModePicker = viewer._sceneModePicker;
  198. const projectionPicker = viewer._projectionPicker;
  199. const baseLayerPicker = viewer._baseLayerPicker;
  200. const animation = viewer._animation;
  201. const timeline = viewer._timeline;
  202. const fullscreenButton = viewer._fullscreenButton;
  203. const infoBox = viewer._infoBox;
  204. const selectionIndicator = viewer._selectionIndicator;
  205. const visibility = enabled ? "hidden" : "visible";
  206. if (defined(geocoder)) {
  207. geocoder.container.style.visibility = visibility;
  208. }
  209. if (defined(homeButton)) {
  210. homeButton.container.style.visibility = visibility;
  211. }
  212. if (defined(sceneModePicker)) {
  213. sceneModePicker.container.style.visibility = visibility;
  214. }
  215. if (defined(projectionPicker)) {
  216. projectionPicker.container.style.visibility = visibility;
  217. }
  218. if (defined(baseLayerPicker)) {
  219. baseLayerPicker.container.style.visibility = visibility;
  220. }
  221. if (defined(animation)) {
  222. animation.container.style.visibility = visibility;
  223. }
  224. if (defined(timeline)) {
  225. timeline.container.style.visibility = visibility;
  226. }
  227. if (
  228. defined(fullscreenButton) &&
  229. fullscreenButton.viewModel.isFullscreenEnabled
  230. ) {
  231. fullscreenButton.container.style.visibility = visibility;
  232. }
  233. if (defined(infoBox)) {
  234. infoBox.container.style.visibility = visibility;
  235. }
  236. if (defined(selectionIndicator)) {
  237. selectionIndicator.container.style.visibility = visibility;
  238. }
  239. if (viewer._container) {
  240. const right =
  241. enabled || !defined(fullscreenButton)
  242. ? 0
  243. : fullscreenButton.container.clientWidth;
  244. viewer._vrButton.container.style.right = `${right}px`;
  245. viewer.forceResize();
  246. }
  247. }
  248. /**
  249. * @typedef {object} Viewer.ConstructorOptions
  250. *
  251. * Initialization options for the Viewer constructor
  252. *
  253. * @property {boolean} [animation=true] If set to false, the Animation widget will not be created.
  254. * @property {boolean} [baseLayerPicker=true] If set to false, the BaseLayerPicker widget will not be created.
  255. * @property {boolean} [fullscreenButton=true] If set to false, the FullscreenButton widget will not be created.
  256. * @property {boolean} [vrButton=false] If set to true, the VRButton widget will be created.
  257. * @property {boolean|IonGeocodeProviderType|GeocoderService[]} [geocoder=IonGeocodeProviderType.DEFAULT] The geocoding service or services to use when searching with the Geocoder widget. If set to false, the Geocoder widget will not be created.
  258. * @property {boolean} [homeButton=true] If set to false, the HomeButton widget will not be created.
  259. * @property {boolean} [infoBox=true] If set to false, the InfoBox widget will not be created.
  260. * @property {boolean} [sceneModePicker=true] If set to false, the SceneModePicker widget will not be created.
  261. * @property {boolean} [selectionIndicator=true] If set to false, the SelectionIndicator widget will not be created.
  262. * @property {boolean} [timeline=true] If set to false, the Timeline widget will not be created.
  263. * @property {boolean} [navigationHelpButton=true] If set to false, the navigation help button will not be created.
  264. * @property {boolean} [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.
  265. * @property {boolean} [scene3DOnly=false] When <code>true</code>, each geometry instance will only be rendered in 3D to save GPU memory.
  266. * @property {boolean} [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}.
  267. * @property {ClockViewModel} [clockViewModel=new ClockViewModel(clock)] The clock view model to use to control current time.
  268. * @property {ProviderViewModel} [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 `baseLayerPicker` is set to true.
  269. * @property {ProviderViewModel[]} [imageryProviderViewModels=createDefaultImageryProviderViewModels()] The array of ProviderViewModels to be selectable from the BaseLayerPicker. This value is only valid if `baseLayerPicker` is set to true.
  270. * @property {ProviderViewModel} [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 `baseLayerPicker` is set to true.
  271. * @property {ProviderViewModel[]} [terrainProviderViewModels=createDefaultTerrainProviderViewModels()] The array of ProviderViewModels to be selectable from the BaseLayerPicker. This value is only valid if `baseLayerPicker` is set to true.
  272. * @property {ImageryLayer|false} [baseLayer=ImageryLayer.fromWorldImagery()] The bottommost imagery layer applied to the globe. If set to <code>false</code>, no imagery provider will be added. This value is only valid if `baseLayerPicker` is set to false. Cannot be used when `globe` is set to false.
  273. * @property {Ellipsoid} [ellipsoid = Ellipsoid.default] The default ellipsoid.
  274. * @property {TerrainProvider} [terrainProvider=new EllipsoidTerrainProvider()] The terrain provider to use
  275. * @property {Terrain} [terrain] A terrain object which handles asynchronous terrain provider. Can only specify if options.terrainProvider is undefined.
  276. * @property {SkyBox|false} [skyBox] The skybox used to render the stars. When <code>undefined</code> and the WGS84 ellipsoid used, the default stars are used. If set to <code>false</code>, no skyBox, Sun, or Moon will be added.
  277. * @property {SkyAtmosphere|false} [skyAtmosphere] Blue sky, and the glow around the Earth's limb. Enabled when the WGS84 ellipsoid used. Set to <code>false</code> to turn it off.
  278. * @property {Element|string} [fullscreenElement=document.body] The element or id to be placed into fullscreen mode when the full screen button is pressed.
  279. * @property {boolean} [useDefaultRenderLoop=true] True if this widget should control the render loop, false otherwise.
  280. * @property {number} [targetFrameRate] The target frame rate when using the default render loop.
  281. * @property {boolean} [showRenderLoopErrors=true] If true, this widget will automatically display an HTML panel to the user containing the error, if a render loop error occurs.
  282. * @property {boolean} [useBrowserRecommendedResolution=true] If true, render at the browser's recommended resolution and ignore <code>window.devicePixelRatio</code>.
  283. * @property {boolean} [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.
  284. * @property {ContextOptions} [contextOptions] Context and WebGL creation properties passed to {@link Scene}.
  285. * @property {SceneMode} [sceneMode=SceneMode.SCENE3D] The initial scene mode.
  286. * @property {MapProjection} [mapProjection=new GeographicProjection(options.ellipsoid)] The map projection to use in 2D and Columbus View modes.
  287. * @property {Globe|false} [globe=new Globe(options.ellipsoid)] The globe to use in the scene. If set to <code>false</code>, no globe will be added and the sky atmosphere will be hidden by default.
  288. * @property {boolean} [orderIndependentTranslucency=true] If true and the configuration supports it, use order independent translucency.
  289. * @property {Element|string} [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.
  290. * @property {Element|string} [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.
  291. * @property {DataSourceCollection} [dataSources=new DataSourceCollection()] The collection of data sources visualized by the widget. If this parameter is provided,
  292. * the instance is assumed to be owned by the caller and will not be destroyed when the viewer is destroyed.
  293. * @property {boolean} [shadows=false] Determines if shadows are cast by light sources.
  294. * @property {ShadowMode} [terrainShadows=ShadowMode.RECEIVE_ONLY] Determines if the terrain casts or receives shadows from light sources.
  295. * @property {MapMode2D} [mapMode2D=MapMode2D.INFINITE_SCROLL] Determines if the 2D map is rotatable or can be scrolled infinitely in the horizontal direction.
  296. * @property {boolean} [projectionPicker=false] If set to true, the ProjectionPicker widget will be created.
  297. * @property {boolean} [blurActiveElementOnCanvasFocus=true] If true, the active element will blur when the viewer's canvas is clicked. Setting this to false is useful for cases when the canvas is clicked only for retrieving position or an entity data without actually meaning to set the canvas to be the active element.
  298. * @property {boolean} [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}.
  299. * @property {number} [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}.
  300. * @property {number} [depthPlaneEllipsoidOffset=0.0] Adjust the DepthPlane to address rendering artefacts below ellipsoid zero elevation.
  301. * @property {number} [msaaSamples=4] If provided, this value controls the rate of multisample antialiasing. Typical multisampling rates are 2, 4, and sometimes 8 samples per pixel. Higher sampling rates of MSAA may impact performance in exchange for improved visual quality. This value only applies to WebGL2 contexts that support multisample render targets. Set to 1 to disable MSAA.
  302. */
  303. /**
  304. * A base widget for building applications. It composites all of the standard Cesium widgets into one reusable package.
  305. * The widget can always be extended by using mixins, which add functionality useful for a variety of applications.
  306. *
  307. * @alias Viewer
  308. * @constructor
  309. *
  310. * @param {Element|string} container The DOM element or ID that will contain the widget.
  311. * @param {Viewer.ConstructorOptions} [options] Object describing initialization options
  312. *
  313. * @exception {DeveloperError} Element with id "container" does not exist in the document.
  314. * @exception {DeveloperError} options.selectedImageryProviderViewModel is not available when not using the BaseLayerPicker widget, specify options.baseLayer instead.
  315. * @exception {DeveloperError} options.selectedTerrainProviderViewModel is not available when not using the BaseLayerPicker widget, specify options.terrainProvider instead.
  316. *
  317. * @see Animation
  318. * @see BaseLayerPicker
  319. * @see CesiumWidget
  320. * @see FullscreenButton
  321. * @see HomeButton
  322. * @see SceneModePicker
  323. * @see Timeline
  324. * @see viewerDragDropMixin
  325. *
  326. * @demo {@link https://sandcastle.cesium.com/index.html?id=hello-world|Cesium Sandcastle Hello World Demo}
  327. *
  328. * @example
  329. * // Initialize the viewer widget with several custom options and mixins.
  330. * try {
  331. * const viewer = new Cesium.Viewer("cesiumContainer", {
  332. * // Start in Columbus Viewer
  333. * sceneMode: Cesium.SceneMode.COLUMBUS_VIEW,
  334. * // Use Cesium World Terrain
  335. * terrain: Cesium.Terrain.fromWorldTerrain(),
  336. * // Hide the base layer picker
  337. * baseLayerPicker: false,
  338. * // Use OpenStreetMaps
  339. * baseLayer: new Cesium.ImageryLayer(new Cesium.OpenStreetMapImageryProvider({
  340. * url: "https://tile.openstreetmap.org/"
  341. * })),
  342. * skyBox: new Cesium.SkyBox({
  343. * sources: {
  344. * positiveX: "stars/TychoSkymapII.t3_08192x04096_80_px.jpg",
  345. * negativeX: "stars/TychoSkymapII.t3_08192x04096_80_mx.jpg",
  346. * positiveY: "stars/TychoSkymapII.t3_08192x04096_80_py.jpg",
  347. * negativeY: "stars/TychoSkymapII.t3_08192x04096_80_my.jpg",
  348. * positiveZ: "stars/TychoSkymapII.t3_08192x04096_80_pz.jpg",
  349. * negativeZ: "stars/TychoSkymapII.t3_08192x04096_80_mz.jpg"
  350. * }
  351. * }),
  352. * // Show Columbus View map with Web Mercator projection
  353. * mapProjection: new Cesium.WebMercatorProjection()
  354. * });
  355. * } catch (error) {
  356. * console.log(error);
  357. * }
  358. *
  359. * // Add basic drag and drop functionality
  360. * viewer.extend(Cesium.viewerDragDropMixin);
  361. *
  362. * // Show a pop-up alert if we encounter an error when processing a dropped file
  363. * viewer.dropError.addEventListener(function(dropHandler, name, error) {
  364. * console.log(error);
  365. * window.alert(error);
  366. * });
  367. */
  368. function Viewer(container, options) {
  369. //>>includeStart('debug', pragmas.debug);
  370. if (!defined(container)) {
  371. throw new DeveloperError("container is required.");
  372. }
  373. //>>includeEnd('debug');
  374. container = getElement(container);
  375. options = options ?? Frozen.EMPTY_OBJECT;
  376. //>>includeStart('debug', pragmas.debug);
  377. if (
  378. options.globe === false &&
  379. defined(options.baseLayer) &&
  380. options.baseLayer !== false
  381. ) {
  382. throw new DeveloperError("Cannot use baseLayer when globe is disabled.");
  383. }
  384. //>>includeEnd('debug');
  385. const createBaseLayerPicker =
  386. (!defined(options.globe) || options.globe !== false) &&
  387. (!defined(options.baseLayerPicker) || options.baseLayerPicker !== false);
  388. //>>includeStart('debug', pragmas.debug);
  389. // If not using BaseLayerPicker, selectedImageryProviderViewModel is an invalid option
  390. if (
  391. !createBaseLayerPicker &&
  392. defined(options.selectedImageryProviderViewModel)
  393. ) {
  394. throw new DeveloperError(
  395. "options.selectedImageryProviderViewModel is not available when not using the BaseLayerPicker widget. \
  396. Either specify options.baseLayer instead or set options.baseLayerPicker to true.",
  397. );
  398. }
  399. // If not using BaseLayerPicker, selectedTerrainProviderViewModel is an invalid option
  400. if (
  401. !createBaseLayerPicker &&
  402. defined(options.selectedTerrainProviderViewModel)
  403. ) {
  404. throw new DeveloperError(
  405. "options.selectedTerrainProviderViewModel is not available when not using the BaseLayerPicker widget. \
  406. Either specify options.terrainProvider instead or set options.baseLayerPicker to true.",
  407. );
  408. }
  409. //>>includeEnd('debug');
  410. const that = this;
  411. const viewerContainer = document.createElement("div");
  412. viewerContainer.className = "cesium-viewer";
  413. container.appendChild(viewerContainer);
  414. // Cesium widget container
  415. const cesiumWidgetContainer = document.createElement("div");
  416. cesiumWidgetContainer.className = "cesium-viewer-cesiumWidgetContainer";
  417. viewerContainer.appendChild(cesiumWidgetContainer);
  418. // Bottom container
  419. const bottomContainer = document.createElement("div");
  420. bottomContainer.className = "cesium-viewer-bottom";
  421. viewerContainer.appendChild(bottomContainer);
  422. const scene3DOnly = options.scene3DOnly ?? false;
  423. let clock;
  424. let clockViewModel;
  425. let destroyClockViewModel = false;
  426. if (defined(options.clockViewModel)) {
  427. clockViewModel = options.clockViewModel;
  428. clock = clockViewModel.clock;
  429. } else {
  430. clock = new Clock();
  431. clockViewModel = new ClockViewModel(clock);
  432. destroyClockViewModel = true;
  433. }
  434. // Cesium widget
  435. const cesiumWidget = new CesiumWidget(cesiumWidgetContainer, {
  436. baseLayer:
  437. (createBaseLayerPicker &&
  438. defined(options.selectedImageryProviderViewModel)) ||
  439. defined(options.baseLayer) ||
  440. defined(options.imageryProvider)
  441. ? false
  442. : undefined,
  443. clock: clock,
  444. shouldAnimate: options.shouldAnimate,
  445. skyBox: options.skyBox,
  446. skyAtmosphere: options.skyAtmosphere,
  447. sceneMode: options.sceneMode,
  448. ellipsoid: options.ellipsoid,
  449. mapProjection: options.mapProjection,
  450. globe: options.globe,
  451. orderIndependentTranslucency: options.orderIndependentTranslucency,
  452. automaticallyTrackDataSourceClocks:
  453. options.automaticallyTrackDataSourceClocks,
  454. contextOptions: options.contextOptions,
  455. useDefaultRenderLoop: options.useDefaultRenderLoop,
  456. targetFrameRate: options.targetFrameRate,
  457. showRenderLoopErrors: options.showRenderLoopErrors,
  458. useBrowserRecommendedResolution: options.useBrowserRecommendedResolution,
  459. creditContainer: defined(options.creditContainer)
  460. ? options.creditContainer
  461. : bottomContainer,
  462. creditViewport: options.creditViewport,
  463. dataSources: options.dataSources,
  464. scene3DOnly: scene3DOnly,
  465. shadows: options.shadows,
  466. terrainShadows: options.terrainShadows,
  467. mapMode2D: options.mapMode2D,
  468. blurActiveElementOnCanvasFocus: options.blurActiveElementOnCanvasFocus,
  469. requestRenderMode: options.requestRenderMode,
  470. maximumRenderTimeChange: options.maximumRenderTimeChange,
  471. depthPlaneEllipsoidOffset: options.depthPlaneEllipsoidOffset,
  472. msaaSamples: options.msaaSamples,
  473. });
  474. const scene = cesiumWidget.scene;
  475. const eventHelper = new EventHelper();
  476. eventHelper.add(clock.onTick, Viewer.prototype._onTick, this);
  477. // Selection Indicator
  478. let selectionIndicator;
  479. if (
  480. !defined(options.selectionIndicator) ||
  481. options.selectionIndicator !== false
  482. ) {
  483. const selectionIndicatorContainer = document.createElement("div");
  484. selectionIndicatorContainer.className =
  485. "cesium-viewer-selectionIndicatorContainer";
  486. viewerContainer.appendChild(selectionIndicatorContainer);
  487. selectionIndicator = new SelectionIndicator(
  488. selectionIndicatorContainer,
  489. scene,
  490. );
  491. }
  492. // Info Box
  493. let infoBox;
  494. if (!defined(options.infoBox) || options.infoBox !== false) {
  495. const infoBoxContainer = document.createElement("div");
  496. infoBoxContainer.className = "cesium-viewer-infoBoxContainer";
  497. viewerContainer.appendChild(infoBoxContainer);
  498. infoBox = new InfoBox(infoBoxContainer);
  499. const infoBoxViewModel = infoBox.viewModel;
  500. eventHelper.add(
  501. infoBoxViewModel.cameraClicked,
  502. Viewer.prototype._onInfoBoxCameraClicked,
  503. this,
  504. );
  505. eventHelper.add(
  506. infoBoxViewModel.closeClicked,
  507. Viewer.prototype._onInfoBoxClockClicked,
  508. this,
  509. );
  510. }
  511. // Main Toolbar
  512. const toolbar = document.createElement("div");
  513. toolbar.className = "cesium-viewer-toolbar";
  514. viewerContainer.appendChild(toolbar);
  515. // Geocoder
  516. let geocoder;
  517. if (!defined(options.geocoder) || options.geocoder !== false) {
  518. const geocoderContainer = document.createElement("div");
  519. geocoderContainer.className = "cesium-viewer-geocoderContainer";
  520. toolbar.appendChild(geocoderContainer);
  521. let geocoderService;
  522. if (typeof options.geocoder === "string") {
  523. geocoderService = [
  524. new IonGeocoderService({
  525. scene,
  526. geocodeProviderType: options.geocoder,
  527. }),
  528. ];
  529. } else if (
  530. defined(options.geocoder) &&
  531. typeof options.geocoder !== "boolean"
  532. ) {
  533. geocoderService = Array.isArray(options.geocoder)
  534. ? options.geocoder
  535. : [options.geocoder];
  536. }
  537. geocoder = new Geocoder({
  538. container: geocoderContainer,
  539. geocoderServices: geocoderService,
  540. scene: scene,
  541. });
  542. // Subscribe to search so that we can clear the trackedEntity when it is clicked.
  543. eventHelper.add(
  544. geocoder.viewModel.search.beforeExecute,
  545. Viewer.prototype._clearObjects,
  546. this,
  547. );
  548. }
  549. // HomeButton
  550. let homeButton;
  551. if (!defined(options.homeButton) || options.homeButton !== false) {
  552. homeButton = new HomeButton(toolbar, scene);
  553. if (defined(geocoder)) {
  554. eventHelper.add(homeButton.viewModel.command.afterExecute, function () {
  555. const viewModel = geocoder.viewModel;
  556. viewModel.searchText = "";
  557. if (viewModel.isSearchInProgress) {
  558. viewModel.search();
  559. }
  560. });
  561. }
  562. // Subscribe to the home button beforeExecute event so that we can clear the trackedEntity.
  563. eventHelper.add(
  564. homeButton.viewModel.command.beforeExecute,
  565. Viewer.prototype._clearTrackedObject,
  566. this,
  567. );
  568. }
  569. // SceneModePicker
  570. // By default, we silently disable the scene mode picker if scene3DOnly is true,
  571. // but if sceneModePicker is explicitly set to true, throw an error.
  572. //>>includeStart('debug', pragmas.debug);
  573. if (options.sceneModePicker === true && scene3DOnly) {
  574. throw new DeveloperError(
  575. "options.sceneModePicker is not available when options.scene3DOnly is set to true.",
  576. );
  577. }
  578. //>>includeEnd('debug');
  579. let sceneModePicker;
  580. if (
  581. !scene3DOnly &&
  582. (!defined(options.sceneModePicker) || options.sceneModePicker !== false)
  583. ) {
  584. sceneModePicker = new SceneModePicker(toolbar, scene);
  585. }
  586. let projectionPicker;
  587. if (options.projectionPicker) {
  588. projectionPicker = new ProjectionPicker(toolbar, scene);
  589. }
  590. // BaseLayerPicker
  591. let baseLayerPicker;
  592. let baseLayerPickerDropDown;
  593. if (createBaseLayerPicker) {
  594. const imageryProviderViewModels =
  595. options.imageryProviderViewModels ??
  596. createDefaultImageryProviderViewModels();
  597. const terrainProviderViewModels =
  598. options.terrainProviderViewModels ??
  599. createDefaultTerrainProviderViewModels();
  600. baseLayerPicker = new BaseLayerPicker(toolbar, {
  601. globe: scene.globe,
  602. imageryProviderViewModels: imageryProviderViewModels,
  603. selectedImageryProviderViewModel:
  604. options.selectedImageryProviderViewModel,
  605. terrainProviderViewModels: terrainProviderViewModels,
  606. selectedTerrainProviderViewModel:
  607. options.selectedTerrainProviderViewModel,
  608. });
  609. //Grab the dropdown for resize code.
  610. const elements = toolbar.getElementsByClassName(
  611. "cesium-baseLayerPicker-dropDown",
  612. );
  613. baseLayerPickerDropDown = elements[0];
  614. }
  615. // These need to be set after the BaseLayerPicker is created in order to take effect
  616. if (defined(options.baseLayer) && options.baseLayer !== false) {
  617. if (createBaseLayerPicker) {
  618. baseLayerPicker.viewModel.selectedImagery = undefined;
  619. }
  620. scene.imageryLayers.removeAll();
  621. scene.imageryLayers.add(options.baseLayer);
  622. }
  623. if (defined(options.terrainProvider)) {
  624. if (createBaseLayerPicker) {
  625. baseLayerPicker.viewModel.selectedTerrain = undefined;
  626. }
  627. scene.terrainProvider = options.terrainProvider;
  628. }
  629. if (defined(options.terrain)) {
  630. //>>includeStart('debug', pragmas.debug);
  631. if (defined(options.terrainProvider)) {
  632. throw new DeveloperError(
  633. "Specify either options.terrainProvider or options.terrain.",
  634. );
  635. }
  636. //>>includeEnd('debug');
  637. if (createBaseLayerPicker) {
  638. // Required as this is otherwise set by the baseLayerPicker
  639. scene.globe.depthTestAgainstTerrain = true;
  640. }
  641. scene.setTerrain(options.terrain);
  642. }
  643. // Navigation Help Button
  644. let navigationHelpButton;
  645. if (
  646. !defined(options.navigationHelpButton) ||
  647. options.navigationHelpButton !== false
  648. ) {
  649. let showNavHelp = true;
  650. try {
  651. //window.localStorage is null if disabled in Firefox or undefined in browsers with implementation
  652. if (defined(window.localStorage)) {
  653. const hasSeenNavHelp = window.localStorage.getItem(
  654. "cesium-hasSeenNavHelp",
  655. );
  656. if (defined(hasSeenNavHelp) && Boolean(hasSeenNavHelp)) {
  657. showNavHelp = false;
  658. } else {
  659. window.localStorage.setItem("cesium-hasSeenNavHelp", "true");
  660. }
  661. }
  662. } catch (e) {
  663. //Accessing window.localStorage throws if disabled in Chrome
  664. //window.localStorage.setItem throws if in Safari private browsing mode or in any browser if we are over quota.
  665. }
  666. navigationHelpButton = new NavigationHelpButton({
  667. container: toolbar,
  668. instructionsInitiallyVisible:
  669. options.navigationInstructionsInitiallyVisible ?? showNavHelp,
  670. });
  671. }
  672. // Animation
  673. let animation;
  674. if (!defined(options.animation) || options.animation !== false) {
  675. const animationContainer = document.createElement("div");
  676. animationContainer.className = "cesium-viewer-animationContainer";
  677. viewerContainer.appendChild(animationContainer);
  678. animation = new Animation(
  679. animationContainer,
  680. new AnimationViewModel(clockViewModel),
  681. );
  682. }
  683. // Timeline
  684. let timeline;
  685. if (!defined(options.timeline) || options.timeline !== false) {
  686. const timelineContainer = document.createElement("div");
  687. timelineContainer.className = "cesium-viewer-timelineContainer";
  688. viewerContainer.appendChild(timelineContainer);
  689. timeline = new Timeline(timelineContainer, clock);
  690. timeline.addEventListener("settime", onTimelineScrubfunction, false);
  691. timeline.zoomTo(clock.startTime, clock.stopTime);
  692. }
  693. // Fullscreen
  694. let fullscreenButton;
  695. let fullscreenSubscription;
  696. let fullscreenContainer;
  697. if (
  698. !defined(options.fullscreenButton) ||
  699. options.fullscreenButton !== false
  700. ) {
  701. fullscreenContainer = document.createElement("div");
  702. fullscreenContainer.className = "cesium-viewer-fullscreenContainer";
  703. viewerContainer.appendChild(fullscreenContainer);
  704. fullscreenButton = new FullscreenButton(
  705. fullscreenContainer,
  706. options.fullscreenElement,
  707. );
  708. //Subscribe to fullscreenButton.viewModel.isFullscreenEnabled so
  709. //that we can hide/show the button as well as size the timeline.
  710. fullscreenSubscription = subscribeAndEvaluate(
  711. fullscreenButton.viewModel,
  712. "isFullscreenEnabled",
  713. function (isFullscreenEnabled) {
  714. fullscreenContainer.style.display = isFullscreenEnabled
  715. ? "block"
  716. : "none";
  717. if (defined(timeline)) {
  718. timeline.container.style.right = `${fullscreenContainer.clientWidth}px`;
  719. timeline.resize();
  720. }
  721. },
  722. );
  723. }
  724. // VR
  725. let vrButton;
  726. let vrSubscription;
  727. let vrModeSubscription;
  728. if (options.vrButton) {
  729. const vrContainer = document.createElement("div");
  730. vrContainer.className = "cesium-viewer-vrContainer";
  731. viewerContainer.appendChild(vrContainer);
  732. vrButton = new VRButton(vrContainer, scene, options.fullScreenElement);
  733. vrSubscription = subscribeAndEvaluate(
  734. vrButton.viewModel,
  735. "isVREnabled",
  736. function (isVREnabled) {
  737. vrContainer.style.display = isVREnabled ? "block" : "none";
  738. if (defined(fullscreenButton)) {
  739. vrContainer.style.right = `${fullscreenContainer.clientWidth}px`;
  740. }
  741. if (defined(timeline)) {
  742. timeline.container.style.right = `${vrContainer.clientWidth}px`;
  743. timeline.resize();
  744. }
  745. },
  746. );
  747. vrModeSubscription = subscribeAndEvaluate(
  748. vrButton.viewModel,
  749. "isVRMode",
  750. function (isVRMode) {
  751. enableVRUI(that, isVRMode);
  752. },
  753. );
  754. }
  755. //Assign all properties to this instance. No "this" assignments should
  756. //take place above this line.
  757. this._baseLayerPickerDropDown = baseLayerPickerDropDown;
  758. this._fullscreenSubscription = fullscreenSubscription;
  759. this._vrSubscription = vrSubscription;
  760. this._vrModeSubscription = vrModeSubscription;
  761. this._dataSourceChangedListeners = {};
  762. this._container = container;
  763. this._bottomContainer = bottomContainer;
  764. this._element = viewerContainer;
  765. this._cesiumWidget = cesiumWidget;
  766. this._selectionIndicator = selectionIndicator;
  767. this._infoBox = infoBox;
  768. this._clockViewModel = clockViewModel;
  769. this._destroyClockViewModel = destroyClockViewModel;
  770. this._toolbar = toolbar;
  771. this._homeButton = homeButton;
  772. this._sceneModePicker = sceneModePicker;
  773. this._projectionPicker = projectionPicker;
  774. this._baseLayerPicker = baseLayerPicker;
  775. this._navigationHelpButton = navigationHelpButton;
  776. this._animation = animation;
  777. this._timeline = timeline;
  778. this._fullscreenButton = fullscreenButton;
  779. this._vrButton = vrButton;
  780. this._geocoder = geocoder;
  781. this._eventHelper = eventHelper;
  782. this._lastWidth = 0;
  783. this._lastHeight = 0;
  784. this._enableInfoOrSelection = defined(infoBox) || defined(selectionIndicator);
  785. this._selectedEntity = undefined;
  786. this._selectedEntityChanged = new Event();
  787. const dataSourceCollection = this._cesiumWidget.dataSources;
  788. const dataSourceDisplay = this._cesiumWidget.dataSourceDisplay;
  789. //Listen to data source events in order to track clock changes.
  790. eventHelper.add(
  791. dataSourceCollection.dataSourceAdded,
  792. Viewer.prototype._onDataSourceAdded,
  793. this,
  794. );
  795. eventHelper.add(
  796. dataSourceCollection.dataSourceRemoved,
  797. Viewer.prototype._onDataSourceRemoved,
  798. this,
  799. );
  800. // Prior to each render, check if anything needs to be resized.
  801. eventHelper.add(scene.postUpdate, Viewer.prototype.resize, this);
  802. // We need to subscribe to the data sources and collections so that we can clear the
  803. // tracked object when it is removed from the scene.
  804. // Subscribe to current data sources
  805. const dataSourceLength = dataSourceCollection.length;
  806. for (let i = 0; i < dataSourceLength; i++) {
  807. this._dataSourceAdded(dataSourceCollection, dataSourceCollection.get(i));
  808. }
  809. this._dataSourceAdded(undefined, dataSourceDisplay.defaultDataSource);
  810. // Hook up events so that we can subscribe to future sources.
  811. eventHelper.add(
  812. dataSourceCollection.dataSourceAdded,
  813. Viewer.prototype._dataSourceAdded,
  814. this,
  815. );
  816. eventHelper.add(
  817. dataSourceCollection.dataSourceRemoved,
  818. Viewer.prototype._dataSourceRemoved,
  819. this,
  820. );
  821. // Subscribe to left clicks and zoom to the picked object.
  822. function pickAndTrackObject(e) {
  823. const entity = pickEntity(that, e);
  824. if (defined(entity)) {
  825. //Only track the entity if it has a valid position at the current time.
  826. if (
  827. Property.getValueOrUndefined(entity.position, that.clock.currentTime)
  828. ) {
  829. that.trackedEntity = entity;
  830. } else {
  831. that.zoomTo(entity);
  832. }
  833. } else if (defined(that.trackedEntity)) {
  834. that.trackedEntity = undefined;
  835. }
  836. }
  837. function pickAndSelectObject(e) {
  838. that.selectedEntity = pickEntity(that, e);
  839. }
  840. cesiumWidget.screenSpaceEventHandler.setInputAction(
  841. pickAndSelectObject,
  842. ScreenSpaceEventType.LEFT_CLICK,
  843. );
  844. cesiumWidget.screenSpaceEventHandler.setInputAction(
  845. pickAndTrackObject,
  846. ScreenSpaceEventType.LEFT_DOUBLE_CLICK,
  847. );
  848. // This allows to update the Viewer's _clockViewModel instead of the CesiumWidget's _clock
  849. // when CesiumWidget is created from the Viewer.
  850. cesiumWidget._canAnimateUpdateCallback = this._updateCanAnimate(this);
  851. }
  852. Object.defineProperties(Viewer.prototype, {
  853. /**
  854. * Gets the parent container.
  855. * @memberof Viewer.prototype
  856. * @type {Element}
  857. * @readonly
  858. */
  859. container: {
  860. get: function () {
  861. return this._container;
  862. },
  863. },
  864. /**
  865. * Manages the list of credits to display on screen and in the lightbox.
  866. * @memberof Viewer.prototype
  867. *
  868. * @type {CreditDisplay}
  869. */
  870. creditDisplay: {
  871. get: function () {
  872. return this._cesiumWidget.creditDisplay;
  873. },
  874. },
  875. /**
  876. * Gets the DOM element for the area at the bottom of the window containing the
  877. * {@link CreditDisplay} and potentially other things.
  878. * @memberof Viewer.prototype
  879. * @type {Element}
  880. * @readonly
  881. */
  882. bottomContainer: {
  883. get: function () {
  884. return this._bottomContainer;
  885. },
  886. },
  887. /**
  888. * Gets the CesiumWidget.
  889. * @memberof Viewer.prototype
  890. * @type {CesiumWidget}
  891. * @readonly
  892. */
  893. cesiumWidget: {
  894. get: function () {
  895. return this._cesiumWidget;
  896. },
  897. },
  898. /**
  899. * Gets the selection indicator.
  900. * @memberof Viewer.prototype
  901. * @type {SelectionIndicator}
  902. * @readonly
  903. */
  904. selectionIndicator: {
  905. get: function () {
  906. return this._selectionIndicator;
  907. },
  908. },
  909. /**
  910. * Gets the info box.
  911. * @memberof Viewer.prototype
  912. * @type {InfoBox}
  913. * @readonly
  914. */
  915. infoBox: {
  916. get: function () {
  917. return this._infoBox;
  918. },
  919. },
  920. /**
  921. * Gets the Geocoder.
  922. * @memberof Viewer.prototype
  923. * @type {Geocoder}
  924. * @readonly
  925. */
  926. geocoder: {
  927. get: function () {
  928. return this._geocoder;
  929. },
  930. },
  931. /**
  932. * Gets the HomeButton.
  933. * @memberof Viewer.prototype
  934. * @type {HomeButton}
  935. * @readonly
  936. */
  937. homeButton: {
  938. get: function () {
  939. return this._homeButton;
  940. },
  941. },
  942. /**
  943. * Gets the SceneModePicker.
  944. * @memberof Viewer.prototype
  945. * @type {SceneModePicker}
  946. * @readonly
  947. */
  948. sceneModePicker: {
  949. get: function () {
  950. return this._sceneModePicker;
  951. },
  952. },
  953. /**
  954. * Gets the ProjectionPicker.
  955. * @memberof Viewer.prototype
  956. * @type {ProjectionPicker}
  957. * @readonly
  958. */
  959. projectionPicker: {
  960. get: function () {
  961. return this._projectionPicker;
  962. },
  963. },
  964. /**
  965. * Gets the BaseLayerPicker.
  966. * @memberof Viewer.prototype
  967. * @type {BaseLayerPicker}
  968. * @readonly
  969. */
  970. baseLayerPicker: {
  971. get: function () {
  972. return this._baseLayerPicker;
  973. },
  974. },
  975. /**
  976. * Gets the NavigationHelpButton.
  977. * @memberof Viewer.prototype
  978. * @type {NavigationHelpButton}
  979. * @readonly
  980. */
  981. navigationHelpButton: {
  982. get: function () {
  983. return this._navigationHelpButton;
  984. },
  985. },
  986. /**
  987. * Gets the Animation widget.
  988. * @memberof Viewer.prototype
  989. * @type {Animation}
  990. * @readonly
  991. */
  992. animation: {
  993. get: function () {
  994. return this._animation;
  995. },
  996. },
  997. /**
  998. * Gets the Timeline widget.
  999. * @memberof Viewer.prototype
  1000. * @type {Timeline}
  1001. * @readonly
  1002. */
  1003. timeline: {
  1004. get: function () {
  1005. return this._timeline;
  1006. },
  1007. },
  1008. /**
  1009. * Gets the FullscreenButton.
  1010. * @memberof Viewer.prototype
  1011. * @type {FullscreenButton}
  1012. * @readonly
  1013. */
  1014. fullscreenButton: {
  1015. get: function () {
  1016. return this._fullscreenButton;
  1017. },
  1018. },
  1019. /**
  1020. * Gets the VRButton.
  1021. * @memberof Viewer.prototype
  1022. * @type {VRButton}
  1023. * @readonly
  1024. */
  1025. vrButton: {
  1026. get: function () {
  1027. return this._vrButton;
  1028. },
  1029. },
  1030. /**
  1031. * Gets the display used for {@link DataSource} visualization.
  1032. * @memberof Viewer.prototype
  1033. * @type {DataSourceDisplay}
  1034. * @readonly
  1035. */
  1036. dataSourceDisplay: {
  1037. get: function () {
  1038. return this._cesiumWidget.dataSourceDisplay;
  1039. },
  1040. },
  1041. /**
  1042. * Gets the collection of entities not tied to a particular data source.
  1043. * This is a shortcut to [dataSourceDisplay.defaultDataSource.entities]{@link Viewer#dataSourceDisplay}.
  1044. * @memberof Viewer.prototype
  1045. * @type {EntityCollection}
  1046. * @readonly
  1047. */
  1048. entities: {
  1049. get: function () {
  1050. return this._cesiumWidget.entities;
  1051. },
  1052. },
  1053. /**
  1054. * Gets the set of {@link DataSource} instances to be visualized.
  1055. * @memberof Viewer.prototype
  1056. * @type {DataSourceCollection}
  1057. * @readonly
  1058. */
  1059. dataSources: {
  1060. get: function () {
  1061. return this._cesiumWidget.dataSources;
  1062. },
  1063. },
  1064. /**
  1065. * Gets the canvas.
  1066. * @memberof Viewer.prototype
  1067. * @type {HTMLCanvasElement}
  1068. * @readonly
  1069. */
  1070. canvas: {
  1071. get: function () {
  1072. return this._cesiumWidget.canvas;
  1073. },
  1074. },
  1075. /**
  1076. * Gets the scene.
  1077. * @memberof Viewer.prototype
  1078. * @type {Scene}
  1079. * @readonly
  1080. */
  1081. scene: {
  1082. get: function () {
  1083. return this._cesiumWidget.scene;
  1084. },
  1085. },
  1086. /**
  1087. * Determines if shadows are cast by light sources.
  1088. * @memberof Viewer.prototype
  1089. * @type {boolean}
  1090. */
  1091. shadows: {
  1092. get: function () {
  1093. return this.scene.shadowMap.enabled;
  1094. },
  1095. set: function (value) {
  1096. this.scene.shadowMap.enabled = value;
  1097. },
  1098. },
  1099. /**
  1100. * Determines if the terrain casts or shadows from light sources.
  1101. * @memberof Viewer.prototype
  1102. * @type {ShadowMode}
  1103. */
  1104. terrainShadows: {
  1105. get: function () {
  1106. return this.scene.globe.shadows;
  1107. },
  1108. set: function (value) {
  1109. this.scene.globe.shadows = value;
  1110. },
  1111. },
  1112. /**
  1113. * Get the scene's shadow map
  1114. * @memberof Viewer.prototype
  1115. * @type {ShadowMap}
  1116. * @readonly
  1117. */
  1118. shadowMap: {
  1119. get: function () {
  1120. return this.scene.shadowMap;
  1121. },
  1122. },
  1123. /**
  1124. * Gets the collection of image layers that will be rendered on the globe.
  1125. * @memberof Viewer.prototype
  1126. *
  1127. * @type {ImageryLayerCollection}
  1128. * @readonly
  1129. */
  1130. imageryLayers: {
  1131. get: function () {
  1132. return this.scene.imageryLayers;
  1133. },
  1134. },
  1135. /**
  1136. * The terrain provider providing surface geometry for the globe.
  1137. * @memberof Viewer.prototype
  1138. *
  1139. * @type {TerrainProvider}
  1140. */
  1141. terrainProvider: {
  1142. get: function () {
  1143. return this.scene.terrainProvider;
  1144. },
  1145. set: function (terrainProvider) {
  1146. this.scene.terrainProvider = terrainProvider;
  1147. },
  1148. },
  1149. /**
  1150. * Gets the camera.
  1151. * @memberof Viewer.prototype
  1152. *
  1153. * @type {Camera}
  1154. * @readonly
  1155. */
  1156. camera: {
  1157. get: function () {
  1158. return this.scene.camera;
  1159. },
  1160. },
  1161. /**
  1162. * Gets the default ellipsoid for the scene.
  1163. * @memberof Viewer.prototype
  1164. *
  1165. * @type {Ellipsoid}
  1166. * @default Ellipsoid.default
  1167. * @readonly
  1168. */
  1169. ellipsoid: {
  1170. get: function () {
  1171. return this.scene.ellipsoid;
  1172. },
  1173. },
  1174. /**
  1175. * Gets the post-process stages.
  1176. * @memberof Viewer.prototype
  1177. *
  1178. * @type {PostProcessStageCollection}
  1179. * @readonly
  1180. */
  1181. postProcessStages: {
  1182. get: function () {
  1183. return this.scene.postProcessStages;
  1184. },
  1185. },
  1186. /**
  1187. * Gets the clock.
  1188. * @memberof Viewer.prototype
  1189. * @type {Clock}
  1190. * @readonly
  1191. */
  1192. clock: {
  1193. get: function () {
  1194. return this._clockViewModel.clock;
  1195. },
  1196. },
  1197. /**
  1198. * Gets the clock view model.
  1199. * @memberof Viewer.prototype
  1200. * @type {ClockViewModel}
  1201. * @readonly
  1202. */
  1203. clockViewModel: {
  1204. get: function () {
  1205. return this._clockViewModel;
  1206. },
  1207. },
  1208. /**
  1209. * Gets the screen space event handler.
  1210. * @memberof Viewer.prototype
  1211. * @type {ScreenSpaceEventHandler}
  1212. * @readonly
  1213. */
  1214. screenSpaceEventHandler: {
  1215. get: function () {
  1216. return this._cesiumWidget.screenSpaceEventHandler;
  1217. },
  1218. },
  1219. /**
  1220. * Gets or sets the target frame rate of the widget when <code>useDefaultRenderLoop</code>
  1221. * is true. If undefined, the browser's requestAnimationFrame implementation
  1222. * determines the frame rate. If defined, this value must be greater than 0. A value higher
  1223. * than the underlying requestAnimationFrame implementation will have no effect.
  1224. * @memberof Viewer.prototype
  1225. *
  1226. * @type {number}
  1227. */
  1228. targetFrameRate: {
  1229. get: function () {
  1230. return this._cesiumWidget.targetFrameRate;
  1231. },
  1232. set: function (value) {
  1233. this._cesiumWidget.targetFrameRate = value;
  1234. },
  1235. },
  1236. /**
  1237. * Gets or sets whether or not this widget should control the render loop.
  1238. * If true the widget will use requestAnimationFrame to
  1239. * perform rendering and resizing of the widget, as well as drive the
  1240. * simulation clock. If set to false, you must manually call the
  1241. * <code>resize</code>, <code>render</code> methods
  1242. * as part of a custom render loop. If an error occurs during rendering, {@link Scene}'s
  1243. * <code>renderError</code> event will be raised and this property
  1244. * will be set to false. It must be set back to true to continue rendering
  1245. * after the error.
  1246. * @memberof Viewer.prototype
  1247. *
  1248. * @type {boolean}
  1249. */
  1250. useDefaultRenderLoop: {
  1251. get: function () {
  1252. return this._cesiumWidget.useDefaultRenderLoop;
  1253. },
  1254. set: function (value) {
  1255. this._cesiumWidget.useDefaultRenderLoop = value;
  1256. },
  1257. },
  1258. /**
  1259. * Gets or sets a scaling factor for rendering resolution. Values less than 1.0 can improve
  1260. * performance on less powerful devices while values greater than 1.0 will render at a higher
  1261. * resolution and then scale down, resulting in improved visual fidelity.
  1262. * For example, if the widget is laid out at a size of 640x480, setting this value to 0.5
  1263. * will cause the scene to be rendered at 320x240 and then scaled up while setting
  1264. * it to 2.0 will cause the scene to be rendered at 1280x960 and then scaled down.
  1265. * @memberof Viewer.prototype
  1266. *
  1267. * @type {number}
  1268. * @default 1.0
  1269. */
  1270. resolutionScale: {
  1271. get: function () {
  1272. return this._cesiumWidget.resolutionScale;
  1273. },
  1274. set: function (value) {
  1275. this._cesiumWidget.resolutionScale = value;
  1276. },
  1277. },
  1278. /**
  1279. * Boolean flag indicating if the browser's recommended resolution is used.
  1280. * If true, the browser's device pixel ratio is ignored and 1.0 is used instead,
  1281. * effectively rendering based on CSS pixels instead of device pixels. This can improve
  1282. * performance on less powerful devices that have high pixel density. When false, rendering
  1283. * will be in device pixels. {@link Viewer#resolutionScale} will still take effect whether
  1284. * this flag is true or false.
  1285. * @memberof Viewer.prototype
  1286. *
  1287. * @type {boolean}
  1288. * @default true
  1289. */
  1290. useBrowserRecommendedResolution: {
  1291. get: function () {
  1292. return this._cesiumWidget.useBrowserRecommendedResolution;
  1293. },
  1294. set: function (value) {
  1295. this._cesiumWidget.useBrowserRecommendedResolution = value;
  1296. },
  1297. },
  1298. /**
  1299. * Gets or sets whether or not data sources can temporarily pause
  1300. * animation in order to avoid showing an incomplete picture to the user.
  1301. * For example, if asynchronous primitives are being processed in the
  1302. * background, the clock will not advance until the geometry is ready.
  1303. *
  1304. * @memberof Viewer.prototype
  1305. *
  1306. * @type {boolean}
  1307. */
  1308. allowDataSourcesToSuspendAnimation: {
  1309. get: function () {
  1310. return this._cesiumWidget.allowDataSourcesToSuspendAnimation;
  1311. },
  1312. set: function (value) {
  1313. this._cesiumWidget.allowDataSourcesToSuspendAnimation = value;
  1314. },
  1315. },
  1316. /**
  1317. * Gets or sets the Entity instance currently being tracked by the camera.
  1318. * @memberof Viewer.prototype
  1319. * @type {Entity | undefined}
  1320. */
  1321. trackedEntity: {
  1322. get: function () {
  1323. return this._cesiumWidget.trackedEntity;
  1324. },
  1325. set: function (value) {
  1326. this._cesiumWidget.trackedEntity = value;
  1327. },
  1328. },
  1329. /**
  1330. * Gets or sets the object instance for which to display a selection indicator.
  1331. *
  1332. * If a user interactively picks a Cesium3DTilesFeature instance, then this property
  1333. * will contain a transient Entity instance with a property named "feature" that is
  1334. * the instance that was picked.
  1335. * @memberof Viewer.prototype
  1336. * @type {Entity | undefined}
  1337. */
  1338. selectedEntity: {
  1339. get: function () {
  1340. return this._selectedEntity;
  1341. },
  1342. set: function (value) {
  1343. if (this._selectedEntity !== value) {
  1344. this._selectedEntity = value;
  1345. const selectionIndicatorViewModel = defined(this._selectionIndicator)
  1346. ? this._selectionIndicator.viewModel
  1347. : undefined;
  1348. if (defined(value)) {
  1349. if (defined(selectionIndicatorViewModel)) {
  1350. selectionIndicatorViewModel.animateAppear();
  1351. }
  1352. } else if (defined(selectionIndicatorViewModel)) {
  1353. // Leave the info text in place here, it is needed during the exit animation.
  1354. selectionIndicatorViewModel.animateDepart();
  1355. }
  1356. this._selectedEntityChanged.raiseEvent(value);
  1357. }
  1358. },
  1359. },
  1360. /**
  1361. * Gets the event that is raised when the selected entity changes.
  1362. * @memberof Viewer.prototype
  1363. * @type {Event}
  1364. * @readonly
  1365. */
  1366. selectedEntityChanged: {
  1367. get: function () {
  1368. return this._selectedEntityChanged;
  1369. },
  1370. },
  1371. /**
  1372. * Gets the event that is raised when the tracked entity changes.
  1373. * @memberof Viewer.prototype
  1374. * @type {Event}
  1375. * @readonly
  1376. */
  1377. trackedEntityChanged: {
  1378. get: function () {
  1379. return this._cesiumWidget.trackedEntityChanged;
  1380. },
  1381. },
  1382. /**
  1383. * Gets or sets the data source to track with the viewer's clock.
  1384. * @memberof Viewer.prototype
  1385. * @type {DataSource}
  1386. */
  1387. clockTrackedDataSource: {
  1388. get: function () {
  1389. return this._cesiumWidget.clockTrackedDataSource;
  1390. },
  1391. set: function (value) {
  1392. if (this._cesiumWidget.clockTrackedDataSource !== value) {
  1393. this._cesiumWidget.clockTrackedDataSource = value;
  1394. linkTimelineToDataSourceClock(this._timeline, value);
  1395. }
  1396. },
  1397. },
  1398. });
  1399. /**
  1400. * Extends the base viewer functionality with the provided mixin.
  1401. * A mixin may add additional properties, functions, or other behavior
  1402. * to the provided viewer instance.
  1403. *
  1404. * @param {Viewer.ViewerMixin} mixin The Viewer mixin to add to this instance.
  1405. * @param {object} [options] The options object to be passed to the mixin function.
  1406. *
  1407. * @see viewerDragDropMixin
  1408. */
  1409. Viewer.prototype.extend = function (mixin, options) {
  1410. //>>includeStart('debug', pragmas.debug);
  1411. if (!defined(mixin)) {
  1412. throw new DeveloperError("mixin is required.");
  1413. }
  1414. //>>includeEnd('debug');
  1415. mixin(this, options);
  1416. };
  1417. /**
  1418. * Resizes the widget to match the container size.
  1419. * This function is called automatically as needed unless
  1420. * <code>useDefaultRenderLoop</code> is set to false.
  1421. */
  1422. Viewer.prototype.resize = function () {
  1423. const cesiumWidget = this._cesiumWidget;
  1424. const container = this._container;
  1425. const width = container.clientWidth;
  1426. const height = container.clientHeight;
  1427. const animationExists = defined(this._animation);
  1428. const timelineExists = defined(this._timeline);
  1429. cesiumWidget.resize();
  1430. if (width === this._lastWidth && height === this._lastHeight) {
  1431. return;
  1432. }
  1433. const panelMaxHeight = height - 125;
  1434. const baseLayerPickerDropDown = this._baseLayerPickerDropDown;
  1435. if (defined(baseLayerPickerDropDown)) {
  1436. baseLayerPickerDropDown.style.maxHeight = `${panelMaxHeight}px`;
  1437. }
  1438. if (defined(this._geocoder)) {
  1439. const geocoderSuggestions = this._geocoder.searchSuggestionsContainer;
  1440. geocoderSuggestions.style.maxHeight = `${panelMaxHeight}px`;
  1441. }
  1442. if (defined(this._infoBox)) {
  1443. this._infoBox.viewModel.maxHeight = panelMaxHeight;
  1444. }
  1445. const timeline = this._timeline;
  1446. let animationContainer;
  1447. let animationWidth = 0;
  1448. let creditLeft = 5;
  1449. let creditBottom = 3;
  1450. let creditRight = 0;
  1451. if (
  1452. animationExists &&
  1453. window.getComputedStyle(this._animation.container).visibility !== "hidden"
  1454. ) {
  1455. const lastWidth = this._lastWidth;
  1456. animationContainer = this._animation.container;
  1457. if (width > 900) {
  1458. animationWidth = 169;
  1459. if (lastWidth <= 900) {
  1460. animationContainer.style.width = "169px";
  1461. animationContainer.style.height = "112px";
  1462. this._animation.resize();
  1463. }
  1464. } else if (width >= 600) {
  1465. animationWidth = 136;
  1466. if (lastWidth < 600 || lastWidth > 900) {
  1467. animationContainer.style.width = "136px";
  1468. animationContainer.style.height = "90px";
  1469. this._animation.resize();
  1470. }
  1471. } else {
  1472. animationWidth = 106;
  1473. if (lastWidth > 600 || lastWidth === 0) {
  1474. animationContainer.style.width = "106px";
  1475. animationContainer.style.height = "70px";
  1476. this._animation.resize();
  1477. }
  1478. }
  1479. creditLeft = animationWidth + 5;
  1480. }
  1481. if (
  1482. timelineExists &&
  1483. window.getComputedStyle(this._timeline.container).visibility !== "hidden"
  1484. ) {
  1485. const fullscreenButton = this._fullscreenButton;
  1486. const vrButton = this._vrButton;
  1487. const timelineContainer = timeline.container;
  1488. const timelineStyle = timelineContainer.style;
  1489. creditBottom = timelineContainer.clientHeight + 3;
  1490. timelineStyle.left = `${animationWidth}px`;
  1491. let pixels = 0;
  1492. if (defined(fullscreenButton)) {
  1493. pixels += fullscreenButton.container.clientWidth;
  1494. }
  1495. if (defined(vrButton)) {
  1496. pixels += vrButton.container.clientWidth;
  1497. }
  1498. timelineStyle.right = `${pixels}px`;
  1499. timeline.resize();
  1500. }
  1501. if (!timelineExists && defined(this._fullscreenButton)) {
  1502. // don't let long credits (like the default ion token) go behind the fullscreen button
  1503. creditRight = this._fullscreenButton.container.clientWidth;
  1504. }
  1505. this._bottomContainer.style.left = `${creditLeft}px`;
  1506. this._bottomContainer.style.bottom = `${creditBottom}px`;
  1507. this._bottomContainer.style.right = `${creditRight}px`;
  1508. this._lastWidth = width;
  1509. this._lastHeight = height;
  1510. };
  1511. /**
  1512. * This forces the widget to re-think its layout, including
  1513. * widget sizes and credit placement.
  1514. */
  1515. Viewer.prototype.forceResize = function () {
  1516. this._lastWidth = 0;
  1517. this.resize();
  1518. };
  1519. /**
  1520. * Renders the scene. This function is called automatically
  1521. * unless <code>useDefaultRenderLoop</code> is set to false;
  1522. */
  1523. Viewer.prototype.render = function () {
  1524. this._cesiumWidget.render();
  1525. };
  1526. /**
  1527. * @returns {boolean} true if the object has been destroyed, false otherwise.
  1528. */
  1529. Viewer.prototype.isDestroyed = function () {
  1530. return false;
  1531. };
  1532. /**
  1533. * Destroys the widget. Should be called if permanently
  1534. * removing the widget from layout.
  1535. */
  1536. Viewer.prototype.destroy = function () {
  1537. if (
  1538. defined(this.screenSpaceEventHandler) &&
  1539. !this.screenSpaceEventHandler.isDestroyed()
  1540. ) {
  1541. this.screenSpaceEventHandler.removeInputAction(
  1542. ScreenSpaceEventType.LEFT_CLICK,
  1543. );
  1544. this.screenSpaceEventHandler.removeInputAction(
  1545. ScreenSpaceEventType.LEFT_DOUBLE_CLICK,
  1546. );
  1547. }
  1548. this._container.removeChild(this._element);
  1549. this._element.removeChild(this._toolbar);
  1550. this._eventHelper.removeAll();
  1551. if (defined(this._geocoder)) {
  1552. this._geocoder = this._geocoder.destroy();
  1553. }
  1554. if (defined(this._homeButton)) {
  1555. this._homeButton = this._homeButton.destroy();
  1556. }
  1557. if (defined(this._sceneModePicker)) {
  1558. this._sceneModePicker = this._sceneModePicker.destroy();
  1559. }
  1560. if (defined(this._projectionPicker)) {
  1561. this._projectionPicker = this._projectionPicker.destroy();
  1562. }
  1563. if (defined(this._baseLayerPicker)) {
  1564. this._baseLayerPicker = this._baseLayerPicker.destroy();
  1565. }
  1566. if (defined(this._animation)) {
  1567. this._element.removeChild(this._animation.container);
  1568. this._animation = this._animation.destroy();
  1569. }
  1570. if (defined(this._timeline)) {
  1571. this._timeline.removeEventListener(
  1572. "settime",
  1573. onTimelineScrubfunction,
  1574. false,
  1575. );
  1576. this._element.removeChild(this._timeline.container);
  1577. this._timeline = this._timeline.destroy();
  1578. }
  1579. if (defined(this._fullscreenButton)) {
  1580. this._fullscreenSubscription.dispose();
  1581. this._element.removeChild(this._fullscreenButton.container);
  1582. this._fullscreenButton = this._fullscreenButton.destroy();
  1583. }
  1584. if (defined(this._vrButton)) {
  1585. this._vrSubscription.dispose();
  1586. this._vrModeSubscription.dispose();
  1587. this._element.removeChild(this._vrButton.container);
  1588. this._vrButton = this._vrButton.destroy();
  1589. }
  1590. if (defined(this._infoBox)) {
  1591. this._element.removeChild(this._infoBox.container);
  1592. this._infoBox = this._infoBox.destroy();
  1593. }
  1594. if (defined(this._selectionIndicator)) {
  1595. this._element.removeChild(this._selectionIndicator.container);
  1596. this._selectionIndicator = this._selectionIndicator.destroy();
  1597. }
  1598. if (this._destroyClockViewModel) {
  1599. this._clockViewModel = this._clockViewModel.destroy();
  1600. }
  1601. this._cesiumWidget = this._cesiumWidget.destroy();
  1602. return destroyObject(this);
  1603. };
  1604. /**
  1605. * @private
  1606. */
  1607. Viewer.prototype._dataSourceAdded = function (
  1608. dataSourceCollection,
  1609. dataSource,
  1610. ) {
  1611. const entityCollection = dataSource.entities;
  1612. entityCollection.collectionChanged.addEventListener(
  1613. Viewer.prototype._onEntityCollectionChanged,
  1614. this,
  1615. );
  1616. };
  1617. /**
  1618. * @private
  1619. */
  1620. Viewer.prototype._dataSourceRemoved = function (
  1621. dataSourceCollection,
  1622. dataSource,
  1623. ) {
  1624. const entityCollection = dataSource.entities;
  1625. entityCollection.collectionChanged.removeEventListener(
  1626. Viewer.prototype._onEntityCollectionChanged,
  1627. this,
  1628. );
  1629. if (defined(this.selectedEntity)) {
  1630. if (
  1631. entityCollection.getById(this.selectedEntity.id) === this.selectedEntity
  1632. ) {
  1633. this.selectedEntity = undefined;
  1634. }
  1635. }
  1636. };
  1637. /**
  1638. * @private
  1639. */
  1640. Viewer.prototype._updateCanAnimate = function (that) {
  1641. return function (isUpdated) {
  1642. that._clockViewModel.canAnimate = isUpdated;
  1643. };
  1644. };
  1645. /**
  1646. * @private
  1647. */
  1648. Viewer.prototype._onTick = function (clock) {
  1649. const time = clock.currentTime;
  1650. let position;
  1651. let enableCamera = false;
  1652. const selectedEntity = this.selectedEntity;
  1653. const showSelection = defined(selectedEntity) && this._enableInfoOrSelection;
  1654. if (
  1655. showSelection &&
  1656. selectedEntity.isShowing &&
  1657. selectedEntity.isAvailable(time)
  1658. ) {
  1659. const state = this._cesiumWidget.dataSourceDisplay.getBoundingSphere(
  1660. selectedEntity,
  1661. true,
  1662. boundingSphereScratch,
  1663. );
  1664. if (state !== BoundingSphereState.FAILED) {
  1665. position = boundingSphereScratch.center;
  1666. } else if (defined(selectedEntity.position)) {
  1667. position = selectedEntity.position.getValue(time, position);
  1668. }
  1669. enableCamera = defined(position);
  1670. }
  1671. const selectionIndicatorViewModel = defined(this._selectionIndicator)
  1672. ? this._selectionIndicator.viewModel
  1673. : undefined;
  1674. if (defined(selectionIndicatorViewModel)) {
  1675. selectionIndicatorViewModel.position = Cartesian3.clone(
  1676. position,
  1677. selectionIndicatorViewModel.position,
  1678. );
  1679. selectionIndicatorViewModel.showSelection = showSelection && enableCamera;
  1680. selectionIndicatorViewModel.update();
  1681. }
  1682. const infoBoxViewModel = defined(this._infoBox)
  1683. ? this._infoBox.viewModel
  1684. : undefined;
  1685. if (defined(infoBoxViewModel)) {
  1686. infoBoxViewModel.showInfo = showSelection;
  1687. infoBoxViewModel.enableCamera = enableCamera;
  1688. infoBoxViewModel.isCameraTracking =
  1689. this.trackedEntity === this.selectedEntity;
  1690. if (showSelection) {
  1691. infoBoxViewModel.titleText = selectedEntity.name ?? selectedEntity.id;
  1692. infoBoxViewModel.description = Property.getValueOrDefault(
  1693. selectedEntity.description,
  1694. time,
  1695. "",
  1696. );
  1697. } else {
  1698. infoBoxViewModel.titleText = "";
  1699. infoBoxViewModel.description = "";
  1700. }
  1701. }
  1702. };
  1703. /**
  1704. * @private
  1705. */
  1706. Viewer.prototype._onEntityCollectionChanged = function (
  1707. collection,
  1708. added,
  1709. removed,
  1710. ) {
  1711. const length = removed.length;
  1712. for (let i = 0; i < length; i++) {
  1713. const removedObject = removed[i];
  1714. if (this.selectedEntity === removedObject) {
  1715. this.selectedEntity = undefined;
  1716. }
  1717. }
  1718. };
  1719. /**
  1720. * @private
  1721. */
  1722. Viewer.prototype._onInfoBoxCameraClicked = function (infoBoxViewModel) {
  1723. if (
  1724. infoBoxViewModel.isCameraTracking &&
  1725. this.trackedEntity === this.selectedEntity
  1726. ) {
  1727. this.trackedEntity = undefined;
  1728. } else {
  1729. const selectedEntity = this.selectedEntity;
  1730. const position = selectedEntity.position;
  1731. if (defined(position)) {
  1732. this.trackedEntity = this.selectedEntity;
  1733. } else {
  1734. this.zoomTo(this.selectedEntity);
  1735. }
  1736. }
  1737. };
  1738. /**
  1739. * @private
  1740. */
  1741. Viewer.prototype._clearTrackedObject = function () {
  1742. this.trackedEntity = undefined;
  1743. };
  1744. /**
  1745. * @private
  1746. */
  1747. Viewer.prototype._onInfoBoxClockClicked = function (infoBoxViewModel) {
  1748. this.selectedEntity = undefined;
  1749. };
  1750. /**
  1751. * @private
  1752. */
  1753. Viewer.prototype._clearObjects = function () {
  1754. this.trackedEntity = undefined;
  1755. this.selectedEntity = undefined;
  1756. };
  1757. /**
  1758. * @private
  1759. */
  1760. Viewer.prototype._onDataSourceChanged = function (dataSource) {
  1761. if (this.clockTrackedDataSource === dataSource) {
  1762. linkTimelineToDataSourceClock(this.timeline, dataSource);
  1763. }
  1764. };
  1765. /**
  1766. * @private
  1767. */
  1768. Viewer.prototype._onDataSourceAdded = function (
  1769. dataSourceCollection,
  1770. dataSource,
  1771. ) {
  1772. if (
  1773. this._cesiumWidget._automaticallyTrackDataSourceClocks &&
  1774. dataSource === this.clockTrackedDataSource
  1775. ) {
  1776. // When data sources are added to the CesiumWidget they may be automatically
  1777. // tracked in that class but we also need to update the timeline in this class
  1778. linkTimelineToDataSourceClock(this._timeline, dataSource);
  1779. }
  1780. const id = dataSource.entities.id;
  1781. const removalFunc = this._eventHelper.add(
  1782. dataSource.changedEvent,
  1783. Viewer.prototype._onDataSourceChanged,
  1784. this,
  1785. );
  1786. this._dataSourceChangedListeners[id] = removalFunc;
  1787. };
  1788. /**
  1789. * @private
  1790. */
  1791. Viewer.prototype._onDataSourceRemoved = function (
  1792. dataSourceCollection,
  1793. dataSource,
  1794. ) {
  1795. const id = dataSource.entities.id;
  1796. this._dataSourceChangedListeners[id]();
  1797. this._dataSourceChangedListeners[id] = undefined;
  1798. };
  1799. /**
  1800. * Asynchronously sets the camera to view the provided entity, entities, or data source.
  1801. * If the data source is still in the process of loading or the visualization is otherwise still loading,
  1802. * this method waits for the data to be ready before performing the zoom.
  1803. *
  1804. * <p>The offset is heading/pitch/range in the local east-north-up reference frame centered at the center of the bounding sphere.
  1805. * The heading and the pitch angles are defined in the local east-north-up reference frame.
  1806. * The heading is the angle from y axis and increasing towards the x axis. Pitch is the rotation from the xy-plane. Positive pitch
  1807. * angles are above the plane. Negative pitch angles are below the plane. The range is the distance from the center. If the range is
  1808. * zero, a range will be computed such that the whole bounding sphere is visible.</p>
  1809. *
  1810. * <p>In 2D, there must be a top down view. The camera will be placed above the target looking down. The height above the
  1811. * target will be the range. The heading will be determined from the offset. If the heading cannot be
  1812. * determined from the offset, the heading will be north.</p>
  1813. *
  1814. * @param {Entity|Entity[]|EntityCollection|DataSource|ImageryLayer|Cesium3DTileset|TimeDynamicPointCloud|Promise<Entity|Entity[]|EntityCollection|DataSource|ImageryLayer|Cesium3DTileset|TimeDynamicPointCloud|VoxelPrimitive|BufferPrimitiveCollection<BufferPrimitive>>} target The entity, array of entities, entity collection, data source, Cesium3DTileset, point cloud, or imagery layer to view. You can also pass a promise that resolves to one of the previously mentioned types.
  1815. * @param {HeadingPitchRange} [offset] The offset from the center of the entity in the local east-north-up reference frame.
  1816. * @returns {Promise<boolean>} A Promise that resolves to true if the zoom was successful or false if the target is not currently visualized in the scene or the zoom was cancelled.
  1817. */
  1818. Viewer.prototype.zoomTo = function (target, offset) {
  1819. return this._cesiumWidget.zoomTo(target, offset);
  1820. };
  1821. /**
  1822. * Flies the camera to the provided entity, entities, or data source.
  1823. * If the data source is still in the process of loading or the visualization is otherwise still loading,
  1824. * this method waits for the data to be ready before performing the flight.
  1825. *
  1826. * <p>The offset is heading/pitch/range in the local east-north-up reference frame centered at the center of the bounding sphere.
  1827. * The heading and the pitch angles are defined in the local east-north-up reference frame.
  1828. * The heading is the angle from y axis and increasing towards the x axis. Pitch is the rotation from the xy-plane. Positive pitch
  1829. * angles are above the plane. Negative pitch angles are below the plane. The range is the distance from the center. If the range is
  1830. * zero, a range will be computed such that the whole bounding sphere is visible.</p>
  1831. *
  1832. * <p>In 2D, there must be a top down view. The camera will be placed above the target looking down. The height above the
  1833. * target will be the range. The heading will be determined from the offset. If the heading cannot be
  1834. * determined from the offset, the heading will be north.</p>
  1835. *
  1836. * @param {Entity|Entity[]|EntityCollection|DataSource|ImageryLayer|Cesium3DTileset|TimeDynamicPointCloud|Promise<Entity|Entity[]|EntityCollection|DataSource|ImageryLayer|Cesium3DTileset|TimeDynamicPointCloud|VoxelPrimitive|BufferPrimitiveCollection<BufferPrimitive>>} target The entity, array of entities, entity collection, data source, Cesium3DTileset, point cloud, or imagery layer to view. You can also pass a promise that resolves to one of the previously mentioned types.
  1837. * @param {object} [options] Object with the following properties:
  1838. * @param {number} [options.duration=3.0] The duration of the flight in seconds.
  1839. * @param {number} [options.maximumHeight] The maximum height at the peak of the flight.
  1840. * @param {HeadingPitchRange} [options.offset] The offset from the target in the local east-north-up reference frame centered at the target.
  1841. * @returns {Promise<boolean>} A Promise that resolves to true if the flight was successful or false if the target is not currently visualized in the scene or the flight was cancelled. //TODO: Cleanup entity mentions
  1842. */
  1843. Viewer.prototype.flyTo = function (target, options) {
  1844. return this._cesiumWidget.flyTo(target, options);
  1845. };
  1846. /**
  1847. * A function that augments a Viewer instance with additional functionality.
  1848. * @callback Viewer.ViewerMixin
  1849. * @param {Viewer} viewer The viewer instance.
  1850. * @param {object} options Options object to be passed to the mixin function.
  1851. *
  1852. * @see Viewer#extend
  1853. */
  1854. export default Viewer;