module powerbi . extensibility . visual { import valueFormatter = powerbi . extensibility . utils . formatting . valueFormatter ; export abstract class Layer { protected parent : MapboxMap ; protected source : data . Datasource ; protected id : string ; protected prevLabelPositionSetting : string ; protected colorStops : ColorStops ; constructor ( map ) { this . parent = map ; this . prevLabelPositionSetting = map . getSettings ( ) . api . labelPosition ; } updateSource ( features , roleMap , settings ) { if ( settings [ this . id ] . show ) { this . source . update ( this . parent . getMap ( ) , features , roleMap , settings ) ; } } getBounds ( settings ) { if ( settings [ this . id ] . show ) { return this . source . getBounds ( ) ; } return null ; } getId ( ) { return this . id ; } abstract getLayerIDs ( ) ; updateSelection ( features , roleMap ) { } hoverHighLight ( e ) { } removeHighlight ( roleMap ) { } public getColorStops ( ) { return this . colorStops ; } static mapValuesToColorStops ( colorInterval , method , classCount , values ) { if ( ! values || values . length == 0 ) { return [ ] ; } if ( values . length == 1 ) { const colorStop = values [ 0 ] ; const color = colorInterval [ 0 ] ; return [ { colorStop , color } ] ; } const domain = classCount ? mapboxUtils . getBreaks ( values , method , classCount ) : values ; const colors = chroma . scale ( colorInterval ) . colors ( domain . length ) ; return domain . map ( ( colorStop , idx ) => { const color = colors [ idx ] . toString ( ) ; return { colorStop , color } ; } ) ; } generateColorStops ( settings , isGradient , colorLimits , colorPalette ) { if ( ! isGradient ) { return colorLimits . values . map ( value => { const colorStop = value . toString ( ) ; const color = colorPalette . getColor ( colorStop ) ; return { colorStop , color } ; } ) ; } if ( settings instanceof ClusterSettings || ! settings . diverging ) { const classCount = mapboxUtils . getClassCount ( colorLimits . values ) ; return Layer . mapValuesToColorStops ( [ settings . minColor , settings . maxColor ] , this . getClassificationMethod ( ) , classCount , colorLimits . values ) ; } const { minValue , midValue , maxValue , minColor , midColor , maxColor } = settings ; const filteredValues = mapboxUtils . filterValues ( colorLimits . values , minValue , maxValue ) ; if ( midValue != null ) { const lowerHalf = [ ] ; const upperHalf = [ ] ; if ( minValue != null ) { lowerHalf . push ( minValue ) ; } filteredValues . forEach ( value => { if ( value < midValue ) { lowerHalf . push ( value ) ; } else { upperHalf . push ( value ) ; } } ) ; if ( maxValue != null ) { upperHalf . push ( maxValue ) ; } lowerHalf . push ( midValue ) ; upperHalf . unshift ( midValue ) ; const lowerHalfClassCount = mapboxUtils . getClassCount ( lowerHalf ) >> 1 ; const upperHalfClassCount = mapboxUtils . getClassCount ( upperHalf ) >> 1 ; const lowerColorStops = Layer . mapValuesToColorStops ( [ minColor , midColor ] , this . getClassificationMethod ( ) , lowerHalfClassCount , lowerHalf ) ; const upperColorStops = Layer . mapValuesToColorStops ( [ midColor , maxColor ] , this . getClassificationMethod ( ) , upperHalfClassCount , upperHalf ) ; lowerColorStops . pop ( ) ; return lowerColorStops . concat ( upperColorStops ) ; } if ( minValue != null ) { filteredValues . push ( minValue ) ; } if ( maxValue != null ) { filteredValues . push ( maxValue ) ; } const classCount = mapboxUtils . getClassCount ( filteredValues ) ; return Layer . mapValuesToColorStops ( [ minColor , midColor , maxColor ] , this . getClassificationMethod ( ) , classCount , filteredValues ) ; } getClassificationMethod ( ) { return ClassificationMethod . Quantile ; } applySettings ( settings , roleMap ) { const map = this . parent . getMap ( ) ; if ( settings [ this . id ] . show ) { if ( this . prevLabelPositionSetting === settings . api . labelPosition ) { if ( ! this . layerExists ( ) ) { let firstSymbolId = this . calculateLabelPosition ( settings , map ) ; this . addLayer ( settings , firstSymbolId , roleMap ) ; } } else { const firstSymbolId = this . calculateLabelPosition ( settings , map ) ; this . moveLayer ( firstSymbolId ) ; } } else { if ( this . layerExists ( ) ) { this . removeLayer ( ) ; } } if ( this . prevLabelPositionSetting !== settings . api . labelPosition ) { this . prevLabelPositionSetting = settings . api . labelPosition ; } } addLayer ( settings , beforeLayerId , roleMap ) { } moveLayer ( beforeLayerId ) { } abstract removeLayer ( ) ; layerExists ( ) { const map = this . parent . getMap ( ) ; const layer = map . getLayer ( this . id ) ; return layer != null ; } getSource ( settings ) { if ( settings [ this . id ] . show ) { this . source . ensure ( this . parent . getMap ( ) , this . id , settings ) ; return this . source ; } return null ; } handleZoom ( settings ) { if ( settings [ this . id ] . show ) { return this . source . handleZoom ( this . parent . getMap ( ) , settings ) ; } return false ; } hasTooltip ( tooltips ) { if ( ! tooltips ) { return false ; } return true ; } getFormattedTooltipValue ( roleMap , data ) { const displayName = data . displayName ; const tooltipData = roleMap . tooltips [ displayName ] ; let value = data . value ; if ( tooltipData && tooltipData . format ) { const { format , type } = tooltipData ; if ( type . dateTime ) { value = new Date ( data . value ) ; if ( isNaN ( value ) ) { value = data . value ; } } else if ( type . numeric ) { value = Number ( data . value ) ; } value = valueFormatter . format ( value , format ) ; } return value ; } handleTooltip ( tooltipEvent , roleMap , settings ) { return [ ] ; } calculateLabelPosition ( settings , map ) { let firstSymbolId = null ; if ( settings . api . labelPosition === 'above' ) { firstSymbolId = 'waterway-label' ; if ( settings . api . style == 'mapbox://styles/mapbox/satellite-v9?optimize=true' || settings . api . style == 'custom' ) { firstSymbolId = '' ; let layers = map . getStyle ( ) . layers ; for ( let i = 0 ; i < layers . length ; i ++ ) { if ( layers [ i ] . type === 'symbol' ) { firstSymbolId = layers [ i ] . id ; break ; } } } } return firstSymbolId ; } static getTooltipData ( value ) { if ( ! value ) { return [ ] ; } return [ ] . concat . apply ( [ ] , value . map ( properties => { return properties . map ( prop => { return { displayName : prop . key , value : prop . value . toString ( ) , } ; } ) ; } ) ) ; } showLegend ( settings , roleMap ) { return this . layerExists ( ) ; } addLegend ( legend , roleMap , settings ) { const id = this . getId ( ) ; const title = roleMap . color . displayName ; const colorStops = this . getColorStops ( ) ; const format = roleMap . color . format ; legend . addLegend ( id , title , colorStops , format ) ; } } }