'use strict' ; import type { AnimationStepCallbackFunction } from '../../Core/Animation/AnimationOptions' ; import type ColorType from '../../Core/Color/ColorType' ; import type { GeoJSON , Polygon , TopoJSON } from '../../Maps/GeoJSON' ; import type { LonLatArray , MapBounds } from '../../Maps/MapViewOptions' ; import type MapPointOptions from './MapPointOptions' ; import type MapSeriesOptions from './MapSeriesOptions' ; import type PointerEvent from '../../Core/PointerEvent' ; import type { PointOptions , PointShortOptions } from '../../Core/Series/PointOptions' ; import type ScatterPoint from '../Scatter/ScatterPoint' ; import type { StatesOptionsKey } from '../../Core/Series/StatesOptions' ; import type SVGAttributes from '../../Core/Renderer/SVG/SVGAttributes' ; import type SVGElement from '../../Core/Renderer/SVG/SVGElement' ; import type SVGPath from '../../Core/Renderer/SVG/SVGPath' ; import A from '../../Core/Animation/AnimationUtilities.js' ; const { animObject } = A ; import ColorMapMixin from '../ColorMapMixin.js' ; import CU from '../CenteredUtilities.js' ; import H from '../../Core/Globals.js' ; const { noop } = H ; import LegendSymbol from '../../Core/Legend/LegendSymbol.js' ; import MapChart from '../../Core/Chart/MapChart.js' ; const { splitPath } = MapChart ; import MapPoint from './MapPoint.js' ; import MapView from '../../Maps/MapView.js' ; import { Palette } from '../../Core/Color/Palettes.js' ; import Series from '../../Core/Series/Series.js' ; import SeriesRegistry from '../../Core/Series/SeriesRegistry.js' ; const { seriesTypes : { column : ColumnSeries , scatter : ScatterSeries } } = SeriesRegistry ; import SVGRenderer from '../../Core/Renderer/SVG/SVGRenderer.js' ; import U from '../../Core/Utilities.js' ; const { extend , find , fireEvent , getNestedProperty , isArray , isNumber , isObject , merge , objectEach , pick , splat } = U ; type SVGTransformType = { scaleX : number ; scaleY : number ; translateX : number ; translateY : number ; } ; declare module '../../Core/Series/SeriesLike' { interface SeriesLike { clearBounds ? ( ) : void ; getProjectedBounds ? ( ) : MapBounds | undefined ; mapTitle ? : string ; useMapGeometry ? : boolean ; } } declare module '../../Core/Series/SeriesOptions' { interface SeriesOptions { requires mapData ? : ( Array < MapPointOptions > | GeoJSON | TopoJSON ) ; } interface SeriesStateHoverOptions { brightness ? : number ; color ? : ColorType ; } } declare global { namespace Highcharts { class MapPoint extends ScatterPoint { public colorInterval ? : unknown ; public dataLabelOnNull : ColorMapMixin . ColorMapPoint [ 'dataLabelOnNull' ] ; public isValid : ColorMapMixin . ColorMapPoint [ 'isValid' ] ; public middleX : number ; public middleY : number ; public options : MapPointOptions ; public path : SVGPath ; public properties ? : object ; public series : MapSeries ; public value : ColorMapMixin . ColorMapPoint [ 'value' ] ; public applyOptions ( options , x ? ) ; public onMouseOver ( e ? ) ; public zoomTo ( ) ; } } } private class name augments Highcharts . Series class MapSeries extends ScatterSeries { sample extends plotOptions . scatter excluding product optionparent public static defaultOptions : MapSeriesOptions = merge ( ScatterSeries . defaultOptions , { animation : false , dataLabels : { crop : false , formatter : function ( ) : string { const { numberFormatter } = this . series . chart ; const { value } = this . point as MapPoint ; return isNumber ( value ) ? numberFormatter ( value , - 1 ) : '' ; } , inside : true , overflow : false as any , padding : 0 , verticalAlign : 'middle' } , ignore-option private marker : null as any , sample type { Highcharts . ColorString | Highcharts . GradientColorObject | Highcharts . PatternObject } private nullColor : Palette . neutralColor3 , type { boolean } since apioption private stickyTracking : false , tooltip : { followPointer : true , pointFormat : '{point.name}: {point.value}<br/>' } , ignore-option private turboThreshold : 0 , sample type { boolean } default product apioption private allAreas : true , sample type { Highcharts . ColorString | Highcharts . GradientColorObject | Highcharts . PatternObject } default product apioption private borderColor : Palette . neutralColor20 , sample type { number } default product apioption private borderWidth : 1 , type { string } default apioption sample sample sample type { string | Array < string > } default product apioption private joinBy : 'hc-key' , type { number } product apioption apioption private states : { apioption hover : { ignore-option halo : null as any , sample type { Highcharts . ColorString | Highcharts . GradientColorObject | Highcharts . PatternObject } product apioption type { Highcharts . ColorString | Highcharts . GradientColorObject | Highcharts . PatternObject } product apioption type { number } product apioption type { number } product default apioption brightness : 0.2 } , apioption normal : { productdesc sample apioption animation : true } , apioption select : { type { Highcharts . ColorString | Highcharts . GradientColorObject | Highcharts . PatternObject } default product apioption color : Palette . neutralColor20 } , inactive : { opacity : 1 } } } as MapSeriesOptions ) ; public bounds ? : MapBounds ; public chart : MapChart = void 0 as any ; public data : Array < MapPoint > = void 0 as any ; public group : SVGElement = void 0 as any ; public joinBy : Array < string > = void 0 as any ; public mapData ? : unknown ; public mapMap ? : AnyRecord ; public mapTitle ? : string ; public options : MapSeriesOptions = void 0 as any ; public pointAttrToOptions : unknown ; public points : Array < MapPoint > = void 0 as any ; public processedData : Array < ( MapPointOptions | PointOptions | PointShortOptions ) > = [ ] ; public transformGroups : Array < SVGElement > | undefined ; public valueData ? : Array < number > ; public valueMax ? : number ; public valueMin ? : number ; private public animate ( init ? ) { const { chart , group } = this , animation = animObject ( this . options . animation ) ; if ( chart . renderer . isSVG ) { if ( init ) { group . attr ( { translateX : chart . plotLeft + chart . plotWidth / 2 , translateY : chart . plotTop + chart . plotHeight / 2 , scaleX : 0.001 , scaleY : 0.001 } ) ; } else { group . animate ( { translateX : chart . plotLeft , translateY : chart . plotTop , scaleX : 1 , scaleY : 1 } , animation ) ; } } } private public animateDrilldown ( init ? ) { const chart = this . chart , group = this . group ; if ( chart . renderer . isSVG ) { if ( init ) { group . attr ( { translateX : chart . plotLeft + chart . plotWidth / 2 , translateY : chart . plotTop + chart . plotHeight / 2 , scaleX : 0.1 , scaleY : 0.1 , opacity : 0.01 } ) ; } else { group . animate ( { translateX : chart . plotLeft , translateY : chart . plotTop , scaleX : 1 , scaleY : 1 , opacity : 1 } , ( this . chart . options . drilldown as any ) . animation ) ; if ( chart . drilldown ) { chart . drilldown . fadeInGroup ( this . dataLabelsGroup ) ; } } } } private public animateDrillupFrom ( ) { const chart = this . chart ; if ( chart . renderer . isSVG ) { this . group . animate ( { translateX : chart . plotLeft + chart . plotWidth / 2 , translateY : chart . plotTop + chart . plotHeight / 2 , scaleX : 0.1 , scaleY : 0.1 , opacity : 0.01 } ) ; } } private public animateDrillupTo ( init ? ) { ColumnSeries . prototype . animateDrillupTo . call ( this , init ) ; } public clearBounds ( ) { this . points . forEach ( ( point ) : void => { delete point . bounds ; delete point . projectedPath ; } ) ; delete this . bounds ; } private public doFullTranslate ( ) { return Boolean ( this . isDirtyData || this . chart . isResizing || this . chart . renderer . isVML || ! this . hasRendered ) ; } private public drawMapDataLabels ( ) { Series . prototype . drawDataLabels . call ( this ) ; if ( this . dataLabelsGroup ) { this . dataLabelsGroup . clip ( this . chart . clipRect ) ; } } private public drawPoints ( ) { const { chart , group , transformGroups = [ ] } = this ; const { mapView , renderer } = chart ; if ( ! mapView ) { return ; } this . transformGroups = transformGroups ; if ( ! transformGroups . length ) { [ mapView ] . concat ( mapView . insets ) . forEach ( ( view ) : void => { const transformGroup = renderer . g ( ) . add ( group ) ; transformGroups . push ( transformGroup ) ; } ) ; } if ( this . doFullTranslate ( ) ) { this . points . forEach ( ( point ) : void => { point . group = transformGroups [ typeof point . insetIndex === 'number' ? point . insetIndex + 1 : 0 ] ; if ( point . shapeArgs && chart . hasRendered && ! chart . styledMode ) { point . shapeArgs . fill = this . pointAttribs ( point , point . state ) . fill ; } } ) ; ColumnSeries . prototype . drawPoints . apply ( this ) ; this . points . forEach ( ( point ) : void => { if ( point . graphic ) { let className = '' ; if ( point . name ) { className += 'highcharts-name-' + point . name . replace ( / /g , '-' ) . toLowerCase ( ) ; } if ( point . properties && point . properties [ 'hc-key' ] ) { className += ' highcharts-key-' + point . properties [ 'hc-key' ] . toString ( ) . toLowerCase ( ) ; } if ( className ) { point . graphic . addClass ( className ) ; } if ( chart . styledMode ) { point . graphic . css ( this . pointAttribs ( point , point . selected && 'select' || void 0 ) as any ) ; } } } ) ; } transformGroups . forEach ( ( transformGroup , i ) : void => { const view = i === 0 ? mapView : mapView . insets [ i - 1 ] , svgTransform = view . getSVGTransform ( ) , strokeWidth = pick ( ( this . options as any ) [ ( this . pointAttrToOptions && ( this . pointAttrToOptions as any ) [ 'stroke-width' ] ) || 'borderWidth' ] , 1 ) ; const scale = svgTransform . scaleX ; const flipFactor = svgTransform . scaleY > 0 ? 1 : - 1 ; if ( renderer . globalAnimation && chart . hasRendered ) { const startTranslateX = Number ( transformGroup . attr ( 'translateX' ) ) ; const startTranslateY = Number ( transformGroup . attr ( 'translateY' ) ) ; const startScale = Number ( transformGroup . attr ( 'scaleX' ) ) ; const step = ( now , fx ) : void => { const scaleStep = startScale + ( scale - startScale ) * fx . pos ; transformGroup . attr ( { translateX : ( startTranslateX + ( svgTransform . translateX - startTranslateX ) * fx . pos ) , translateY : ( startTranslateY + ( svgTransform . translateY - startTranslateY ) * fx . pos ) , scaleX : scaleStep , scaleY : scaleStep * flipFactor } ) ; group . element . setAttribute ( 'stroke-width' , strokeWidth / scaleStep ) ; } ; transformGroup . attr ( { animator : 0 } ) . animate ( { animator : 1 } , { step } ) ; } else { transformGroup . attr ( svgTransform ) ; transformGroup . element . setAttribute ( 'stroke-width' , strokeWidth / scale ) ; } } ) ; this . drawMapDataLabels ( ) ; } /**
     * Get the bounding box of all paths in the map combined.
     *
     */ public getProjectedBounds ( ) { if ( ! this . bounds && this . chart . mapView ) { const MAX_VALUE = Number . MAX_VALUE , { insets , projection } = this . chart . mapView , allBounds = [ ] ; ( this . points || [ ] ) . forEach ( function ( point ) : void { if ( point . path || point . geometry ) { if ( typeof point . path === 'string' ) { point . path = splitPath ( point . path ) ; } else if ( isArray ( point . path ) && point . path [ 0 ] as any === 'M' ) { point . path = SVGRenderer . prototype . pathToSegments ( point . path as any ) ; } if ( ! point . bounds ) { let bounds = point . getProjectedBounds ( projection ) ; if ( bounds ) { point . labelrank = pick ( point . labelrank , ( ( bounds . x2 - bounds . x1 ) * ( bounds . y2 - bounds . y1 ) ) ) ; const { midX , midY } = bounds ; if ( insets && isNumber ( midX ) && isNumber ( midY ) ) { const inset = find ( insets , ( inset ) : boolean | undefined => inset . isInside ( { x : midX , y : midY } ) ) ; if ( inset ) { delete point . projectedPath ; bounds = point . getProjectedBounds ( inset . projection ) ; if ( bounds ) { inset . allBounds . push ( bounds ) ; } point . insetIndex = insets . indexOf ( inset ) ; } } point . bounds = bounds ; } } if ( point . bounds && point . insetIndex === void 0 ) { allBounds . push ( point . bounds ) ; } } } ) ; this . bounds = MapView . compositeBounds ( allBounds ) ; } return this . bounds ; } private public hasData ( ) { return ! ! this . processedXData . length ; } private public pointAttribs ( point , state ? ) { const { mapView , styledMode } = point . series . chart ; const attr = styledMode ? this . colorAttribs ( point ) : ColumnSeries . prototype . pointAttribs . call ( this , point as any , state ) ; let pointStrokeWidth = ( point . options as any ) [ ( this . pointAttrToOptions && ( this . pointAttrToOptions as any ) [ 'stroke-width' ] ) || 'borderWidth' ] ; if ( pointStrokeWidth && mapView ) { pointStrokeWidth /= mapView . getScale ( ) ; } if ( attr . dashstyle && mapView && this . options . borderWidth ) { pointStrokeWidth = this . options . borderWidth / mapView . getScale ( ) ; } attr [ 'stroke-width' ] = pick ( pointStrokeWidth , 'inherit' ) ; return attr ; } private public setData ( ) { super . setData . apply ( this , arguments ) ; this . processData ( ) ; this . generatePoints ( ) ; } private public processData ( ) { const options = this . options , data = options . data , chartOptions = this . chart . options . chart , joinBy = this . joinBy , pointArrayMap = options . keys || this . pointArrayMap , dataUsed = [ ] , mapMap = { } ; let mapView = this . chart . mapView , mapDataObject = mapView && ( isObject ( options . mapData , true ) ? mapView . getGeoMap ( options . mapData ) : mapView . geoMap ) , mapTransforms = this . chart . mapTransforms , mapPoint , props , i ; this . chart . mapTransforms = mapTransforms = chartOptions . mapTransforms || mapDataObject && mapDataObject [ 'hc-transform' ] || mapTransforms ; if ( mapTransforms ) { objectEach ( mapTransforms , function ( transform ) : void { if ( transform . rotation ) { transform . cosAngle = Math . cos ( transform . rotation ) ; transform . sinAngle = Math . sin ( transform . rotation ) ; } } ) ; } let mapData ; if ( isArray ( options . mapData ) ) { mapData = options . mapData ; } else if ( mapDataObject && mapDataObject . type === 'FeatureCollection' ) { this . mapTitle = mapDataObject . title ; mapData = H . geojson ( mapDataObject , this . type , this ) ; } this . processedData = [ ] ; const processedData = this . processedData ; if ( data ) { data . forEach ( function ( val , i ) : void { let ix = 0 ; if ( isNumber ( val ) ) { processedData [ i ] = { value : val } ; } else if ( isArray ( val ) ) { processedData [ i ] = { } ; if ( ! options . keys && val . length > pointArrayMap . length && typeof val [ 0 ] === 'string' ) { ( processedData [ i ] as any ) [ 'hc-key' ] = val [ 0 ] ; ++ ix ; } for ( let j = 0 ; j < pointArrayMap . length ; ++ j , ++ ix ) { if ( pointArrayMap [ j ] && typeof val [ ix ] !== 'undefined' ) { if ( pointArrayMap [ j ] . indexOf ( '.' ) > 0 ) { MapPoint . prototype . setNestedProperty ( processedData [ i ] , val [ ix ] , pointArrayMap [ j ] ) ; } else { ( processedData [ i ] as any ) [ pointArrayMap [ j ] ] = val [ ix ] ; } } } } else { processedData [ i ] = data [ i ] ; } if ( joinBy && joinBy [ 0 ] === '_i' ) { ( processedData [ i ] as any ) . _i = i ; } } ) ; } if ( mapData ) { this . mapData = mapData ; this . mapMap = { } ; for ( i = 0 ; i < mapData . length ; i ++ ) { mapPoint = mapData [ i ] ; props = mapPoint . properties ; ( mapPoint as any ) . _i = i ; if ( joinBy [ 0 ] && props && props [ joinBy [ 0 ] ] ) { ( mapPoint as any ) [ joinBy [ 0 ] ] = props [ joinBy [ 0 ] ] ; } mapMap [ ( mapPoint as any ) [ joinBy [ 0 ] ] ] = mapPoint ; } this . mapMap = mapMap ; if ( joinBy [ 1 ] ) { const joinKey = joinBy [ 1 ] ; processedData . forEach ( function ( pointOptions ) : void { const mapKey = getNestedProperty ( joinKey , pointOptions ) as string ; if ( mapMap [ mapKey ] ) { dataUsed . push ( mapMap [ mapKey ] ) ; } } as any ) ; } if ( options . allAreas ) { if ( joinBy [ 1 ] ) { const joinKey = joinBy [ 1 ] ; processedData . forEach ( function ( pointOptions ) : void { dataUsed . push ( getNestedProperty ( joinKey , pointOptions ) as MapPointOptions ) ; } as any ) ; } const dataUsedString = ( '|' + dataUsed . map ( function ( point ) : void { return point && ( point as any ) [ joinBy [ 0 ] ] ; } ) . join ( '|' ) + '|' ) ; mapData . forEach ( function ( mapPoint ) : void { if ( ! joinBy [ 0 ] || dataUsedString . indexOf ( '|' + mapPoint [ joinBy [ 0 ] ] + '|' ) === - 1 ) { processedData . push ( merge ( mapPoint , { value : null } ) ) ; } } ) ; } } this . processedXData = new Array ( processedData . length ) ; return void 0 ; } private public setOptions ( itemOptions ) { let options = Series . prototype . setOptions . call ( this , itemOptions ) , joinBy = options . joinBy , joinByNull = joinBy === null ; if ( joinByNull ) { joinBy = '_i' ; } joinBy = this . joinBy = splat ( joinBy ) ; if ( ! joinBy [ 1 ] ) { joinBy [ 1 ] = joinBy [ 0 ] ; } return options ; } private public translate ( ) { const series = this , doFullTranslate = series . doFullTranslate ( ) , mapView = this . chart . mapView , projection = mapView && mapView . projection ; if ( this . chart . hasRendered && ( this . isDirtyData || ! this . hasRendered ) ) { this . processData ( ) ; this . generatePoints ( ) ; delete this . bounds ; this . getProjectedBounds ( ) ; } if ( mapView ) { const mainSvgTransform = mapView . getSVGTransform ( ) ; series . points . forEach ( function ( point ) : void { const svgTransform = ( isNumber ( point . insetIndex ) && mapView . insets [ point . insetIndex ] . getSVGTransform ( ) ) || mainSvgTransform ; if ( svgTransform && point . bounds && isNumber ( point . bounds . midX ) && isNumber ( point . bounds . midY ) ) { point . plotX = point . bounds . midX * svgTransform . scaleX + svgTransform . translateX ; point . plotY = point . bounds . midY * svgTransform . scaleY + svgTransform . translateY ; } if ( doFullTranslate ) { point . shapeType = 'path' ; point . shapeArgs = { d : MapPoint . getProjectedPath ( point , projection ) } ; } } ) ; } fireEvent ( series , 'afterTranslate' ) ; } } interface MapSeries { colorAttribs : ColorMapMixin . ColorMapSeries [ 'colorAttribs' ] ; drawLegendSymbol : typeof LegendSymbol . drawRectangle ; getCenter : typeof CU [ 'getCenter' ] ; pointArrayMap : ColorMapMixin . ColorMapSeries [ 'pointArrayMap' ] ; pointClass : typeof MapPoint ; preserveAspectRatio : boolean ; trackerGroups : ColorMapMixin . ColorMapSeries [ 'trackerGroups' ] ; animate ( init ? ) : void ; animateDrilldown ( init ? ) : void ; animateDrillupTo ( init ? ) : void ; doFullTranslate ( ) : boolean ; drawMapDataLabels ( ) : void ; drawPoints ( ) : void ; hasData ( ) : boolean ; pointAttribs ( point ? , state ? ) : SVGAttributes ; render ( ) : void ; } extend ( MapSeries . prototype , { type : 'map' , axisTypes : ColorMapMixin . SeriesMixin . axisTypes , colorAttribs : ColorMapMixin . SeriesMixin . colorAttribs , colorKey : ColorMapMixin . SeriesMixin . colorKey , directTouch : true , drawDataLabels : noop , drawGraph : noop , drawLegendSymbol : LegendSymbol . drawRectangle , forceDL : true , getCenter : CU . getCenter , getExtremesFromAll : true , getSymbol : ColorMapMixin . SeriesMixin . getSymbol , isCartesian : false , parallelArrays : ColorMapMixin . SeriesMixin . parallelArrays , pointArrayMap : ColorMapMixin . SeriesMixin . pointArrayMap , pointClass : MapPoint , preserveAspectRatio : true , searchPoint : noop as any , trackerGroups : ColorMapMixin . SeriesMixin . trackerGroups , useMapGeometry : true } ) ; declare module '../../Core/Series/SeriesType' { interface SeriesTypeRegistry { map : typeof MapSeries ; } } SeriesRegistry . registerSeriesType ( 'map' , MapSeries ) ; export default MapSeries ; sample sample type { Array < Highcharts . SeriesMapDataOptions > | Highcharts . GeoJSON | Highcharts . TopoJSON } product apioption extends series excluding product apioption type { Array < number | Array < string , ( number | null ) > | null | * > } product apioption type { Highcharts . ColorString | Highcharts . GradientColorObject | Highcharts . PatternObject } product apioption sample type { Highcharts . DataLabelsOptions } product apioption sample type { string } product apioption sample type { Object } since product apioption type { string } since product validvalue apioption type { Array < LonLatArray > | Array < Array < LonLatArray > > } since product apioption sample type { string } product apioption type { number } product apioption type { number } default product apioption type { number } default product apioption sample type { string } product apioption sample type { string } product apioption type { number | null } product apioption extends plotOptions . series . point . events product apioption '' ;