# BubbleMap

> Markers on a map by longitude and latitude, sized by value (area proportional) and colored by category, with optional clustering.

Source: https://ui.jcsoftdev.com/docs/components/bubble-map

Markers on a map by longitude and latitude, sized by value (area
proportional) and colored by category, with optional clustering. Sizes stay
in screen pixels while you zoom. Hover or arrow keys select a marker (keyboard
order follows the visible bubbles), click or Enter calls `onPointClick`
(a cluster zooms in instead), `+` / `-` zoom, `0` fits.

## Import

```ts
import { BubbleMap } from "@jcsoftdev/ui/charts";
```

Single-component entry points tree-shake to just this component: `@jcsoftdev/ui/charts/bubble-map`.

## Usage

```tsx
<BubbleMap
  title="Sedes en Lima"
  map={limaMap}
  points={branches}
  categories={categories}
  valueName="Citas al mes"
  valueFormat="integer"
  minRadius={5}
  maxRadius={22}
  cluster={false}
  clusterDistance={36}
  regionLabels={false}
  legend
  tooltip
  zoomable
  wheelZoom="always"
  maxZoom={16}
  animate
  loading={false}
  labels={spanishLabels}
  height={480}
/>
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `title` * | `string` |  |  |
| `map` * | `GeoMap` |  | Base map under the markers, e.g. `peruMap`. |
| `points` * | `readonly MapPoint[]` |  |  |
| `categories` | `readonly BubbleMapCategory[]` |  | Legend entries and colors; derived from the points' `category` by order when omitted. |
| `valueFormat` | `ValueFormat` |  |  |
| `valueName` | `string` |  | Name of the value in the tooltip and the data table, e.g. "Citas". |
| `projection` | `"equirectangular" \| "mercator" \| "equalEarth" \| "fitted"` |  |  |
| `minRadius` | `number` | `6` | Area-proportional bubbles between these radii (px). Points without `value` use `minRadius`. |
| `maxRadius` | `number` | `28` |  |
| `cluster` | `boolean` | `false` | Merge markers closer than `clusterDistance` px into one bubble (count inside, click to zoom in). |
| `clusterDistance` | `number` | `36` |  |
| `regionLabels` | `boolean` | `false` | Write the region names on the base map where they fit. |
| `routes` | `readonly MapRoute[]` | `[]` | Flows between places, drawn as arcs under the bubbles with animated dashes. |
| `routeStyle` | `"curve" \| "great-circle"` | `"curve"` | "curve": a soft arch in screen space (regions); "great-circle": the shortest path on the globe (world). |
| `routeBend` | `number` | `0.25` | Arch height of a "curve" as a fraction of the distance (0 straight, default 0.25). |
| `animateRoutes` | `boolean` | `true` | Marching dashes along the routes; off under `prefers-reduced-motion`. |
| `height` | `number \| "fill"` |  |  |
| `aspectRatio` | `number` |  |  |
| `legend` | `boolean` | `true` |  |
| `tooltip` | `boolean` | `true` |  |
| `zoomable` | `boolean` | `true` |  |
| `wheelZoom` | `"always" \| "modifier"` | `"modifier"` |  |
| `maxZoom` | `number` | `16` |  |
| `animate` | `boolean` | `true` |  |
| `loading` | `boolean` |  |  |
| `labels` | `Partial<MapLabels>` |  |  |
| `onPointClick` | `((point: MapPoint) => void)` |  |  |
| `className` | `string` |  |  |

`*` marks a required prop.

## Storybook stories

- [Default](/storybook/?path=/story/charts-bubblemap--default)
- [With Region Names](/storybook/?path=/story/charts-bubblemap--with-region-names)
- [Clustered](/storybook/?path=/story/charts-bubblemap--clustered)
- [Clustering Off](/storybook/?path=/story/charts-bubblemap--clustering-off)
- [World](/storybook/?path=/story/charts-bubblemap--world)
- [Routes](/storybook/?path=/story/charts-bubblemap--routes)
- [Routes World](/storybook/?path=/story/charts-bubblemap--routes-world)
- [Routes Narrow](/storybook/?path=/story/charts-bubblemap--routes-narrow)
- [Loading](/storybook/?path=/story/charts-bubblemap--loading)
- [Empty](/storybook/?path=/story/charts-bubblemap--empty)
- [Narrow](/storybook/?path=/story/charts-bubblemap--narrow)
- [Fill Parent](/storybook/?path=/story/charts-bubblemap--fill-parent)

## Source

`packages/ui/src/charts/bubble-map/bubble-map.tsx`
