<!-- Generated from docs/API.md; @kahwee/sf-map-svg 4.3.1 · npm package. -->

# Map options and lifecycle

Start with the [static and interactive examples](https://github.com/kahwee/sf-map-svg/blob/v4.3.1/README.md). Exact option
and event types are exported by [src/api.ts](https://github.com/kahwee/sf-map-svg/blob/v4.3.1/src/api.ts) and defined in
[src/types.ts](https://github.com/kahwee/sf-map-svg/blob/v4.3.1/src/types.ts) and [src/controller-types.ts](https://github.com/kahwee/sf-map-svg/blob/v4.3.1/src/controller-types.ts).

## Static maps

Static maps accept compact `StaticMapData` or the same source-aware `MapData` as interactive maps. With `MapData`, `source` selects a supplied neighborhood definition; SFAR realtor is preferred by default, followed by SF Find and analysis when available. A requested unavailable source throws before styling callbacks run. Compact `StaticMapData` already chooses its neighborhoods, so it rejects a `source` option. Both data shapes retain the static renderer defaults.

Static maps accept the same grouped presentation vocabulary as interactive maps:

```ts
const presentation = {
  layers: { landmarks: true, bartStations: true },
  appearance: { theme: 'districts' as const, colors: { water: '#e7f0f3' } },
};
const { svg } = renderMap(staticMapData, presentation);
const map = createMap(fullMapData, presentation);
```

Static `layers` supports every interactive layer switch except `neighborhoodLabels`. Static `appearance` supports `theme`, `colors`, and `districtStyle`; browser typography, area interaction styles, and screen-space marker sizing remain interactive settings. Unknown keys and invalid values are rejected for both flat and grouped options. Supplied grouped keys override flat compatibility keys; omitted or `undefined` grouped keys preserve the flat value or renderer default. Undefined color tokens use the theme default instead of entering SVG attributes. The two renderers retain their existing defaults. Marker arrays accept readonly inputs.

Flat compatibility options include `theme`, `width`, `height`, `padding`, `year` (2002, 2012, 2022), `districtLines`, `districtFills`, `districtStyle`, `districtLabels`, `neighborhoodLines`, `labels`, `highways`, `keyRoads`, `roadLabels`, `landmarks`, `bartStations`, `markers`, `overlays`, `title`, `idPrefix`, `colors`, and `animation`. Each optional layer is independent. User-supplied text and attributes are escaped in SVG output.

Custom district IDs must be finite numbers. An empty `labelPoints` array uses the district's primary `label` position.

The SVG description names the selected neighborhood source for source-aware data and uses a generic description for compact inputs. It describes enabled layers only when their data is supplied, using actual park and station counts.

`animation: true` (or `{ duration, delay }`) makes a static map draw itself with self-contained, scoped CSS: land fades in, the coast and lines draw, districts grow, then labels and points appear. It plays when the SVG is inserted into a page or loaded as an image, needs no JavaScript, and stays still under `prefers-reduced-motion`. Re-insert the markup to replay it. Default output is unchanged.

## Interactive maps

`createMap` defaults to `mode: 'basemap'` and `appearance.theme: 'transit'`. Set the mode explicitly for neighborhood or district exploration. District and neighborhood layer defaults follow the mode; other supplied layers default on. The [developer guide](https://github.com/kahwee/sf-map-svg/blob/v4.3.1/docs/developer-guide.md) compares static and interactive defaults and explains configuration resets, coordinates, and cleanup.

Construction options include `mode` (`basemap`, `neighborhoods`, or `districts`), `source`, `neighborhood`, `year`, `labels`, `layers`, `controls`, `features`, `appearance`, `markers`, `overlays`, `legend`, `strings`, `attribution`, and `fitPadding`. Enable map touch gestures with the touch control or `map.setTouchNavigation(true)`. `features` holds motion, marker entrances, selected marker rings, clustering, north arrow, scale bar, layer transitions, and district morphs. `appearance` holds theme, color tokens, label and area styles, marker colors and sizes, and district styling. `layers` controls district fill/line/labels, neighborhood lines/labels, landmarks, BART, highways, key roads, and road labels. See the exported `MapOptions` type for exact values and the [Storybook examples](https://github.com/kahwee/sf-map-svg/blob/v4.3.1/stories/) for live controls.

The controller also supports marker, neighborhood, and district selection; district year and style changes; source and mode changes; labels and touch navigation; screen projection; and typed `markerchange`, `neighborhoodchange`, `districtchange`, `districthover`, `districtactivate`, `districtyearchange`, `overlayactivate`, `clusteractivate`, `viewportchange`, and `mapresize` events. `destroy()` releases browser resources; operations after destruction throw.

If a district callback destroys the map or selects a newer district, the interrupted activation stops without moving keyboard focus or sending a stale event.

`setMarkers()` reconciles by stable marker `id`: retained markers keep their DOM nodes, focus, and in-progress entrance animations; only new IDs animate in. Identical ordered marker updates are a visual no-op. Camera state and `viewportchange` events remain synchronous, while animated camera steps and their dependent label/marker layout commit in the same browser frame.

For a small guide, import `guideMapData` from `/guide/data` and pass it to `createMap`. The optional `/guide` entry also provides `createGuideMap`, `mountGuideMap`, and detailed-data loading for existing guide layouts. `/transit` provides the standalone schematic transit animation. See [examples](https://github.com/kahwee/sf-map-svg/blob/v4.3.1/docs/EXAMPLES.md) and [consumer integration](https://github.com/kahwee/sf-map-svg/blob/v4.3.1/docs/consumer-integration.md).

For the guide preset with the same controller API, use `createGuideController(options)` from `/guide` (or `/guide/map`). It accepts grouped `MapOptions` and returns `MapController`; `mountGuideController(shell, options)` enhances a `createGuideShell()` container. Both use the lightweight guide geography. Existing `createGuideMap` and `mountGuideMap` calls retain their element-based API.

Two opt-in features animate the map's lines and layers. `features.layerTransitions` fades layers as `configure({ layers })` and `setMode()` switch them, crossfades neighborhood boundaries when `setSource()` changes the definition, and crossfades district fills on `setDistrictStyle()`. `features.districtMorph` moves district outlines from one map year to the next on `setDistrictYear()`, then settles on the exact published geometry; pass `{ animate: false }` for an instant change. Both default off, own and cancel their animations, and yield to reduced motion.

District style callbacks are evaluated once per district at construction and on style or year updates. Hover, selection, and layer toggles reuse the prepared styles; call `setDistrictStyle()` again when external styling data changes. Invalid styles leave the current map unchanged. District fades respect reduced motion and are removed on interruption or destruction.

## Live configuration and inspection

`map.configure({ appearance, mode, source, year, labels, layers, features, controls })` updates an existing map. Appearance updates preserve the camera, selections, retained marker nodes, and keyboard focus. Mode/source changes through `configure` preserve the camera; the `setMode(mode, { resetView: false })` and `setSource(source, { resetView: false })` convenience methods use the same atomic update path and preserve the camera. Omit the second argument to retain their existing reset behavior. Invalid view-update options fail before changing configuration. Switching neighborhood source clears the neighborhood selection. Changing district year retains a selected district when it exists in the new year. Unsupported datasets, invalid appearance values, and invalid district-style callback results fail during preparation before commit. A reentrant styling callback can supersede the pending patch.

Appearance token objects (`colors`, `style`, `labelStyle`, `labelSize`, `areaStyle`) merge supplied keys; other appearance properties replace. Omitted keys retain their value, an explicit `undefined` property removes that override, and `appearance: undefined` resets the whole appearance group. Feature objects retain their existing replacement semantics. Mode/source/year/labels ignore `undefined`; reset them with an explicit value. Patch types explicitly permit these resets with TypeScript’s `exactOptionalPropertyTypes` enabled.

```ts
map.configure({
  appearance: { colors: { water: '#142d45' }, labelStyle: { fontFamily: 'Georgia,serif' } },
  layers: { bartStations: false },
});
map.configure({ appearance: undefined }); // restore construction defaults, not initial overrides

const overrides = map.getConfiguration();
const effective = map.getResolvedConfiguration();
const available = map.getCapabilities();
```

`getConfiguration()` returns the current mode, source, year and master labels, together with detached feature, layer, control, and appearance overrides; district style callbacks retain their function identity. `getResolvedConfiguration()` adds the current mode, source, year and master labels and resolves **layer switches** through mode defaults, available data, and the master label switch. Appearance/control fields remain overrides. It does not claim that every label is visible: zoom and collision filtering still apply. `getCapabilities()` returns supplied `sources`, usable district `years`, and layer data availability for the current source/year, independent of visibility overrides. These reads throw after destruction.

## Common selection event

`selectionchange` adds a consistent envelope without changing existing event payloads:

```ts
map.on('selectionchange', (event) => {
  // kind narrows current and previous to the corresponding selection type.
  if (event.kind === 'marker') console.log(event.current?.id, event.previous?.id);
});
```

`kind` is `marker`, `neighborhood`, or `district`. `current` and `previous` are detached selection snapshots or `null`. The event follows committed selection changes, including clearing and a changed district vintage; it does not represent every activation. Existing `markerchange`, `neighborhoodchange`, and `districtchange` events remain supported.

## Source-aware feature selection

`selectFeature(reference, options)` accepts one typed identity for each selectable kind:

```ts
map.selectFeature({ kind: 'marker', id: 'ferry' }, { fit: false });
map.selectFeature({ kind: 'neighborhood', source: 'realtor', id: 'inner-mission' });
map.selectFeature({ kind: 'district', year: 2022, id: 3 });
```

Neighborhood IDs are exact source-scoped feature IDs, rather than display names or aliases. District identity includes the boundary year. This operation returns `false` for a missing feature or a source/year other than the map's current supplied geography; it never silently switches definitions or years. Choose geography through `configure` first. An `id` of `null` clears that kind in the referenced current geography. Malformed references throw. Existing `selectMarker`, `selectNeighborhood`, and `selectDistrict` shortcuts remain supported.

## Coordinates and units

GeoJSON and static projection inputs use `[longitude, latitude]` in WGS84; markers use `{ lng, lat }`. Static `project` returns SVG units. `projectToScreen(lng, lat)` returns pixels relative to the mounted canvas. Camera viewports and pan deltas use the fixed 800 × 800 projected map space, independent of canvas size. Marker radius uses screen pixels in browser maps and SVG units in static output.

Explore presentation options in the [live map design playground](https://kahwee.github.io/sf-map-svg/playground.html). The page identifies its package version; released builds use compatible options for that installed version.

# Published type contracts

@kahwee/sf-map-svg 4.3.1 · npm package. These declarations are generated by TypeScript. Use package export paths, not the internal filenames below. Imported relative contracts are included for context, not as additional public entry points.

| Public import | Declaration |
| --- | --- |
| @kahwee/sf-map-svg | ./dist/src/api.d.ts |
| @kahwee/sf-map-svg/data | ./dist/data/index.d.ts |
| @kahwee/sf-map-svg/data/analysis | ./dist/data/analysis.d.ts |
| @kahwee/sf-map-svg/data/catalog | ./dist/data/catalog.d.ts |
| @kahwee/sf-map-svg/data/full | ./dist/src/full-data.d.ts |
| @kahwee/sf-map-svg/data/static | ./dist/src/static-data.d.ts |
| @kahwee/sf-map-svg/data/coast | ./dist/data/coast.d.ts |
| @kahwee/sf-map-svg/data/districts | ./dist/data/districts.d.ts |
| @kahwee/sf-map-svg/data/highways | ./dist/data/highways.d.ts |
| @kahwee/sf-map-svg/data/landmarks | ./dist/data/landmarks.d.ts |
| @kahwee/sf-map-svg/data/lookup | ./dist/data/lookup.d.ts |
| @kahwee/sf-map-svg/data/realtor | ./dist/data/realtor.d.ts |
| @kahwee/sf-map-svg/data/roads | ./dist/data/roads.d.ts |
| @kahwee/sf-map-svg/data/sf-find | ./dist/data/sf-find.d.ts |
| @kahwee/sf-map-svg/data/stations | ./dist/data/stations.d.ts |
| @kahwee/sf-map-svg/geometry | ./dist/src/geometry.d.ts |
| @kahwee/sf-map-svg/guide | ./dist/src/guide.d.ts |
| @kahwee/sf-map-svg/guide/data | ./dist/src/guide-data.d.ts |
| @kahwee/sf-map-svg/guide/detailed | ./dist/src/guide-detailed.d.ts |
| @kahwee/sf-map-svg/guide/map | ./dist/src/guide-map.d.ts |
| @kahwee/sf-map-svg/transit | ./dist/src/transit.d.ts |
| @kahwee/sf-map-svg/guide/static | ./dist/src/guide-static.d.ts |
| @kahwee/sf-map-svg/map | ./dist/src/map.d.ts |
| @kahwee/sf-map-svg/static | ./dist/src/static.d.ts |
| @kahwee/sf-map-svg/presets | ./dist/src/presets.d.ts |

### ./dist/src/api.d.ts

```ts
/** Runtime and types only. Geography is always an explicit import. */
export * from './map.js';
export * from './static.js';
```

### dist/src/map.d.ts

```ts
import type { MapController, MapOptions } from './controller-types.js';
import type { InteractiveSFMapData } from './explorer-data.js';
export type * from './controller-types.js';
export type { InteractiveSFMapData as MapData } from './explorer-data.js';
export type { CameraOptions, DistrictSelection, DistrictStyle, DistrictYear, InteractiveLayers, MapFeatures, MapMarker, MapOverlay, MapPadding, MapViewport, NeighborhoodSelection, } from './types.js';
/** No geography is imported. Supply a preset or your own immutable data. */
export declare function createMap(data: InteractiveSFMapData, options?: MapOptions): MapController;
```

### dist/src/controller-types.d.ts

```ts
import type { Geometry, NeighborhoodSource } from '../data/types.js';
import type { CameraOptions, DistrictSelection, DistrictStyle, DistrictYear, InteractiveLayers, MapFeatures, MapMarker, MapOverlay, MapPadding, MapViewport, NeighborhoodExplorerOptions, NeighborhoodSelection } from './types.js';
/** Patch properties can explicitly remove overrides, including with exactOptionalPropertyTypes. */
type Resettable<T> = {
    [K in keyof T]?: T[K] | undefined;
};
type AppearanceOptions = Pick<NeighborhoodExplorerOptions, 'theme' | 'colors' | 'labelStyle' | 'areaStyle' | 'districtStyle' | 'labelSize' | 'style' | 'markerRadius' | 'markerHitSize' | 'markerColor' | 'selectedMarkerColor'>;
export type MapAppearance = {
    [K in keyof AppearanceOptions]?: K extends 'colors' | 'style' | 'labelStyle' | 'labelSize' | 'areaStyle' ? Resettable<NonNullable<AppearanceOptions[K]>> | undefined : AppearanceOptions[K] | undefined;
};
export type MapControls = NonNullable<NeighborhoodExplorerOptions['controls']>;
/** Omitted keys retain values. Undefined presentation groups reset; undefined mode/source/year/labels retain their current value. */
export interface MapConfiguration {
    features?: Resettable<MapFeatures> | undefined;
    layers?: Resettable<InteractiveLayers> | undefined;
    controls?: Resettable<MapControls> | undefined;
    appearance?: MapAppearance | undefined;
    mode?: NonNullable<NeighborhoodExplorerOptions['mode']> | undefined;
    source?: NeighborhoodSource | undefined;
    year?: DistrictYear | undefined;
    labels?: boolean | undefined;
}
export interface MapOptions extends Omit<NeighborhoodExplorerOptions, keyof MapFeatures | keyof MapAppearance | 'interface' | 'onMarkerActivate' | 'onOverlayActivate'> {
    features?: MapFeatures;
    appearance?: MapAppearance | undefined;
}
/** Current geographic choices and detached presentation overrides; excludes camera and selection. */
export interface MapConfigurationSnapshot {
    mode: NonNullable<MapOptions['mode']>;
    source: NeighborhoodSource;
    year: DistrictYear;
    labels: boolean;
    features: MapFeatures;
    /** Explicit overrides; absent values continue to follow the current mode. */
    layers: InteractiveLayers;
    controls: MapControls;
    appearance: MapAppearance;
}
export interface MapCapabilities {
    sources: readonly NeighborhoodSource[];
    years: readonly DistrictYear[];
    /** Data availability, independent of requested layer visibility. */
    layers: Readonly<Record<keyof InteractiveLayers, boolean>>;
}
export interface ResolvedMapConfiguration extends MapConfigurationSnapshot {
    mode: NonNullable<MapOptions['mode']>;
    source: NeighborhoodSource;
    year: DistrictYear;
    labels: boolean;
    /** Effective switches after mode defaults and data availability; label switches honor labels. */
    layers: Record<keyof InteractiveLayers, boolean>;
}
export type MapSelectionChange = {
    kind: 'marker';
    current: MapMarker | null;
    previous: MapMarker | null;
} | {
    kind: 'neighborhood';
    current: NeighborhoodSelection | null;
    previous: NeighborhoodSelection | null;
} | {
    kind: 'district';
    current: DistrictSelection | null;
    previous: DistrictSelection | null;
};
/** Dataset identity is explicit; selection never switches geography implicitly. */
export type MapFeatureReference = {
    kind: 'marker';
    id: string | null;
} | {
    kind: 'neighborhood';
    source: NeighborhoodSource;
    id: string | null;
} | {
    kind: 'district';
    year: DistrictYear;
    id: number | null;
};
export interface MapEvents {
    /** Common selection envelope; existing change events retain their payloads. */
    selectionchange: MapSelectionChange;
    districtchange: DistrictSelection | {
        id: null;
        year: DistrictYear;
        district: null;
    };
    districthover: DistrictSelection | {
        id: null;
        year: DistrictYear;
        district: null;
    };
    districtactivate: DistrictSelection;
    districtyearchange: {
        year: DistrictYear;
        previousYear: DistrictYear;
    };
    markerchange: {
        id: string | null;
        marker: MapMarker | null;
    };
    neighborhoodchange: NeighborhoodSelection | {
        id: null;
        name: null;
        source: NeighborhoodSource;
        feature: null;
    };
    overlayactivate: {
        overlay: MapOverlay;
    };
    clusteractivate: {
        markers: MapMarker[];
    };
    viewportchange: {
        viewport: MapViewport;
    };
    mapresize: undefined;
}
/** Convenience setters retain their historic reset default; false preserves the camera. */
export interface MapViewUpdateOptions {
    resetView?: boolean;
}
export interface MapCamera {
    /** Fixed 800 × 800 projected space, not longitude/latitude. */
    get(): MapViewport;
    set(view: MapViewport, options?: CameraOptions): void;
    fit(geometry: Geometry, options?: CameraOptions & {
        padding?: number | MapPadding;
    }): void;
    /** Delta in projected map units. */
    pan(x: number, y: number, options?: CameraOptions): void;
    zoom(factor: number, options?: CameraOptions): void;
    reset(options?: CameraOptions): void;
    stop(): void;
}
/** Owns the map lifecycle separately from its mountable DOM element. */
export interface MapController {
    readonly element: HTMLElement;
    readonly overlayElement: HTMLDivElement;
    readonly camera: MapCamera;
    readonly destroyed: boolean;
    configure(patch: MapConfiguration): void;
    getConfiguration(): MapConfigurationSnapshot;
    getResolvedConfiguration(): ResolvedMapConfiguration;
    getCapabilities(): MapCapabilities;
    on<K extends keyof MapEvents>(type: K, listener: (detail: MapEvents[K]) => void): () => void;
    /** Reconcile stable IDs; retained markers preserve nodes, focus, and entrance animations. */
    setMarkers(markers: readonly MapMarker[]): void;
    setOverlays(overlays: readonly MapOverlay[]): void;
    /** False for missing IDs or a source/year other than the current geography. */
    selectFeature(reference: MapFeatureReference, options?: CameraOptions & {
        fit?: boolean;
    }): boolean;
    selectMarker(id: string | null, options?: CameraOptions & {
        fit?: boolean;
    }): boolean;
    getSelectedMarker(): MapMarker | null;
    selectNeighborhood(name: string | null, options?: CameraOptions & {
        fit?: boolean;
    }): boolean;
    getSelectedNeighborhood(): NeighborhoodSelection | null;
    selectDistrict(id: number | null, options?: CameraOptions & {
        fit?: boolean;
    }): boolean;
    getSelectedDistrict(): DistrictSelection | null;
    setDistrictYear(year: DistrictYear, options?: CameraOptions): void;
    setDistrictStyle(style: ((district: DistrictSelection['district']) => DistrictStyle) | undefined): void;
    setSource(source: NeighborhoodSource, options?: MapViewUpdateOptions): void;
    setMode(mode: NonNullable<MapOptions['mode']>, options?: MapViewUpdateOptions): void;
    setLabels(visible: boolean): void;
    setTouchNavigation(enabled: boolean): void;
    projectToScreen(lng: number, lat: number): {
        x: number;
        y: number;
        visible: boolean;
    };
    /** Idempotent; stops work and managed subscriptions. Does not remove host-owned DOM. */
    destroy(): void;
}
export {};
```

### dist/data/types.d.ts

```ts
export type Position = readonly [number, number, ...number[]];
export type Bounds = readonly [number, number, number, number];
export type Geometry = {
    readonly type: 'Point';
    readonly coordinates: Position;
} | {
    readonly type: 'LineString';
    readonly coordinates: readonly Position[];
} | {
    readonly type: 'MultiPoint';
    readonly coordinates: readonly Position[];
} | {
    readonly type: 'Polygon';
    readonly coordinates: readonly (readonly Position[])[];
} | {
    readonly type: 'MultiLineString';
    readonly coordinates: readonly (readonly Position[])[];
} | {
    readonly type: 'MultiPolygon';
    readonly coordinates: readonly (readonly (readonly Position[])[])[];
} | {
    readonly type: 'GeometryCollection';
    readonly geometries: readonly Geometry[];
};
export interface Feature<P = Readonly<Record<string, unknown>>> {
    readonly type: 'Feature';
    readonly id: string;
    readonly bbox: Bounds;
    readonly properties: P;
    readonly geometry: Geometry;
}
export interface DataSource {
    readonly id: string;
    readonly title: string;
    readonly url: string;
    readonly retrievedAt: string;
    readonly definitionYear?: number;
    readonly licenseUrl: string;
}
export interface Definition {
    readonly kind: string;
    readonly year?: number;
    readonly description: string;
}
export interface FeatureCollection<P = Readonly<Record<string, unknown>>> {
    readonly type: 'FeatureCollection';
    readonly schemaVersion: 1;
    readonly id: string;
    readonly title: string;
    readonly coordinateSystem: string;
    readonly definition: Definition;
    readonly sources: readonly DataSource[];
    readonly topology?: {
        readonly policy: 'disjoint-interiors';
        readonly sharedBoundariesAllowed: boolean;
        readonly method: string;
        readonly tool: string;
        readonly processedAt: string;
        readonly sourceOverlapPairs: number;
    };
    readonly features: readonly Feature<P>[];
}
export type PointFeatureCollection<P = Readonly<Record<string, unknown>>> = Omit<FeatureCollection<P>, 'features'> & {
    readonly features: readonly (Feature<P> & {
        readonly geometry: Extract<Geometry, {
            readonly type: 'Point';
        }>;
    })[];
};
export type NeighborhoodSource = 'sf-find' | 'analysis' | 'realtor';
export interface NeighborhoodProperties {
    readonly name: string;
    readonly canonicalName: string;
    readonly sourceName: string;
    readonly aliases: readonly string[];
    readonly definitionSource: NeighborhoodSource;
    readonly nameSources: readonly string[];
    readonly note?: string;
    readonly sourceCode?: string | null;
    readonly realtorDistrict?: string;
}
export interface NeighborhoodEntry extends NeighborhoodProperties {
    readonly id: string;
    readonly source: NeighborhoodSource;
    readonly file: string;
    readonly bbox: Bounds;
}
export interface DistrictProperties {
    readonly district: number;
    readonly name: string;
    readonly year: 2002 | 2012 | 2022;
    readonly label: Position;
    readonly labelPoints: readonly Position[];
    readonly displayExtras: Geometry | null;
}
export interface Catalog {
    readonly schemaVersion: 1;
    readonly description: string;
    readonly datasets: readonly {
        readonly id: string;
        readonly file: string;
        readonly title: string;
        readonly featureCount: number;
        readonly definition: Definition;
        readonly sources: readonly DataSource[];
    }[];
    readonly neighborhoods: readonly NeighborhoodEntry[];
}
export type NeighborhoodFeature = Feature<NeighborhoodProperties>;
export type PolygonGeometry = Extract<Geometry, {
    readonly type: 'Polygon';
}>;
export type MultiPolygonGeometry = Extract<Geometry, {
    readonly type: 'MultiPolygon';
}>;
export interface LandmarkProperties {
    readonly name: string;
    readonly label: Position;
    readonly offset: readonly [number, number];
    readonly anchor: 'middle' | 'start' | 'end';
}
export interface KeyRoadProperties {
    readonly name: string;
    readonly level?: 'primary' | 'secondary';
    readonly sourceNames: readonly string[];
    readonly label: Position;
    readonly segmentIds: readonly string[];
}
```

### dist/src/types.d.ts

```ts
import type { Geometry, NeighborhoodFeature, NeighborhoodSource } from '../data/types.js';
export type DistrictYear = 2002 | 2012 | 2022;
export interface DistrictSelection {
    id: number;
    year: DistrictYear;
    district: import('./map-core.js').DistrictRowData;
}
export interface DistrictStyle {
    fill?: string;
    stroke?: string;
    opacity?: number;
}
export interface MapMarker {
    id: string;
    lng: number;
    lat: number;
    label?: string;
    selected?: boolean;
    color?: string;
    /** Visible radius in screen pixels (interactive), SVG units (static). */
    radius?: number;
}
export interface MapOverlay {
    id: string;
    geometry: Extract<Geometry, {
        type: 'LineString' | 'MultiLineString' | 'Polygon' | 'MultiPolygon';
    }>;
    stroke?: string;
    strokeWidth?: number;
    fill?: string;
    fillOpacity?: number;
    visible?: boolean;
    label?: string;
}
export interface SFMapOptions {
    /** Grouped presentation; supplied keys take precedence over flat compatibility options. */
    layers?: StaticMapLayers;
    appearance?: StaticMapAppearance;
    theme?: 'districts' | 'transit';
    width?: number;
    height?: number;
    padding?: number;
    year?: DistrictYear;
    /** Select a supplied neighborhood definition when rendering source-aware MapData. */
    source?: NeighborhoodSource;
    districtLines?: boolean;
    neighborhoodLines?: boolean;
    districtFills?: boolean;
    /** Evaluated once per district at construction/style/year updates; call setDistrictStyle again when external data changes. */
    districtStyle?: (district: import('./map-core.js').DistrictRowData) => DistrictStyle;
    districtLabels?: boolean;
    /** Hide all visible text labels while retaining geographic symbols and accessible titles. */
    labels?: boolean;
    highways?: boolean;
    keyRoads?: boolean;
    roadLabels?: boolean;
    landmarks?: boolean;
    bartStations?: boolean;
    markers?: readonly MapMarker[];
    overlays?: readonly MapOverlay[];
    title?: string;
    idPrefix?: string;
    /**
     * Opt-in, self-contained CSS choreography for static SVGs: land fades in, the coast and
     * lines draw, districts grow, then labels and points appear. It plays when the SVG is
     * inserted into a page or loaded as an image, and stays still under reduced motion.
     * `duration` (default 2400 ms) scales the whole sequence; `delay` offsets it.
     */
    animation?: boolean | {
        duration?: number;
        delay?: number;
    };
    colors?: Partial<Record<'water' | 'land' | 'district' | 'neighborhood' | 'highway' | 'road' | 'park' | 'landmark' | 'bart' | 'label' | 'marker' | 'selected', string>>;
}
/** Presentation supported by both the static and interactive renderers. */
export type StaticMapLayers = Omit<InteractiveLayers, 'neighborhoodLabels'>;
export type StaticMapAppearance = Pick<SFMapOptions, 'theme' | 'colors' | 'districtStyle'>;
/** Serializable viewport in the interactive map's fixed 800 × 800 projected space. */
export type MapViewport = readonly [x: number, y: number, size: number];
export type ExplorerMode = 'neighborhoods' | 'districts' | 'basemap';
export interface MapPadding {
    top?: number;
    right?: number;
    bottom?: number;
    left?: number;
}
export interface InteractiveLayers {
    districtFills?: boolean;
    districtLines?: boolean;
    districtLabels?: boolean;
    neighborhoodLines?: boolean;
    neighborhoodLabels?: boolean;
    landmarks?: boolean;
    bartStations?: boolean;
    highways?: boolean;
    keyRoads?: boolean;
    roadLabels?: boolean;
}
export interface NeighborhoodSelection {
    id: string;
    name: string;
    source: NeighborhoodSource;
    feature: NeighborhoodFeature;
}
export interface MapFeatures {
    /** Opt-in camera motion; reduced-motion always takes precedence. */
    motion?: boolean | {
        duration?: number;
    };
    markerEntrance?: boolean | {
        duration?: number;
        stagger?: number;
    };
    selectedMarkerRing?: boolean | {
        color?: string;
        width?: number;
        gap?: number;
    };
    /** Screen-space clustering. The selected marker and full chooser remain available. */
    clustering?: boolean | {
        radius?: number;
    };
    northArrow?: boolean;
    scaleBar?: boolean;
    /**
     * Fade layers as `setLayers` and `setMode` switch them, crossfade neighborhood boundaries
     * when `setSource` changes the definition, and crossfade district fills on
     * `setDistrictStyle`. Reduced motion takes precedence.
     */
    layerTransitions?: boolean | {
        duration?: number;
    };
    /**
     * Morph district outlines from one map year to the next on `setDistrictYear`; pass
     * `{ animate: false }` for an instant change. Reduced motion takes precedence.
     */
    districtMorph?: boolean | {
        duration?: number;
    };
}
export interface NeighborhoodExplorerOptions extends MapFeatures {
    mode?: ExplorerMode;
    labels?: boolean;
    source?: NeighborhoodSource;
    neighborhood?: string;
    year?: DistrictYear;
    theme?: SFMapOptions['theme'];
    colors?: SFMapOptions['colors'];
    labelStyle?: {
        fontFamily?: string;
        fontWeight?: number;
        haloColor?: string;
    };
    areaStyle?: {
        selectedFill?: string;
        selectedStroke?: string;
        hoverFill?: string;
        hoverStroke?: string;
    };
    districtStyle?: SFMapOptions['districtStyle'];
    legend?: {
        builtins?: boolean;
        hidden?: readonly ('bart' | 'park' | 'highway' | 'road')[];
        items?: readonly {
            label: string;
            color: string;
        }[];
    };
    attribution?: 'full' | 'compact';
    /** Explorer chrome or the reusable map with only controls and attribution. */
    interface?: 'explorer' | 'map';
    /** Independent overrides; omitted layers follow mode defaults. */
    layers?: InteractiveLayers;
    selectableNeighborhoods?: boolean;
    /** Screen pixels; defaults preserve 11px roads / 12px other labels. */
    labelSize?: {
        min?: number;
        max?: number;
    };
    /** Screen-pixel space reserved when fitting geometry. */
    fitPadding?: number | MapPadding;
    markers?: readonly MapMarker[];
    /** Screen pixels, independent of zoom. */
    markerRadius?: number;
    markerHitSize?: number;
    markerColor?: string;
    selectedMarkerColor?: string;
    onMarkerActivate?: (marker: MapMarker) => void;
    overlays?: readonly MapOverlay[];
    onOverlayActivate?: (overlay: MapOverlay) => void;
    /** Stable theme tokens consumed by the explorer chrome. */
    style?: Partial<Record<'ink' | 'surface' | 'accent' | 'border' | 'focus' | 'controlGap' | 'font', string>>;
    strings?: Partial<Record<'title' | 'mode' | 'source' | 'search' | 'chooseNeighborhood' | 'chooseMarker' | 'touchNavigation' | 'touchNavigationLabel' | 'touchNavigationExitLabel' | 'touchNavigationDone' | 'reset' | 'emptyResults' | 'gestureHelp', string>>;
    /**
     * Independently hide chrome. `neighborhoodPicker` and `markerPicker` hide the native
     * choosers (supply your own accessible list); `help` keeps the gesture help as the map's
     * accessible description; `status` keeps a visually hidden live region. Attribution stays.
     */
    controls?: Partial<Record<'zoom' | 'pan' | 'reset' | 'labels' | 'touch' | 'legend' | 'neighborhoodPicker' | 'markerPicker' | 'help' | 'status', boolean>>;
}
export interface CameraOptions {
    animate?: boolean;
    duration?: number;
}
export interface NeighborhoodExplorerElement extends HTMLElement {
    /** Append positioned HTML children here; coordinates are relative to this layer. */
    readonly overlayElement: HTMLDivElement;
    projectToScreen(lng: number, lat: number): {
        x: number;
        y: number;
        visible: boolean;
    };
    stopAnimation(): void;
    /** Atomic patch; false disables, undefined resets a feature to its default. */
    setFeatures(patch: MapFeatures): void;
    getFeatures(): MapFeatures;
    /** Layer overrides; undefined restores mode defaults. Preserves camera and selection. */
    setLayers(patch: InteractiveLayers): void;
    /** Chrome switches are independent. */
    setControls(patch: NonNullable<NeighborhoodExplorerOptions['controls']>): void;
    selectNeighborhood(name: string | null, options?: {
        fit?: boolean;
    } & CameraOptions): boolean;
    getSelection(): NeighborhoodSelection | null;
    selectDistrict(id: number | null, options?: {
        fit?: boolean;
    } & CameraOptions): boolean;
    getSelectedDistrict(): DistrictSelection | null;
    setDistrictYear(year: DistrictYear, options?: CameraOptions): void;
    setDistrictStyle(style: SFMapOptions['districtStyle']): void;
    setSource(source: NeighborhoodSource): void;
    setMode(mode: ExplorerMode): void;
    setLabels(visible: boolean): void;
    resetView(options?: CameraOptions): void;
    zoomBy(factor: number, options?: CameraOptions): void;
    panBy(x: number, y: number, options?: CameraOptions): void;
    getViewport(): MapViewport;
    setViewport(view: MapViewport, options?: CameraOptions): void;
    /** Fit WGS84 geometry, including Point, MultiPoint, or a GeometryCollection. */
    fitGeometry(geometry: Geometry, padding?: number | MapPadding, options?: CameraOptions): void;
    /** Explicitly engage map touch gestures; false restores page gestures. */
    setTouchNavigation(enabled: boolean): void;
    /** Reconcile stable IDs; retained markers preserve nodes, focus, and entrance animations. */
    setMarkers(markers: readonly MapMarker[]): void;
    setOverlays(overlays: readonly MapOverlay[]): void;
    selectMarker(id: string | null, options?: {
        fit?: boolean;
    } & CameraOptions): boolean;
    getSelectedMarker(): MapMarker | null;
    destroy(): void;
}
/** Paused-by-default schematic BART animation with keyboard-operable controls. */
export interface TransitAnimationElement extends HTMLElement {
    destroy(): void;
}
export type { MapAppearance, MapCamera, MapCapabilities, MapConfiguration, MapConfigurationSnapshot, MapController, MapControls, MapEvents, MapFeatureReference, MapOptions, MapSelectionChange, MapViewUpdateOptions, ResolvedMapConfiguration, } from './controller-types.js';
```

### dist/src/explorer-data.d.ts

```ts
import type { DistrictProperties, FeatureCollection, NeighborhoodProperties, NeighborhoodSource } from '../data/types.js';
import type { SFMapData } from './map-core.js';
import type { DistrictYear } from './types.js';
/** Geographic inputs for the data-injected interactive map entry point. */
export interface InteractiveSFMapData {
    map: SFMapData;
    districts?: Partial<Readonly<Record<DistrictYear, FeatureCollection<DistrictProperties>>>>;
    neighborhoods: Partial<Readonly<Record<NeighborhoodSource, FeatureCollection<NeighborhoodProperties>>>>;
}
```

### dist/src/map-core.d.ts

```ts
import type { Geometry, Position } from '../data/types.js';
import type { DistrictYear, SFMapOptions } from './types.js';
export type { DistrictYear, MapMarker, MapOverlay, SFMapOptions } from './types.js';
export interface SFMapData {
    coast: Geometry;
    districts?: Readonly<Partial<Record<DistrictYear, readonly DistrictRowData[]>>>;
    neighborhoods?: readonly {
        name: string;
        geometry: Geometry;
    }[];
    highways?: readonly {
        route: string;
        geometry: Geometry;
    }[];
    landmarks?: readonly LandmarkData[];
    keyRoads?: readonly KeyRoadData[];
    bartStations?: readonly BartStationData[];
}
export interface DistrictRowData {
    id: number;
    label: Position;
    labelPoints: readonly Position[];
    geometry: Geometry;
    extras: Geometry | null;
}
export interface LandmarkData {
    id: string;
    name: string;
    label: Position;
    offset: readonly [number, number];
    anchor: 'middle' | 'start' | 'end';
    geometry: Geometry;
}
export interface KeyRoadData {
    id: string;
    name: string;
    level?: 'primary' | 'secondary';
    sourceNames: readonly string[];
    label: Position;
    segmentIds: readonly string[];
    geometry: Geometry;
}
export interface BartStationData {
    id: string;
    name: string;
    coordinates: Position;
}
export declare const districtYears: readonly DistrictYear[];
export declare const districtColors: readonly string[];
/** Canonical geometry in the same fitted coordinate space used by renderMap. */
export declare function getLayerPathsWithData(options: SFMapOptions, data: SFMapData, complete?: boolean): {
    year: DistrictYear;
    viewBox: [number, number, number, number];
    project: (coordinates: Position) => [number, number];
    coast: string;
    districts: {
        id: number;
        geometry: string;
        extras: string;
        path: string;
    }[];
    neighborhoods: {
        name: string;
        path: string;
    }[];
    highways: {
        route: string;
        path: string;
    }[];
    landmarks: {
        id: string;
        path: string;
    }[];
    keyRoads: {
        id: string;
        path: string;
    }[];
    bartStations: {
        id: string;
        point: [number, number];
    }[];
};
export declare function resolveMapColors(theme: SFMapOptions['theme'], colors?: SFMapOptions['colors']): {
    water: string;
    land: string;
    district: string;
    neighborhood: string;
    highway: string;
    road: string;
    park: string;
    landmark: string;
    bart: string;
    label: string;
    marker: string;
    selected: string;
};
/** Make an offline SVG and the matching longitude/latitude projection. */
export declare function createSFMapWithData(options: SFMapOptions, data: SFMapData): {
    svg: string;
    project: (coordinates: Position) => [number, number];
    viewBox: [number, number, number, number];
};
```

### dist/src/static.d.ts

```ts
import type { InteractiveSFMapData } from './explorer-data.js';
import { type SFMapData } from './map-core.js';
import type { SFMapOptions } from './types.js';
/** Either compact static geography or the same source-aware geography used by createMap. */
export type StaticMapInput = SFMapData | InteractiveSFMapData;
export type { DistrictRowData, SFMapData as StaticMapData } from './map-core.js';
export type { DistrictStyle, DistrictYear, SFMapOptions as StaticMapOptions, StaticMapAppearance, StaticMapLayers, } from './types.js';
/** Server-safe SVG rendering with explicit data; returns SVG plus projection helpers. */
export declare function renderMap(data: StaticMapInput, options?: SFMapOptions): {
    svg: string;
    project: (coordinates: import("../data/types.js").Position) => [number, number];
    viewBox: [number, number, number, number];
};
/** Project canonical layer geometry without constructing an SVG string. */
export declare function getLayerPaths(data: StaticMapInput, options?: SFMapOptions): {
    year: import("./types.js").DistrictYear;
    viewBox: [number, number, number, number];
    project: (coordinates: import("../data/types.js").Position) => [number, number];
    coast: string;
    districts: {
        id: number;
        geometry: string;
        extras: string;
        path: string;
    }[];
    neighborhoods: {
        name: string;
        path: string;
    }[];
    highways: {
        route: string;
        path: string;
    }[];
    landmarks: {
        id: string;
        path: string;
    }[];
    keyRoads: {
        id: string;
        path: string;
    }[];
    bartStations: {
        id: string;
        point: [number, number];
    }[];
};
```

### ./dist/data/index.d.ts

```ts
export { catalog, searchNeighborhoods } from './catalog.js';
export { coast } from './coast.js';
export { districtMaps } from './districts.js';
export { highways } from './highways.js';
export { landmarks } from './landmarks.js';
export { getNeighborhood, neighborhoodCollections, neighborhoodSources } from './lookup.js';
export { getRealtorNeighborhood, neighborhoods } from './realtor.js';
export { keyRoads } from './roads.js';
export { bartStations } from './stations.js';
export type * from './types.js';
```

### dist/data/catalog.d.ts

```ts
import type { Catalog, NeighborhoodSource } from './types.js';
export declare const catalog: Catalog;
/** Search name metadata across sources without importing polygon geometry. */
export declare function searchNeighborhoods(query?: string, { source }?: {
    source?: NeighborhoodSource;
}): import("./types.js").NeighborhoodEntry[];
```

### dist/data/coast.d.ts

```ts
import type { FeatureCollection } from './types.js';
export declare const coast: FeatureCollection<{
    readonly name: string;
}>;
```

### dist/data/districts.d.ts

```ts
import type { DistrictProperties, FeatureCollection } from './types.js';
export declare const districtMaps: Readonly<Record<2002 | 2012 | 2022, FeatureCollection<DistrictProperties>>>;
```

### dist/data/highways.d.ts

```ts
import type { FeatureCollection } from './types.js';
export declare const highways: FeatureCollection<{
    readonly route: string;
}>;
```

### dist/data/landmarks.d.ts

```ts
import type { FeatureCollection, LandmarkProperties } from './types.js';
export declare const landmarks: FeatureCollection<LandmarkProperties>;
```

### dist/data/lookup.d.ts

```ts
import type { FeatureCollection, NeighborhoodProperties, NeighborhoodSource } from './types.js';
export declare const neighborhoodCollections: Readonly<Record<NeighborhoodSource, FeatureCollection<NeighborhoodProperties>>>;
export declare const neighborhoodSources: readonly NeighborhoodSource[];
/** Exact ID, canonical name, source name, or alias lookup within one definition set. */
export declare function getNeighborhood(name: string, { source }?: {
    source?: NeighborhoodSource;
}): import("./types.js").Feature<NeighborhoodProperties> | undefined;
```

### dist/data/realtor.d.ts

```ts
import type { FeatureCollection, NeighborhoodProperties } from './types.js';
export declare const neighborhoods: FeatureCollection<NeighborhoodProperties>;
/** Look up one SFAR area without importing the other neighborhood definitions. */
export declare function getRealtorNeighborhood(name: string): import("./types.js").Feature<NeighborhoodProperties> | undefined;
```

### dist/data/roads.d.ts

```ts
import type { FeatureCollection, KeyRoadProperties } from './types.js';
export declare const keyRoads: FeatureCollection<KeyRoadProperties>;
```

### dist/data/stations.d.ts

```ts
import type { PointFeatureCollection } from './types.js';
export declare const bartStations: PointFeatureCollection<{
    readonly name: string;
}>;
```

### ./dist/data/analysis.d.ts

```ts
import type { FeatureCollection, NeighborhoodProperties } from './types.js';
export declare const analysisNeighborhoods: FeatureCollection<NeighborhoodProperties>;
```

### ./dist/src/full-data.d.ts

```ts
import type { MapData } from './map.js';
/** Complete, explicit geographic preset. Import this only when all datasets are needed. */
export declare const fullMapData: MapData;
```

### ./dist/src/static-data.d.ts

```ts
import type { StaticMapData } from './static.js';
/** Complete data for static rendering, without interactive lookup collections. */
export declare const staticMapData: StaticMapData;
```

### ./dist/data/sf-find.d.ts

```ts
import type { FeatureCollection, NeighborhoodProperties } from './types.js';
export declare const sfFindNeighborhoods: FeatureCollection<NeighborhoodProperties>;
```

### ./dist/src/geometry.d.ts

```ts
import type { Geometry, Position } from '../data/types.js';
export type Project = (position: Position) => [number, number];
export declare function rawProject([longitude, latitude]: Position): [number, number];
export declare function positions(geometry: Geometry | null | undefined): Position[];
export declare function geometryPath(geometry: Geometry | null | undefined, project: Project): string;
```

### ./dist/src/guide.d.ts

```ts
export type { MapController, MapOptions } from './controller-types.js';
export type { InteractiveSFMapData } from './explorer-data.js';
export { guideMapData } from './guide-data.js';
export { loadGuideDetailedData } from './guide-detailed.js';
export { createGuideController, createGuideMap, mountGuideController, mountGuideMap, } from './guide-map.js';
export type { CameraOptions, MapFeatures, MapMarker, NeighborhoodExplorerElement as InteractiveSFMapElement, NeighborhoodExplorerOptions as InteractiveSFMapOptions, } from './types.js';
```

### dist/src/guide-data.d.ts

```ts
import type { InteractiveSFMapData } from './explorer-data.js';
/** Compact, source-aware geography with no district or alternate-neighborhood files. */
export declare const guideMapData: InteractiveSFMapData;
```

### dist/src/guide-detailed.d.ts

```ts
import type { InteractiveSFMapData } from './explorer-data.js';
/** Load detailed selected geography only after the caller requests it. */
export declare function loadGuideDetailedData(): Promise<InteractiveSFMapData>;
```

### dist/src/guide-map.d.ts

```ts
import type { MapController, MapOptions } from './controller-types.js';
import type { NeighborhoodExplorerElement as InteractiveSFMapElement, NeighborhoodExplorerOptions as InteractiveSFMapOptions } from './types.js';
/** Preferred guide API: the same grouped options, events, camera and lifecycle as createMap. */
export declare function createGuideController(options?: MapOptions): MapController;
/** Enhance a static guide shell and return its owning controller. */
export declare function mountGuideController(shell: HTMLElement, options?: MapOptions): MapController;
/** Element-based compatibility API. Prefer createGuideController for new integrations. */
export declare function createGuideMap(options?: InteractiveSFMapOptions): InteractiveSFMapElement;
/** Enhance createGuideShell() in place with a fixed compact chrome layout.
 * Keep an accessible external place list when hiding the native pickers.
 */
export declare function mountGuideMap(shell: HTMLElement, options?: InteractiveSFMapOptions): InteractiveSFMapElement;
```

### ./dist/src/transit.d.ts

```ts
import type { TransitAnimationElement } from './types.js';
/** A deliberately schematic, offline station-to-station animation. Starts paused. */
export declare function createTransitAnimation(): TransitAnimationElement;
```

### ./dist/src/guide-static.d.ts

```ts
import type { SFMapOptions } from './types.js';
/** Server-safe overview using exactly the guide browser geography. */
export declare function createGuideSVG(options?: SFMapOptions): {
    svg: string;
    project: (coordinates: import("../data/types.js").Position) => [number, number];
    viewBox: [number, number, number, number];
};
/** Stable compact frame, progressively enhanced with mountGuideMap(). No browser globals. */
export declare function createGuideShell(options?: SFMapOptions): string;
```

### ./dist/src/presets.d.ts

```ts
import type { MapOptions } from './controller-types.js';
/** Configuration only: pair with guide/data or a compatible caller-supplied dataset. */
export declare const guideOptions: Readonly<MapOptions>;
```
