import { addBuffersToTransferList , BufferAttribute , getProjection , InterleavedBufferAttribute , ITileDecoder , WorkerDecoderProtocol } from "@here/harp-datasource-protocol" ; import { TileKey } from "@here/harp-geoutils" ; import { LoggerManager } from "@here/harp-utils" ; import { WorkerService , WorkerServiceResponse } from "./WorkerService" ; const logger = LoggerManager . instance . create ( "TileDecoderService" ) ; /**
 * An extension to [[WorkerService]], the `TileDecoderService` implements an asynchronous
 * (message based) service to decode tile content in web workers. The `TileDecoderService` itself
 * lives in the web worker, and communicates with messages by means of a [[ConcurrentWorkerSet]]
 * with the application.
 *
 * The `TileDecoderService` handles a [[DecodeTileRequest]], which contains a tile and its freshly
 * loaded binary data, decodes the content with the [[ITileDecoder]] that the service is configured
 * to use, and sends the data back in form of a [[WorkerServiceResponse]].
 */ export class TileDecoderService extends WorkerService { param serviceId param decoder static start ( serviceId , decoder ) { return new TileDecoderService ( serviceId , decoder ) ; } param serviceId param m_decoder constructor ( readonly serviceId , private readonly m_decoder ) { super ( serviceId ) ; this . m_decoder . connect ( ) ; } param request returns @override
      protected handleRequest ( request ) { if ( WorkerDecoderProtocol . isDecodeTileRequest ( request ) ) { return this . handleDecodeTileRequest ( request ) ; } else if ( WorkerDecoderProtocol . isTileInfoRequest ( request ) ) { return this . handleTileInfoRequest ( request ) ; } else { return super . handleRequest ( request ) ; } } param request @override
      protected handleMessage ( message ) { if ( WorkerDecoderProtocol . isConfigurationMessage ( message ) ) { this . handleConfigurationMessage ( message ) ; } else { logger . error ( `[${ this . serviceId }]: invalid message ${ message . type }` ) ; } } private async handleDecodeTileRequest ( request ) { const tileKey = TileKey . fromMortonCode ( request . tileKey ) ; const projection = getProjection ( request . projection ) ; const decodedTile = await this . m_decoder . decodeTile ( request . data , tileKey , projection ) ; const transferList = [ ] ; const transferBufferAttribute = ( attribute ? ) => { if ( attribute !== undefined && attribute . buffer . byteLength > 0 && ! transferList . includes ( attribute . buffer ) ) { transferList . push ( attribute . buffer ) ; } } ; decodedTile ?. geometries . forEach ( geom => { geom . vertexAttributes ?. forEach ( attr => transferBufferAttribute ( attr ) ) ; geom . interleavedVertexAttributes ?. forEach ( attr => transferBufferAttribute ( attr ) ) ; transferBufferAttribute ( geom . index ) ; transferBufferAttribute ( geom . edgeIndex ) ; if ( Array . isArray ( geom . objInfos ) && geom . objInfos . length === 1 && typeof geom . objInfos [ 0 ] === "object" && geom . objInfos [ 0 ] ?. hasOwnProperty ( "displacementMap" ) ) { const obj = geom . objInfos [ 0 ] as any ; transferBufferAttribute ( obj . displacementMap ) ; } if ( Array . isArray ( geom . attachments ) ) { geom . attachments . forEach ( attachment => { transferBufferAttribute ( attachment . index ) ; transferBufferAttribute ( attachment . edgeIndex ) ; } ) ; } } ) ; decodedTile ?. techniques . forEach ( technique => { addBuffersToTransferList ( technique , transferList ) ; } ) ; return { response : decodedTile , transferList } ; } private handleTileInfoRequest ( request ) { const tileKey = TileKey . fromMortonCode ( request . tileKey ) ; const projection = getProjection ( request . projection ) ; return this . m_decoder . getTileInfo ( request . data , tileKey , projection ) . then ( tileInfo => { const transferList = tileInfo !== undefined && tileInfo . transferList !== undefined ? tileInfo . transferList : [ ] ; return { response : tileInfo , transferList } ; } ) ; } private handleConfigurationMessage ( message ) { this . m_decoder . configure ( message , message . options ) ; } }