{"version":3,"file":"index.d.ts","sources":["../src/scales/ProjectionScale.ts","../src/scales/LegendScale.ts","../src/scales/ColorScale.ts","../src/scales/SizeScale.ts","../src/elements/GeoFeature.ts","../src/controllers/GeoController.ts","../src/controllers/ChoroplethController.ts","../src/controllers/BubbleMapController.ts"],"sourcesContent":["import { Scale, CoreScaleOptions } from 'chart.js';\nimport {\n  geoPath,\n  geoAzimuthalEqualArea,\n  geoAzimuthalEquidistant,\n  geoGnomonic,\n  geoOrthographic,\n  geoStereographic,\n  geoEqualEarth,\n  geoAlbers,\n  geoAlbersUsa,\n  geoConicConformal,\n  geoConicEqualArea,\n  geoConicEquidistant,\n  geoEquirectangular,\n  geoMercator,\n  geoTransverseMercator,\n  geoNaturalEarth1,\n  GeoProjection,\n  GeoPath,\n  GeoPermissibleObjects,\n  ExtendedFeatureCollection,\n  ExtendedFeature,\n  GeoGeometryObjects,\n  ExtendedGeometryCollection,\n} from 'd3-geo';\n\nconst lookup: { [key: string]: () => GeoProjection } = {\n  geoAzimuthalEqualArea,\n  geoAzimuthalEquidistant,\n  geoGnomonic,\n  geoOrthographic,\n  geoStereographic,\n  geoEqualEarth,\n  geoAlbers,\n  geoAlbersUsa,\n  geoConicConformal,\n  geoConicEqualArea,\n  geoConicEquidistant,\n  geoEquirectangular,\n  geoMercator,\n  geoTransverseMercator,\n  geoNaturalEarth1,\n};\nObject.keys(lookup).forEach((key) => {\n  lookup[`${key.charAt(3).toLowerCase()}${key.slice(4)}`] = lookup[key];\n});\n\nexport interface IProjectionScaleOptions extends CoreScaleOptions {\n  /**\n   * projection method used\n   * @default albersUsa\n   */\n  projection:\n    | GeoProjection\n    | 'azimuthalEqualArea'\n    | 'azimuthalEquidistant'\n    | 'gnomonic'\n    | 'orthographic'\n    | 'stereographic'\n    | 'equalEarth'\n    | 'albers'\n    | 'albersUsa'\n    | 'conicConformal'\n    | 'conicEqualArea'\n    | 'conicEquidistant'\n    | 'equirectangular'\n    | 'mercator'\n    | 'transverseMercator'\n    | 'naturalEarth1';\n\n  /**\n   * extra scale factor applied to projection\n   */\n  projectionScale: number;\n  /**\n   * extra offset applied after projection\n   */\n  projectionOffset: [number, number];\n  /**\n   * padding applied during auto scaling of the map in pixels\n   * i.e. the chart size is reduce by the padding before fitting the map\n   */\n  padding: number | { top: number; left: number; right: number; bottom: number };\n}\n\nexport class ProjectionScale extends Scale<IProjectionScaleOptions> {\n  /**\n   * @hidden\n   */\n  readonly geoPath: GeoPath<any, GeoPermissibleObjects>;\n\n  /**\n   * @hidden\n   */\n  projection!: GeoProjection;\n\n  private outlineBounds: {\n    refX: number;\n    refY: number;\n    refScale: number;\n    width: number;\n    height: number;\n    aspectRatio: number;\n  } | null = null;\n\n  private oldChartBounds: { chartWidth: number; chartHeight: number } | null = null;\n\n  constructor(cfg: any) {\n    super(cfg);\n    this.geoPath = geoPath();\n  }\n\n  /**\n   * @hidden\n   */\n  init(options: IProjectionScaleOptions): void {\n    (options as any).position = 'chartArea';\n    super.init(options);\n    if (typeof options.projection === 'function') {\n      this.projection = options.projection;\n    } else {\n      this.projection = (lookup[options.projection] || lookup.albersUsa)();\n    }\n    this.geoPath.projection(this.projection);\n\n    this.outlineBounds = null;\n    this.oldChartBounds = null;\n  }\n\n  /**\n   * @hidden\n   */\n  computeBounds(outline: ExtendedFeature): void;\n  computeBounds(outline: ExtendedFeatureCollection): void;\n  computeBounds(outline: GeoGeometryObjects): void;\n  computeBounds(outline: ExtendedGeometryCollection): void;\n\n  computeBounds(outline: any): void {\n    const bb = geoPath(this.projection.fitWidth(1000, outline)).bounds(outline);\n    const bHeight = Math.ceil(bb[1][1] - bb[0][1]);\n    const bWidth = Math.ceil(bb[1][0] - bb[0][0]);\n    const t = this.projection.translate();\n\n    this.outlineBounds = {\n      width: bWidth,\n      height: bHeight,\n      aspectRatio: bWidth / bHeight,\n      refScale: this.projection.scale(),\n      refX: t[0],\n      refY: t[1],\n    };\n  }\n\n  /**\n   * @hidden\n   */\n  updateBounds(): boolean {\n    const area = this.chart.chartArea;\n\n    const bb = this.outlineBounds;\n\n    if (!bb) {\n      return false;\n    }\n    const padding = this.options.padding;\n    const paddingTop = typeof padding === 'number' ? padding : padding.top;\n    const paddingLeft = typeof padding === 'number' ? padding : padding.left;\n    const paddingBottom = typeof padding === 'number' ? padding : padding.bottom;\n    const paddingRight = typeof padding === 'number' ? padding : padding.right;\n\n    const chartWidth = area.right - area.left - paddingLeft - paddingRight;\n    const chartHeight = area.bottom - area.top - paddingTop - paddingBottom;\n\n    const bak = this.oldChartBounds;\n    this.oldChartBounds = {\n      chartWidth,\n      chartHeight,\n    };\n\n    const scale = Math.min(chartWidth / bb.width, chartHeight / bb.height);\n    const viewWidth = bb.width * scale;\n    const viewHeight = bb.height * scale;\n\n    const x = (chartWidth - viewWidth) * 0.5 + area.left + paddingLeft;\n    const y = (chartHeight - viewHeight) * 0.5 + area.top + paddingTop;\n\n    // this.mapScale = scale;\n    // this.mapTranslate = {x, y};\n\n    const o = this.options;\n\n    this.projection\n      .scale(bb.refScale * scale * o.projectionScale)\n      .translate([scale * bb.refX + x + o.projectionOffset[0], scale * bb.refY + y + o.projectionOffset[1]]);\n\n    return (\n      !bak || bak.chartWidth !== this.oldChartBounds.chartWidth || bak.chartHeight !== this.oldChartBounds.chartHeight\n    );\n  }\n\n  static readonly id = 'projection';\n\n  /**\n   * @hidden\n   */\n  static readonly defaults: Partial<IProjectionScaleOptions> = {\n    projection: 'albersUsa',\n    projectionScale: 1,\n    projectionOffset: [0, 0],\n    padding: 0,\n  };\n\n  /**\n   * @hidden\n   */\n  static readonly descriptors = /* #__PURE__ */ {\n    _scriptable: (name: keyof IProjectionScaleOptions): boolean => name !== 'projection',\n    _indexable: (name: keyof IProjectionScaleOptions): boolean => name !== 'projectionOffset',\n  };\n}\n\ndeclare module 'chart.js' {\n  export interface ProjectionScaleTypeRegistry {\n    projection: {\n      options: IProjectionScaleOptions;\n    };\n  }\n\n  // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n  export interface ScaleTypeRegistry extends ProjectionScaleTypeRegistry {}\n}\n","import {\n  ChartArea,\n  CartesianScaleOptions,\n  LinearScale,\n  LinearScaleOptions,\n  LogarithmicScale,\n  LogarithmicScaleOptions,\n} from 'chart.js';\n\nexport interface ILegendScaleOptions extends CartesianScaleOptions {\n  /**\n   * whether to render a color legend\n   * @default true\n   */\n  display: boolean;\n\n  /**\n   * the property name that stores the value in the data elements\n   * @default value\n   */\n  property: string;\n\n  legend: {\n    /**\n     * location of the legend on the chart area\n     * @default bottom-right\n     */\n    position:\n      | 'left'\n      | 'right'\n      | 'top'\n      | 'bottom'\n      | 'top-left'\n      | 'top-right'\n      | 'top-right'\n      | 'bottom-right'\n      | 'bottom-left'\n      | { x: number; y: number };\n    /**\n     * alignment of the scale, e.g., `right` means that it is a vertical scale\n     * with the ticks on the right side\n     * @default right\n     */\n    align: 'left' | 'right' | 'top' | 'bottom';\n    /**\n     * length of the legend, i.e., for a horizontal scale the width\n     * if a value < 1 is given, is it assume to be a ratio of the corresponding\n     * chart area\n     * @default 100\n     */\n    length: number;\n    /**\n     * how wide the scale is, i.e., for a horizontal scale the height\n     * if a value < 1 is given, is it assume to be a ratio of the corresponding\n     * chart area\n     * @default 50\n     */\n    width: number;\n    /**\n     * how many pixels should be used for the color bar\n     * @default 10\n     */\n    indicatorWidth: number;\n    /**\n     * margin pixels such that it doesn't stick to the edge of the chart\n     * @default 8\n     */\n    margin: number | ChartArea;\n  };\n}\n\nexport const baseDefaults = {\n  position: 'chartArea',\n  property: 'value',\n  grid: {\n    z: 1,\n    drawOnChartArea: false,\n  },\n  ticks: {\n    z: 1,\n  },\n  legend: {\n    align: 'right',\n    position: 'bottom-right',\n    length: 100,\n    width: 50,\n    margin: 8,\n    indicatorWidth: 10,\n  },\n};\n\ninterface IPositionOption {\n  position?: string;\n}\n\nfunction computeLegendMargin(legend: ILegendScaleOptions['legend']): {\n  left: number;\n  top: number;\n  right: number;\n  bottom: number;\n} {\n  const { indicatorWidth, align: pos, margin } = legend;\n\n  const left = (typeof margin === 'number' ? margin : margin.left) + (pos === 'right' ? indicatorWidth : 0);\n  const top = (typeof margin === 'number' ? margin : margin.top) + (pos === 'bottom' ? indicatorWidth : 0);\n  const right = (typeof margin === 'number' ? margin : margin.right) + (pos === 'left' ? indicatorWidth : 0);\n  const bottom = (typeof margin === 'number' ? margin : margin.bottom) + (pos === 'top' ? indicatorWidth : 0);\n  return { left, top, right, bottom };\n}\n\nfunction computeLegendPosition(\n  chartArea: ChartArea,\n  legend: ILegendScaleOptions['legend'],\n  width: number,\n  height: number,\n  legendSize: { w: number; h: number }\n): [number, number] {\n  const { indicatorWidth, align: axisPos, position: pos } = legend;\n  const isHor = axisPos === 'top' || axisPos === 'bottom';\n  const w = (axisPos === 'left' ? legendSize.w : width) + (isHor ? indicatorWidth : 0);\n  const h = (axisPos === 'top' ? legendSize.h : height) + (!isHor ? indicatorWidth : 0);\n  const margin = computeLegendMargin(legend);\n\n  if (typeof pos === 'string') {\n    switch (pos) {\n      case 'top-left':\n        return [margin.left, margin.top];\n      case 'top':\n        return [(chartArea.right - w) / 2, margin.top];\n      case 'left':\n        return [margin.left, (chartArea.bottom - h) / 2];\n      case 'top-right':\n        return [chartArea.right - w - margin.right, margin.top];\n      case 'bottom-right':\n        return [chartArea.right - w - margin.right, chartArea.bottom - h - margin.bottom];\n      case 'bottom':\n        return [(chartArea.right - w) / 2, chartArea.bottom - h - margin.bottom];\n      case 'bottom-left':\n        return [margin.left, chartArea.bottom - h - margin.bottom];\n      default:\n        // right\n        return [chartArea.right - w - margin.right, (chartArea.bottom - h) / 2];\n    }\n  }\n  return [pos.x, pos.y];\n}\n\nexport class LegendScale<O extends ILegendScaleOptions & LinearScaleOptions> extends LinearScale<O> {\n  /**\n   * @hidden\n   */\n  legendSize: { w: number; h: number } = { w: 0, h: 0 };\n\n  /**\n   * @hidden\n   */\n  init(options: O): void {\n    (options as unknown as IPositionOption).position = 'chartArea';\n    super.init(options);\n    this.axis = 'r';\n  }\n\n  /**\n   * @hidden\n   */\n\n  parse(raw: any, index: number): number {\n    if (raw && typeof raw[this.options.property] === 'number') {\n      return raw[this.options.property];\n    }\n    return super.parse(raw, index) as number;\n  }\n\n  /**\n   * @hidden\n   */\n  isHorizontal(): boolean {\n    return this.options.legend.align === 'top' || this.options.legend.align === 'bottom';\n  }\n\n  protected _getNormalizedValue(v: number): number | null {\n    if (v == null || Number.isNaN(v)) {\n      return null;\n    }\n    return (v - (this as any)._startValue) / (this as any)._valueRange;\n  }\n\n  /**\n   * @hidden\n   */\n  update(maxWidth: number, maxHeight: number, margins: ChartArea): void {\n    const ch = Math.min(maxHeight, this.bottom == null ? Number.POSITIVE_INFINITY : this.bottom);\n    const cw = Math.min(maxWidth, this.right == null ? Number.POSITIVE_INFINITY : this.right);\n\n    const l = this.options.legend;\n    const isHor = this.isHorizontal();\n    const factor = (v: number, full: number) => (v < 1 ? full * v : v);\n    const w = Math.min(cw, factor(isHor ? l.length : l.width, cw)) - (!isHor ? l.indicatorWidth : 0);\n    const h = Math.min(ch, factor(!isHor ? l.length : l.width, ch)) - (isHor ? l.indicatorWidth : 0);\n    this.legendSize = { w, h };\n    this.bottom = h;\n    this.height = h;\n    this.right = w;\n    this.width = w;\n\n    const bak = (this.options as IPositionOption).position;\n    (this.options as IPositionOption).position = this.options.legend.align;\n    const r = super.update(w, h, margins);\n    (this.options as IPositionOption).position = bak;\n    this.height = Math.min(h, this.height);\n    this.width = Math.min(w, this.width);\n    return r;\n  }\n\n  /**\n   * @hidden\n   */\n\n  _computeLabelArea(): void {\n    return undefined;\n  }\n\n  /**\n   * @hidden\n   */\n  draw(chartArea: ChartArea): void {\n    if (!(this as any)._isVisible()) {\n      return;\n    }\n    const pos = computeLegendPosition(chartArea, this.options.legend, this.width, this.height, this.legendSize);\n    /** @type {CanvasRenderingContext2D} */\n    const { ctx } = this;\n    ctx.save();\n    ctx.translate(pos[0], pos[1]);\n\n    const bak = (this.options as IPositionOption).position;\n    (this.options as IPositionOption).position = this.options.legend.align;\n    super.draw({ ...chartArea, bottom: this.height + 10, right: this.width });\n    (this.options as IPositionOption).position = bak;\n    const { indicatorWidth } = this.options.legend;\n    switch (this.options.legend.align) {\n      case 'left':\n        ctx.translate(this.legendSize.w, 0);\n        break;\n      case 'top':\n        ctx.translate(0, this.legendSize.h);\n        break;\n      case 'bottom':\n        ctx.translate(0, -indicatorWidth);\n        break;\n      default:\n        ctx.translate(-indicatorWidth, 0);\n        break;\n    }\n    this._drawIndicator();\n    ctx.restore();\n  }\n\n  /**\n   * @hidden\n   */\n\n  protected _drawIndicator(): void {\n    // hook\n  }\n}\n\nexport class LogarithmicLegendScale<\n  O extends ILegendScaleOptions & LogarithmicScaleOptions,\n> extends LogarithmicScale<O> {\n  /**\n   * @hidden\n   */\n  legendSize: { w: number; h: number } = { w: 0, h: 0 };\n\n  /**\n   * @hidden\n   */\n  init(options: O): void {\n    LegendScale.prototype.init.call(this, options);\n  }\n\n  /**\n   * @hidden\n   */\n\n  parse(raw: any, index: number): number {\n    return LegendScale.prototype.parse.call(this, raw, index);\n  }\n\n  /**\n   * @hidden\n   */\n  isHorizontal(): boolean {\n    return this.options.legend.align === 'top' || this.options.legend.align === 'bottom';\n  }\n\n  protected _getNormalizedValue(v: number): number | null {\n    if (v == null || Number.isNaN(v)) {\n      return null;\n    }\n    return (Math.log10(v) - (this as any)._startValue) / (this as any)._valueRange;\n  }\n\n  /**\n   * @hidden\n   */\n  update(maxWidth: number, maxHeight: number, margins: ChartArea): void {\n    return LegendScale.prototype.update.call(this, maxWidth, maxHeight, margins);\n  }\n\n  /**\n   * @hidden\n   */\n\n  _computeLabelArea(): void {\n    return undefined;\n  }\n\n  /**\n   * @hidden\n   */\n  draw(chartArea: ChartArea): void {\n    return LegendScale.prototype.draw.call(this, chartArea);\n  }\n\n  protected _drawIndicator(): void {\n    // hook\n  }\n}\n","import { LinearScale, LogarithmicScale, LogarithmicScaleOptions, LinearScaleOptions } from 'chart.js';\nimport { merge } from 'chart.js/helpers';\nimport {\n  interpolateBlues,\n  interpolateBrBG,\n  interpolateBuGn,\n  interpolateBuPu,\n  interpolateCividis,\n  interpolateCool,\n  interpolateCubehelixDefault,\n  interpolateGnBu,\n  interpolateGreens,\n  interpolateGreys,\n  interpolateInferno,\n  interpolateMagma,\n  interpolateOrRd,\n  interpolateOranges,\n  interpolatePRGn,\n  interpolatePiYG,\n  interpolatePlasma,\n  interpolatePuBu,\n  interpolatePuBuGn,\n  interpolatePuOr,\n  interpolatePuRd,\n  interpolatePurples,\n  interpolateRainbow,\n  interpolateRdBu,\n  interpolateRdGy,\n  interpolateRdPu,\n  interpolateRdYlBu,\n  interpolateRdYlGn,\n  interpolateReds,\n  interpolateSinebow,\n  interpolateSpectral,\n  interpolateTurbo,\n  interpolateViridis,\n  interpolateWarm,\n  interpolateYlGn,\n  interpolateYlGnBu,\n  interpolateYlOrBr,\n  interpolateYlOrRd,\n} from 'd3-scale-chromatic';\nimport { baseDefaults, LegendScale, LogarithmicLegendScale, ILegendScaleOptions } from './LegendScale';\n\nconst lookup: { [key: string]: (normalizedValue: number) => string } = {\n  interpolateBlues,\n  interpolateBrBG,\n  interpolateBuGn,\n  interpolateBuPu,\n  interpolateCividis,\n  interpolateCool,\n  interpolateCubehelixDefault,\n  interpolateGnBu,\n  interpolateGreens,\n  interpolateGreys,\n  interpolateInferno,\n  interpolateMagma,\n  interpolateOrRd,\n  interpolateOranges,\n  interpolatePRGn,\n  interpolatePiYG,\n  interpolatePlasma,\n  interpolatePuBu,\n  interpolatePuBuGn,\n  interpolatePuOr,\n  interpolatePuRd,\n  interpolatePurples,\n  interpolateRainbow,\n  interpolateRdBu,\n  interpolateRdGy,\n  interpolateRdPu,\n  interpolateRdYlBu,\n  interpolateRdYlGn,\n  interpolateReds,\n  interpolateSinebow,\n  interpolateSpectral,\n  interpolateTurbo,\n  interpolateViridis,\n  interpolateWarm,\n  interpolateYlGn,\n  interpolateYlGnBu,\n  interpolateYlOrBr,\n  interpolateYlOrRd,\n};\n\nObject.keys(lookup).forEach((key) => {\n  lookup[`${key.charAt(11).toLowerCase()}${key.slice(12)}`] = lookup[key];\n  lookup[key.slice(11)] = lookup[key];\n});\n\nfunction quantize(v: number, steps: number) {\n  const perStep = 1 / steps;\n  if (v <= perStep) {\n    return 0;\n  }\n  if (v >= 1 - perStep) {\n    return 1;\n  }\n  for (let acc = 0; acc < 1; acc += perStep) {\n    if (v < acc) {\n      return acc - perStep / 2; // center\n    }\n  }\n  return v;\n}\n\nexport interface IColorScaleOptions extends ILegendScaleOptions {\n  // support all options from linear scale -> https://www.chartjs.org/docs/latest/axes/cartesian/linear.html#linear-cartesian-axis\n  // e.g. for tick manipulation, ...\n\n  /**\n   * color interpolation method which is either a function\n   * converting a normalized value to string or a\n   * well defined string of all the interpolation scales\n   * from https://github.com/d3/d3-scale-chromatic.\n   * e.g. interpolateBlues -> blues\n   *\n   * @default blues\n   */\n  interpolate:\n    | ((normalizedValue: number) => string)\n    | 'blues'\n    | 'brBG'\n    | 'buGn'\n    | 'buPu'\n    | 'cividis'\n    | 'cool'\n    | 'cubehelixDefault'\n    | 'gnBu'\n    | 'greens'\n    | 'greys'\n    | 'inferno'\n    | 'magma'\n    | 'orRd'\n    | 'oranges'\n    | 'pRGn'\n    | 'piYG'\n    | 'plasma'\n    | 'puBu'\n    | 'puBuGn'\n    | 'puOr'\n    | 'puRd'\n    | 'purples'\n    | 'rainbow'\n    | 'rdBu'\n    | 'rdGy'\n    | 'rdPu'\n    | 'rdYlBu'\n    | 'rdYlGn'\n    | 'reds'\n    | 'sinebow'\n    | 'spectral'\n    | 'turbo'\n    | 'viridis'\n    | 'warm'\n    | 'ylGn'\n    | 'ylGnBu'\n    | 'ylOrBr'\n    | 'ylOrRd';\n\n  /**\n   * color value to render for missing values\n   * @default transparent\n   */\n  missing: string;\n\n  /**\n   * allows to split the color scale in N quantized equal bins.\n   * @default 0\n   */\n  quantize: number;\n}\n\nconst colorScaleDefaults = {\n  interpolate: 'blues',\n  missing: 'transparent',\n  quantize: 0,\n};\n\nexport class ColorScale extends LegendScale<IColorScaleOptions & LinearScaleOptions> {\n  /**\n   * @hidden\n   */\n  get interpolate(): (v: number) => string {\n    const o = this.options as IColorScaleOptions & LinearScaleOptions;\n    if (!o) {\n      return (v: number) => `rgb(${v},${v},${v})`;\n    }\n    if (typeof o.interpolate === 'function') {\n      return o.interpolate;\n    }\n    return lookup[o.interpolate] || lookup.blues;\n  }\n\n  /**\n   * @hidden\n   */\n  getColorForValue(value: number): string {\n    const v = this._getNormalizedValue(value);\n    if (v == null || Number.isNaN(v)) {\n      return this.options.missing;\n    }\n    return this.getColor(v);\n  }\n\n  /**\n   * @hidden\n   */\n  getColor(normalized: number): string {\n    let v = normalized;\n    if (this.options.quantize > 0) {\n      v = quantize(v, this.options.quantize);\n    }\n    return this.interpolate(v);\n  }\n\n  /**\n   * @hidden\n   */\n  _drawIndicator(): void {\n    const { indicatorWidth: indicatorSize } = this.options.legend;\n    const reverse = (this as any)._reversePixels;\n\n    if (this.isHorizontal()) {\n      const w = this.width;\n      if (this.options.quantize > 0) {\n        const stepWidth = w / this.options.quantize;\n        const offset = !reverse ? (i: number) => i : (i: number) => w - stepWidth - i;\n        for (let i = 0; i < w; i += stepWidth) {\n          const v = (i + stepWidth / 2) / w;\n          this.ctx.fillStyle = this.getColor(v);\n          this.ctx.fillRect(offset(i), 0, stepWidth, indicatorSize);\n        }\n      } else {\n        const offset = !reverse ? (i: number) => i : (i: number) => w - 1 - i;\n        for (let i = 0; i < w; i += 1) {\n          this.ctx.fillStyle = this.getColor((i + 0.5) / w);\n          this.ctx.fillRect(offset(i), 0, 1, indicatorSize);\n        }\n      }\n    } else {\n      const h = this.height;\n      if (this.options.quantize > 0) {\n        const stepWidth = h / this.options.quantize;\n        const offset = !reverse ? (i: number) => i : (i: number) => h - stepWidth - i;\n        for (let i = 0; i < h; i += stepWidth) {\n          const v = (i + stepWidth / 2) / h;\n          this.ctx.fillStyle = this.getColor(v);\n          this.ctx.fillRect(0, offset(i), indicatorSize, stepWidth);\n        }\n      } else {\n        const offset = !reverse ? (i: number) => i : (i: number) => h - 1 - i;\n        for (let i = 0; i < h; i += 1) {\n          this.ctx.fillStyle = this.getColor((i + 0.5) / h);\n          this.ctx.fillRect(0, offset(i), indicatorSize, 1);\n        }\n      }\n    }\n  }\n\n  static readonly id = 'color';\n\n  /**\n   * @hidden\n   */\n  static readonly defaults: any = /* #__PURE__ */ merge({}, [LinearScale.defaults, baseDefaults, colorScaleDefaults]);\n\n  /**\n   * @hidden\n   */\n  static readonly descriptors = /* #__PURE__ */ {\n    _scriptable: (name: string): boolean => name !== 'interpolate',\n    _indexable: false,\n  };\n}\n\nexport class ColorLogarithmicScale extends LogarithmicLegendScale<IColorScaleOptions & LogarithmicScaleOptions> {\n  private interpolate = (v: number) => `rgb(${v},${v},${v})`;\n\n  /**\n   * @hidden\n   */\n  init(options: IColorScaleOptions & LinearScaleOptions): void {\n    super.init(options);\n    if (typeof options.interpolate === 'function') {\n      this.interpolate = options.interpolate;\n    } else {\n      this.interpolate = lookup[options.interpolate] || lookup.blues;\n    }\n  }\n\n  /**\n   * @hidden\n   */\n  getColorForValue(value: number): string {\n    return ColorScale.prototype.getColorForValue.call(this, value);\n  }\n\n  /**\n   * @hidden\n   */\n  getColor(normalized: number): string {\n    let v = normalized;\n    if (this.options.quantize > 0) {\n      v = quantize(v, this.options.quantize);\n    }\n    return this.interpolate(v);\n  }\n\n  protected _drawIndicator(): void {\n    return ColorScale.prototype._drawIndicator.call(this);\n  }\n\n  static readonly id = 'colorLogarithmic';\n\n  /**\n   * @hidden\n   */\n  static readonly defaults: any = /* #__PURE__ */ merge({}, [\n    LogarithmicScale.defaults,\n    baseDefaults,\n    colorScaleDefaults,\n  ]);\n\n  /**\n   * @hidden\n   */\n  static readonly descriptors = /* #__PURE__ */ {\n    _scriptable: (name: string): boolean => name !== 'interpolate',\n    _indexable: false,\n  };\n}\n\ndeclare module 'chart.js' {\n  export interface ColorScaleTypeRegistry {\n    color: {\n      options: IColorScaleOptions & LinearScaleOptions;\n    };\n    colorLogarithmic: {\n      options: IColorScaleOptions & LogarithmicScaleOptions;\n    };\n  }\n\n  // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n  export interface ScaleTypeRegistry extends ColorScaleTypeRegistry {}\n}\n","import { LinearScale, LogarithmicScale, PointOptions, LinearScaleOptions, LogarithmicScaleOptions } from 'chart.js';\nimport { merge, drawPoint } from 'chart.js/helpers';\nimport { baseDefaults, ILegendScaleOptions, LegendScale, LogarithmicLegendScale } from './LegendScale';\n\nexport interface ISizeScaleOptions extends ILegendScaleOptions {\n  // support all options from linear scale -> https://www.chartjs.org/docs/latest/axes/cartesian/linear.html#linear-cartesian-axis\n  // e.g. for tick manipulation, ...\n\n  /**\n   * radius range in pixel, the minimal data value will be mapped to the\n   * first entry,  the maximal one to the second and a linear interpolation\n   * for all values in between.\n   *\n   * @default [2, 20]\n   */\n  range: [number, number];\n\n  /**\n   * operation mode for the scale, area means that the area is linearly increasing whereas radius the radius is.\n   * The area one is the default since it gives a better visual comparison of values\n   * @default area\n   */\n  mode: 'radius' | 'area';\n\n  /**\n   * radius to render for missing values\n   * @default 1\n   */\n  missing: number;\n}\n\nconst scaleDefaults = {\n  missing: 1,\n  mode: 'area', // 'radius'\n  // mode: 'radius',\n  range: [2, 20],\n  legend: {\n    align: 'bottom',\n    length: 90,\n    width: 70,\n    indicatorWidth: 42,\n  },\n};\n\nexport class SizeScale extends LegendScale<ISizeScaleOptions & LinearScaleOptions> {\n  /**\n   * @hidden\n   */\n  _model: PointOptions | null = null;\n\n  /**\n   * @hidden\n   */\n  getSizeForValue(value: number): number {\n    const v = this._getNormalizedValue(value);\n    if (v == null || Number.isNaN(v)) {\n      return this.options.missing;\n    }\n    return this.getSizeImpl(v);\n  }\n\n  /**\n   * @hidden\n   */\n  getSizeImpl(normalized: number): number {\n    const [r0, r1] = this.options.range;\n    if (this.options.mode === 'area') {\n      const a1 = r1 * r1 * Math.PI;\n      const a0 = r0 * r0 * Math.PI;\n      const range = a1 - a0;\n      const a = normalized * range + a0;\n      return Math.sqrt(a / Math.PI);\n    }\n    const range = r1 - r0;\n    return normalized * range + r0;\n  }\n\n  /**\n   * @hidden\n   */\n  _drawIndicator(): void {\n    /** @type {CanvasRenderingContext2D} */\n    const { ctx } = this;\n    const shift = this.options.legend.indicatorWidth / 2;\n\n    const isHor = this.isHorizontal();\n    const values = this.ticks;\n    const labelItems = this.getLabelItems();\n    const positions = labelItems\n      ? labelItems.map((el: any) => ({ [isHor ? 'x' : 'y']: el.options.translation[isHor ? 0 : 1] }))\n      : values.map((_, i) => ({ [isHor ? 'x' : 'y']: this.getPixelForTick(i) }));\n\n    ((this as any)._gridLineItems || []).forEach((item: any) => {\n      ctx.save();\n      ctx.strokeStyle = item.color;\n      ctx.lineWidth = item.width;\n\n      if (ctx.setLineDash) {\n        ctx.setLineDash(item.borderDash);\n        ctx.lineDashOffset = item.borderDashOffset;\n      }\n\n      ctx.beginPath();\n\n      if (this.options.grid.drawTicks) {\n        switch (this.options.legend.align) {\n          case 'left':\n            ctx.moveTo(0, item.ty1);\n            ctx.lineTo(shift, item.ty2);\n            break;\n          case 'top':\n            ctx.moveTo(item.tx1, 0);\n            ctx.lineTo(item.tx2, shift);\n            break;\n          case 'bottom':\n            ctx.moveTo(item.tx1, shift);\n            ctx.lineTo(item.tx2, shift * 2);\n            break;\n          default:\n            // right\n            ctx.moveTo(shift, item.ty1);\n            ctx.lineTo(shift * 2, item.ty2);\n            break;\n        }\n      }\n      ctx.stroke();\n      ctx.restore();\n    });\n\n    if (this._model) {\n      const props = this._model;\n      ctx.strokeStyle = props.borderColor;\n      ctx.lineWidth = props.borderWidth || 0;\n      ctx.fillStyle = props.backgroundColor;\n    } else {\n      ctx.fillStyle = 'blue';\n    }\n\n    values.forEach((v, i) => {\n      const pos = positions[i];\n      const radius = this.getSizeForValue(v.value);\n      const x = isHor ? pos.x : shift;\n      const y = isHor ? shift : pos.y;\n      const renderOptions = {\n        pointStyle: 'circle' as const,\n        borderWidth: 0,\n        ...(this._model || {}),\n        radius,\n      };\n      drawPoint(ctx, renderOptions, x, y);\n    });\n  }\n\n  static readonly id = 'size';\n\n  /**\n   * @hidden\n   */\n  static readonly defaults: any = /* #__PURE__ */ merge({}, [LinearScale.defaults, baseDefaults, scaleDefaults]);\n\n  /**\n   * @hidden\n   */\n  static readonly descriptors = /* #__PURE__ */ {\n    _scriptable: true,\n    _indexable: (name: string): boolean => name !== 'range',\n  };\n}\n\nexport class SizeLogarithmicScale extends LogarithmicLegendScale<ISizeScaleOptions & LogarithmicScaleOptions> {\n  /**\n   * @hidden\n   */\n  _model: PointOptions | null = null;\n\n  /**\n   * @hidden\n   */\n  getSizeForValue(value: number): number {\n    const v = this._getNormalizedValue(value);\n    if (v == null || Number.isNaN(v)) {\n      return this.options.missing;\n    }\n    return this.getSizeImpl(v);\n  }\n\n  /**\n   * @hidden\n   */\n  getSizeImpl(normalized: number): number {\n    return SizeScale.prototype.getSizeImpl.call(this, normalized);\n  }\n\n  /**\n   * @hidden\n   */\n  _drawIndicator(): void {\n    SizeScale.prototype._drawIndicator.call(this);\n  }\n\n  static readonly id = 'sizeLogarithmic';\n\n  /**\n   * @hidden\n   */\n  static readonly defaults: any = /* #__PURE__ */ merge({}, [LogarithmicScale.defaults, baseDefaults, scaleDefaults]);\n}\n\ndeclare module 'chart.js' {\n  export interface SizeScaleTypeRegistry {\n    size: {\n      options: ISizeScaleOptions & LinearScaleOptions;\n    };\n    sizeLogarithmic: {\n      options: ISizeScaleOptions & LogarithmicScaleOptions;\n    };\n  }\n\n  // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n  export interface ScaleTypeRegistry extends SizeScaleTypeRegistry {}\n}\n","import {\n  Element,\n  BarElement,\n  BarOptions,\n  VisualElement,\n  Point,\n  ChartType,\n  ScriptableAndArrayOptions,\n  CommonHoverOptions,\n  ScriptableContext,\n  UpdateMode,\n} from 'chart.js';\nimport { geoContains, GeoPath, GeoProjection } from 'd3-geo';\nimport type { ProjectionScale } from '../scales';\n\nexport interface IGeoFeatureOptions extends Omit<BarOptions, 'borderWidth'>, Record<string, unknown> {\n  /**\n   * Width of the border\n   * @default 0\n   */\n  borderWidth: number;\n\n  /**\n   * background color for the outline\n   * @default null\n   */\n  outlineBackgroundColor: string | null;\n  /**\n   * border color for the outline\n   * @default defaultColor of Chart.js\n   */\n  outlineBorderColor: string;\n  /**\n   * border width for the outline\n   * @default 0\n   */\n  outlineBorderWidth: number;\n\n  /**\n   * border color for the graticule\n   * @default #CCCCCC\n   */\n  graticuleBorderColor: string;\n  /**\n   * border width for the graticule\n   * @default 0\n   */\n  graticuleBorderWidth: number;\n}\n\nexport type Feature = any;\n\ntype GeoBounds = ReturnType<GeoPath['bounds']>;\n\nfunction growGeoBounds(bounds: GeoBounds, amount: number): GeoBounds {\n  return [\n    [bounds[0][0] - amount, bounds[0][1] - amount],\n    [bounds[1][0] + amount, bounds[1][1] + amount],\n  ];\n}\n\nexport interface IGeoFeatureProps {\n  x: number;\n  y: number;\n}\n\nexport class GeoFeature extends Element<IGeoFeatureProps, IGeoFeatureOptions> implements VisualElement {\n  /**\n   * @hidden\n   */\n  cache?:\n    | {\n        center?: Point;\n        bounds?: {\n          x: number;\n          y: number;\n          width: number;\n          height: number;\n          x2: number;\n          y2: number;\n        };\n        canvasKey?: string;\n        canvas?: HTMLCanvasElement;\n      }\n    | undefined = undefined;\n\n  /**\n   * @hidden\n   */\n  projectionScale!: ProjectionScale;\n\n  /**\n   * @hidden\n   */\n  feature!: Feature;\n\n  /**\n   * @hidden\n   */\n  center?: { longitude: number; latitude: number };\n\n  /**\n   * @hidden\n   */\n  pixelRatio?: number;\n\n  updateExtras({\n    scale,\n    feature,\n    center,\n    pixelRatio,\n    mode,\n  }: {\n    scale: ProjectionScale;\n    feature: Feature;\n    center?: { longitude: number; latitude: number };\n    pixelRatio: number;\n    mode: UpdateMode;\n  }): Point {\n    const changed =\n      mode === 'resize' ||\n      mode === 'reset' ||\n      this.projectionScale !== scale ||\n      this.feature !== feature ||\n      this.center?.longitude !== center?.longitude ||\n      this.center?.latitude !== center?.latitude ||\n      this.pixelRatio !== pixelRatio;\n    this.projectionScale = scale;\n    this.feature = feature;\n    this.center = center;\n    this.pixelRatio = pixelRatio;\n    if (changed) {\n      this.cache = undefined;\n    }\n    return this.getCenterPoint();\n  }\n\n  /**\n   * @hidden\n   */\n  inRange(mouseX: number, mouseY: number): boolean {\n    const bb = this.getBounds();\n    const r =\n      (Number.isNaN(mouseX) || (mouseX >= bb.x && mouseX <= bb.x2)) &&\n      (Number.isNaN(mouseY) || (mouseY >= bb.y && mouseY <= bb.y2));\n\n    const projection = this.projectionScale.geoPath.projection() as unknown as GeoProjection;\n    if (r && !Number.isNaN(mouseX) && !Number.isNaN(mouseY) && typeof projection.invert === 'function') {\n      // test for real if within the bounds\n      const longLat = projection.invert([mouseX, mouseY]);\n      return longLat != null && geoContains(this.feature, longLat);\n    }\n\n    return r;\n  }\n\n  /**\n   * @hidden\n   */\n  inXRange(mouseX: number): boolean {\n    return this.inRange(mouseX, Number.NaN);\n  }\n\n  /**\n   * @hidden\n   */\n  inYRange(mouseY: number): boolean {\n    return this.inRange(Number.NaN, mouseY);\n  }\n\n  /**\n   * @hidden\n   */\n  getCenterPoint(): { x: number; y: number } {\n    if (this.cache && this.cache.center) {\n      return this.cache.center;\n    }\n    let center: { x: number; y: number };\n    if (this.center) {\n      const p = this.projectionScale.projection([this.center.longitude, this.center.latitude])!;\n      center = {\n        x: p[0]!,\n        y: p[1]!,\n      };\n    } else {\n      const centroid = this.projectionScale.geoPath.centroid(this.feature);\n      center = {\n        x: centroid[0],\n        y: centroid[1],\n      };\n    }\n    this.cache = { ...(this.cache || {}), center };\n    return center;\n  }\n\n  /**\n   * @hidden\n   */\n  getBounds(): { x: number; y: number; x2: number; y2: number; width: number; height: number } {\n    if (this.cache && this.cache.bounds) {\n      return this.cache.bounds;\n    }\n    const bb = growGeoBounds(this.projectionScale.geoPath.bounds(this.feature), this.options.borderWidth / 2);\n    const bounds = {\n      x: bb[0][0],\n      x2: bb[1][0],\n      y: bb[0][1],\n      y2: bb[1][1],\n      width: bb[1][0] - bb[0][0],\n      height: bb[1][1] - bb[0][1],\n    };\n    this.cache = { ...(this.cache || {}), bounds };\n    return bounds;\n  }\n\n  /**\n   * @hidden\n   */\n  _drawInCache(doc: Document): void {\n    const bounds = this.getBounds();\n    if (!Number.isFinite(bounds.x)) {\n      return;\n    }\n    const canvas = this.cache && this.cache.canvas ? this.cache.canvas : doc.createElement('canvas');\n    const x1 = Math.floor(bounds.x);\n    const y1 = Math.floor(bounds.y);\n    const x2 = Math.ceil(bounds.x + bounds.width);\n    const y2 = Math.ceil(bounds.y + bounds.height);\n    const pixelRatio = this.pixelRatio || 1;\n    const width = Math.ceil(Math.max(x2 - x1, 1) * pixelRatio);\n    const height = Math.ceil(Math.max(y2 - y1, 1) * pixelRatio);\n    if (width <= 0 || height <= 0) {\n      return;\n    }\n    canvas.width = width;\n    canvas.height = height;\n\n    const ctx = canvas.getContext('2d');\n    if (ctx) {\n      ctx.clearRect(0, 0, canvas.width, canvas.height);\n      ctx.save();\n      ctx.scale(pixelRatio, pixelRatio);\n      ctx.translate(-x1, -y1);\n      this._drawImpl(ctx);\n      ctx.restore();\n\n      this.cache = { ...(this.cache || {}), canvas, canvasKey: this._optionsToKey() };\n    }\n  }\n\n  /**\n   * @hidden\n   */\n  _optionsToKey(): string {\n    const { options } = this;\n    return `${options.backgroundColor};${options.borderColor};${options.borderWidth};${this.pixelRatio}`;\n  }\n\n  /**\n   * @hidden\n   */\n  _drawImpl(ctx: CanvasRenderingContext2D): void {\n    const { feature } = this;\n    const { options } = this;\n    ctx.beginPath();\n    this.projectionScale.geoPath.context(ctx)(feature);\n    if (options.backgroundColor) {\n      ctx.fillStyle = options.backgroundColor;\n      ctx.fill();\n    }\n    if (options.borderColor) {\n      ctx.strokeStyle = options.borderColor;\n      ctx.lineWidth = options.borderWidth as number;\n      ctx.stroke();\n    }\n  }\n\n  /**\n   * @hidden\n   */\n  draw(ctx: CanvasRenderingContext2D): void {\n    const { feature } = this;\n    if (!feature) {\n      return;\n    }\n    if ((!this.cache || this.cache.canvasKey !== this._optionsToKey()) && ctx.canvas.ownerDocument != null) {\n      this._drawInCache(ctx.canvas.ownerDocument);\n    }\n    const bounds = this.getBounds();\n    if (this.cache && this.cache.canvas && this.cache.canvas.width > 0 && this.cache.canvas.height > 0) {\n      const x1 = Math.floor(bounds.x);\n      const y1 = Math.floor(bounds.y);\n      const x2 = Math.ceil(bounds.x + bounds.width);\n      const y2 = Math.ceil(bounds.y + bounds.height);\n      const width = x2 - x1;\n      const height = y2 - y1;\n      if (width > 0 && height > 0) {\n        ctx.drawImage(this.cache.canvas, x1, y1, x2 - x1, y2 - y1);\n      }\n    } else if (Number.isFinite(bounds.x)) {\n      ctx.save();\n      this._drawImpl(ctx);\n      ctx.restore();\n    }\n  }\n\n  static id = 'geoFeature';\n\n  /**\n   * @hidden\n   */\n  static defaults = /* #__PURE__ */ {\n    ...BarElement.defaults,\n    outlineBackgroundColor: null,\n    outlineBorderWidth: 0,\n\n    graticuleBorderColor: '#CCCCCC',\n    graticuleBorderWidth: 0,\n  };\n\n  /**\n   * @hidden\n   */\n  static defaultRoutes = /* #__PURE__ */ {\n    outlineBorderColor: 'borderColor',\n    ...(BarElement.defaultRoutes || {}),\n  };\n}\n\ndeclare module 'chart.js' {\n  export interface ElementOptionsByType<TType extends ChartType> {\n    geoFeature: ScriptableAndArrayOptions<IGeoFeatureOptions & CommonHoverOptions, ScriptableContext<TType>>;\n  }\n}\n","import {\n  DatasetController,\n  ChartDataset,\n  ScriptableAndArrayOptions,\n  UpdateMode,\n  Element,\n  VisualElement,\n  ScriptableContext,\n  ChartTypeRegistry,\n  AnimationOptions,\n} from 'chart.js';\nimport { clipArea, unclipArea, valueOrDefault } from 'chart.js/helpers';\nimport { geoGraticule, geoGraticule10, ExtendedFeature } from 'd3-geo';\nimport { ProjectionScale } from '../scales';\nimport type { GeoFeature, IGeoFeatureOptions } from '../elements';\n\nexport const geoDefaults = {\n  showOutline: false,\n  showGraticule: false,\n  clipMap: true,\n};\n\nexport const geoOverrides = {\n  scales: {\n    projection: {\n      axis: 'x',\n      type: ProjectionScale.id,\n      position: 'chartArea',\n      display: false,\n    },\n  },\n};\n\nfunction patchDatasetElementOptions(options: any) {\n  // patch the options by removing the `outline` or `hoverOutline` option;\n  // see https://github.com/chartjs/Chart.js/issues/7362\n  const r: any = { ...options };\n  Object.keys(options).forEach((key) => {\n    let targetKey = key;\n    if (key.startsWith('outline')) {\n      const sub = key.slice('outline'.length);\n      targetKey = sub[0].toLowerCase() + sub.slice(1);\n    } else if (key.startsWith('hoverOutline')) {\n      targetKey = `hover${key.slice('hoverOutline'.length)}`;\n    } else {\n      return;\n    }\n    delete r[key];\n    r[targetKey] = options[key];\n  });\n  return r;\n}\n\nexport class GeoController<\n  TYPE extends keyof ChartTypeRegistry,\n  TElement extends Element & VisualElement,\n> extends DatasetController<TYPE, TElement, GeoFeature> {\n  getGeoDataset(): ChartDataset<'choropleth' | 'bubbleMap'> & IGeoControllerDatasetOptions {\n    return super.getDataset() as unknown as ChartDataset<'choropleth' | 'bubbleMap'> & IGeoControllerDatasetOptions;\n  }\n\n  getGeoOptions(): IGeoChartOptions {\n    return this.chart.options as unknown as IGeoChartOptions;\n  }\n\n  getProjectionScale(): ProjectionScale {\n    return this.getScaleForId('projection') as ProjectionScale;\n  }\n\n  linkScales(): void {\n    const dataset = this.getGeoDataset();\n    const meta = this.getMeta();\n    meta.xAxisID = 'projection';\n    dataset.xAxisID = 'projection';\n    meta.yAxisID = 'projection';\n    dataset.yAxisID = 'projection';\n    meta.xScale = this.getScaleForId('projection');\n    meta.yScale = this.getScaleForId('projection');\n\n    this.getProjectionScale().computeBounds(this.resolveOutline());\n  }\n\n  showOutline(): IGeoChartOptions['showOutline'] {\n    return valueOrDefault(this.getGeoDataset().showOutline, this.getGeoOptions().showOutline);\n  }\n\n  clipMap(): IGeoChartOptions['clipMap'] {\n    return valueOrDefault(this.getGeoDataset().clipMap, this.getGeoOptions().clipMap);\n  }\n\n  getGraticule(): IGeoChartOptions['showGraticule'] {\n    return valueOrDefault(this.getGeoDataset().showGraticule, this.getGeoOptions().showGraticule);\n  }\n\n  update(mode: UpdateMode): void {\n    super.update(mode);\n\n    const meta = this.getMeta();\n\n    const scale = this.getProjectionScale();\n    const dirtyCache = scale.updateBounds() || mode === 'resize' || mode === 'reset';\n\n    if (this.showOutline()) {\n      const elem = meta.dataset!;\n      if (dirtyCache) {\n        delete elem.cache;\n      }\n      elem.projectionScale = scale;\n      elem.pixelRatio = this.chart.currentDevicePixelRatio;\n      if (mode !== 'resize') {\n        const options = patchDatasetElementOptions(this.resolveDatasetElementOptions(mode));\n        const properties = {\n          feature: this.resolveOutline(),\n          options,\n        };\n        this.updateElement(elem, undefined, properties, mode);\n        if (this.getGraticule()) {\n          (meta as any).graticule = options;\n        }\n      }\n    } else if (this.getGraticule() && mode !== 'resize') {\n      (meta as any).graticule = patchDatasetElementOptions(this.resolveDatasetElementOptions(mode));\n    }\n\n    if (dirtyCache) {\n      meta.data.forEach((elem) => delete (elem as any).cache);\n    }\n    this.updateElements(meta.data, 0, meta.data.length, mode);\n  }\n\n  resolveOutline(): any {\n    const ds = this.getGeoDataset();\n    const outline = ds.outline || { type: 'Sphere' };\n    if (Array.isArray(outline)) {\n      return {\n        type: 'FeatureCollection',\n        features: outline,\n      };\n    }\n    return outline;\n  }\n\n  showGraticule(): void {\n    const g = this.getGraticule();\n    const options = (this.getMeta() as any).graticule;\n    if (!g || !options) {\n      return;\n    }\n    const { ctx } = this.chart;\n    const scale = this.getProjectionScale();\n    const path = scale.geoPath.context(ctx);\n\n    ctx.save();\n    ctx.beginPath();\n\n    if (typeof g === 'boolean') {\n      if (g) {\n        path(geoGraticule10());\n      }\n    } else {\n      const geo = geoGraticule();\n      if (g.stepMajor) {\n        geo.stepMajor(g.stepMajor as unknown as [number, number]);\n      }\n      if (g.stepMinor) {\n        geo.stepMinor(g.stepMinor as unknown as [number, number]);\n      }\n      path(geo());\n    }\n\n    ctx.strokeStyle = options.graticuleBorderColor;\n    ctx.lineWidth = options.graticuleBorderWidth;\n    ctx.stroke();\n    ctx.restore();\n  }\n\n  draw(): void {\n    const { chart } = this;\n\n    const clipMap = this.clipMap();\n\n    // enable clipping based on the option\n    let enabled = false;\n    if (clipMap === true || clipMap === 'outline' || clipMap === 'outline+graticule') {\n      enabled = true;\n      clipArea(chart.ctx, chart.chartArea);\n    }\n\n    if (this.showOutline() && this.getMeta().dataset) {\n      (this.getMeta().dataset!.draw.call as any)(this.getMeta().dataset!, chart.ctx, chart.chartArea);\n    }\n\n    if (clipMap === true || clipMap === 'graticule' || clipMap === 'outline+graticule') {\n      if (!enabled) {\n        clipArea(chart.ctx, chart.chartArea);\n      }\n    } else if (enabled) {\n      enabled = false;\n      unclipArea(chart.ctx);\n    }\n\n    this.showGraticule();\n\n    if (clipMap === true || clipMap === 'items') {\n      if (!enabled) {\n        clipArea(chart.ctx, chart.chartArea);\n      }\n    } else if (enabled) {\n      enabled = false;\n      unclipArea(chart.ctx);\n    }\n\n    this.getMeta().data.forEach((elem) => (elem.draw.call as any)(elem, chart.ctx, chart.chartArea));\n\n    if (enabled) {\n      enabled = false;\n      unclipArea(chart.ctx);\n    }\n  }\n}\n\nexport interface IGeoChartOptions {\n  /**\n   * Outline used to scale and centralize the projection in the chart area.\n   * By default a sphere is used\n   * @default { type: 'Sphere\" }\n   */\n  outline: any[];\n  /**\n   * option to render the outline in the background, see also the outline... styling option\n   * @default false\n   */\n  showOutline: boolean;\n\n  /**\n   * option to render a graticule in the background, see also the outline... styling option\n   * @default false\n   */\n  showGraticule:\n    | boolean\n    | {\n        stepMajor: [number, number];\n        stepMinor: [number, number];\n      };\n\n  /**\n   * option whether to clip the rendering to the chartArea of the graph\n   * @default choropleth: true bubbleMap: 'outline+graticule'\n   */\n  clipMap: boolean | 'outline' | 'graticule' | 'outline+graticule' | 'items';\n}\n\nexport interface IGeoControllerDatasetOptions\n  extends IGeoChartOptions,\n    ScriptableAndArrayOptions<IGeoFeatureOptions, ScriptableContext<'choropleth' | 'bubbleMap'>>,\n    AnimationOptions<'choropleth' | 'bubbleMap'> {\n  xAxisID?: string;\n  yAxisID?: string;\n  rAxisID?: string;\n  iAxisID?: string;\n  vAxisID?: string;\n}\n\nexport interface IGeoDataPoint {\n  feature: ExtendedFeature;\n  center?: {\n    longitude: number;\n    latitude: number;\n  };\n}\n","import {\n  Chart,\n  UpdateMode,\n  ScriptableContext,\n  TooltipItem,\n  CommonHoverOptions,\n  ScriptableAndArrayOptions,\n  ControllerDatasetOptions,\n  ChartConfiguration,\n  ChartItem,\n  PointOptions,\n  Scale,\n  AnimationOptions,\n} from 'chart.js';\nimport { merge } from 'chart.js/helpers';\nimport { geoDefaults, GeoController, IGeoChartOptions, IGeoDataPoint, geoOverrides } from './GeoController';\nimport { GeoFeature, IGeoFeatureOptions, IGeoFeatureProps } from '../elements';\nimport { ColorScale, ProjectionScale } from '../scales';\nimport patchController from './patchController';\n\nexport class ChoroplethController extends GeoController<'choropleth', GeoFeature> {\n  initialize(): void {\n    super.initialize();\n    this.enableOptionSharing = true;\n  }\n\n  linkScales(): void {\n    super.linkScales();\n    const dataset = this.getGeoDataset();\n    const meta = this.getMeta();\n    meta.vAxisID = 'color';\n    meta.rAxisID = 'color';\n    dataset.vAxisID = 'color';\n    dataset.rAxisID = 'color';\n    meta.rScale = this.getScaleForId('color');\n    meta.vScale = meta.rScale;\n    meta.iScale = meta.xScale;\n\n    meta.iAxisID = meta.xAxisID!;\n\n    dataset.iAxisID = meta.xAxisID!;\n  }\n\n  _getOtherScale(scale: Scale): Scale {\n    // for strange get min max with other scale\n    return scale;\n  }\n\n  parse(start: number, count: number): void {\n    const rScale = this.getMeta().rScale!;\n    const { data } = this.getDataset();\n    const meta = this._cachedMeta;\n    for (let i = start; i < start + count; i += 1) {\n      meta._parsed[i] = {\n        [rScale.axis]: rScale.parse(data[i], i),\n      };\n    }\n  }\n\n  updateElements(elems: GeoFeature[], start: number, count: number, mode: UpdateMode): void {\n    const firstOpts = this.resolveDataElementOptions(start, mode);\n\n    const sharedOptions = this.getSharedOptions(firstOpts)!;\n    const includeOptions = this.includeOptions(mode, sharedOptions);\n    const scale = this.getProjectionScale();\n    this.updateSharedOptions(sharedOptions, mode, firstOpts);\n\n    for (let i = start; i < start + count; i += 1) {\n      const elem = elems[i];\n      elem.projectionScale = scale;\n      const center = elem.updateExtras({\n        scale,\n        feature: (this as any)._data[i].feature,\n        center: (this as any)._data[i].center,\n        pixelRatio: this.chart.currentDevicePixelRatio,\n        mode,\n      });\n\n      const properties: IGeoFeatureProps & { options?: PointOptions } = {\n        x: center.x,\n        y: center.y,\n      };\n      if (includeOptions) {\n        properties.options = (sharedOptions || this.resolveDataElementOptions(i, mode)) as unknown as PointOptions;\n      }\n      this.updateElement(elem, i, properties as unknown as Record<string, unknown>, mode);\n    }\n  }\n\n  indexToColor(index: number): string {\n    const rScale = this.getMeta().rScale as unknown as ColorScale;\n    return rScale.getColorForValue(this.getParsed(index)[rScale.axis as 'r']);\n  }\n\n  static readonly id = 'choropleth';\n\n  /**\n   * @hidden\n   */\n  static readonly defaults: any = /* #__PURE__ */ merge({}, [\n    geoDefaults,\n    {\n      datasetElementType: GeoFeature.id,\n      dataElementType: GeoFeature.id,\n    },\n  ]);\n\n  /**\n   * @hidden\n   */\n  static readonly overrides: any = /* #__PURE__ */ merge({}, [\n    geoOverrides,\n    {\n      plugins: {\n        tooltip: {\n          callbacks: {\n            title() {\n              // Title doesn't make sense for scatter since we format the data as a point\n              return '';\n            },\n            label(item: TooltipItem<'choropleth'>) {\n              if (item.formattedValue == null) {\n                return item.chart.data?.labels?.[item.dataIndex];\n              }\n              return `${item.chart.data?.labels?.[item.dataIndex]}: ${item.formattedValue}`;\n            },\n          },\n        },\n        colors: {\n          enabled: false,\n        },\n      },\n      scales: {\n        color: {\n          type: ColorScale.id,\n          axis: 'x',\n        },\n      },\n      elements: {\n        geoFeature: {\n          backgroundColor(context: ScriptableContext<'choropleth'>) {\n            if (context.dataIndex == null) {\n              return null;\n            }\n            const controller = (context.chart as Chart<'choropleth'>).getDatasetMeta(context.datasetIndex)\n              .controller as ChoroplethController;\n            return controller.indexToColor(context.dataIndex);\n          },\n        },\n      },\n    },\n  ]);\n}\n\nexport interface IChoroplethControllerDatasetOptions\n  extends ControllerDatasetOptions,\n    IGeoChartOptions,\n    ScriptableAndArrayOptions<IGeoFeatureOptions, ScriptableContext<'choropleth'>>,\n    ScriptableAndArrayOptions<CommonHoverOptions, ScriptableContext<'choropleth'>>,\n    AnimationOptions<'choropleth'> {}\n\nexport interface IChoroplethDataPoint extends IGeoDataPoint {\n  value: number;\n}\n\ndeclare module 'chart.js' {\n  export interface ChartTypeRegistry {\n    choropleth: {\n      chartOptions: IGeoChartOptions;\n      datasetOptions: IChoroplethControllerDatasetOptions;\n      defaultDataPoint: IChoroplethDataPoint;\n      scales: keyof (ProjectionScaleTypeRegistry & ColorScaleTypeRegistry);\n      metaExtensions: Record<string, never>;\n      parsedDataType: { r: number };\n    };\n  }\n}\n\nexport class ChoroplethChart<DATA extends unknown[] = IGeoDataPoint[], LABEL = string> extends Chart<\n  'choropleth',\n  DATA,\n  LABEL\n> {\n  static id = ChoroplethController.id;\n\n  constructor(item: ChartItem, config: Omit<ChartConfiguration<'choropleth', DATA, LABEL>, 'type'>) {\n    super(item, patchController('choropleth', config, ChoroplethController, GeoFeature, [ColorScale, ProjectionScale]));\n  }\n}\n","import {\n  Chart,\n  ChartItem,\n  ChartConfiguration,\n  CommonHoverOptions,\n  ControllerDatasetOptions,\n  PointOptions,\n  PointProps,\n  ScriptableContext,\n  TooltipItem,\n  PointElement,\n  PointHoverOptions,\n  Element,\n  Scale,\n  ScriptableAndArrayOptions,\n  UpdateMode,\n  AnimationOptions,\n} from 'chart.js';\nimport { merge } from 'chart.js/helpers';\nimport { GeoFeature, IGeoFeatureOptions } from '../elements';\nimport { ProjectionScale, SizeScale } from '../scales';\nimport { GeoController, geoDefaults, geoOverrides, IGeoChartOptions } from './GeoController';\nimport patchController from './patchController';\n\ntype MyPointElement = PointElement & Element<PointProps, PointOptions & PointHoverOptions & Record<string, unknown>>;\n\nexport class BubbleMapController extends GeoController<'bubbleMap', MyPointElement> {\n  initialize(): void {\n    super.initialize();\n    this.enableOptionSharing = true;\n  }\n\n  linkScales(): void {\n    super.linkScales();\n    const dataset = this.getGeoDataset();\n    const meta = this.getMeta();\n    meta.vAxisID = 'size';\n    meta.rAxisID = 'size';\n    dataset.vAxisID = 'size';\n    dataset.rAxisID = 'size';\n    meta.rScale = this.getScaleForId('size');\n    meta.vScale = meta.rScale;\n    meta.iScale = meta.xScale;\n\n    meta.iAxisID = meta.xAxisID!;\n\n    dataset.iAxisID = meta.xAxisID!;\n  }\n\n  _getOtherScale(scale: Scale): Scale {\n    // for strange get min max with other scale\n    return scale;\n  }\n\n  parse(start: number, count: number): void {\n    const rScale = this.getMeta().rScale!;\n    const data = this.getDataset().data as unknown as IBubbleMapDataPoint[];\n    const meta = this._cachedMeta;\n    for (let i = start; i < start + count; i += 1) {\n      const d = data[i];\n      meta._parsed[i] = {\n        x: d.longitude == null ? d.x : d.longitude,\n        y: d.latitude == null ? d.y : d.latitude,\n        [rScale.axis]: rScale.parse(d, i),\n      };\n    }\n  }\n\n  updateElements(elems: MyPointElement[], start: number, count: number, mode: UpdateMode): void {\n    const reset = mode === 'reset';\n    const firstOpts = this.resolveDataElementOptions(start, mode);\n\n    const sharedOptions = this.getSharedOptions(firstOpts)!;\n    const includeOptions = this.includeOptions(mode, sharedOptions);\n    const scale = this.getProjectionScale();\n\n    (this.getMeta().rScale as unknown as SizeScale)._model = firstOpts as unknown as PointOptions; // for legend rendering styling\n\n    this.updateSharedOptions(sharedOptions, mode, firstOpts);\n\n    for (let i = start; i < start + count; i += 1) {\n      const elem = elems[i];\n      const parsed = this.getParsed(i);\n      const projection = scale.projection([parsed.x, parsed.y]);\n      const properties: PointProps & { options?: PointOptions; skip: boolean } = {\n        x: projection ? projection[0] : 0,\n        y: projection ? projection[1] : 0,\n        skip: Number.isNaN(parsed.x) || Number.isNaN(parsed.y),\n      };\n      if (includeOptions) {\n        properties.options = (sharedOptions || this.resolveDataElementOptions(i, mode)) as unknown as PointOptions;\n        if (reset) {\n          properties.options.radius = 0;\n        }\n      }\n      this.updateElement(elem, i, properties as unknown as Record<string, unknown>, mode);\n    }\n  }\n\n  indexToRadius(index: number): number {\n    const rScale = this.getMeta().rScale as SizeScale;\n    return rScale.getSizeForValue(this.getParsed(index)[rScale.axis as 'r']);\n  }\n\n  static readonly id = 'bubbleMap';\n\n  /**\n   * @hidden\n   */\n  static readonly defaults: any = /* #__PURE__ */ merge({}, [\n    geoDefaults,\n    {\n      dataElementType: PointElement.id,\n      datasetElementType: GeoFeature.id,\n      showOutline: true,\n      clipMap: 'outline+graticule',\n    },\n  ]);\n\n  /**\n   * @hidden\n   */\n  static readonly overrides: any = /* #__PURE__ */ merge({}, [\n    geoOverrides,\n    {\n      plugins: {\n        tooltip: {\n          callbacks: {\n            title() {\n              // Title doesn't make sense for scatter since we format the data as a point\n              return '';\n            },\n            label(item: TooltipItem<'bubbleMap'>) {\n              if (item.formattedValue == null) {\n                return item.chart.data?.labels?.[item.dataIndex];\n              }\n              return `${item.chart.data?.labels?.[item.dataIndex]}: ${item.formattedValue}`;\n            },\n          },\n        },\n      },\n      scales: {\n        size: {\n          axis: 'x',\n          type: SizeScale.id,\n        },\n      },\n      elements: {\n        point: {\n          radius(context: ScriptableContext<'bubbleMap'>) {\n            if (context.dataIndex == null) {\n              return null;\n            }\n            const controller = (context.chart as Chart<'bubbleMap'>).getDatasetMeta(context.datasetIndex)\n              .controller as BubbleMapController;\n            return controller.indexToRadius(context.dataIndex);\n          },\n          hoverRadius(context: ScriptableContext<'bubbleMap'>) {\n            if (context.dataIndex == null) {\n              return null;\n            }\n            const controller = (context.chart as Chart<'bubbleMap'>).getDatasetMeta(context.datasetIndex)\n              .controller as BubbleMapController;\n            return controller.indexToRadius(context.dataIndex) + 1;\n          },\n        },\n      },\n    },\n  ]);\n}\n\nexport interface IBubbleMapDataPoint {\n  longitude: number;\n  latitude: number;\n  x?: number;\n  y?: number;\n  value: number;\n}\n\nexport interface IBubbleMapControllerDatasetOptions\n  extends ControllerDatasetOptions,\n    IGeoChartOptions,\n    ScriptableAndArrayOptions<IGeoFeatureOptions, ScriptableContext<'bubbleMap'>>,\n    ScriptableAndArrayOptions<CommonHoverOptions, ScriptableContext<'bubbleMap'>>,\n    AnimationOptions<'bubbleMap'> {}\n\ndeclare module 'chart.js' {\n  export interface ChartTypeRegistry {\n    bubbleMap: {\n      chartOptions: IGeoChartOptions;\n      datasetOptions: IBubbleMapControllerDatasetOptions;\n      defaultDataPoint: IBubbleMapDataPoint;\n      scales: keyof (ProjectionScaleTypeRegistry & SizeScaleTypeRegistry);\n      metaExtensions: Record<string, never>;\n      parsedDataType: { r: number; x: number; y: number };\n    };\n  }\n}\n\nexport class BubbleMapChart<DATA extends unknown[] = IBubbleMapDataPoint[], LABEL = string> extends Chart<\n  'bubbleMap',\n  DATA,\n  LABEL\n> {\n  static id = BubbleMapController.id;\n\n  constructor(item: ChartItem, config: Omit<ChartConfiguration<'bubbleMap', DATA, LABEL>, 'type'>) {\n    super(item, patchController('bubbleMap', config, BubbleMapController, GeoFeature, [SizeScale, ProjectionScale]));\n  }\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;AAEO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACvCO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAoBO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC5DO;AACP;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACzCO;AACP;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACpCO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACA;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC7DO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;ACnDO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;;ACjCA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;;;"}