UNPKG

@juun-roh/cesium-utils

Version:

Utilities to handle Cesium classes easier.

473 lines (469 loc) 16 kB
import { BillboardCollection, DataSourceCollection, EntityCollection, ImageryLayerCollection, LabelCollection, PointPrimitiveCollection, PolylineCollection, PrimitiveCollection, Billboard, DataSource, Entity, ImageryLayer, Label, PointPrimitive, Polyline, Primitive, Cesium3DTileset, GroundPrimitive } from 'cesium'; type CesiumCollection = BillboardCollection | DataSourceCollection | EntityCollection | ImageryLayerCollection | LabelCollection | PointPrimitiveCollection | PolylineCollection | PrimitiveCollection; type Primitives = Primitive | Cesium3DTileset | GroundPrimitive; type CesiumCollectionItem = Billboard | DataSource | Entity | ImageryLayer | Label | PointPrimitive | Polyline | Primitives; type Tag = string | number; interface WithTag { [key: symbol]: Tag; } /** * Collection event types */ type CollectionEventType = 'add' | 'remove' | 'update' | 'clear'; /** * Event handler function type */ type EventHandler<I> = (event: { type: CollectionEventType; items?: I[]; tag?: Tag; }) => void; type NonFunction<T> = { [K in keyof T]: T[K] extends Function ? never : K; }[keyof T]; /** * @class * A wrapper class that enhances Cesium collection objects with tagging functionality. * This class provides a consistent API for working with different types of Cesium collections * and allows grouping and manipulating collection items by custom tags. * * @template C - The type of Cesium collection (e.g., EntityCollection, PrimitiveCollection) * @template I - The type of items in the collection (e.g., Entity, Primitive) * * @example * // Example 1: Managing Complex Scene with Multiple Object Types * class SceneOrganizer { * private entities: Collection<EntityCollection, Entity>; * private billboards: Collection<BillboardCollection, Billboard>; * private primitives: Collection<PrimitiveCollection, Primitive>; * * constructor(viewer: Viewer) { * this.entities = new Collection({ collection: viewer.entities }); * this.billboards = new Collection({ * collection: viewer.scene.primitives.add(new BillboardCollection()) * }); * this.primitives = new Collection({ * collection: viewer.scene.primitives * }); * } * * // Unified API across different collection types! * showLayer(layerName: string) { * this.entities.show(layerName); * this.billboards.show(layerName); * this.primitives.show(layerName); * } * * hideLayer(layerName: string) { * this.entities.hide(layerName); * this.billboards.hide(layerName); * this.primitives.hide(layerName); * } * * removeLayer(layerName: string) { * this.entities.remove(layerName); * this.billboards.remove(layerName); * this.primitives.remove(layerName); * } * } * * // Example 2: Extend the class for Domain-Specific Needs * class BuildingCollection extends Collection<EntityCollection, Entity> { * constructor(viewer: Viewer) { * super({ collection: viewer.entities, tag: 'buildings' }); * } * * addBuilding(options: { * position: Cartesian3; * height: number; * floors: number; * type: 'residential' | 'commercial' | 'industrial'; * }): Entity { * const building = new Entity({ * position: options.position, * box: { * dimensions: new Cartesian3(50, 50, options.height), * material: this.getMaterialForType(options.type) * } * }); * * // Tag by type AND by floor count * this.add(building, options.type); * this.add(building, `floors-${options.floors}`); * * return building; * } * * getByFloorRange(min: number, max: number): Entity[] { * const results: Entity[] = []; * for (let i = min; i <= max; i++) { * results.push(...this.get(`floors-${i}`)); * } * return results; * } * * private getMaterialForType(type: string): Material { * const colors = { * residential: Color.GREEN, * commercial: Color.BLUE, * industrial: Color.YELLOW * }; * return new ColorMaterialProperty(colors[type] || Color.WHITE); * } * } */ declare class Collection<C extends CesiumCollection, I extends CesiumCollectionItem> { /** * Symbol used as a property key to store tags on collection items. * Using a Symbol ensures no property naming conflicts with the item's own properties. * @readonly */ static readonly symbol: unique symbol; /** * Default tag used when adding items without specifying a tag. * @protected */ protected tag: Tag; /** * The underlying Cesium collection being wrapped. * @protected */ protected collection: C; /** * Cache for values array to improve performance * @private */ private _valuesCache; /** * Tag to items map for faster lookups * @private */ private _tagMap; /** * Event listeners * @private */ private _eventListeners; /** * Creates a new Collection instance. * * @param options - Configuration options * @param options.collection - The Cesium collection to wrap * @param options.tag - The default tag to use for items (defaults to 'default') */ constructor({ collection, tag }: { collection: C; tag?: Tag; }); /** * Makes the collection directly iterable, allowing it to be used in `for...of` loops * and with spread operators. * * @returns An iterator for the items in the collection * * @example * // Iterate through all items in the collection * for (const entity of collection) { * console.log(entity.id); * } * * // Convert collection to array using spread syntax * const entitiesArray = [...collection]; */ [Symbol.iterator](): Iterator<I>; /** * Emits an event to all registered listeners. * * @private * @param type - The event type * @param data - Additional event data */ private _emit; /** * Adds an item to the internal tag map for quick lookups. * * @private * @param item - The item to add * @param tag - The tag to associate with the item */ private _addToTagMap; /** * Removes an item from the internal tag map. * * @private * @param item - The item to remove */ private _removeFromTagMap; /** * Invalidates the values cache when collection changes. * * @private */ private _invalidateCache; /** * Registers an event listener for collection events. * * @param type - The event type to listen for * @param handler - The callback function * @returns The collection instance for method chaining */ addEventListener(type: CollectionEventType, handler: EventHandler<I>): this; /** * Removes an event listener. * * @param type - The event type * @param handler - The callback function to remove * @returns The collection instance for method chaining */ removeEventListener(type: CollectionEventType, handler: EventHandler<I>): this; /** * Adds a single item with a tag to the collection. * * @param item - The item to add to the collection * @param tag - Tag to associate with this item (defaults to the collection's default tag) * @param index - The index to add the item at (if supported by the collection) * @returns The added item for chaining * * @example * const entity = collection.add(new Entity({ ... }), 'landmarks'); */ add(item: I, tag?: Tag, index?: number): I; /** * Adds multiple items with the same tag to the collection. * * @param items - The array of items to add to the collection * @param tag - Tag to associate with this item (defaults to the collection's default tag) * @returns The array of added items * * @example * // Add multiple entities with the same tag * const entities = [new Entity({ ... }), new Entity({ ... })]; * const addedEntities = collection.add(entities, 'buildings'); */ add(items: I[], tag?: Tag): I[]; /** * Returns true if the provided item is in this collection, false otherwise. * * @param item - The item instance to check for * @returns True if the item is in the collection, false otherwise */ contains(item: I): boolean; /** * Checks if the collection has any items with the specified tag. * * @param tag - The tag to check for * @returns True if items with the tag exist, false otherwise * * @example * if (collection.contains('temporary')) { * console.log('Temporary items exist'); * } */ contains(tag: Tag): boolean; /** * Removes an item from the collection. * * @param item - The item to remove * @returns True if the item was removed, false if it wasn't found */ remove(item: I): boolean; /** * Removes all items with the specified tag from the collection. * * @param by - The tag identifying which items to remove * @returns True if the item was removed, false if it wasn't found */ remove(by: Tag): boolean; /** * Removes all items with the array of tags from the collection. * * @param by - The tags identifying which items to remove * @returns True if the item was removed, false if it wasn't found */ remove(by: Tag[]): boolean; /** * Removes all items from the collection. */ removeAll(): void; /** * Gets all item instances in the collection. * This array should not be modified directly. * * @returns An array of all items in the collection */ get values(): I[]; /** * Gets the number of items in the collection. * * @returns The item count */ get length(): number; /** * Gets all items with the specified tag from the collection. * Uses an optimized internal map for faster lookups. * * @param by - The tag to filter by * @returns An array of items with the specified tag, or an empty array if none found * * @example * // Get all buildings * const buildings = collection.get('buildings'); */ get(by: Tag): I[]; /** * Gets the first item matching the tag. More efficient than `get` when * you only need one item, especially for large collections. * * @param by - The tag to search for * @returns The first matching item or undefined if none found * * @example * // Get the first building * const firstBuilding = collection.first('buildings'); */ first(by: Tag): I | undefined; /** * Gets all unique tags currently in use in the collection. * * @returns An array of all unique tags * * @example * // Get all tags * const tags = collection.tags; * console.log(`Collection has these tags: ${tags.join(', ')}`); */ get tags(): Tag[]; /** * Updates the tag for all items with the specified tag. * * @param from - The tag to replace * @param to - The new tag to assign * @returns The number of items updated * * @example * // Rename a tag * const count = collection.update('temp', 'temporary'); * console.log(`Updated ${count} items`); */ update(from: Tag, to: Tag): number; /** * Makes all items with the specified tag visible. * Only affects items that have a 'show' property. * * @param by - The tag identifying which items to show * @returns The collection itself. * * @example * // Show all buildings * collection.show('buildings'); */ show(by: Tag): this; /** * Hides all items with the specified tag. * Only affects items that have a 'show' property. * * @param by - The tag identifying which items to hide * @returns The collection itself. * * @example * // Hide all buildings * collection.hide('buildings'); */ hide(by: Tag): this; /** * Toggles visibility of all items with the specified tag. * Only affects items that have a 'show' property. * * @param by - The tag identifying which items to toggle * @returns The collection itself. * * @example * // Toggle visibility of all buildings * collection.toggle('buildings'); */ toggle(by: Tag): this; /** * Sets a property value on all items with the specified tag. * * @param by - The tag identifying which items to update * @param property - The property name to set * @param value - The value to set * @returns The number of items updated * * @example * // Change color of all buildings to red * collection.setProperty('buildings', 'color', Color.RED); */ setProperty<K extends NonFunction<I>>(property: K, value: I[K], by?: Tag): this; /** * Filters items in the collection based on a predicate function. * Optionally only filters items with a specific tag. * * @param predicate - Function that tests each item * @param by - Optional tag to filter by before applying the predicate * @returns Array of items that pass the test * * @example * // Get all buildings taller than 100 meters * const tallBuildings = collection.filter( * entity => entity.properties?.height?.getValue() > 100, * 'buildings' * ); */ filter(predicate: (item: I) => boolean, by?: Tag): I[]; /** * Executes a callback function for each item in the collection. * Optionally filters items by tag before execution. * * @param callback - Function to execute for each item * @param by - Optional tag to filter items (if not provided, processes all items) * * @example * // Highlight all buildings * collection.forEach((entity) => { * if (entity.polygon) { * entity.polygon.material = new ColorMaterialProperty(Color.YELLOW); * } * }, 'buildings'); */ forEach(callback: (value: I, index: number) => void, by?: Tag): void; /** * Creates a new array with the results of calling a provided function on every element * in the collection. Optionally filters by tag before mapping. * * @param callbackfn - Function that produces an element of the new array * @param by - Optional tag to filter items by before mapping * @returns A new array with each element being the result of the callback function * * @example * // Get all entity IDs * const entityIds = collection.map(entity => entity.id); * * // Get positions of all buildings * const buildingPositions = collection.map( * entity => entity.position.getValue(Cesium.JulianDate.now()), * 'buildings' * ); */ map<R>(callbackfn: (value: I, index: number) => R, by?: Tag): R[]; /** * Returns the first element in the collection that satisfies the provided testing function. * Optionally filters by tag before searching. * * @param predicate - Function to test each element * @param by - Optional tag to filter items by before searching * @returns The first element that passes the test, or undefined if no elements pass * * @example * // Find the first entity with a specific name * const namedEntity = collection.find(entity => entity.name === 'Target'); * * // Find the first building taller than 100 meters * const tallBuilding = collection.find( * entity => entity.properties?.height?.getValue() > 100, * 'buildings' * ); */ find(predicate: (value: I) => boolean, by?: Tag): I | undefined; } export { type CesiumCollection, type CesiumCollectionItem, Collection, type CollectionEventType, type EventHandler, type NonFunction, type Tag, type WithTag };