US Map
PixieDust.Charting includes a reusable, data-driven US map — an SVG rendered from database geometry, with per-app colors, automatic hover content, and one of two click behaviors (navigate, or multi-select). It deliberately separates four concerns that hand-rolled maps tend to fuse: geometry (the shared state shapes), presentation (color), content (hover), and behavior (click). The component loads its own geometry from the database, so a page just says new UsMap("YourApp").
The Two Tables
The map is backed by two tables under OpsO.StateLawTable. MapGeometry is shared cartography — the 50 states, one row each, knowing nothing about color or behavior. MapFeature holds each app's overlays and markers, keyed by App.
MapGeometry (shared, 50 rows)
| Column | Purpose |
|---|---|
| StateCode | 2-letter code, e.g. 'VA' — the primary key |
| Name / Capital | Used to compose a state's default hover content |
| PathData | The SVG path 'd' string for the state outline |
MapFeature (per app, many rows)
| Column | Purpose |
|---|---|
| App | The map key, e.g. 'ProcLob' — every UsMap(app) loads the rows for this value |
| FeatureCode | 'VA', 'DC', 'Broward', 'NYC'… — becomes the shape's code |
| GeometryKind | 0 = overlay on a shared state, 1 = circle marker, 2 = path marker |
| Cx / Cy / Radius | Circle marker geometry (GeometryKind = 1) |
| PathData | Own path (GeometryKind = 2, rare) |
| Fill | The resolved color string — 'Green', '#A9A9A9' |
| DisplayName | Marker hover text, e.g. 'Broward County FL' |
GeometryKind is the key field. 0 (overlay) means the row colors an existing state — its FeatureCode matches a MapGeometry.StateCode, and the geometry comes from there. 1 (circle) and 2 (path) are markers that carry their own geometry — DC, Puerto Rico, cities.
Fill stores the resolved color, not a status. The component never needs to know why a state is green — your app decides the color however it likes (a status column mapped to colors, or the color written directly) and writes the result into Fill. To define a new app's map, insert MapFeature rows with App = your key: one overlay row per state, plus marker rows for any cities/DC/PR.
Basic Usage
Construct with the App key and add it to a page. You get a colored, hoverable map with no click behavior:
content.Add(new UsMap("ProcLob"));
Hover content is composed automatically — Name + Capital for states, DisplayName for markers — and shown in a floating info box. The component self-emits everything it needs: the SVG, the info box, and the shared runtime script. You wire up no JavaScript.
Required Setup (once per app)
Two one-time things in a consuming app:
- Reference PixieDust.Charting (which itself references PixieDust.SqlServer — the map loads its geometry from the database).
- Register the map's CSS in your app's ConfigureAppStyles.
protected override void ConfigureAppStyles(StyleLibrary styles)
{
base.ConfigureAppStyles(styles);
MapStyles.AddTo(styles); // responsive sizing, hover outline, info box
}
Without MapStyles the map still renders, but unstyled (fixed size, no hover highlight, unformatted info box).
Behavior Modes
A map is in exactly one mode, chosen by a fluent call. With none, it is static + hover. Hover works in every mode.
Act Mode — Click Navigates
WithNavigate takes a URL template; a click navigates to it with {code} replaced by the clicked feature's FeatureCode (URL-encoded). This is the "click a state to drill into it" pattern — the page writes no JS.
content.Add(new UsMap("ProcLob")
.WithNavigate("/ProcLob/Regulations?juris={code}"));
Select Mode — Click Toggles a Multi-Select
WithSelect turns the map into a multi-select. Clicking a feature toggles it — its fill swaps to selectedFill and is restored exactly on deselect — and the chosen codes sync into a form field you provide. Read that field server-side on submit.
// field name, selected color, optional clear-checkbox name
content.Add(new UsMap("ProcLob").WithSelect("juris", "gray", "NoJuris"));
// a readonly box that both displays and posts the codes
form.Add(new Input(InputType.Text, "juris", GetFormValue("juris")).WithReadOnly());
// optional: a checkbox that clears all selections when checked
form.Add(new Input(InputType.Checkbox, "NoJuris").WithLabel("None of these"));
The form field is the source of truth, so selections survive postbacks — the runtime re-applies them from the field on load, which is what lets the map work inside a multi-step wizard. On submit, GetFormValue("juris") is a comma-separated list of FeatureCodes. The optional clear checkbox, when checked, restores every fill and empties the field; conversely, choosing a feature unchecks it (mutual exclusivity).
How It Renders (under the hood)
The component builds an in-memory model from the two tables and emits one SVG: a states layer, the AK/HI inset leader lines (an invariant chrome constant), then a markers layer painted on top. Each interactive shape carries data attributes the runtime reads — data-code (its FeatureCode), data-base-fill (its original color, for exact restore on deselect), and data-hover (the composed hover markup). The behavior config rides on the <svg> itself as data-mode plus mode-specific attributes.
A single shared vanilla-JS runtime (/js/Map.js, served statically) drives all of it — hover, raising the hovered shape so its outline is not clipped by neighbors, and click-by-mode. There is no per-project JavaScript and no per-map config in code beyond the fluent calls; the script include is cache-busted by the file's timestamp.
Mutating the Model Before Render
The render model is exposed so a page can tweak it before render — override a feature's Fill or its composed hover — for cases where the color comes from live runtime data rather than the stored Fill.
var map = new UsMap("ProcLob");
// map.Model.Features ... adjust Fill / HoverContent as needed
content.Add(map);
Adding the Map to a New App — Checklist
- Reference PixieDust.Charting in the app's csproj.
- Insert MapFeature rows for your App key: overlay rows (GeometryKind 0) for the states you want colored, plus marker rows for any cities/DC/PR — each with the Fill you want.
- Call MapStyles.AddTo(styles) in ConfigureAppStyles.
- Add new UsMap("YourApp") with the mode you need: nothing (static + hover), .WithNavigate(...), or .WithSelect(...).