✈️ FlightPlan Developer Guide

← Back to Guide

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:

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

← Back to Guide

Please wait...