1. /**
  2. * @module meteoJS/modelviewer/container
  3. */
  4. import Unique from '../base/Unique.js';
  5. import addEventFunctions from '../Events.js';
  6. import Resource from './Resource.js';
  7. import Node from './Node.js';
  8. import Display from './Display.js';
  9. import Variable from './Variable.js';
  10. import VariableCollection from './VariableCollection.js';
  11. /**
  12. * Triggered, when visible Resource changes.
  13. *
  14. * @event module:meteoJS/modelviewer/container#change:enabledResources
  15. * @type {Object}
  16. * @property {Object} options - Options.
  17. * @property {Map.<integer,module:meteoJS/modelviewer/resource.Resource>}
  18. * [options.enabledResources] - Enabled resources, selected by selectedVariables.
  19. */
  20. /**
  21. * Triggered, when visible Resource changes.
  22. *
  23. * @event module:meteoJS/modelviewer/container#change:visibleResource
  24. */
  25. /**
  26. * Triggered, when displayVariables is changed.
  27. *
  28. * @event module:meteoJS/modelviewer/container#change:displayVariables
  29. * @type {Object}
  30. * @property {Object} options - Options.
  31. * @property {Set.<module:meteoJS/modelviewer/variable.Variable>}
  32. * [options.addedVariables] - Added variables to displayVariables.
  33. * @property {Set.<module:meteoJS/modelviewer/variable.Variable>}
  34. * [options.removedVariables] - Removed variables to displayVariables.
  35. */
  36. /**
  37. * Triggered, when selectedVariables is changed.
  38. *
  39. * @event module:meteoJS/modelviewer/container#change:selectedVariables
  40. * @type {Object}
  41. * @property {Object} options - Options.
  42. * @property {Set.<module:meteoJS/modelviewer/variable.Variable>}
  43. * [options.addedVariables] - Added variables to selectedVariables.
  44. * @property {Set.<module:meteoJS/modelviewer/variable.Variable>}
  45. * [options.removedVariables] - Removed variables to selectedVariables.
  46. */
  47. /**
  48. * If a suitable resource is searched, this method will be called several times.
  49. * The first argument ist a list of variables. These variables are collected of
  50. * one hierarchy level, defined by the
  51. * {@link module:meteoJS/modelviewer/resources.Resources|resources object}.
  52. * The method returns an ordered list of these passed variables (or a subset).
  53. * For these variables, further down in the hierarchy, a possible resource is
  54. * searched. If one is found, this variable will be used for selectedVariables.
  55. * Method is only used if adaptSuitableResource is enabled. Default algorythm is
  56. * to return the list in the order of the Iterator.
  57. *
  58. * @typedef {Function} module:meteoJS/modelviewer/container~getPossibleVariables
  59. * @param {module:meteoJS/modelviewer/variable.Variable[]}
  60. * possibleSelectedVariables - Variables to return an ordered list.
  61. * @param {Set<module:meteoJS/modelviewer/variable.Variable>}
  62. * selectedVariables - Already selected variables so far, top-down in
  63. * hierarchy.
  64. * @returns {module:meteoJS/modelviewer/variable.Variable[]} - Ordered list.
  65. */
  66. /**
  67. * With the passed selectedVariables, the method determines if already a
  68. * suitable resource should be selected. If method returns true, the property
  69. * selectedVariables will be set by the passed Set.
  70. * Method used if adaptSuitableResource is enabled. Default algorythm is to
  71. * return true if the Node of the lastAddedVariable contains resources.
  72. *
  73. * @typedef {Function} module:meteoJS/modelviewer/container~isResourceSelected
  74. * @param {Set<module:meteoJS/modelviewer/variable.Variable>}
  75. * selectedVariables - Selected variables so far.
  76. * @param {undefined|module:meteoJS/modelviewer/variable.Variable}
  77. * lastAddedVariable - Last added variable to selectedVariables.
  78. * @returns {boolean} - True if a suitable resource should be selected with the
  79. * current state of selectedVariables.
  80. */
  81. /**
  82. * Options to adapt a suitable resource to display.
  83. *
  84. * @typedef {Object}
  85. * module:meteoJS/modelviewer/container~adaptSuitableResource
  86. * @param {boolean} enabled - Enabled adapt suitable resource.
  87. * @param {module:meteoJS/modelviewer/container~getPossibleVariables}
  88. * getPossibleVariables - Determines order of variables of a hierarchy level.
  89. * @param {module:meteoJS/modelviewer/container~isResourceSelected}
  90. * isResourceSelected - Is selectedVariables complete.
  91. */
  92. /**
  93. * Options for constructor.
  94. *
  95. * @typedef {module:meteoJS/base/unique~options}
  96. * module:meteoJS/modelviewer/container~options
  97. * @param {module:meteoJS/modelviewer/display.Display} [display]
  98. * Display object to output the container content to DOM.
  99. * @param {module:meteoJS/modelviewer/container~adaptSuitableResource}
  100. * [adaptSuitableResource] - Options for adapt suitable resource.
  101. */
  102. /**
  103. * This object represents a container, that displays one resource.
  104. * Via displayVariables the appropriate resource is chosen.
  105. *
  106. * @extends module:meteoJS/base/unique.Unique
  107. * @fires module:meteoJS/modelviewer/container#change:visibleResource
  108. * @fires module:meteoJS/modelviewer/container#change:enabledResources
  109. * @fires module:meteoJS/modelviewer/container#change:displayVariables
  110. * @fires module:meteoJS/modelviewer/container#change:selectedVariables
  111. */
  112. export class Container extends Unique {
  113. /**
  114. * @param {module:meteoJS/modelviewer/container~options} [options] - Options.
  115. */
  116. constructor({
  117. id,
  118. display = undefined,
  119. adaptSuitableResource = {}
  120. } = {}) {
  121. super({
  122. id
  123. });
  124. /**
  125. * @type undefined|module:meteoJS/modelviewer/display.Display
  126. * @private
  127. */
  128. this._display = (display === undefined) ? new Display() : display;
  129. this._display.modelviewer = this.modelviewer;
  130. this._display.container = this;
  131. this._display.parentNode = this._containerNode;
  132. /**
  133. * @type module:meteoJS/modelviewer/container~adaptSuitableResource
  134. * @private
  135. */
  136. this._adaptSuitableResource = {};
  137. this._initAdaptSuitableResource(adaptSuitableResource);
  138. /**
  139. * @type undefined|module:meteoJS/modelviewer.Modelviewer
  140. * @private
  141. */
  142. this._modelviewer = undefined;
  143. /**
  144. * @type undefined|module:meteoJS/modelviewer/resource.Resource
  145. * @private
  146. */
  147. this._visibleResource;
  148. /**
  149. * @type Set<module:meteoJS/modelviewer/variable.Variable>
  150. * @private
  151. */
  152. this._displayVariables = new Set();
  153. /**
  154. * @type Map.<module:meteoJS/modelviewer/variableCollection.VariableCollection,module:meteoJS/modelviewer/variable.Variable>
  155. * @private
  156. */
  157. this._selectedVariables = new Map();
  158. /**
  159. * @type module:meteoJS/modelviewer/node.Node|undefined
  160. * @private
  161. */
  162. this._selectedNode = undefined;
  163. /**
  164. * @type Map<integer,module:meteoJS/modelviewer/resource.Resource>
  165. * @private
  166. */
  167. this._enabledResources = new Map();
  168. /**
  169. * @type undefined|external:HTMLElement
  170. * @private
  171. */
  172. this._containerNode = undefined;
  173. /**
  174. * Function to call change:selectedVariables debouncec.
  175. *
  176. * @type Function
  177. * @private
  178. */
  179. this._debouncedChangeSelectedVariables = (() => {
  180. let timeoutId;
  181. let totalAddedVariables = new Set();
  182. let totalRemovedVariables = new Set();
  183. return ({ addedVariables, removedVariables }) => {
  184. for (const v of addedVariables)
  185. if (totalRemovedVariables.has(v))
  186. totalRemovedVariables.delete(v);
  187. for (const v of removedVariables)
  188. if (totalAddedVariables.has(v))
  189. totalAddedVariables.delete(v);
  190. totalAddedVariables = new Set([...totalAddedVariables, ...addedVariables]);
  191. totalRemovedVariables = new Set([...totalRemovedVariables, ...removedVariables]);
  192. /*console.log([
  193. [...addedVariables].map(v => v.id),
  194. [...removedVariables].map(v => v.id),
  195. [...totalAddedVariables].map(v => v.id),
  196. [...totalRemovedVariables].map(v => v.id),
  197. ]);*/
  198. clearTimeout(timeoutId);
  199. timeoutId = setTimeout(() => {
  200. this.trigger('change:selectedVariables', {
  201. addedVariables: totalAddedVariables,
  202. removedVariables: totalRemovedVariables
  203. });
  204. totalAddedVariables.clear();
  205. totalRemovedVariables.clear();
  206. }, 300);
  207. };
  208. })();
  209. /**
  210. * @type Object<string,Object<string,mixed>>
  211. * @private
  212. */
  213. this._listeners = {
  214. mirror: [],
  215. timeline: {
  216. timeline: undefined,
  217. listenerKey: undefined
  218. },
  219. resources: {
  220. resources: undefined,
  221. listenerKey: undefined
  222. }
  223. };
  224. }
  225. /**
  226. * Display object to generate dom output.
  227. *
  228. * @type undefined|module:meteoJS/modelviewer/display.Display
  229. * @readonly
  230. */
  231. get display() {
  232. return this._display;
  233. }
  234. /**
  235. * This container belongs to this modelviewer object.
  236. *
  237. * @type undefined|module:meteoJS/modelviewer.Modelviewer
  238. * @package
  239. */
  240. get modelviewer() {
  241. return this._modelviewer;
  242. }
  243. set modelviewer(modelviewer) {
  244. this._modelviewer = modelviewer;
  245. if (this._modelviewer === undefined) {
  246. if (this._listeners.timeline.listenerKey !== undefined)
  247. this._listeners.timeline.timeline
  248. .un('change:time', this._listeners.timeline.listenerKey);
  249. if (this._listeners.resources.listenerKey !== undefined)
  250. this._listeners.resources.resources
  251. .un('change:resources', this._listeners.resources.listenerKey);
  252. return;
  253. }
  254. this._display.modelviewer = modelviewer;
  255. this._listeners.timeline.timeline = this._modelviewer.timeline;
  256. this._listeners.timeline.listenerKey = this._modelviewer.timeline
  257. .on('change:time', () => this._setVisibleResource());
  258. this._listeners.resources.resources = this._modelviewer.resources;
  259. this._listeners.resources.listenerKey = this._modelviewer.resources
  260. .on('change:resources', () => {
  261. this._setTimes();
  262. this._setEnabledResources();
  263. this._updateSelectedVariables();
  264. });
  265. this._setTimes();
  266. }
  267. /**
  268. * DOM node to append container's output.
  269. *
  270. * @type undefined|external:HTMLElement
  271. * @package
  272. */
  273. get containerNode() {
  274. return this._containerNode;
  275. }
  276. set containerNode(containerNode) {
  277. this._containerNode = containerNode;
  278. if (this._containerNode === undefined)
  279. return;
  280. this._display.parentNode = this._containerNode;
  281. }
  282. /**
  283. * Currently visible resource. Could be an empty resource.
  284. *
  285. * @type module:meteoJS/modelviewer/resource.Resource
  286. * @readonly
  287. */
  288. get visibleResource() {
  289. return (this._visibleResource === undefined) ?
  290. new Resource() : this._visibleResource;
  291. }
  292. /**
  293. * These variables define, which resource is displayed.
  294. * If adaptSuitableResource is not enabled, then the displayed resource is
  295. * exactly defined by these variables (and additionally the datetime selected
  296. * by the timeline object). If adaptSuitableResource is enabled, then a
  297. * resource is displayed, that matches the variables but can be defined by
  298. * additional variables.
  299. * Setter allows Set or Array. Getter returns always Set.
  300. *
  301. * @type Set<module:meteoJS/modelviewer/variable.Variable>
  302. */
  303. get displayVariables() {
  304. return this._displayVariables;
  305. }
  306. set displayVariables(variables) {
  307. let addedVariables = new Set();
  308. variables = new Set(variables);
  309. for (let variable of variables)
  310. if (!this._displayVariables.has(variable))
  311. addedVariables.add(variable);
  312. let removedVariables = new Set();
  313. for (let displayVariable of this.displayVariables)
  314. if (!variables.has(displayVariable))
  315. removedVariables.add(displayVariable);
  316. if (
  317. addedVariables.size > 0 ||
  318. removedVariables.size > 0
  319. ) {
  320. this._displayVariables = variables;
  321. this._updateSelectedVariables({
  322. addedVariables,
  323. removedVariables
  324. });
  325. this.trigger(
  326. 'change:displayVariables',
  327. { addedVariables, removedVariables }
  328. );
  329. }
  330. }
  331. /**
  332. * These variables define excactly, which resource will be displayed. These
  333. * variables are retrieved from the available resources and displayVariables.
  334. * Together with the selected time in the timeline, the resource to display
  335. * is uniquely defined.
  336. * If adaptSuitableResource is not enabled, selectedVariables is equal to
  337. * displayVariables.
  338. *
  339. * @type Set<module:meteoJS/modelviewer/variable.Variable>
  340. * @readonly
  341. */
  342. get selectedVariables() {
  343. return new Set([...this._selectedVariables.values()]);
  344. }
  345. /**
  346. * Returns the selected Variable of a VariableCollection. If no selected
  347. * variable exists, an empty Variable-Object will be returned.
  348. *
  349. * @param {module:meteoJS/modelviewer/variableCollection.VariableCollection}
  350. * variableCollection - VariableCollection.
  351. * @returns {module:meteoJS/modelviewer/variable.Variable}
  352. * The selected Variable of the colleciton.
  353. */
  354. getSelectedVariable(variableCollection) {
  355. const result = this._selectedVariables.get(variableCollection);
  356. return (result === undefined) ? new Variable({ id: undefined }) : result;
  357. }
  358. /**
  359. * Returns an array of times (for the timeline). For all of these times, there
  360. * exists resources which match with the current displayVariables.
  361. *
  362. * @type Date[]
  363. * @readonly
  364. */
  365. get enabledTimes() {
  366. return [...this._enabledResources.keys()]
  367. .filter(datetime => !isNaN(datetime))
  368. .map(datetime => new Date(datetime));
  369. }
  370. /**
  371. * Exchanges variables in displayVariables. The variable with the same
  372. * Collection will be exchanged. If none is found, the variable will be added.
  373. *
  374. * @param {Set<module:meteoJS/modelviewer/variable.Variable>} variables
  375. * Add these variables to the set of displayVariables.
  376. * @returns {module:meteoJS/modelviewer/container.Container} - This.
  377. * @fires module:meteoJS/modelviewer/container#change:displayVariables
  378. */
  379. exchangeDisplayVariable(variables) {
  380. let displayVariables = new Set(this.displayVariables);
  381. for (let variable of variables)
  382. for (let displayVariable of this.displayVariables)
  383. if (displayVariable.variableCollection ===
  384. variable.variableCollection) {
  385. displayVariables.delete(displayVariable);
  386. displayVariables.add(variable);
  387. }
  388. for (let variable of variables)
  389. if (!displayVariables.has(variable))
  390. displayVariables.add(variable);
  391. this.displayVariables = displayVariables;
  392. return this;
  393. }
  394. /**
  395. * Mirrors (parts of) the displayVariables form another container. With this
  396. * feature, e.g. in different containers can be viewed plots of different
  397. * models. If you change e.g. the field in the first container, all other
  398. * containers, that mirrors form this container, will also change the viewed
  399. * content. It is possible to mirror different VariableCollections from
  400. * different containers.
  401. *
  402. * @param {module:meteoJS/modelviewer/container.Container} [container]
  403. * Mirrors from this container.
  404. * @param {module:meteoJS/modelviewer/variableCollection.VariableCollection[]}
  405. * [variableCollections] - The displayVariables of these VariableCollections
  406. * are mirrored. If omitted, all VariableCollections are mirrored.
  407. */
  408. mirrorsFrom(container = undefined, variableCollections = undefined) {
  409. this._listeners.mirror =
  410. this._listeners.mirror.filter(mirrorConfig => {
  411. if (mirrorConfig.container === container
  412. || container === undefined) {
  413. mirrorConfig.container
  414. .un('change:displayVariables', mirrorConfig.listenerKey);
  415. return false;
  416. }
  417. return true;
  418. });
  419. if (container === undefined)
  420. return;
  421. if (variableCollections !== undefined
  422. && variableCollections.length < 1)
  423. return;
  424. if (variableCollections === undefined)
  425. variableCollections = this.modelviewer.resources.variableCollections;
  426. const onChangeDisplayVariables = () => {
  427. const newDisplayVariables = new Set();
  428. for (const variable of container.displayVariables)
  429. variableCollections.forEach(collection => {
  430. if (variable.variableCollection === collection)
  431. newDisplayVariables.add(variable);
  432. });
  433. this.exchangeDisplayVariable(newDisplayVariables);
  434. };
  435. const listenerKey = container
  436. .on('change:displayVariables', onChangeDisplayVariables);
  437. const mirrorConfig = {
  438. container,
  439. listenerKey,
  440. variableCollections
  441. };
  442. this._listeners.mirror.forEach(mC => {
  443. const newVariableCollection = [];
  444. mC.variableCollections.forEach(collection => {
  445. let isContained = false;
  446. variableCollections.forEach(variableCollection => {
  447. if (variableCollection === collection)
  448. isContained = true;
  449. });
  450. if (!isContained)
  451. newVariableCollection.push(collection);
  452. });
  453. if (newVariableCollection.length < mC.variableCollections.length)
  454. this.mirrorsFrom(mC.container, newVariableCollection);
  455. });
  456. this._listeners.mirror.push(mirrorConfig);
  457. onChangeDisplayVariables();
  458. }
  459. /**
  460. * Get all containers, from which this container mirrors some variables from.
  461. * As values of the returned Map-Object an array with the mirrored
  462. * VariableColletions is returned.
  463. *
  464. * @returns {Map.<module:meteoJS/modelviewer/container.Container,module:meteoJS/modelviewer/variableCollection.VariableCollection[]>}
  465. */
  466. getMirrorsFrom() {
  467. const result = new Map();
  468. this._listeners.mirror.forEach(mirrorConfig => {
  469. result.set(mirrorConfig.container, mirrorConfig.variableCollections);
  470. });
  471. return result;
  472. }
  473. /**
  474. * Sets all available times in the timeline object for this container.
  475. *
  476. * @private
  477. */
  478. _setTimes() {
  479. let [selectedVariables] =
  480. this._getSelectedVariablesWithResources(
  481. [this.modelviewer.resources.topNode],
  482. new Set(),
  483. undefined,
  484. selectedVariables => {
  485. let result = true;
  486. this.modelviewer.resources._timesVariableCollections.forEach(collection => {
  487. let contained = false;
  488. for (let selectedVariable of selectedVariables) {
  489. if (collection.contains(selectedVariable))
  490. contained = true;
  491. }
  492. if (!contained)
  493. result = false;
  494. });
  495. return result;
  496. }
  497. );
  498. if (selectedVariables === undefined)
  499. selectedVariables = new Set();
  500. const availableTimes = (selectedVariables.size == 0)
  501. ? []
  502. : this.modelviewer.resources
  503. .getTimesByVariables({ variables: selectedVariables });
  504. this.modelviewer.timeline.setTimesBySetID(this.id, availableTimes);
  505. }
  506. /**
  507. * Updates the selected variables, according to displayVariables.
  508. *
  509. * @private
  510. */
  511. _updateSelectedVariables({
  512. addedVariables = undefined,
  513. removedVariables = undefined
  514. } = {}) {
  515. if (!this._adaptSuitableResource.enabled) {
  516. let selectedNode = undefined;
  517. const findFirstNodeWithVariable = node => {
  518. if (node.hasResourcesByVariables(true, ...this.displayVariables))
  519. selectedNode = node;
  520. else
  521. for (const childNode of node.children)
  522. findFirstNodeWithVariable(childNode);
  523. };
  524. findFirstNodeWithVariable(this.modelviewer.resources.topNode);
  525. this._setSelectedVariables(this.displayVariables, selectedNode);
  526. return;
  527. }
  528. let nodes = [];
  529. const sV = new Set();
  530. let lSV = undefined;
  531. if (addedVariables === undefined || removedVariables === undefined)
  532. nodes.push(this.modelviewer.resources.topNode);
  533. else {
  534. const findFirstNodeWithVariable = node => {
  535. let isFound = false;
  536. for (const variable of [...addedVariables, ...removedVariables]) {
  537. if (variable.variableCollection !== undefined
  538. && variable.variableCollection.node === node) {
  539. nodes.push(node);
  540. isFound = true;
  541. break;
  542. }
  543. }
  544. if (!isFound) {
  545. const tempSV = this.getSelectedVariable(node.variableCollection);
  546. if (tempSV.id !== undefined) {
  547. lSV = tempSV;
  548. sV.add(lSV);
  549. for (const childNode of node.children)
  550. findFirstNodeWithVariable(childNode);
  551. }
  552. }
  553. };
  554. findFirstNodeWithVariable(this.modelviewer.resources.topNode);
  555. nodes = nodes.filter((n,i,a) => i===a.indexOf(n));
  556. if (nodes.length < 1)
  557. nodes.push(this.modelviewer.resources.topNode);
  558. }
  559. let [selectedVariables, lastSelectedVariable] =
  560. this._getSelectedVariablesWithResources(
  561. nodes,
  562. sV,
  563. lSV
  564. );
  565. let node;
  566. if (selectedVariables === undefined) {
  567. selectedVariables = sV;
  568. node = (lSV !== undefined)
  569. ? lSV.variableCollection.node
  570. : new Node({ variableCollection: new VariableCollection() });
  571. }
  572. else
  573. node = lastSelectedVariable.variableCollection.node;
  574. this._setSelectedVariables(selectedVariables, node);
  575. }
  576. /**
  577. * @typedef result_getSelectedVariablesWithResources
  578. * @type {Array}
  579. * @property {undefined|Set<module:meteoJS/modelviewer/variable.Variable>} 0
  580. * @property {undefined|module:meteoJS/modelviewer/variable.Variable} 1
  581. */
  582. /**
  583. *
  584. *
  585. * @param {Set<module:meteoJS/modelviewer/node.Node>} nodes - Nodes to check.
  586. * @param {Set<module:meteoJS/modelviewer/variable.Variable>}
  587. * selectedVariables - Selected Variables from top until this node.
  588. * @returns {result_getSelectedVariablesWithResources}
  589. * Array with first element the SelectedVariables, second element the last
  590. * selectedVariable (node most down in the tree).
  591. * @private
  592. */
  593. _getSelectedVariablesWithResources(
  594. nodes,
  595. selectedVariables,
  596. lastSelectedVariable,
  597. isResourceSelected = this._adaptSuitableResource.isResourceSelected
  598. ) {
  599. if (isResourceSelected.call(this, selectedVariables, lastSelectedVariable))
  600. return [selectedVariables, lastSelectedVariable];
  601. let result = [undefined, undefined];
  602. const checkPossibleVariable = possibleSelectedVariable => {
  603. let tempSelectedVariables = new Set(selectedVariables);
  604. tempSelectedVariables.add(possibleSelectedVariable);
  605. let [resultSelectedVariables, resultLastSelectedVariable] =
  606. this
  607. ._getSelectedVariablesWithResources(
  608. possibleSelectedVariable.variableCollection.node.children,
  609. tempSelectedVariables,
  610. possibleSelectedVariable,
  611. isResourceSelected
  612. );
  613. if (resultSelectedVariables !== undefined) {
  614. result[0] = resultSelectedVariables;
  615. result[1] = resultLastSelectedVariable;
  616. return;
  617. }
  618. let isOnlyTimesVariables = true;
  619. for (let selectedVariable of tempSelectedVariables) {
  620. let contained = false;
  621. this.modelviewer.resources._timesVariableCollections.forEach(collection => {
  622. if (collection.contains(selectedVariable))
  623. contained = true;
  624. });
  625. if (!contained)
  626. isOnlyTimesVariables = false;
  627. }
  628. if (isOnlyTimesVariables &&
  629. tempSelectedVariables.size == this.modelviewer.resources._timesVariableCollections.size) {
  630. result[0] = tempSelectedVariables;
  631. result[1] = possibleSelectedVariable;
  632. }
  633. };
  634. let availableSelectedVariables = [];
  635. for (let childNode of nodes) {
  636. if (this.modelviewer.resources.availableVariablesMap.has(childNode) &&
  637. this.modelviewer.resources.availableVariablesMap.get(childNode).size)
  638. for (const variable of childNode.variableCollection) {
  639. if (!this.modelviewer.resources
  640. .availableVariablesMap.get(childNode).has(variable))
  641. continue;
  642. if (this.displayVariables.has(variable))
  643. checkPossibleVariable(variable);
  644. else if (this._adaptSuitableResource.enabled)
  645. availableSelectedVariables.push(variable);
  646. if (result[0] !== undefined)
  647. break;
  648. }
  649. if (result[0] !== undefined)
  650. break;
  651. }
  652. if (result[0] !== undefined)
  653. return result;
  654. const possibleSelectedVariables = this._adaptSuitableResource
  655. .getPossibleVariables
  656. .call(this, availableSelectedVariables, selectedVariables);
  657. for (const variable of possibleSelectedVariables) {
  658. checkPossibleVariable(variable);
  659. if (result[0] !== undefined)
  660. break;
  661. }
  662. return result;
  663. }
  664. /**
  665. * @param {Set<module:meteoJS/modelviewer/variable.Variable>}
  666. * selectedVariables - New selectedVariables.
  667. * @param {module:meteoJS/modelviewer/node.Node} selectedNode
  668. * Selectes resources from this Node.
  669. * @private
  670. */
  671. _setSelectedVariables(selectedVariables, selectedNode) {
  672. let addedVariables = new Set();
  673. selectedVariables = new Set(selectedVariables);
  674. for (let variable of selectedVariables)
  675. if (!this.selectedVariables.has(variable))
  676. addedVariables.add(variable);
  677. let removedVariables = new Set();
  678. for (let selectedVariable of this.selectedVariables)
  679. if (!selectedVariables.has(selectedVariable))
  680. removedVariables.add(selectedVariable);
  681. if (
  682. addedVariables.size > 0 ||
  683. removedVariables.size > 0 ||
  684. this._selectedNode !== selectedNode
  685. ) {
  686. this._selectedVariables.clear();
  687. for (const variable of selectedVariables)
  688. this._selectedVariables.set(variable.variableCollection, variable);
  689. this._selectedNode = selectedNode;
  690. this._setTimes();
  691. this._setEnabledResources();
  692. this._debouncedChangeSelectedVariables({
  693. addedVariables,
  694. removedVariables
  695. });
  696. }
  697. }
  698. /**
  699. * Sets internally _enabledResources. These resources are selected by
  700. * selectedVariable. The visibleResource is determined from this resources.
  701. *
  702. * @private
  703. */
  704. _setEnabledResources() {
  705. this._enabledResources.clear();
  706. if (this._selectedNode === undefined)
  707. return;
  708. if (this.selectedVariables.size != 0)
  709. this._selectedNode
  710. .getResourcesByVariables(true, ...this.selectedVariables)
  711. .filter(r => r.datetime && !isNaN(r.datetime.valueOf()))
  712. .forEach(r => this._enabledResources.set(r.datetime.valueOf(), r));
  713. this.modelviewer.timeline
  714. .setEnabledTimesBySetID(this.id, this.enabledTimes);
  715. this.trigger('change:enabledResources', this._enabledResources);
  716. this._setVisibleResource();
  717. }
  718. /**
  719. * Sets visible resource.
  720. *
  721. * @private
  722. */
  723. _setVisibleResource() {
  724. let oldVisibleResource = this._visibleResource;
  725. let datetime = this.modelviewer.timeline.getSelectedTime().valueOf();
  726. if (this._enabledResources.has(datetime))
  727. this._visibleResource = this._enabledResources.get(datetime);
  728. else
  729. this._visibleResource = undefined;
  730. if (this._visibleResource !== oldVisibleResource)
  731. this.trigger('change:visibleResource');
  732. }
  733. /**
  734. * Inits private property _adaptSuitableResource.
  735. *
  736. * @param {module:meteoJS/modelviewer/container~adaptSuitableResource}
  737. * [adaptSuitableResource] - Adapt suitable resource.
  738. * @private
  739. */
  740. _initAdaptSuitableResource({ enabled = true,
  741. getPossibleVariables = undefined,
  742. isResourceSelected = undefined,
  743. //excludeVariableCollectionFromSimiliarDisplay = []
  744. } = {}) {
  745. this._adaptSuitableResource = {
  746. enabled,
  747. getPossibleVariables,
  748. isResourceSelected
  749. };
  750. if (this._adaptSuitableResource.getPossibleVariables === undefined)
  751. this._adaptSuitableResource.getPossibleVariables =
  752. availableSV => availableSV;
  753. if (this._adaptSuitableResource.isResourceSelected === undefined)
  754. this._adaptSuitableResource.isResourceSelected =
  755. (selectedVariables, lastAddedVariable) => {
  756. if (lastAddedVariable === undefined)
  757. return false;
  758. let resources = lastAddedVariable.variableCollection
  759. .node.getResourcesByVariables(true, ...selectedVariables);
  760. return resources.length > 0;
  761. };
  762. }
  763. }
  764. addEventFunctions(Container.prototype);
  765. export default Container;