API Bản đồ ký hiệu
Một bản đồ ký hiệu/bong bóng khử chồng lấn bằng mô phỏng lực: bạn cung cấp lng/lat cho mỗi mục, một mô phỏng một lần đẩy các vòng tròn chồng lấn ra xa nhau - xem demo Bản đồ ký hiệu.
Nhập
import "@michi-vz/wc/symbol-map-chart";
// <michi-vz-symbol-map-chart> is now definedimport { mountSymbolMapChart } from "@michi-vz/core";
const chart = mountSymbolMapChart(el, props);import { SymbolMapChart } from "@michi-vz/react/symbol-map-chart";import { SymbolMapChart } from "@michi-vz/vue/symbol-map-chart";import { symbolMapChart } from "@michi-vz/svelte/symbol-map-chart";import { bindChart, applySymbolMapChartProps } from "@michi-vz/angular/symbol-map-chart";
// needs CUSTOM_ELEMENTS_SCHEMA on the component that hosts <michi-vz-symbol-map-chart>Thuộc tính
| Prop | Type | Default | Description |
|---|---|---|---|
timeline | boolean | TimelinePeriodConfig | - | Opt-in "play through years": snapshots one period at a time over the distinct per-row `date` values, with a headless controller (`chart.timeline()`) plus an optional built-in play button + scrubber. Off by default; requires rows with `date`. Values tween between periods unless `interpolate: false`. |
dataSet* | SymbolMapDataItem[] | - | Array of symbols; each supplies its own lng/lat (core bundles no coordinate table). |
geography | GeoJSON.FeatureCollection | GeoFeatureItem[] | - | OPTIONAL muted backdrop landmass (new capability - the legacy chart never drew one). A full GeoJSON FeatureCollection or a pre-normalized `GeoFeatureItem[]`, same contract as `ChoroplethMapChartProps.geography`. Omit for the legacy chart's dot-only look (the default). |
title | string | - | Optional chart title rendered above the plot |
projection | GeoProjectionName | - | d3-geo / d3-geo-projection projection (default "geoMercator", matching the legacy chart). With no `geography`, the projection is used UNTUNED (bare `factory()`, no translate/scale/rotate/center) and the projected point extent is rescaled to fill the plot - see the chart's docs page. With `geography` supplied, the SAME tuned dispatch as ChoroplethMapChart is used instead, so the backdrop and the symbols share one consistent geographic framing. |
projectionConfig | GeoProjectionConfig | - | Fine-tunes the projection; only consulted when `geography` is supplied (see `projection`). |
radiusRange | [number, number] | DEFAULT_RADIUS_RANGE | [min, max] circle radius in px (default [3, 70], the legacy `circleRange`). Mapped from the value domain via `buildSymbolMapRadiusScale`, whose domain is the TRUE combined extent of every item's `value` and `valueSecond` - DELIBERATELY NOT legacy Chart.js's own domain formula, which was defective (`[min(primaryMin, secondaryMax), max(primaryMin, secondaryMax)]` silently drops the primary max and secondary min). Relative circle sizes will differ from the legacy chart whenever `value` and `valueSecond` ranges diverge - see `symbolMap/scales.ts`'s `buildSymbolMapRadiusScale` for the worked example and the pinned regression test. |
radiusVisibleMin | number | - | Hides items whose RAW `value` is <= this threshold (and, when `valueSecond` is set, whose `valueSecond` is also < this threshold) BEFORE the force layout runs - ported from the legacy chart's own filter, which compares the raw value/valueSecond, not the scaled radius. Omit to show every located item. |
positionMode | "force" | "precise" | "honeycomb" | "force" | How symbols are placed: "force" (default) runs a one-shot de-overlap simulation that pulls colliding circles apart, while "precise" keeps every symbol at its exact projected lng/lat (overlaps allowed). - "force": a one-shot de-overlap simulation pulls colliding circles apart - readable for dense data, but symbols DRIFT from their true lng/lat (on small plots the drift can be large). Avoid it when the audience will read exact geographic position off the chart: a symbol drawn over the wrong country is a cartographic-accuracy problem and can be politically sensitive. - "precise": every symbol stays at its exact projected lng/lat; overlapping circles are allowed. Prefer this whenever a `geography` backdrop is shown, since a visible landmass invites reading positions literally. |
shape | "circle" | "hexagon" | "circle" | Mark geometry: "circle" (default) or a "hexagon" whose CIRCUMRADIUS is the mark radius - collision, hit-testing and label fitting stay circle-based on `radius`, only the painted outline changes. Orientation follows `honeycomb.orientation` (default "flat"). Pair with `positionMode: "honeycomb"` for tessellating tiles. |
honeycomb | { radius?: number; gap?: number; orientation?: "flat" | "pointy"; placement?: "first" | "geographic"; reserve?: "valued" | "all"; } | - | Lattice settings for `positionMode: "honeycomb"`: tile circumradius in px (default 11), gap between tile edges in px (default 2), and orientation (default "flat" = a flat edge on top, columns offset; "pointy" = a vertex on top, rows offset). Also sets the hexagon outline orientation for `shape`. - `placement` (default "first"): how a crowded cell is shared. "first" resolves collisions in `dataSet` order with a fixed ring walk (stable, but a displaced tile can land on the wrong side of its neighbours). "geographic" keeps neighbours in their real order: the tile nearest a cell's centre keeps it, a displaced tile takes the free cell nearest its true position, and a repair pass swaps or nudges neighbours that read east-for-west or north-for-south. Independent of `dataSet` order; the neighbouring pairs still in the wrong order are counted in `stats.outOfOrderCount` (measured for every placement) and, with "geographic" only, reported as a `layout-out-of-order` warning. - `reserve` (default "valued"): which tiles claim a cell. "valued" = only tiles with a `value` (no-data tiles never push real data around); "all" = every tile, so the layout does not change when the values do (a map that switches between products or years). Tiles removed by `disabledItems` or `radiusVisibleMin` take no part in the layout either way. |
fit | "none" | "width" | "none" | How the chart fits its container (default "none"). - "none": the chart is drawn at exactly `width` x `height` pixels (today). - "width": the chart is LAID OUT once at `width` x `height` (the design size) and the drawing is scaled to the container's width, keeping the aspect ratio, never larger than `width`. Positions do not change when the window resizes - useful for a tile map whose honeycomb layout should not reshuffle with the screen size. The svg renderer uses a `viewBox`; canvas and webgpu scale their layer and map the pointer back to design coordinates. The tooltip stays at screen size next to the cursor. |
colorScale | { domain: number[]; range: string[] } | - | Value-driven colour encoding: a resolved hex `range` keyed to a numeric `domain`, built into a d3 `scaleThreshold` (values outside the domain clamp to the first/last colour). Precedence: `colorsMapping[label]` > `colorScale(value)` > the item's `color` > the palette. Same contract as ChoroplethMapChart's `colorScale`. With a colorScale the colour IS the encoding, so marks paint at full opacity (the legacy value->alpha ramp applies only without one). |
noDataColor | string | DEFAULT_NO_DATA_COLOR | Fill for items without a `value` (default `#d2d7dd`, ChoroplethMap's no-data grey). Pass "transparent" to hide them while keeping their tooltip target. |
markers | SymbolMapMarker[] | - | Decorative pins drawn above the symbols (a home location, an office). Anchored tip-down at the projected lng/lat; never laid out or hit-tested; no tooltip. Built-in map-pin glyph, or pass your own `path` + `pathSize`. |
geographyColor | string | DEFAULT_GEOGRAPHY_COLOR | Fill for the optional backdrop `geography` (default `#eef1f5`, a muted neutral). |
strokeColor | string | DEFAULT_STROKE_COLOR | Border colour for the optional backdrop `geography` (default `#d7dce3`). |
strokeWidth | number | 1 | Border width for the optional backdrop `geography`, in px (default 1). |
showLabels | boolean | true | Draw a fitted label inside each circle when it is large enough (default true). |
isLoading | boolean | - | Loading overlay (stale symbols hidden while true) |
isNodata | boolean | ((dataSet: SymbolMapDataItem[] | null | undefined) => boolean) | - | No-data predicate/flag; default = empty dataSet |
noDataLabel | string | - | Text for the built-in no-data overlay |
suppressDefaultOverlay | boolean | - | Set by a framework wrapper passing its own overlay node - suppresses the default overlay |
tooltipFormatter | (d: SymbolMapDataItem) => string | - | Formats the tooltip for a hovered symbol. |
labelFormatter | (d: SymbolMapDataItem) => string | - | Text drawn inside a symbol when it is large enough (default: the label). Receives the item with its resolved lng/lat, so a share or a value can sit next to the name; the text is still fitted to the circle (ellipsis past the width). |
onSymbolClick | (d: SymbolMapDataItem, event: MouseEvent) => void | - | Called when a symbol is clicked (svg marks and the canvas/webgpu hit-test alike), with the same item the tooltip formatter receives and the mouse event. The click still pins the tooltip; use this to navigate or select from the symbol itself. |
onHighlightItem | (labels: string[]) => void | - | Called when the hovered/highlighted label(s) change |
Common props - shared by every chart (14)
| Prop | Type | Default | Description |
|---|---|---|---|
width | number | 900 | Chart width in pixels |
height | number | 520 | Chart height in pixels |
margin | Margin | { top: 40, right: 10, bottom: 10, left: 10 } | Inner margins (top/right/bottom/left, in px); default 8 (36 top with a title) |
colors | string[] | - | Categorical palette for rings without an explicit colour or colorsMapping entry |
colorsMapping | Record<string, string> | - | Explicit label -> colour map; takes precedence over the palette and per-item colours |
highlightItems | string[] | - | Labels to emphasise (active styling); others keep their base opacity |
disabledItems | string[] | - | Labels to hide entirely (ring + track removed; remaining rings keep their radii order) |
renderer | "svg" | "canvas" | "webgpu" | "svg" | Render as inline SVG (default) or to a canvas / WebGPU layer; getContext() is identical either way |
locale | string | - | BCP-47 locale used for number formatting |
skipColorMappingDispatch | boolean | false | External-CSS mode: unmapped labels resolve to transparent and onColorMappingGenerated is not emitted, so arc colours come from your CSS via the data-label-safe contract |
enableTransitions | boolean | true | Animate opacity/arc changes with CSS transitions (SVG renderer; default true) |
onColorMappingGenerated | (mapping: Record<string, string>) => void | - | Called with the resolved label -> colour map after the chart assigns colours |
onChartDataProcessed | (context: ChartContext) => void | - | Called with the renderer-agnostic ChartContext whenever the data is (re)processed |
onDataWarning | (warnings: DataWarning[]) => void | - | Called with any non-fatal data warnings (out-of-range values, duplicate labels, ...) |
Sự kiện
Web component phát ra (dispatch) các CustomEvent nổi bọt (bubbling) sau đây (engine cũng phơi bày các sự kiện tương tự thông qua các callback on* trong bảng ở trên):
| Sự kiện | Chi tiết | Kích hoạt khi |
|---|---|---|
michi-vz:highlight | string[] | trạng thái nổi bật lúc di chuột thay đổi |
michi-vz:colormapping | Record<string, string> | một ánh xạ màu được tạo ra |
michi-vz:dataprocessed | ChartContext | dữ liệu được xử lý (lại) |
michi-vz:datawarning | DataWarning[] | phát hiện cảnh báo (lng/lat thiếu hoặc không hợp lệ, giá trị âm, id trùng lặp) |
michi-vz:symbolclick | SymbolMapDataItem | khi nhấp vào một ký hiệu (callback onSymbolClick) |
getContext()
mountSymbolMapChart(el, props).getContext() trả về SymbolMapChartContext độc lập với renderer: stats.locatedCount / visibleCount / hiddenCount (bị loại bởi radiusVisibleMin) / invalidCount (bị loại vì tọa độ không hợp lệ) / valueDomain / min / max / displacedCount / outOfOrderCount (chỉ với honeycomb, được đo với mọi placement), một mảng symbols[] (một hàng cho mỗi mục hiển thị: id, label, value, valueSecond, radius, radiusSecond, color, x, y), tên projection đã phân giải, một bản tóm tắt bằng ngôn ngữ tự nhiên có tính xác định, và một a11yTable. Xem ngữ cảnh LLM.
Nguồn
Các thuộc tính (props) được định kiểu là SymbolMapChartProps trong @michi-vz/core.
