import { DecodedTile , GeometryKindSet , GeometryType , TextPathGeometry } from "@here/harp-datasource-protocol" ; import { GeoBox , OrientedBox3 , Projection , TileKey , TileKeyUtils } from "@here/harp-geoutils" ; import { assert , CachedResource , chainCallbacks , LoggerManager } from "@here/harp-utils" ; import * as THREE from "three" ; import { CopyrightInfo } from "./copyrights/CopyrightInfo" ; import { DataSource } from "./DataSource" ; import { ElevationRange } from "./ElevationRangeSource" ; import { LodMesh } from "./geometry/LodMesh" ; import { Object3DUtils } from "./geometry/Object3DUtils" ; import { TileGeometryLoader } from "./geometry/TileGeometryLoader" ; import { ITileLoader , TileLoaderState } from "./ITileLoader" ; import { MapView } from "./MapView" ; import { PathBlockingElement } from "./PathBlockingElement" ; import { PerformanceStatistics } from "./Statistics" ; import { TextElement } from "./text/TextElement" ; import { TextElementGroup } from "./text/TextElementGroup" ; import { TextElementGroupPriorityList } from "./text/TextElementGroupPriorityList" ; import { TileTextStyleCache } from "./text/TileTextStyleCache" ; const logger = LoggerManager . instance . create ( "Tile" ) ; export type TileObject = THREE . Object3D & { /**
     * Distance of this object from the  Tile 's center.
      displacement ? : THREE . Vector3 ; } ; interface DisposableObject { geometry ? : THREE . BufferGeometry ; geometries ? : THREE . BufferGeometry [ ] ; material ? : THREE . Material [ ] | THREE . Material ; } /**
 * An interface for optional feature data that is saved in a `THREE.Object3D`'s `userData`
 * property.
 */ export interface TileFeatureData { /**
     * The original type of geometry.
     */ geometryType ? : GeometryType ; /**
     * An optional array of sorted indices into geometry where the feature starts. The lists of IDs
     * and starting indices (starts) must have the same size.
     * Feature i starts at starts[i] and ends at starts[i+1]-1, except for the last feature, which
     * ends at the last index in the object's geometry.
     */ starts ? : number [ ] ; /**
     * An optional object containing properties defined by the developer. It has the same size as
     * the list of IDs and the starting indices (starts).
     */ objInfos ? : Array < { } | undefined > ; } /**
 * Minimum estimated size of a JS object.
 */ const MINIMUM_SMALL_OBJECT_SIZE_ESTIMATION = 16 ; const MINIMUM_OBJECT_SIZE_ESTIMATION = 100 ; internal export function getFeatureDataSize ( featureData ) { let numBytes = MINIMUM_OBJECT_SIZE_ESTIMATION ; if ( featureData . starts !== undefined ) { numBytes += featureData . starts . length * 8 ; } if ( featureData . objInfos !== undefined ) { numBytes += featureData . objInfos . length * MINIMUM_SMALL_OBJECT_SIZE_ESTIMATION ; } return numBytes ; } /**
 * An object that contains information about resources used by a tile.
 */ export interface TileResourceUsage { /**
     * The estimated memory usage, in bytes.
     */ estimatedMemoryUsage : number ; /**
     * The amount of vertices used by a tile.
     */ numVertices : number ; /**
     * The amount of colors used by a tile.
     */ numColors : number ; /**
     * The amount of objects used by a tile.
     */ numObjects : number ; /**
     * The amount of geometries used by a tile.
     */ numGeometries : number ; /**
     * The amount of materials used by a tile.
     */ numMaterials : number ; } /**
 * Simple information about resource usage by the  Tile .
 *
 *  remarks export interface TileResourceInfo { /**
     * Estimated number of bytes used on the heap.
     */ heapSize : number ; /**
     * Estimated number of bytes used on the GPU.
     */ gpuSize : number ; /**
     * Number of [[THREE.Object3D]] in this tile.
     */ num3dObjects : number ; /**
     * Number of  TextElement s in this tile.
      numTextElements : number ; deprecated This counter has been merged with numTextElements.
     * Number of user  TextElement s in this tile.
      numUserTextElements : number ; } internal export interface TextElementIndex { groupIndex : number ; elementIndex : number ; } type TileCallback = ( tile : Tile ) => void ; /**
 * The class that holds the tiled data for a  DataSource .
  export class Tile implements CachedResource { /**
     * A list of the THREE.js objects stored in this `Tile`.
     */ readonly objects : TileObject [ ] = [ ] ; /**
     * The optional list of HERE TileKeys of tiles with geometries that cross the boundaries of this
     * `Tile`.
     */ readonly dependencies : TileKey [ ] = [ ] ; /**
     * The bounding box of this `Tile` in geocoordinates.
     */ readonly geoBox : GeoBox ; /**
     * Copyright information of this `Tile`'s data.
     */ copyrightInfo ? : CopyrightInfo [ ] ; /**
     * Keeping some stats for the individual  Tile s to analyze caching behavior.
     *
     * The frame the  Tile was last requested. This is
     * required to know when the given  Tile * can be removed from the cache.
      frameNumLastRequested : number = - 1 ; /**
     * The frame the `Tile` was first visible.
     */ frameNumVisible : number = - 1 ; /**
     * The last frame this `Tile` has been rendered (or was in the visible set). Used to determine
     * visibility of `Tile` at the end of a frame, if the number is the current frame number, it is
     * visible.
     */ frameNumLastVisible : number = - 1 ; /**
     * After removing from cache, this is the number of frames the `Tile` was visible.
     */ numFramesVisible : number = 0 ; /**
     * Version stamp of the visibility set in the [[TileManager]]. If the counter is different, the
     * visibility of the Tile's objects has to be calculated. Optimization to reduce overhead of
     * computing visibility.
     */ visibilityCounter : number = - 1 ; hidden Used to tell if the Tile is used temporarily as a fallback tile.
     *
     * levelOffset is in in the range [-quadTreeSearchDistanceUp,
     * quadTreeSearchDistanceDown], where these values come from the
     *  VisibleTileSetOptions levelOffset : number = 0 ; /**
     * If the tile should not be rendered, this is used typically when the tile in question
     * is completely covered by another tile and therefore can be skipped without any visual
     * impact. Setting this value directly affects the [[willRender]] method, unless
     * overriden by deriving classes.
     */ skipRendering : boolean = false ; /**
     * If the tile should not yet be rendered, this is used typically when the tile in question
     * does not fit into the gpu upload limit of the current frame.
     * Setting this value directly affects the [[willRender]] method, unless
     * overriden by deriving classes.
     */ delayRendering = false ; hidden protected preparedTextPaths : TextPathGeometry [ ] | undefined ; protected readonly m_tileGeometryLoader ? : TileGeometryLoader ; /**
     * The bounding box of this `Tile` in world coordinates.
     */ private readonly m_boundingBox = new OrientedBox3 ( ) ; private m_disposed : boolean = false ; private m_disposeCallback ? : TileCallback ; private readonly m_localTangentSpace : boolean ; private m_forceHasGeometry : boolean | undefined = undefined ; private m_tileLoader ? : ITileLoader ; private m_decodedTile ? : DecodedTile ; private m_textElementGroups = new TextElementGroupPriorityList ( ) ; private readonly m_pathBlockingElements : PathBlockingElement [ ] = [ ] ; private m_textElementsChanged : boolean | undefined ; private readonly m_worldCenter = new THREE . Vector3 ( ) ; private m_visibleArea : number = 0 ; private readonly m_elevationRange : ElevationRange = { minElevation : 0 , maxElevation : 0 } ; private m_maxGeometryHeight ? : number ; private m_minGeometryHeight ? : number ; private m_resourceInfo : TileResourceInfo | undefined ; private readonly m_ownedTextures : WeakSet < THREE . Texture > = new WeakSet ( ) ; private readonly m_textStyleCache : TileTextStyleCache ; private m_uniqueKey : number ; private m_offset : number ; /**
     * Creates a new  Tile .
     *
     *  param dataSource - The  DataSource that created this  Tile .
     *  param tileKey - The unique identifier for this  Tile .
     *                  Currently only up to level 24 is
     *                  supported, because of the use of the upper bits for the offset.
     *  param offset param localTangentSpace constructor ( readonly dataSource , readonly tileKey , offset = 0 , localTangentSpace ? ) { this . geoBox = this . dataSource . getTilingScheme ( ) . getGeoBox ( this . tileKey ) ; this . updateBoundingBox ( ) ; this . m_worldCenter . copy ( this . boundingBox . position ) ; this . m_localTangentSpace = localTangentSpace ?? false ; this . m_textStyleCache = new TileTextStyleCache ( this ) ; this . m_offset = offset ; this . m_uniqueKey = TileKeyUtils . getKeyForTileKeyAndOffset ( this . tileKey , this . offset ) ; if ( dataSource . useGeometryLoader ) { this . m_tileGeometryLoader = new TileGeometryLoader ( this , this . mapView . taskQueue ) ; this . attachGeometryLoadedCallback ( ) ; } } /**
     * The visibility status of the  Tile . It is actually
     * visible or planned to become visible.
      get isVisible ( ) : boolean { try { return this . frameNumLastRequested >= this . dataSource . mapView . frameNumber - 1 ; } catch ( error ) { logger . debug ( error ) ; return false ; } } param visible set isVisible ( visible ) { this . frameNumLastRequested = visible ? this . dataSource . mapView . frameNumber : - 1 ; if ( ! visible && this . m_tileGeometryLoader && ! this . m_tileGeometryLoader . isSettled ) { this . m_tileGeometryLoader . cancel ( ) ; } } /**
     * The  {@link @here/harp-geoutils#Projection} currently used by the  MapView .
      get projection ( ) : Projection { return this . dataSource . projection ; } /**
     * The  MapView this `Tile` belongs to.
      get mapView ( ) : MapView { return this . dataSource . mapView ; } remarks If the data is in local tangent space (i.e. up vector is (0,0,1) for high zoomlevels) then
     *  MapView will rotate the objects before rendering using the rotation matrix of the
     * oriented [[boundingBox]].
      get localTangentSpace ( ) : boolean { return this . m_localTangentSpace ; } get memoryUsage ( ) : number { if ( this . m_resourceInfo === undefined ) { this . computeResourceInfo ( ) ; } return this . m_resourceInfo ! . heapSize ; } /**
     * The center of this `Tile` in world coordinates.
     */ get center ( ) : THREE . Vector3 { return this . m_worldCenter ; } /**
     * Gets the key to uniquely represent this tile (based on
     * the  tileKey and  offset ).
     *
     *  remarks This key is only unique within the given  DataSource ,
     * to get a key which is unique across
     *  DataSource s see [[DataSourceCache.getKeyForTile]].
      get uniqueKey ( ) : number { return this . m_uniqueKey ; } /**
     * The optional offset, this is an integer which represents what multiple of 360 degrees to
     * shift, only useful for flat projections, hence optional.
     */ get offset ( ) : number { return this . m_offset ; } param offset - Which multiple of 360 degrees to apply to the  Tile .
      set offset ( offset ) { if ( this . m_offset !== offset ) { this . m_uniqueKey = TileKeyUtils . getKeyForTileKeyAndOffset ( this . tileKey , offset ) ; } this . m_offset = offset ; } /**
     * Compute  TileResourceInfo of this `Tile`.
     *
     *  remarks returns getResourceInfo ( ) { if ( this . m_resourceInfo === undefined ) { this . computeResourceInfo ( ) ; } return this . m_resourceInfo ! ; } /**
     * Force invalidation of the cached  TileResourceInfo .
     *
     *  remarks invalidateResourceInfo ( ) { this . m_resourceInfo = undefined ; } remarks param texture addOwnedTexture ( texture ) { this . m_ownedTextures . add ( texture ) ; } internal deprecated User text elements are deprecated.
     *
     * Gets the list of developer-defined  TextElement in this `Tile`.
     *
     *  remarks get userTextElements ( ) : TextElementGroup { let group = this . m_textElementGroups . groups . get ( TextElement . HIGHEST_PRIORITY ) ; if ( group === undefined ) { group = new TextElementGroup ( TextElement . HIGHEST_PRIORITY ) ; this . m_textElementGroups . groups . set ( group . priority , group ) ; } return group ; } /**
     * Adds a developer-defined  TextElement to this `Tile`.
     *
     *  remarks The  TextElement is always
     * visible, if it's in the map's currently visible area.
     *
     *  deprecated param textElement addUserTextElement ( textElement ) { textElement . priority = TextElement . HIGHEST_PRIORITY ; this . addTextElement ( textElement ) ; } /**
     * Removes a developer-defined  TextElement from this `Tile`.
     *
     *  deprecated param textElement returns removeUserTextElement ( textElement ) { textElement . priority = TextElement . HIGHEST_PRIORITY ; return this . removeTextElement ( textElement ) ; } /**
     * Adds a  TextElement to this `Tile`, which is added to the visible set of
     *  TextElement s based on the capacity and visibility.
     *
     *  remarks The  TextElement 's priority controls if or when it becomes visible.
     *
     * To ensure that a TextElement is visible, use a high value for its priority, such as
     * `TextElement.HIGHEST_PRIORITY`. Since the number of visible TextElements is limited by the
     * screen space, not all TextElements are visible at all times.
     *
     *  param textElement addTextElement ( textElement ) { this . textElementGroups . add ( textElement ) ; if ( this . m_textElementsChanged === false ) { this . m_textElementGroups = this . textElementGroups . clone ( ) ; } this . textElementsChanged = true ; } remarks param blockingElement addBlockingElement ( blockingElement ) { this . m_pathBlockingElements . push ( blockingElement ) ; } /**
     * Removes a  TextElement from this `Tile`.
     *
     *  remarks For the element to be removed successfully, the
     * priority of the  TextElement has to be equal to its priority when it was added.
     *
     *  param textElement returns removeTextElement ( textElement ) { const groups = this . textElementGroups ; if ( ! groups . remove ( textElement ) ) { return false ; } if ( this . m_textElementsChanged === false ) { this . m_textElementGroups = groups . clone ( ) ; } this . textElementsChanged = true ; return true ; } internal Gets the current `GroupedPriorityList` which
     * contains a list of all  TextElement s to be
     * selected and placed for rendering.
      get textElementGroups ( ) : TextElementGroupPriorityList { return this . m_textElementGroups ; } /**
     * Gets the current modification state for the list
     * of  TextElement s in the `Tile`.
     *
     *  remarks get textElementsChanged ( ) : boolean { return this . m_textElementsChanged ?? false ; } set textElementsChanged ( changed ) { this . m_textElementsChanged = changed ; } /**
     * Returns true if the `Tile` has any text elements to render.
     */ hasTextElements ( ) { return this . m_textElementGroups . count ( ) > 0 ; } /**
     * Get the current blocking elements.
     */ get blockingElements ( ) : PathBlockingElement [ ] { return this . m_pathBlockingElements ; } /**
     * Called before  MapView starts rendering this `Tile`.
     *
     *  remarks param zoomLevel returns willRender ( _zoomLevel ) { return ! this . skipRendering && ! this . delayRendering ; } /**
     * Called after  MapView has rendered this `Tile`.
      didRender ( ) { } /**
     * Estimated visible area of tile used for sorting the priorities during loading.
     */ get visibleArea ( ) : number { return this . m_visibleArea ; } set visibleArea ( area ) { this . m_visibleArea = area ; if ( this . tileLoader !== undefined ) { this . tileLoader . priority = area ; } } internal get elevationRange ( ) : ElevationRange { return this . m_elevationRange ; } internal param elevationRange set elevationRange ( elevationRange ) { if ( elevationRange . minElevation === this . m_elevationRange . minElevation && elevationRange . maxElevation === this . m_elevationRange . maxElevation && elevationRange . calculationStatus === this . m_elevationRange . calculationStatus ) { return ; } this . m_elevationRange . minElevation = elevationRange . minElevation ; this . m_elevationRange . maxElevation = elevationRange . maxElevation ; this . m_elevationRange . calculationStatus = elevationRange . calculationStatus ; this . elevateGeoBox ( ) ; if ( this . m_maxGeometryHeight !== undefined || this . m_minGeometryHeight !== undefined ) { assert ( this . decodedTile ?. boundingBox === undefined ) ; this . updateBoundingBox ( ) ; } } /**
     * Gets the decoded tile; it is removed after geometry handling.
     */ get decodedTile ( ) : DecodedTile | undefined { return this . m_decodedTile ; } remarks param decodedTile set decodedTile ( decodedTile ) { this . m_decodedTile = decodedTile ; this . invalidateResourceInfo ( ) ; if ( decodedTile === undefined ) { return ; } if ( decodedTile . geometries . length === 0 ) { this . forceHasGeometry ( true ) ; } this . m_maxGeometryHeight = decodedTile . boundingBox ? undefined : decodedTile . maxGeometryHeight ?? 0 ; this . m_minGeometryHeight = decodedTile . boundingBox ? undefined : decodedTile . minGeometryHeight ?? 0 ; this . elevateGeoBox ( ) ; this . updateBoundingBox ( decodedTile . boundingBox ) ; const stats = PerformanceStatistics . instance ; if ( stats . enabled && decodedTile . decodeTime !== undefined ) { stats . currentFrame . addValue ( "decode.decodingTime" , decodedTile . decodeTime ) ; stats . currentFrame . addValue ( "decode.decodedTiles" , 1 ) ; } if ( decodedTile . copyrightHolderIds !== undefined ) { this . copyrightInfo = decodedTile . copyrightHolderIds . map ( id => ( { id } ) ) ; } this . dataSource . requestUpdate ( ) ; } param object returns shouldDisposeObjectGeometry ( object ) { return true ; } param object returns shouldDisposeObjectMaterial ( object ) { return true ; } param texture returns shouldDisposeTexture ( texture ) { return this . m_ownedTextures . has ( texture ) ; } /**
     * Returns `true` if this `Tile` has been disposed.
     */ get disposed ( ) : boolean { return this . m_disposed ; } /**
     * `True` if all geometry of the `Tile` has been loaded.
     */ get allGeometryLoaded ( ) : boolean { return this . m_tileGeometryLoader ?. isFinished ?? this . hasGeometry ; } /**
     * MapView checks if this `Tile` is ready to be rendered while culling.
     *
     * By default, MapView checks if the [[objects]] list is not empty. However, you can override
     * this check by manually setting this property.
     */ get hasGeometry ( ) : boolean { if ( this . m_forceHasGeometry === undefined ) { return this . objects . length !== 0 ; } else { return this . m_forceHasGeometry ; } } param value forceHasGeometry ( value ) { this . m_forceHasGeometry = value ; } /**
     * Reset the visibility counter. This will force the visibility check to be rerun on all objects
     * in this `Tile`.
     */ resetVisibilityCounter ( ) { this . visibilityCounter = - 1 ; } /**
     * Gets the  ITileLoader that manages this tile.
      get tileLoader ( ) : ITileLoader | undefined { return this . m_tileLoader ; } /**
     * Sets the  ITileLoader to manage this tile.
     *
     *  param tileLoader - A  ITileLoader instance to manage
     *                     the loading process for this tile.
      set tileLoader ( tileLoader ) { this . m_tileLoader = tileLoader ; } returns async load ( ) { const tileLoader = this . tileLoader ; if ( tileLoader === undefined ) { return await Promise . resolve ( ) ; } if ( this . m_tileGeometryLoader ) { const wasSettled = this . m_tileGeometryLoader . isSettled ; this . m_tileGeometryLoader . reset ( ) ; if ( wasSettled ) { this . attachGeometryLoadedCallback ( ) ; } } return await tileLoader . loadAndDecode ( ) . then ( tileLoaderState => { assert ( tileLoaderState === TileLoaderState . Ready ) ; const decodedTile = tileLoader . decodedTile ; this . decodedTile = decodedTile ; decodedTile ?. dependencies ?. forEach ( mortonCode => { this . dependencies . push ( TileKey . fromMortonCode ( mortonCode ) ) ; } ) ; } ) . catch ( tileLoaderState => { if ( tileLoaderState === TileLoaderState . Failed ) { this . dispose ( ) ; } else if ( tileLoaderState !== TileLoaderState . Canceled ) { logger . error ( "Unknown error" + tileLoaderState ) ; } } ) ; } hidden get textStyleCache ( ) : TileTextStyleCache { return this . m_textStyleCache ; } remarks clear ( ) { const disposeMaterial = ( material ) => { Object . getOwnPropertyNames ( material ) . forEach ( ( property ) => { const materialProperty = ( material as any ) [ property ] ; if ( materialProperty !== undefined && materialProperty instanceof THREE . Texture ) { const texture = materialProperty ; if ( this . shouldDisposeTexture ( texture ) ) { texture . dispose ( ) ; } } } ) ; material . dispose ( ) ; } ; const disposeObject = ( object ) => { if ( this . shouldDisposeObjectGeometry ( object ) ) { if ( object . geometry !== undefined ) { object . geometry . dispose ( ) ; } if ( object . geometries !== undefined ) { for ( const geometry of object . geometries ) { geometry . dispose ( ) ; } } } if ( object . material !== undefined && this . shouldDisposeObjectMaterial ( object ) ) { if ( object . material instanceof Array ) { object . material . forEach ( ( material ) => { if ( material !== undefined ) { disposeMaterial ( material ) ; } } ) ; } else { disposeMaterial ( object . material ) ; } } } ; this . objects . forEach ( ( rootObject ) => { rootObject . traverse ( ( object ) => { disposeObject ( object ) ; } ) ; disposeObject ( rootObject ) ; } ) ; this . objects . length = 0 ; if ( this . preparedTextPaths ) { this . preparedTextPaths = [ ] ; } this . m_textStyleCache . clear ( ) ; this . clearTextElements ( ) ; this . invalidateResourceInfo ( ) ; } /**
     * Removes all  TextElement from the tile.
      clearTextElements ( ) { if ( ! this . hasTextElements ( ) ) { return ; } this . textElementsChanged = true ; this . m_pathBlockingElements . splice ( 0 ) ; this . textElementGroups . forEach ( ( element ) => { element . dispose ( ) ; } ) ; this . textElementGroups . clear ( ) ; } remarks internal param callback addDisposeCallback ( callback ) { this . m_disposeCallback = chainCallbacks ( this . m_disposeCallback , callback ) ; } /**
     * Disposes this `Tile`, freeing all geometries and materials for the reachable objects.
     */ dispose ( ) { if ( this . m_disposed ) { return ; } if ( this . m_tileLoader ) { this . m_tileLoader . cancel ( ) ; this . m_tileLoader = undefined ; } this . clear ( ) ; this . frameNumLastRequested = 0 ; this . m_disposed = true ; this . m_tileGeometryLoader ?. dispose ( ) ; if ( this . m_disposeCallback ) { this . m_disposeCallback ( this ) ; } } /**
     * Computes the offset in the x world coordinates corresponding to this tile, based on
     * its  offset .
     *
     *  returns computeWorldOffsetX ( ) { return this . projection . worldExtent ( 0 , 0 ) . max . x * this . offset ; } param zoomLevel internal update ( zoomLevel ) { for ( const object of this . objects ) { if ( object instanceof LodMesh ) { object . setLevelOfDetail ( zoomLevel - this . tileKey . level ) ; } } } /**
     * Gets the tile's bounding box.
     */ get boundingBox ( ) : OrientedBox3 { return this . m_boundingBox ; } param priority param enabledKinds -  GeometryKind s that will be created.
     *  param disabledKinds -  GeometryKind s that will not be created.
     *  return internal updateGeometry ( priority ? , enabledKinds ? , disabledKinds ? ) { if ( ! this . m_tileGeometryLoader ) { return false ; } if ( this . m_tileGeometryLoader . isSettled ) { return true ; } if ( this . dataSource . isDetached ( ) ) { this . m_tileGeometryLoader . cancel ( ) ; return true ; } if ( this . tileLoader ) { if ( ! this . tileLoader . isFinished ) { return true ; } else if ( ! this . decodedTile ) { this . m_tileGeometryLoader . finish ( ) ; return true ; } } if ( priority !== undefined ) { this . m_tileGeometryLoader . priority = priority ; } this . m_tileGeometryLoader . update ( enabledKinds , disabledKinds ) ; return true ; } /**
     * Gets a set of the  GeometryKind s that were loaded (if any).
     *  internal get loadedGeometryKinds ( ) : GeometryKindSet | undefined { return this . m_tileGeometryLoader ?. availableGeometryKinds ; } /**
     * Called when  TileGeometryLoader is finished.
     *
     *  remarks It may be used to add content to the `Tile`.
     * The  {@link @here/harp-datasource-protocol#DecodedTile} is still available.
      protected loadingFinished ( ) { } private attachGeometryLoadedCallback ( ) { assert ( this . m_tileGeometryLoader !== undefined ) ; this . m_tileGeometryLoader ! . waitFinished ( ) . then ( ( ) => { this . loadingFinished ( ) ; this . removeDecodedTile ( ) ; } ) . catch ( ( ) => { if ( this . disposed ) { return ; } if ( ! this . dataSource . isDetached ( ) ) { this . mapView . visibleTileSet . disposeTile ( this ) ; } } ) ; } /**
     * Remove the decodedTile when no longer needed.
     */ private removeDecodedTile ( ) { this . m_decodedTile = undefined ; this . invalidateResourceInfo ( ) ; } param newBoundingBox private updateBoundingBox ( newBoundingBox ? ) { if ( newBoundingBox ) { this . m_boundingBox . copy ( newBoundingBox ) ; this . m_worldCenter . copy ( this . boundingBox . position ) ; } else { this . projection . projectBox ( this . geoBox , this . boundingBox ) ; } } /**
     * Elevates the tile's geo box using the elevation range and maximum geometry height.
     */ private elevateGeoBox ( ) { this . geoBox . southWest . altitude = this . m_elevationRange . minElevation + ( this . m_minGeometryHeight ?? 0 ) ; this . geoBox . northEast . altitude = this . m_elevationRange . maxElevation + ( this . m_maxGeometryHeight ?? 0 ) ; } private computeResourceInfo ( ) { let heapSize = 0 ; let num3dObjects = 0 ; let numTextElements = 0 ; const aggregatedObjSize = { heapSize : 0 , gpuSize : 0 } ; const visitedObjects = new Map ( ) ; for ( const object of this . objects ) { if ( object . visible ) { num3dObjects ++ ; } Object3DUtils . estimateSize ( object , aggregatedObjSize , visitedObjects ) ; } for ( const group of this . textElementGroups . groups ) { numTextElements += group [ 1 ] . elements . length ; } heapSize += numTextElements * 312 ; if ( this . m_decodedTile !== undefined && this . m_decodedTile . tileInfo !== undefined ) { aggregatedObjSize . heapSize += this . m_decodedTile . tileInfo . numBytes ; } this . m_resourceInfo = { heapSize : aggregatedObjSize . heapSize + heapSize , gpuSize : aggregatedObjSize . gpuSize , num3dObjects , numTextElements , numUserTextElements : 0 } ; } }