import { DecodedTile , Geometry , GeometryType , InterleavedBufferAttribute } from "@here/harp-datasource-protocol" ; import { MapEnv , StyleSetEvaluator , ThreeBufferUtils } from "@here/harp-datasource-protocol/index-decoder" ; import { GeoCoordinates , Projection , TileKey , webMercatorTilingScheme } from "@here/harp-geoutils" ; import { LineGroup } from "@here/harp-lines" ; import { ThemedTileDecoder , TileDecoderService , WorkerServiceManager } from "@here/harp-mapview-decoder/index-worker" ; import { BoxBufferGeometry , Matrix4 , Vector3 } from "three" ; import { CUSTOM_DECODER_SERVICE_TYPE } from "./custom_decoder_defs" ; const MIN_GEOMETRY_HEIGHT = - 100 ; export class CustomDecoder extends ThemedTileDecoder { @override  connect ( ) { return Promise . resolve ( ) ; } @override  decodeThemedTile ( data , tileKey , styleSetEvaluator , projection ) { const geometries = [ ] ; const array = new Float32Array ( data as ArrayBuffer ) ; const lineHeightRange = this . processLineFeatures ( array , tileKey , styleSetEvaluator , projection , geometries ) ; const meshHeightRange = this . processMeshFeatures ( tileKey , styleSetEvaluator , geometries ) ; const minGeometryHeight = Math . min ( lineHeightRange . minGeometryHeight , meshHeightRange . minGeometryHeight ) ; const maxGeometryHeight = Math . max ( lineHeightRange . maxGeometryHeight , meshHeightRange . maxGeometryHeight ) ; const dependencies = [ ] ; this . processDependencies ( array , dependencies ) ; const decodedTile = { techniques : styleSetEvaluator . techniques , geometries , dependencies , maxGeometryHeight , minGeometryHeight } ; return Promise . resolve ( decodedTile ) ; } private createLineGeometry ( lineGroup , techniqueIndex ) { const { vertices , indices } = lineGroup ; const buffer = new Float32Array ( vertices ) . buffer as ArrayBuffer ; const index = new Uint32Array ( indices ) . buffer as ArrayBuffer ; const attr = { type : "float" , stride : lineGroup . stride , buffer , attributes : lineGroup . vertexAttributes } ; const geometry = { type : GeometryType . SolidLine , index : { buffer : index , itemCount : 1 , type : "uint32" , name : "index" } , interleavedVertexAttributes : [ attr ] , groups : [ { start : 0 , count : indices . length , technique : techniqueIndex } ] , vertexAttributes : [ ] } ; return geometry ; } private processLineFeatures ( data , tileKey , styleSetEvaluator , projection , geometries ) { const env = new MapEnv ( { layer : "line-layer" } ) ; const techniques = styleSetEvaluator . getMatchingTechniques ( env ) ; const geoCenter = webMercatorTilingScheme . getGeoBox ( tileKey ) . center ; const worldCenter = projection . projectPoint ( geoCenter , new Vector3 ( ) ) ; const worldPoints = this . convertToLocalWorldCoordinates ( data , geoCenter , projection , worldCenter ) ; const lineGroup = new LineGroup ( ) ; lineGroup . add ( worldCenter , worldPoints , projection ) ; for ( const technique of techniques ) { geometries . push ( this . createLineGeometry ( lineGroup , technique . _index ) ) ; } return { minGeometryHeight : MIN_GEOMETRY_HEIGHT , maxGeometryHeight : 0 } ; } private convertToLocalWorldCoordinates ( data , geoCenter , projection , worldCenter ) { const numPoints = data [ 0 ] ; const tmpGeoPoint = geoCenter . clone ( ) ; const tmpWorldPoint = new Vector3 ( ) ; const worldPoints = new Array < number > ( ( numPoints / 2 ) * 3 ) ; for ( let i = 0 ; i < numPoints ; i += 2 ) { tmpGeoPoint . copy ( geoCenter ) ; tmpGeoPoint . latitude += data [ i + 1 ] ; tmpGeoPoint . longitude += data [ i + 2 ] ; tmpGeoPoint . altitude = MIN_GEOMETRY_HEIGHT ; projection . projectPoint ( tmpGeoPoint , tmpWorldPoint ) ; tmpWorldPoint . sub ( worldCenter ) . toArray ( worldPoints , ( i / 2 ) * 3 ) ; } return worldPoints ; } private processMeshFeatures ( tileKey , styleSetEvaluator , geometries ) { const env = new MapEnv ( { layer : "mesh-layer" } ) ; const techniques = styleSetEvaluator . getMatchingTechniques ( env ) ; const scale = ( 1 << 20 ) / ( 1 << tileKey . level ) ; const boxGeometry = new BoxBufferGeometry ( 1.0 * scale , 5.0 * scale , 5.0 * scale ) ; const matrix = new Matrix4 ( ) ; matrix . makeTranslation ( 2.0 * scale , 1.0 * scale , 0 ) ; boxGeometry . applyMatrix4 ( matrix ) ; const geometryHeight = 2.5 * scale ; for ( const technique of techniques ) { const geometry = ThreeBufferUtils . fromThreeBufferGeometry ( boxGeometry , technique . _index ) ; geometries . push ( geometry ) ; } return { minGeometryHeight : - geometryHeight , maxGeometryHeight : geometryHeight } ; } private processDependencies ( data , dependencies ) { const numberPoints = data [ 0 ] ; const numberDependenciesIndex = numberPoints + 1 ; const numberDependencies = data [ numberDependenciesIndex ] ; const offset = numberDependenciesIndex + 1 ; for ( let i = 0 ; i < numberDependencies ; i += 3 ) { const row = data [ offset + i ] ; const column = data [ offset + i + 1 ] ; const level = data [ offset + i + 2 ] ; dependencies . push ( TileKey . fromRowColumnLevel ( row , column , level ) . mortonCode ( ) ) ; } } } export class CustomDecoderService { static start ( ) { WorkerServiceManager . getInstance ( ) . register ( { serviceType : CUSTOM_DECODER_SERVICE_TYPE , factory : ( serviceId ) => TileDecoderService . start ( serviceId , new CustomDecoder ( ) ) } ) ; } }