123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440 |
- import AssociativeArray from "../Core/AssociativeArray.js";
- import createGuid from "../Core/createGuid.js";
- import defined from "../Core/defined.js";
- import DeveloperError from "../Core/DeveloperError.js";
- import Event from "../Core/Event.js";
- import Iso8601 from "../Core/Iso8601.js";
- import JulianDate from "../Core/JulianDate.js";
- import RuntimeError from "../Core/RuntimeError.js";
- import TimeInterval from "../Core/TimeInterval.js";
- import Entity from "./Entity.js";
- const entityOptionsScratch = {
- id: undefined,
- };
- function fireChangedEvent(collection) {
- if (collection._firing) {
- collection._refire = true;
- return;
- }
- if (collection._suspendCount === 0) {
- const added = collection._addedEntities;
- const removed = collection._removedEntities;
- const changed = collection._changedEntities;
- if (changed.length !== 0 || added.length !== 0 || removed.length !== 0) {
- collection._firing = true;
- do {
- collection._refire = false;
- const addedArray = added.values.slice(0);
- const removedArray = removed.values.slice(0);
- const changedArray = changed.values.slice(0);
- added.removeAll();
- removed.removeAll();
- changed.removeAll();
- collection._collectionChanged.raiseEvent(
- collection,
- addedArray,
- removedArray,
- changedArray
- );
- } while (collection._refire);
- collection._firing = false;
- }
- }
- }
- /**
- * An observable collection of {@link Entity} instances where each entity has a unique id.
- * @alias EntityCollection
- * @constructor
- *
- * @param {DataSource|CompositeEntityCollection} [owner] The data source (or composite entity collection) which created this collection.
- */
- function EntityCollection(owner) {
- this._owner = owner;
- this._entities = new AssociativeArray();
- this._addedEntities = new AssociativeArray();
- this._removedEntities = new AssociativeArray();
- this._changedEntities = new AssociativeArray();
- this._suspendCount = 0;
- this._collectionChanged = new Event();
- this._id = createGuid();
- this._show = true;
- this._firing = false;
- this._refire = false;
- }
- /**
- * Prevents {@link EntityCollection#collectionChanged} events from being raised
- * until a corresponding call is made to {@link EntityCollection#resumeEvents}, at which
- * point a single event will be raised that covers all suspended operations.
- * This allows for many items to be added and removed efficiently.
- * This function can be safely called multiple times as long as there
- * are corresponding calls to {@link EntityCollection#resumeEvents}.
- */
- EntityCollection.prototype.suspendEvents = function () {
- this._suspendCount++;
- };
- /**
- * Resumes raising {@link EntityCollection#collectionChanged} events immediately
- * when an item is added or removed. Any modifications made while while events were suspended
- * will be triggered as a single event when this function is called.
- * This function is reference counted and can safely be called multiple times as long as there
- * are corresponding calls to {@link EntityCollection#resumeEvents}.
- *
- * @exception {DeveloperError} resumeEvents can not be called before suspendEvents.
- */
- EntityCollection.prototype.resumeEvents = function () {
- //>>includeStart('debug', pragmas.debug);
- if (this._suspendCount === 0) {
- throw new DeveloperError(
- "resumeEvents can not be called before suspendEvents."
- );
- }
- //>>includeEnd('debug');
- this._suspendCount--;
- fireChangedEvent(this);
- };
- /**
- * The signature of the event generated by {@link EntityCollection#collectionChanged}.
- * @callback EntityCollection.CollectionChangedEventCallback
- *
- * @param {EntityCollection} collection The collection that triggered the event.
- * @param {Entity[]} added The array of {@link Entity} instances that have been added to the collection.
- * @param {Entity[]} removed The array of {@link Entity} instances that have been removed from the collection.
- * @param {Entity[]} changed The array of {@link Entity} instances that have been modified.
- */
- Object.defineProperties(EntityCollection.prototype, {
- /**
- * Gets the event that is fired when entities are added or removed from the collection.
- * The generated event is a {@link EntityCollection.CollectionChangedEventCallback}.
- * @memberof EntityCollection.prototype
- * @readonly
- * @type {Event<EntityCollection.CollectionChangedEventCallback>}
- */
- collectionChanged: {
- get: function () {
- return this._collectionChanged;
- },
- },
- /**
- * Gets a globally unique identifier for this collection.
- * @memberof EntityCollection.prototype
- * @readonly
- * @type {string}
- */
- id: {
- get: function () {
- return this._id;
- },
- },
- /**
- * Gets the array of Entity instances in the collection.
- * This array should not be modified directly.
- * @memberof EntityCollection.prototype
- * @readonly
- * @type {Entity[]}
- */
- values: {
- get: function () {
- return this._entities.values;
- },
- },
- /**
- * Gets whether or not this entity collection should be
- * displayed. When true, each entity is only displayed if
- * its own show property is also true.
- * @memberof EntityCollection.prototype
- * @type {boolean}
- */
- show: {
- get: function () {
- return this._show;
- },
- set: function (value) {
- //>>includeStart('debug', pragmas.debug);
- if (!defined(value)) {
- throw new DeveloperError("value is required.");
- }
- //>>includeEnd('debug');
- if (value === this._show) {
- return;
- }
- //Since entity.isShowing includes the EntityCollection.show state
- //in its calculation, we need to loop over the entities array
- //twice, once to get the old showing value and a second time
- //to raise the changed event.
- this.suspendEvents();
- let i;
- const oldShows = [];
- const entities = this._entities.values;
- const entitiesLength = entities.length;
- for (i = 0; i < entitiesLength; i++) {
- oldShows.push(entities[i].isShowing);
- }
- this._show = value;
- for (i = 0; i < entitiesLength; i++) {
- const oldShow = oldShows[i];
- const entity = entities[i];
- if (oldShow !== entity.isShowing) {
- entity.definitionChanged.raiseEvent(
- entity,
- "isShowing",
- entity.isShowing,
- oldShow
- );
- }
- }
- this.resumeEvents();
- },
- },
- /**
- * Gets the owner of this entity collection, ie. the data source or composite entity collection which created it.
- * @memberof EntityCollection.prototype
- * @readonly
- * @type {DataSource|CompositeEntityCollection}
- */
- owner: {
- get: function () {
- return this._owner;
- },
- },
- });
- /**
- * Computes the maximum availability of the entities in the collection.
- * If the collection contains a mix of infinitely available data and non-infinite data,
- * it will return the interval pertaining to the non-infinite data only. If all
- * data is infinite, an infinite interval will be returned.
- *
- * @returns {TimeInterval} The availability of entities in the collection.
- */
- EntityCollection.prototype.computeAvailability = function () {
- let startTime = Iso8601.MAXIMUM_VALUE;
- let stopTime = Iso8601.MINIMUM_VALUE;
- const entities = this._entities.values;
- for (let i = 0, len = entities.length; i < len; i++) {
- const entity = entities[i];
- const availability = entity.availability;
- if (defined(availability)) {
- const start = availability.start;
- const stop = availability.stop;
- if (
- JulianDate.lessThan(start, startTime) &&
- !start.equals(Iso8601.MINIMUM_VALUE)
- ) {
- startTime = start;
- }
- if (
- JulianDate.greaterThan(stop, stopTime) &&
- !stop.equals(Iso8601.MAXIMUM_VALUE)
- ) {
- stopTime = stop;
- }
- }
- }
- if (Iso8601.MAXIMUM_VALUE.equals(startTime)) {
- startTime = Iso8601.MINIMUM_VALUE;
- }
- if (Iso8601.MINIMUM_VALUE.equals(stopTime)) {
- stopTime = Iso8601.MAXIMUM_VALUE;
- }
- return new TimeInterval({
- start: startTime,
- stop: stopTime,
- });
- };
- /**
- * Add an entity to the collection.
- *
- * @param {Entity | Entity.ConstructorOptions} entity The entity to be added.
- * @returns {Entity} The entity that was added.
- * @exception {DeveloperError} An entity with <entity.id> already exists in this collection.
- */
- EntityCollection.prototype.add = function (entity) {
- //>>includeStart('debug', pragmas.debug);
- if (!defined(entity)) {
- throw new DeveloperError("entity is required.");
- }
- //>>includeEnd('debug');
- if (!(entity instanceof Entity)) {
- entity = new Entity(entity);
- }
- const id = entity.id;
- const entities = this._entities;
- if (entities.contains(id)) {
- throw new RuntimeError(
- `An entity with id ${id} already exists in this collection.`
- );
- }
- entity.entityCollection = this;
- entities.set(id, entity);
- if (!this._removedEntities.remove(id)) {
- this._addedEntities.set(id, entity);
- }
- entity.definitionChanged.addEventListener(
- EntityCollection.prototype._onEntityDefinitionChanged,
- this
- );
- fireChangedEvent(this);
- return entity;
- };
- /**
- * Removes an entity from the collection.
- *
- * @param {Entity} entity The entity to be removed.
- * @returns {boolean} true if the item was removed, false if it did not exist in the collection.
- */
- EntityCollection.prototype.remove = function (entity) {
- if (!defined(entity)) {
- return false;
- }
- return this.removeById(entity.id);
- };
- /**
- * Returns true if the provided entity is in this collection, false otherwise.
- *
- * @param {Entity} entity The entity.
- * @returns {boolean} true if the provided entity is in this collection, false otherwise.
- */
- EntityCollection.prototype.contains = function (entity) {
- //>>includeStart('debug', pragmas.debug);
- if (!defined(entity)) {
- throw new DeveloperError("entity is required");
- }
- //>>includeEnd('debug');
- return this._entities.get(entity.id) === entity;
- };
- /**
- * Removes an entity with the provided id from the collection.
- *
- * @param {string} id The id of the entity to remove.
- * @returns {boolean} true if the item was removed, false if no item with the provided id existed in the collection.
- */
- EntityCollection.prototype.removeById = function (id) {
- if (!defined(id)) {
- return false;
- }
- const entities = this._entities;
- const entity = entities.get(id);
- if (!this._entities.remove(id)) {
- return false;
- }
- if (!this._addedEntities.remove(id)) {
- this._removedEntities.set(id, entity);
- this._changedEntities.remove(id);
- }
- this._entities.remove(id);
- entity.definitionChanged.removeEventListener(
- EntityCollection.prototype._onEntityDefinitionChanged,
- this
- );
- fireChangedEvent(this);
- return true;
- };
- /**
- * Removes all Entities from the collection.
- */
- EntityCollection.prototype.removeAll = function () {
- //The event should only contain items added before events were suspended
- //and the contents of the collection.
- const entities = this._entities;
- const entitiesLength = entities.length;
- const array = entities.values;
- const addedEntities = this._addedEntities;
- const removed = this._removedEntities;
- for (let i = 0; i < entitiesLength; i++) {
- const existingItem = array[i];
- const existingItemId = existingItem.id;
- const addedItem = addedEntities.get(existingItemId);
- if (!defined(addedItem)) {
- existingItem.definitionChanged.removeEventListener(
- EntityCollection.prototype._onEntityDefinitionChanged,
- this
- );
- removed.set(existingItemId, existingItem);
- }
- }
- entities.removeAll();
- addedEntities.removeAll();
- this._changedEntities.removeAll();
- fireChangedEvent(this);
- };
- /**
- * Gets an entity with the specified id.
- *
- * @param {string} id The id of the entity to retrieve.
- * @returns {Entity|undefined} The entity with the provided id or undefined if the id did not exist in the collection.
- */
- EntityCollection.prototype.getById = function (id) {
- //>>includeStart('debug', pragmas.debug);
- if (!defined(id)) {
- throw new DeveloperError("id is required.");
- }
- //>>includeEnd('debug');
- return this._entities.get(id);
- };
- /**
- * Gets an entity with the specified id or creates it and adds it to the collection if it does not exist.
- *
- * @param {string} id The id of the entity to retrieve or create.
- * @returns {Entity} The new or existing object.
- */
- EntityCollection.prototype.getOrCreateEntity = function (id) {
- //>>includeStart('debug', pragmas.debug);
- if (!defined(id)) {
- throw new DeveloperError("id is required.");
- }
- //>>includeEnd('debug');
- let entity = this._entities.get(id);
- if (!defined(entity)) {
- entityOptionsScratch.id = id;
- entity = new Entity(entityOptionsScratch);
- this.add(entity);
- }
- return entity;
- };
- EntityCollection.prototype._onEntityDefinitionChanged = function (entity) {
- const id = entity.id;
- if (!this._addedEntities.contains(id)) {
- this._changedEntities.set(id, entity);
- }
- fireChangedEvent(this);
- };
- export default EntityCollection;
|