import { Theme } from "@here/harp-datasource-protocol" ; import { DebugTileDataSource } from "@here/harp-debug-datasource" ; import { TileKey , webMercatorTilingScheme } from "@here/harp-geoutils" ; import { MapControls , MapControlsUI } from "@here/harp-map-controls" ; import { CopyrightElementHandler , MapView , PickResult , Tile } from "@here/harp-mapview" ; import { DataProvider , TileDataSource , TileDataSourceOptions , TileFactory } from "@here/harp-mapview-decoder" ; import { GUI } from "dat.gui" ; import { CUSTOM_DECODER_SERVICE_TYPE } from "../decoder/custom_decoder_defs" ; /**
 * This example shows how to use the  {@link @here/harp-mapview#Tile.dependencies} property to ensure
 * that tiles which have geometry overlapping other.
 *
 * If you pan further enough left / right, you will see that the tile disappears.
 *
 * It combines part of the https://www.harp.gl/docs/master/examples/#object-picking.html and
 * https://www.harp.gl/docs/master/examples/#datasource_custom.html examples.
 * ```
 *  {@link @here/harp-mapview#Tile} s that contain the geometry from another Tile need to have a
 * reference to the Tile containing the overlapping geometry, this is achieved using the
 * `dependencies` property of the  {@link @here/harp-datasource-protocol#DecodedTile} * ```typescript
 * [[include:tile_dependencies.ts]]
 * ```
 * export namespace TileDependenciesExample { const guiOptions = { tileDependencies : false } ; document . body . innerHTML += `
        <style>
            #mouse-picked-result{
                position:absolute;
                bottom:5px;
                border-radius: 5px;
                margin-left:10px;
                padding: 9px 12px;
                background: #37afaa;
                display: inline-block;
                visibility: hidden;
                text-align: left;
                right:50px;
            }
            #mapCanvas {
              top: 0;
            }
        </style>
        <pre id="mouse-picked-result"></pre>
    ` ; class CustomDataProvider extends DataProvider { enableTileDependencies : boolean = false ; constructor ( readonly mainTileKey ) { super ( ) ; } connect ( ) { return Promise . resolve ( ) ; } ready ( ) { return true ; } getTile ( tileKey , abortSignal ? ) { const data = new Array < number > ( ) ; const scale = 10.0 / ( 1 << tileKey . level ) ; data . push ( 0.0 , 0.0 ) ; for ( let t = 0.0 ; t < Math . PI * 4 ; t += 0.1 ) { const x = Math . cos ( t ) * t * scale ; const y = Math . sin ( t ) * t * scale * 10 ; data . push ( x , y ) ; } if ( tileKey . mortonCode ( ) === this . mainTileKey . mortonCode ( ) ) { return Promise . resolve ( new Float32Array ( [ data . length , ... data , 0 ] ) ) ; } else { if ( this . enableTileDependencies && ( tileKey . column >= this . mainTileKey . column - 3 || tileKey . column <= this . mainTileKey . column + 3 ) && tileKey . row === this . mainTileKey . row ) { const { row , column , level } = this . mainTileKey ; return Promise . resolve ( new Float32Array ( [ 0 , 1 , row , column , level ] ) ) ; } } return Promise . resolve ( { } ) ; } @override  dispose ( ) { } } class CustomTile extends Tile { } class CustomDataSource extends TileDataSource < CustomTile > { constructor ( options ) { super ( new TileFactory < CustomTile > ( CustomTile ) , options ) ; } } function customTheme ( ) { const theme = { lights : [ { type : "ambient" , color : "#FFFFFF" , name : "ambientLight" , intensity : 0.9 } ] , styles : { customStyleSet : [ { when : [ "==" , [ "get" , "layer" ] , "line-layer" ] , technique : "solid-line" , attr : { color : "#ff0000" , lineWidth : "10px" , clipping : false } } ] } } ; return theme ; } function getCanvasPosition ( event , canvas ) { const { left , top } = canvas . getBoundingClientRect ( ) ; return { x : event . clientX - Math . floor ( left ) , y : event . clientY - Math . floor ( top ) } ; } let lastCanvasPosition ; function isPick ( eventPosition ) { const MAX_MOVE = 5 ; return ( lastCanvasPosition && Math . abs ( lastCanvasPosition . x - eventPosition . x ) <= MAX_MOVE && Math . abs ( lastCanvasPosition . y - eventPosition . y ) <= MAX_MOVE ) ; } const element = document . getElementById ( "mouse-picked-result" ) as HTMLPreElement ; let current ; function handlePick ( mapViewUsed , x , y ) { let usableIntersections = mapViewUsed . intersectMapObjects ( x , y ) . filter ( pr => ( pr . intersection as any ) . object . userData . dataSource !== "background" ) ; if ( usableIntersections . length > 1 ) { usableIntersections = usableIntersections . filter ( item => item !== current ) ; } if ( usableIntersections . length === 0 ) { element . style . visibility = "hidden" ; return ; } current = usableIntersections [ 0 ] ; element . style . visibility = "visible" ; element . innerText = JSON . stringify ( current . point , undefined , 2 ) ; } function initializeMapView ( id ) { const canvas = document . getElementById ( id ) as HTMLCanvasElement ; const map = new MapView ( { canvas , theme : customTheme ( ) , decoderUrl : "decoder.bundle.js" } ) ; CopyrightElementHandler . install ( "copyrightNotice" , map ) ; const mapControls = new MapControls ( map ) ; mapControls . tiltEnabled = false ; map . lookAt ( { target : [ 0 , 0 ] , zoomLevel : 16 } ) ; const ui = new MapControlsUI ( mapControls ) ; canvas . parentElement ! . appendChild ( ui . domElement ) ; map . resize ( window . innerWidth , window . innerHeight ) ; window . addEventListener ( "resize" , ( ) => { map . resize ( window . innerWidth , window . innerHeight ) ; } ) ; const customDP1 = new CustomDataProvider ( new TileKey ( 16385 , 16384 , 15 ) ) ; const customDP = new CustomDataProvider ( new TileKey ( 16384 , 16384 , 15 ) ) ; const customDatasource = new CustomDataSource ( { name : "customDatasource" , styleSetName : "customStyleSet" , tilingScheme : webMercatorTilingScheme , dataProvider : customDP , concurrentDecoderServiceName : CUSTOM_DECODER_SERVICE_TYPE , storageLevelOffset : - 1 } ) ; const customDatasource1 = new CustomDataSource ( { name : "customDatasource1" , styleSetName : "customStyleSet" , tilingScheme : webMercatorTilingScheme , dataProvider : customDP1 , concurrentDecoderServiceName : CUSTOM_DECODER_SERVICE_TYPE , storageLevelOffset : - 1 } ) ; map . addDataSource ( customDatasource1 ) ; map . addDataSource ( customDatasource ) ; const debugDataSource = new DebugTileDataSource ( webMercatorTilingScheme , "debug" , 20 ) ; map . addDataSource ( debugDataSource ) ; canvas . addEventListener ( "mouseup" , event => { const canvasPos = getCanvasPosition ( event , canvas ) ; if ( isPick ( canvasPos ) ) { handlePick ( map , canvasPos . x , canvasPos . y ) ; } } ) ; canvas . addEventListener ( "mousedown" , event => { lastCanvasPosition = getCanvasPosition ( event , canvas ) ; } ) ; const gui = new GUI ( { width : 300 } ) ; gui . add ( guiOptions , "tileDependencies" ) . onChange ( val => { customDP . enableTileDependencies = ! customDP . enableTileDependencies ; customDP1 . enableTileDependencies = ! customDP1 . enableTileDependencies ; map . clearTileCache ( ) ; map . update ( ) ; } ) ; return map ; } initializeMapView ( "mapCanvas" ) ; const instructions = `
Pan to the left / right until the tile in the center disappears.<br/>
To enable usage of the tile dependencies, check the checkbox,<br/>
the geometry will now always be visible.
Clicking on the geometry will show a box showing where you clicked.
If the tile dependencies checkbox is disabled, this will only work in
the center tile, because otherwise we assume that the contents of any
given tile remain in its boundaries.` ; const message = document . createElement ( "div" ) ; message . style . position = "absolute" ; message . style . cssFloat = "right" ; message . style . top = "60px" ; message . style . right = "10px" ; message . style . backgroundColor = "grey" ; message . innerHTML = instructions ; document . body . appendChild ( message ) ; }