import { DecodedTile , GeometryKind , GeometryKindSet , isExtrudedLineTechnique , isExtrudedPolygonTechnique , isFillTechnique , isLineMarkerTechnique , isLineTechnique , isPoiTechnique , isSegmentsTechnique , isSolidLineTechnique , isTextTechnique , Technique } from "@here/harp-datasource-protocol" ; import { PerformanceTimer , TaskQueue } from "@here/harp-utils" ; import { TileTaskGroups } from "../MapView" ; import { PerformanceStatistics } from "../Statistics" ; import { Tile } from "../Tile" ; import { TileGeometryCreator } from "./TileGeometryCreator" ; function addDiscardedTileToStats ( tile ) { const stats = PerformanceStatistics . instance ; if ( stats . enabled ) { const name = tile . dataSource . name ; const level = tile . tileKey . level ; const col = tile . tileKey . column ; const row = tile . tileKey . row ; const reason = tile . disposed ? `disposed` : `invisible` ; stats . currentFrame . addMessage ( `Decoded tile: ${ name } # lvl=${ level } col=${ col } row=${ row } DISCARDED - ${ reason }` ) ; } } /**
 * The state the  TileGeometryLoader .
  export enum TileGeometryLoaderState { Initialized = 0 , CreationQueued = 1 , CreatingGeometry = 2 , Finished = 3 , Canceled = 4 , Disposed = 5 } /**
 * Loads the geometry for its  Tile . Loads all geometry in a single step.
 *  internal export class TileGeometryLoader { param { DecodedTile } decodedTile returns { GeometryKindSet } static prepareAvailableGeometryKinds ( decodedTile ) { const foundSet = new GeometryKindSet ( ) ; for ( const technique of decodedTile . techniques ) { const geometryKind = TileGeometryLoader . compileGeometryKind ( technique ) ; if ( geometryKind instanceof Set ) { for ( const kind of geometryKind ) { foundSet . add ( kind ) ; } } else { foundSet . add ( geometryKind ) ; } } return foundSet ; } param { Technique } technique static compileGeometryKind ( technique ) { let geometryKind = technique . kind ; if ( geometryKind === undefined ) { if ( isFillTechnique ( technique ) ) { geometryKind = GeometryKind . Area ; } else if ( isLineTechnique ( technique ) || isSolidLineTechnique ( technique ) || isSegmentsTechnique ( technique ) || isExtrudedLineTechnique ( technique ) ) { geometryKind = GeometryKind . Line ; } else if ( isExtrudedPolygonTechnique ( technique ) ) { geometryKind = GeometryKind . Building ; } else if ( isPoiTechnique ( technique ) || isLineMarkerTechnique ( technique ) || isTextTechnique ( technique ) ) { geometryKind = GeometryKind . Label ; } else { geometryKind = GeometryKind . All ; } technique . kind = geometryKind ; } else if ( Array . isArray ( geometryKind ) ) { geometryKind = technique . kind = new GeometryKindSet ( geometryKind ) ; } return geometryKind ; } private m_decodedTile ? : DecodedTile ; private m_availableGeometryKinds : GeometryKindSet | undefined ; private m_enabledKinds : GeometryKindSet | undefined ; private m_disabledKinds : GeometryKindSet | undefined ; private m_priority : number = 0 ; private m_state : TileGeometryLoaderState = TileGeometryLoaderState . Initialized ; private m_finishedPromise : Promise < void > ; private m_resolveFinishedPromise ? : ( ) => void ; private m_rejectFinishedPromise ? : ( ) => void ; constructor ( private readonly m_tile , private readonly m_taskQueue ) { this . m_finishedPromise = new Promise ( ( resolve , reject ) => { this . m_resolveFinishedPromise = resolve ; this . m_rejectFinishedPromise = reject ; } ) ; } set priority ( value ) { this . m_priority = value ; } getPriority ( ) { return this . m_priority ; } /**
     * The  Tile this `TileGeometryLoader` is managing.
      get tile ( ) : Tile { return this . m_tile ; } /**
     * `True` if a decoded Tile is set
     */ get hasDecodedTile ( ) : boolean { return this . m_decodedTile !== undefined ; } /**
     * `True` if all geometry of the `Tile` has been loaded and the loading process is finished.
     */ get isFinished ( ) : boolean { return this . m_state === TileGeometryLoaderState . Finished ; } /**
     * `True` if loader is finished, canceled or disposed.
     */ get isSettled ( ) : boolean { return this . isFinished || this . isCanceled || this . isDisposed ; } /**
     * Returns a promise resolved when this `TileGeometryLoader` is in
     * `TileGeometryLoaderState.Finished` state, or rejected when it's in
     * `TileGeometryLoaderState.Cancelled` or `TileGeometryLoaderState.Disposed` states.
     */ waitFinished ( ) { return this . m_finishedPromise ; } /**
     * Set the  {@link @here/harp-datasource-protocol#DecodedTile} of the tile.
     *
     *  remarks param { DecodedTile } decodedTile returns { DecodedTile } setDecodedTile ( decodedTile ) { this . m_decodedTile = decodedTile ; if ( this . hasDecodedTile ) { this . m_availableGeometryKinds = TileGeometryLoader . prepareAvailableGeometryKinds ( this . m_decodedTile ) ; } return this . m_decodedTile ; } /**
     * The kinds of geometry stored in this  Tile .
      get availableGeometryKinds ( ) : GeometryKindSet | undefined { return this . m_availableGeometryKinds ; } /**
     * Start with or continue with loading geometry. Called repeatedly until `isFinished` is `true`.
     */ update ( enabledKinds ? , disabledKinds ? ) { const tile = this . tile ; if ( this . tile . hasGeometry && ! this . compareGeometryKinds ( enabledKinds , disabledKinds ) ) { this . reset ( ) ; } if ( ( this . m_state === TileGeometryLoaderState . Initialized || this . m_state === TileGeometryLoaderState . Canceled ) && tile . decodedTile !== undefined ) { if ( this . m_state === TileGeometryLoaderState . Initialized ) { TileGeometryCreator . instance . processTechniques ( tile , enabledKinds , disabledKinds ) ; this . setGeometryKinds ( enabledKinds , disabledKinds ) ; this . setDecodedTile ( tile . decodedTile ) ; } this . queueGeometryCreation ( enabledKinds , disabledKinds ) ; } } /**
     * Cancel geometry loading.
     */ cancel ( ) { addDiscardedTileToStats ( this . tile ) ; this . m_state = TileGeometryLoaderState . Canceled ; this . m_rejectFinishedPromise ?. ( ) ; } /**
     * Dispose of any resources.
     */ dispose ( ) { addDiscardedTileToStats ( this . tile ) ; this . clear ( ) ; this . m_state = TileGeometryLoaderState . Disposed ; this . m_rejectFinishedPromise ?. ( ) ; } remarks This method prepares the loader to reload new geometry. Since the loader does not transition
     * to a final state, the promise returned by  TileGeometryLoader . waitFinished is not
     * settled.
      reset ( ) { this . clear ( ) ; if ( this . isSettled ) { this . m_finishedPromise = new Promise ( ( resolve , reject ) => { this . m_resolveFinishedPromise = resolve ; this . m_rejectFinishedPromise = reject ; } ) ; } this . m_state = TileGeometryLoaderState . Initialized ; } /**
     * Finish geometry loading.
     */ finish ( ) { this . m_decodedTile = undefined ; this . m_state = TileGeometryLoaderState . Finished ; this . m_resolveFinishedPromise ?. ( ) ; } private clear ( ) { this . m_availableGeometryKinds ?. clear ( ) ; this . m_enabledKinds ?. clear ( ) ; this . m_disabledKinds ?. clear ( ) ; this . m_decodedTile = undefined ; } private queueGeometryCreation ( enabledKinds , disabledKinds ) { if ( this . m_state === TileGeometryLoaderState . CreationQueued ) { return ; } this . m_taskQueue . add ( { execute : this . createGeometry . bind ( this , enabledKinds , disabledKinds ) , group : TileTaskGroups . CREATE , getPriority : this . getPriority . bind ( this ) , isExpired : ( ) => { return this . m_state !== TileGeometryLoaderState . CreationQueued ; } , estimatedProcessTime : ( ) => { return ( this . tile . decodedTile ?. decodeTime ?? 30 ) / 6 ; } } ) ; this . m_state = TileGeometryLoaderState . CreationQueued ; } private async createGeometry ( enabledKinds , disabledKinds ) { if ( this . m_state === TileGeometryLoaderState . CreatingGeometry ) { return ; } this . m_state = TileGeometryLoaderState . CreatingGeometry ; const tile = this . tile ; const decodedTile = this . m_decodedTile ; if ( decodedTile === undefined ) { this . finish ( ) ; return ; } const stats = PerformanceStatistics . instance ; let now = 0 ; if ( stats . enabled ) { now = PerformanceTimer . now ( ) ; } const geometryCreator = TileGeometryCreator . instance ; tile . clear ( ) ; geometryCreator . initDecodedTile ( decodedTile , enabledKinds , disabledKinds ) ; await geometryCreator . createAllGeometries ( tile , decodedTile ) ; if ( stats . enabled ) { this . addStats ( stats , now ) ; } this . finish ( ) ; tile . dataSource . requestUpdate ( ) ; } private addStats ( stats , now ) { const tile = this . tile ; const decodedTile = this . m_decodedTile ; if ( decodedTile === undefined ) { return ; } const geometryCreationTime = PerformanceTimer . now ( ) - now ; const currentFrame = stats . currentFrame ; currentFrame . addValue ( "render.fullFrameTime" , geometryCreationTime ) ; currentFrame . addValue ( "render.geometryCreationTime" , geometryCreationTime ) ; currentFrame . addValue ( "geometry.geometryCreationTime" , geometryCreationTime ) ; currentFrame . addValue ( "geometryCount.numGeometries" , decodedTile . geometries . length ) ; currentFrame . addValue ( "geometryCount.numTechniques" , decodedTile . techniques . length ) ; currentFrame . addValue ( "geometryCount.numPoiGeometries" , decodedTile . poiGeometries !== undefined ? decodedTile . poiGeometries . length : 0 ) ; currentFrame . addValue ( "geometryCount.numTextGeometries" , decodedTile . textGeometries !== undefined ? decodedTile . textGeometries . length : 0 ) ; currentFrame . addValue ( "geometryCount.numTextPathGeometries" , decodedTile . textPathGeometries !== undefined ? decodedTile . textPathGeometries . length : 0 ) ; currentFrame . addValue ( "geometryCount.numPathGeometries" , decodedTile . pathGeometries !== undefined ? decodedTile . pathGeometries . length : 0 ) ; currentFrame . addMessage ( `Decoded tile: ${ tile . dataSource . name } # lvl=${ tile . tileKey . level } col=${ tile . tileKey . column } row=${ tile . tileKey . row }` ) ; } param enabledKinds param disabledKinds private setGeometryKinds ( enabledKinds , disabledKinds ) { if ( enabledKinds !== undefined ) { this . m_enabledKinds = Object . assign ( this . m_enabledKinds ?? new GeometryKindSet ( ) , enabledKinds ) ; } if ( disabledKinds !== undefined ) { this . m_disabledKinds = Object . assign ( this . m_disabledKinds ?? new GeometryKindSet ( ) , disabledKinds ) ; } } param enabledKinds param disabledKinds return private compareGeometryKinds ( enabledKinds , disabledKinds ) { const enabledSame = this . m_enabledKinds === enabledKinds ; const disabledSame = this . m_disabledKinds === disabledKinds ; if ( enabledSame && disabledSame ) { return true ; } const enabledEmpty = ( this . m_enabledKinds === undefined || this . m_enabledKinds . size === 0 ) && ( enabledKinds === undefined || enabledKinds . size === 0 ) ; const disabledEmpty = ( this . m_disabledKinds === undefined || this . m_disabledKinds . size === 0 ) && ( disabledKinds === undefined || disabledKinds . size === 0 ) ; if ( ( enabledEmpty && disabledEmpty ) || ( enabledSame && disabledEmpty ) || ( disabledSame && enabledEmpty ) ) { return true ; } if ( ! enabledEmpty ) { if ( enabledKinds === undefined || this . m_enabledKinds === undefined ) { return false ; } else if ( ! enabledKinds . has ( this . m_enabledKinds ) ) { return false ; } } if ( ! disabledEmpty ) { if ( disabledKinds === undefined || this . m_disabledKinds === undefined ) { return false ; } else if ( ! disabledKinds . has ( this . m_disabledKinds ) ) { return false ; } } return true ; } /**
     * `True` if TileGeometryLoader was canceled
     */ private get isCanceled ( ) : boolean { return this . m_state === TileGeometryLoaderState . Canceled ; } /**
     * `True` if TileGeometryLoader was disposed
     */ private get isDisposed ( ) : boolean { return this . m_state === TileGeometryLoaderState . Disposed ; } }