@kahwee/sf-map-svg 5.1.3 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +1 -5
  3. package/dist/data/README.md +1 -1
  4. package/dist/src/cluster-renderer.d.ts +1 -1
  5. package/dist/src/district-layer.d.ts +4 -4
  6. package/dist/src/district-layer.js +1 -1
  7. package/dist/src/district-style.d.ts +3 -3
  8. package/dist/src/explorer-data.d.ts +1 -1
  9. package/dist/src/guide-map.d.ts +0 -7
  10. package/dist/src/guide-map.js +0 -33
  11. package/dist/src/guide-static.d.ts +4 -4
  12. package/dist/src/guide-static.js +14 -11
  13. package/dist/src/guide.d.ts +2 -2
  14. package/dist/src/guide.js +1 -1
  15. package/dist/src/{configuration-controller.d.ts → interactive/configuration-controller.d.ts} +7 -7
  16. package/dist/src/{configuration-controller.js → interactive/configuration-controller.js} +2 -2
  17. package/dist/src/{district-controller.d.ts → interactive/district-controller.d.ts} +12 -12
  18. package/dist/src/{district-controller.js → interactive/district-controller.js} +9 -9
  19. package/dist/src/{explorer-sidebar.d.ts → interactive/explorer-sidebar.d.ts} +6 -6
  20. package/dist/src/{explorer-sidebar.js → interactive/explorer-sidebar.js} +4 -4
  21. package/dist/src/{label-coordinator.d.ts → interactive/label-coordinator.d.ts} +7 -7
  22. package/dist/src/{label-coordinator.js → interactive/label-coordinator.js} +2 -2
  23. package/dist/src/{label-renderer.d.ts → interactive/label-renderer.d.ts} +2 -2
  24. package/dist/src/{label-renderer.js → interactive/label-renderer.js} +2 -2
  25. package/dist/src/{explorer-controls.d.ts → interactive/map-controls.d.ts} +4 -4
  26. package/dist/src/{explorer-controls.js → interactive/map-controls.js} +4 -4
  27. package/dist/src/{explorer-core.d.ts → interactive/map-engine.d.ts} +7 -7
  28. package/dist/src/{explorer-core.js → interactive/map-engine.js} +40 -40
  29. package/dist/src/{explorer-shell.d.ts → interactive/map-shell.d.ts} +2 -2
  30. package/dist/src/{explorer-shell.js → interactive/map-shell.js} +2 -2
  31. package/dist/src/{marker-controller.d.ts → interactive/marker-controller.d.ts} +8 -8
  32. package/dist/src/{marker-controller.js → interactive/marker-controller.js} +3 -3
  33. package/dist/src/{marker-layer.d.ts → interactive/marker-renderer.d.ts} +3 -3
  34. package/dist/src/{marker-layer.js → interactive/marker-renderer.js} +2 -2
  35. package/dist/src/map.js +4 -4
  36. package/dist/src/marker-catalog.d.ts +2 -2
  37. package/dist/src/palettes.d.ts +2 -2
  38. package/dist/src/{layers.d.ts → rendering/svg-layers.d.ts} +7 -7
  39. package/dist/src/{layers.js → rendering/svg-layers.js} +1 -1
  40. package/dist/src/{map-core.d.ts → rendering/svg-map-renderer.d.ts} +7 -7
  41. package/dist/src/{map-core.js → rendering/svg-map-renderer.js} +6 -6
  42. package/dist/src/static-animation.d.ts +2 -2
  43. package/dist/src/static-labels.d.ts +3 -3
  44. package/dist/src/static-labels.js +2 -2
  45. package/dist/src/static.d.ts +6 -6
  46. package/dist/src/static.js +4 -9
  47. package/dist/src/transit.js +1 -1
  48. package/dist/src/types.d.ts +13 -11
  49. package/docs/API.md +7 -11
  50. package/docs/EXAMPLES.md +2 -7
  51. package/docs/api-audit.md +3 -3
  52. package/docs/consumer-integration.md +1 -1
  53. package/docs/developer-guide.md +1 -6
  54. package/docs/migration-v6.md +57 -0
  55. package/llms-full.txt +132 -82
  56. package/llms.txt +2 -1
  57. package/package.json +2 -1
  58. /package/dist/src/{svg.d.ts → rendering/svg-primitives.d.ts} +0 -0
  59. /package/dist/src/{svg.js → rendering/svg-primitives.js} +0 -0
@@ -1,28 +1,28 @@
1
- import { copyAppearance } from './appearance.js';
2
- import { createCamera, validateCameraOptions } from './camera.js';
3
- import { createClusterLayer } from './cluster-layer.js';
4
- import { clusterPoints } from './clusters.js';
1
+ import { copyAppearance } from '../appearance.js';
2
+ import { createCamera, validateCameraOptions } from '../camera.js';
3
+ import { createClusterLayer } from '../cluster-layer.js';
4
+ import { clusterPoints } from '../clusters.js';
5
+ import { prepareDistrictStyles } from '../district-style.js';
6
+ import { element, svgElement } from '../dom.js';
7
+ import { fitBounds, projectedBounds } from '../explorer-layout.js';
8
+ import { controlKeys, layerKeys, normalizeFeatures, validateSwitchPatch } from '../features.js';
9
+ import { createFrameScheduler } from '../frame-scheduler.js';
10
+ import { createLayerTransitions } from '../layer-transition.js';
11
+ import { createMarkerCatalog } from '../marker-catalog.js';
12
+ import { createMarkerChooser } from '../marker-chooser.js';
13
+ import { createMarkerNavigation } from '../marker-navigation.js';
14
+ import { createNeighborhoodLayer, createNeighborhoodLookup, formatSourceLabel, } from '../neighborhood-layer.js';
15
+ import { createOverlayLayer } from '../overlay-layer.js';
16
+ import { createSFMapWithData, districtColors, getLayerPathsWithData, resolveMapColors, } from '../rendering/svg-map-renderer.js';
17
+ import { validateExplorerOptions } from '../validation.js';
18
+ import { fitViewport, validateViewport } from '../viewport.js';
5
19
  import { createDistrictController } from './district-controller.js';
6
- import { prepareDistrictStyles } from './district-style.js';
7
- import { element, svgElement } from './dom.js';
8
- import { createExplorerControls } from './explorer-controls.js';
9
- import { fitBounds, projectedBounds } from './explorer-layout.js';
10
- import { controlKeys, layerKeys, normalizeFeatures, validateSwitchPatch } from './features.js';
11
- import { createFrameScheduler } from './frame-scheduler.js';
12
20
  import { createLabelCoordinator } from './label-coordinator.js';
13
- import { createLayerTransitions } from './layer-transition.js';
14
- import { createSFMapWithData, districtColors, getLayerPathsWithData, resolveMapColors, } from './map-core.js';
15
- import { createMarkerCatalog } from './marker-catalog.js';
16
- import { createMarkerChooser } from './marker-chooser.js';
21
+ import { createMapControls } from './map-controls.js';
17
22
  import { createMarkerController } from './marker-controller.js';
18
- import { createMarkerLayer } from './marker-layer.js';
19
- import { createMarkerNavigation } from './marker-navigation.js';
20
- import { createNeighborhoodLayer, createNeighborhoodLookup, formatSourceLabel, } from './neighborhood-layer.js';
21
- import { createOverlayLayer } from './overlay-layer.js';
22
- import { validateExplorerOptions } from './validation.js';
23
- import { fitViewport, validateViewport } from './viewport.js';
23
+ import { createMarkerRenderer } from './marker-renderer.js';
24
24
  /** Create an offline, browser-only neighborhood explorer. Call destroy() before disposal. */
25
- export function createNeighborhoodExplorerCore(options = {}, data, ui) {
25
+ export function createMapEngine(options = {}, data, ui) {
26
26
  validateExplorerOptions(options);
27
27
  let { source = 'realtor', mode = 'neighborhoods', labels = true, neighborhood, year = 2022, theme = 'transit', colors = {}, labelStyle = {}, areaStyle = {}, districtStyle: initialDistrictStyle, motion = false, markerEntrance = false, selectedMarkerRing, clustering = false, legend: legendOptions = {}, attribution: attributionMode = 'full', northArrow = false, scaleBar = false, layerTransitions = false, districtMorph: districtMorphOption = false, interface: chrome = 'explorer', layers = {}, selectableNeighborhoods = true, labelSize = {}, fitPadding = 24, markers: initialMarkers = [], markerRadius = 6, markerHitSize = 44, markerColor = colors.marker ?? '#245b61', selectedMarkerColor = colors.selected ?? '#f04f32', onMarkerActivate, overlays: initialOverlays = [], onOverlayActivate, style: styleOptions = {}, strings = {}, controls = {}, } = options;
28
28
  if (!['neighborhoods', 'districts', 'basemap'].includes(mode))
@@ -188,7 +188,7 @@ export function createNeighborhoodExplorerCore(options = {}, data, ui) {
188
188
  reducedMotion,
189
189
  enabled,
190
190
  fitGeometry,
191
- updateComposition,
191
+ syncLayerComposition,
192
192
  scheduleLabels,
193
193
  get year() {
194
194
  return year;
@@ -229,7 +229,7 @@ export function createNeighborhoodExplorerCore(options = {}, data, ui) {
229
229
  cancel: (id) => cancelAnimationFrame(id),
230
230
  render: drawLabels,
231
231
  });
232
- const markerRenderer = createMarkerLayer();
232
+ const markerRenderer = createMarkerRenderer();
233
233
  const cancelEntrances = markerRenderer.cancelEntrances;
234
234
  const camera = createCamera({
235
235
  read: getViewport,
@@ -271,13 +271,13 @@ export function createNeighborhoodExplorerCore(options = {}, data, ui) {
271
271
  refresh: {
272
272
  selection() {
273
273
  scheduleLabels();
274
- updateResults();
275
- updateDetail();
274
+ updateNeighborhoodResults();
275
+ updateSelectionDetail();
276
276
  },
277
277
  source() {
278
- updateResults();
279
- updateDetail();
280
- updateComposition();
278
+ updateNeighborhoodResults();
279
+ updateSelectionDetail();
280
+ syncLayerComposition();
281
281
  },
282
282
  },
283
283
  moveView,
@@ -466,7 +466,7 @@ export function createNeighborhoodExplorerCore(options = {}, data, ui) {
466
466
  isDestroyed: () => destroyed,
467
467
  });
468
468
  const setOverlays = overlays.set;
469
- const mapControls = createExplorerControls(shell, {
469
+ const mapControls = createMapControls(shell, {
470
470
  svg,
471
471
  signal: controller.signal,
472
472
  strings,
@@ -483,7 +483,7 @@ export function createNeighborhoodExplorerCore(options = {}, data, ui) {
483
483
  setSource,
484
484
  selectNeighborhood,
485
485
  selectMarker,
486
- updateResults,
486
+ updateNeighborhoodResults,
487
487
  search(query) {
488
488
  const exact = getNeighborhood(query, neighborhoods.source);
489
489
  const matches = searchNeighborhoods(query, neighborhoods.source);
@@ -604,16 +604,16 @@ export function createNeighborhoodExplorerCore(options = {}, data, ui) {
604
604
  labelCoordinator.draw();
605
605
  }
606
606
  const sidebar = ui.createSidebar?.(shell, data, chrome, strings.emptyResults, searchNeighborhoods);
607
- function updateDetail() {
608
- sidebar?.updateDetail({
607
+ function updateSelectionDetail() {
608
+ sidebar?.updateSelectionDetail({
609
609
  mode,
610
610
  year,
611
611
  source: neighborhoods.source,
612
612
  selected: neighborhoods.selected,
613
613
  });
614
614
  }
615
- function updateResults() {
616
- sidebar?.updateResults({
615
+ function updateNeighborhoodResults() {
616
+ sidebar?.updateNeighborhoodResults({
617
617
  mode,
618
618
  year,
619
619
  source: neighborhoods.source,
@@ -629,9 +629,9 @@ export function createNeighborhoodExplorerCore(options = {}, data, ui) {
629
629
  validateSwitchPatch(patch, layerKeys);
630
630
  const next = { ...layers, ...patch };
631
631
  layers = next;
632
- updateComposition();
632
+ syncLayerComposition();
633
633
  }
634
- function updateComposition() {
634
+ function syncLayerComposition() {
635
635
  for (const [name, key] of [
636
636
  ['landmarks', 'landmarks'],
637
637
  ['highways', 'highways'],
@@ -715,7 +715,7 @@ export function createNeighborhoodExplorerCore(options = {}, data, ui) {
715
715
  controls = next;
716
716
  mapControls.update(controls);
717
717
  markerLabel.hidden = !markerCatalog.items.length || controls.markerPicker === false;
718
- updateComposition();
718
+ syncLayerComposition();
719
719
  }
720
720
  function setMode(next, reset = true) {
721
721
  if (destroyed)
@@ -734,8 +734,8 @@ export function createNeighborhoodExplorerCore(options = {}, data, ui) {
734
734
  const districts = mode !== 'neighborhoods';
735
735
  for (const node of [sourceLabel, searchLabel, count, results])
736
736
  node.hidden = districts;
737
- updateComposition();
738
- updateDetail();
737
+ syncLayerComposition();
738
+ updateSelectionDetail();
739
739
  status.textContent =
740
740
  mode === 'basemap'
741
741
  ? 'San Francisco basemap.'
@@ -755,7 +755,7 @@ export function createNeighborhoodExplorerCore(options = {}, data, ui) {
755
755
  labels = visible;
756
756
  mapControls.updateLabels(labels);
757
757
  root.dataset.labels = String(labels);
758
- updateComposition();
758
+ syncLayerComposition();
759
759
  if (!labels) {
760
760
  labelLayer.replaceChildren();
761
761
  root.dataset.visibleLabels = '0';
@@ -1,5 +1,5 @@
1
- import type { NeighborhoodSource } from '../data/types.js';
2
- import type { NeighborhoodExplorerOptions } from './types.js';
1
+ import type { NeighborhoodSource } from '../../data/types.js';
2
+ import type { NeighborhoodExplorerOptions } from '../types.js';
3
3
  /** Construct shared map chrome; optional explorer content has its own factory below. */
4
4
  export declare function createMapShell({ chrome, styleOptions, strings, legendOptions, colors, attributionMode, }: {
5
5
  source: NeighborhoodSource;
@@ -1,5 +1,5 @@
1
- import { element } from './dom.js';
2
- import { sourceNames } from './neighborhood-layer.js';
1
+ import { element } from '../dom.js';
2
+ import { sourceNames } from '../neighborhood-layer.js';
3
3
  let explorerCount = 0;
4
4
  /** Construct shared map chrome; optional explorer content has its own factory below. */
5
5
  export function createMapShell({ chrome, styleOptions, strings, legendOptions, colors, attributionMode, }) {
@@ -1,11 +1,11 @@
1
- import type { Position } from '../data/types.js';
2
- import { type View } from './explorer-layout.js';
3
- import type { normalizeFeatures } from './features.js';
4
- import type { createMarkerCatalog } from './marker-catalog.js';
5
- import type { createMarkerChooser } from './marker-chooser.js';
6
- import type { MarkerItem, MarkerVisual } from './marker-layer.js';
7
- import type { createMarkerNavigation } from './marker-navigation.js';
8
- import type { CameraOptions, MapMarker, NeighborhoodExplorerOptions } from './types.js';
1
+ import type { Position } from '../../data/types.js';
2
+ import { type View } from '../explorer-layout.js';
3
+ import type { normalizeFeatures } from '../features.js';
4
+ import type { createMarkerCatalog } from '../marker-catalog.js';
5
+ import type { createMarkerChooser } from '../marker-chooser.js';
6
+ import type { createMarkerNavigation } from '../marker-navigation.js';
7
+ import type { CameraOptions, MapMarker, NeighborhoodExplorerOptions } from '../types.js';
8
+ import type { MarkerItem, MarkerVisual } from './marker-renderer.js';
9
9
  interface MarkerHost {
10
10
  root: HTMLElement;
11
11
  canvas: HTMLElement;
@@ -1,6 +1,6 @@
1
- import { validateCameraOptions } from './camera.js';
2
- import { fitBounds } from './explorer-layout.js';
3
- import { fitViewport } from './viewport.js';
1
+ import { validateCameraOptions } from '../camera.js';
2
+ import { fitBounds } from '../explorer-layout.js';
3
+ import { fitViewport } from '../viewport.js';
4
4
  /** Own selection revisions, activation and focus recovery independently of marker rendering. */
5
5
  export function createMarkerController(host) {
6
6
  const { root, canvas, svg, markerLayer, markerSelect, markerLabel, markerCatalog, markerNavigation, strings, fitPadding, onMarkerActivate, invalidateClusters, drawClusters, materializeMarker, scheduleLabels, syncFeatureControls, moveView, } = host;
@@ -1,5 +1,5 @@
1
- import type { normalizeFeatures } from './features.js';
2
- import type { MapMarker } from './types.js';
1
+ import type { normalizeFeatures } from '../features.js';
2
+ import type { MapMarker } from '../types.js';
3
3
  export interface MarkerVisual {
4
4
  node: SVGGElement;
5
5
  dot: SVGCircleElement;
@@ -15,7 +15,7 @@ export interface MarkerItem {
15
15
  visual: MarkerVisual | undefined;
16
16
  }
17
17
  /** Own marker visuals and entrances; selection/events remain with the controller. */
18
- export declare function createMarkerLayer(): {
18
+ export declare function createMarkerRenderer(): {
19
19
  cancelEntrances: () => void;
20
20
  remove(item: MarkerItem): void;
21
21
  update(item: MarkerItem, marker: MapMarker, point: [number, number], index: number): void;
@@ -1,6 +1,6 @@
1
- import { svgElement } from './dom.js';
1
+ import { svgElement } from '../dom.js';
2
2
  /** Own marker visuals and entrances; selection/events remain with the controller. */
3
- export function createMarkerLayer() {
3
+ export function createMarkerRenderer() {
4
4
  const records = new Set();
5
5
  const animations = new Map();
6
6
  function cancelEntrances() {
package/dist/src/map.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { copyAppearance } from './appearance.js';
2
2
  import { validateCameraOptions } from './camera.js';
3
3
  import { configurationPatch, expandMapOptions, prepareConfiguration } from './configuration.js';
4
- import { createConfigurationController } from './configuration-controller.js';
5
- import { createNeighborhoodExplorerCore } from './explorer-core.js';
6
- import { createMapShell } from './explorer-shell.js';
7
4
  import { layerKeys } from './features.js';
5
+ import { createConfigurationController } from './interactive/configuration-controller.js';
6
+ import { createMapEngine } from './interactive/map-engine.js';
7
+ import { createMapShell } from './interactive/map-shell.js';
8
8
  import { assertOptions } from './validation.js';
9
9
  /** No geography is imported. Supply a preset or your own immutable data. */
10
10
  export function createMap(data, options = {}) {
@@ -15,7 +15,7 @@ export function createMap(data, options = {}) {
15
15
  controls: options.controls,
16
16
  appearance: options.appearance,
17
17
  });
18
- const element = createNeighborhoodExplorerCore({
18
+ const element = createMapEngine({
19
19
  mode: 'basemap',
20
20
  ...expanded,
21
21
  interface: 'map',
@@ -1,7 +1,7 @@
1
- import type { createMarkerLayer, MarkerItem } from './marker-layer.js';
1
+ import type { createMarkerRenderer, MarkerItem } from './interactive/marker-renderer.js';
2
2
  import type { MapMarker } from './types.js';
3
3
  /** Own ordered records and picker reconciliation; prepare before committing DOM changes. */
4
- export declare function createMarkerCatalog(renderer: ReturnType<typeof createMarkerLayer>, picker: HTMLSelectElement, project: (point: import('../data/types.js').Position) => [number, number]): {
4
+ export declare function createMarkerCatalog(renderer: ReturnType<typeof createMarkerRenderer>, picker: HTMLSelectElement, project: (point: import('../data/types.js').Position) => [number, number]): {
5
5
  readonly items: MarkerItem[];
6
6
  prepare(markers: readonly MapMarker[], selected: string | null): {
7
7
  selection: string | null;
@@ -1,7 +1,7 @@
1
- import type { SFMapOptions } from './types.js';
1
+ import type { SvgRenderOptions } from './types.js';
2
2
  /** Shared tokens for static SVGs, interactive maps, and consumer-owned place cards. */
3
3
  export interface MapPalette {
4
- readonly colors: NonNullable<SFMapOptions['colors']>;
4
+ readonly colors: NonNullable<SvgRenderOptions['colors']>;
5
5
  readonly haloColor: string;
6
6
  readonly markerColor: string;
7
7
  readonly selectedMarkerColor: string;
@@ -1,15 +1,15 @@
1
- import type { Geometry } from '../data/types.js';
2
- import type { DistrictRow } from './data.js';
3
- import type { Project } from './geometry.js';
4
- import type { BartStation, KeyRoad, Landmark } from './overlays.js';
5
- import type { MapMarker, SFMapOptions } from './types.js';
1
+ import type { Geometry } from '../../data/types.js';
2
+ import type { DistrictRow } from '../data.js';
3
+ import type { Project } from '../geometry.js';
4
+ import type { BartStation, KeyRoad, Landmark } from '../overlays.js';
5
+ import type { MapMarker, SvgRenderOptions } from '../types.js';
6
6
  interface LayerContext {
7
7
  labels: boolean;
8
8
  /** Animated static maps index items for staggering and measure lines for drawing. */
9
9
  animated?: boolean;
10
- theme: NonNullable<SFMapOptions['theme']>;
10
+ theme: NonNullable<SvgRenderOptions['theme']>;
11
11
  idPrefix: string;
12
- colors: Required<NonNullable<SFMapOptions['colors']>>;
12
+ colors: Required<NonNullable<SvgRenderOptions['colors']>>;
13
13
  project: Project;
14
14
  path: (geometry: Geometry | null | undefined) => string;
15
15
  }
@@ -1,4 +1,4 @@
1
- import { escapeXml, number, overlayLabel, stroke } from './svg.js';
1
+ import { escapeXml, number, overlayLabel, stroke } from './svg-primitives.js';
2
2
  /** Custom JavaScript/JSON rows must not introduce markup or CSS through numeric IDs. */
3
3
  function districtId(id) {
4
4
  if (!Number.isFinite(id))
@@ -1,7 +1,7 @@
1
- import type { Geometry, Position } from '../data/types.js';
2
- import type { DistrictYear, SFMapOptions } from './types.js';
3
- export type { DistrictYear, MapMarker, MapOverlay, SFMapOptions } from './types.js';
4
- import { type Project } from './geometry.js';
1
+ import type { Geometry, Position } from '../../data/types.js';
2
+ import type { DistrictYear, SvgRenderOptions } from '../types.js';
3
+ export type { DistrictYear, MapMarker, MapOverlay, SvgRenderOptions } from '../types.js';
4
+ import { type Project } from '../geometry.js';
5
5
  export interface SFMapData {
6
6
  coast: Geometry;
7
7
  districts?: Readonly<Partial<Record<DistrictYear, readonly DistrictRowData[]>>>;
@@ -50,7 +50,7 @@ export interface BartStationData {
50
50
  export declare const districtYears: readonly DistrictYear[];
51
51
  export declare const districtColors: readonly string[];
52
52
  /** Canonical geometry in the same fitted coordinate space used by renderMap. */
53
- export declare function getLayerPathsWithData(options: SFMapOptions, data: SFMapData, complete?: boolean): {
53
+ export declare function getLayerPathsWithData(options: SvgRenderOptions, data: SFMapData, complete?: boolean): {
54
54
  year: DistrictYear;
55
55
  viewBox: [number, number, number, number];
56
56
  project: (coordinates: Position) => [number, number];
@@ -82,7 +82,7 @@ export declare function getLayerPathsWithData(options: SFMapOptions, data: SFMap
82
82
  point: [number, number];
83
83
  }[];
84
84
  };
85
- export declare function resolveMapColors(theme: SFMapOptions['theme'], colors?: SFMapOptions['colors']): {
85
+ export declare function resolveMapColors(theme: SvgRenderOptions['theme'], colors?: SvgRenderOptions['colors']): {
86
86
  water: string;
87
87
  land: string;
88
88
  district: string;
@@ -97,7 +97,7 @@ export declare function resolveMapColors(theme: SFMapOptions['theme'], colors?:
97
97
  selected: string;
98
98
  };
99
99
  /** Make an offline SVG and the matching longitude/latitude projection. */
100
- export declare function createSFMapWithData(options: SFMapOptions, data: SFMapData, renderLabels?: (project: Project) => string): {
100
+ export declare function createSFMapWithData(options: SvgRenderOptions, data: SFMapData, renderLabels?: (project: Project) => string): {
101
101
  svg: string;
102
102
  project: (coordinates: Position) => [number, number];
103
103
  viewBox: [number, number, number, number];
@@ -1,9 +1,9 @@
1
- import { validateMarkers, validateOverlays } from './validation.js';
2
- import { prepareDistrictStyles } from './district-style.js';
3
- import { geometryPath, positions, rawProject } from './geometry.js';
4
- import * as layers from './layers.js';
5
- import { normalizeStaticAnimation, staticAnimationStyle } from './static-animation.js';
6
- import { escapeXml, stroke } from './svg.js';
1
+ import { validateMarkers, validateOverlays } from '../validation.js';
2
+ import { prepareDistrictStyles } from '../district-style.js';
3
+ import { geometryPath, positions, rawProject } from '../geometry.js';
4
+ import { normalizeStaticAnimation, staticAnimationStyle } from '../static-animation.js';
5
+ import * as layers from './svg-layers.js';
6
+ import { escapeXml, stroke } from './svg-primitives.js';
7
7
  export const districtYears = Object.freeze([2002, 2012, 2022]);
8
8
  export const districtColors = Object.freeze([
9
9
  '#c8dce5',
@@ -1,10 +1,10 @@
1
- import type { SFMapOptions } from './types.js';
1
+ import type { SvgRenderOptions } from './types.js';
2
2
  export interface StaticAnimation {
3
3
  duration: number;
4
4
  delay: number;
5
5
  }
6
6
  /** Validate `animation` and return timings, or null when animation is off. */
7
- export declare function normalizeStaticAnimation(value: SFMapOptions['animation']): StaticAnimation | null;
7
+ export declare function normalizeStaticAnimation(value: SvgRenderOptions['animation']): StaticAnimation | null;
8
8
  /**
9
9
  * Scoped, self-contained CSS for an animated static map. It runs when the SVG is
10
10
  * inserted into a page or loaded as an image, and stays still under reduced motion.
@@ -1,7 +1,7 @@
1
1
  import type { Project } from './geometry.js';
2
- import type { SFMapData } from './map-core.js';
3
- import type { SFMapOptions } from './types.js';
2
+ import type { SFMapData } from './rendering/svg-map-renderer.js';
3
+ import type { SvgRenderOptions } from './types.js';
4
4
  /** Conservative portable metrics; the browser refines these with its actual font. */
5
5
  export declare function estimateLabelWidth(text: string, fontSize: number): number;
6
6
  /** Interactive overview rules, expressed entirely in screen pixels before serialization. */
7
- export declare function staticLabels(data: SFMapData, options: SFMapOptions, project: Project): string;
7
+ export declare function staticLabels(data: SFMapData, options: SvgRenderOptions, project: Project): string;
@@ -1,7 +1,7 @@
1
1
  import { interiorAnchor, layoutLabels, projectedBounds } from './explorer-layout.js';
2
2
  import { labelCandidates, styleLabels } from './label-candidates.js';
3
3
  import { spreadLabels } from './label-spread.js';
4
- import { escapeXml, number } from './svg.js';
4
+ import { escapeXml, number } from './rendering/svg-primitives.js';
5
5
  /** Conservative portable metrics; the browser refines these with its actual font. */
6
6
  export function estimateLabelWidth(text, fontSize) {
7
7
  return Array.from(text).reduce((width, char) => width +
@@ -97,7 +97,7 @@ export function staticLabels(data, options, project) {
97
97
  : []),
98
98
  ...markers.map((item) => {
99
99
  const [x, y] = point(item.position);
100
- // Also cover the legacy SVG marker when it is larger than its interactive counterpart.
100
+ // Also cover the static SVG marker when it is larger than its interactive counterpart.
101
101
  const radius = Math.max(item.radius ?? 6, (item.radius ?? (item.selected ? 8 : 5)) / unit) + 3;
102
102
  return [x - radius, y - radius, x + radius, y + radius];
103
103
  }),
@@ -1,18 +1,18 @@
1
1
  import type { InteractiveSFMapData } from './explorer-data.js';
2
- import { type SFMapData } from './map-core.js';
3
- import type { SFMapOptions } from './types.js';
2
+ import { type SFMapData } from './rendering/svg-map-renderer.js';
3
+ import type { StaticMapOptions } from './types.js';
4
4
  /** Either compact static geography or the same source-aware geography used by createMap. */
5
5
  export type StaticMapInput = SFMapData | InteractiveSFMapData;
6
- export type { DistrictRowData, SFMapData as StaticMapData } from './map-core.js';
7
- export type { DistrictStyle, DistrictYear, SFMapOptions as StaticMapOptions, StaticMapAppearance, StaticMapLayers, } from './types.js';
6
+ export type { DistrictRowData, SFMapData as StaticMapData } from './rendering/svg-map-renderer.js';
7
+ export type { DistrictStyle, DistrictYear, StaticMapAppearance, StaticMapLayers, StaticMapOptions, } from './types.js';
8
8
  /** Server-safe SVG rendering with explicit data; returns SVG plus projection helpers. */
9
- export declare function renderMap(data: StaticMapInput, options?: SFMapOptions): {
9
+ export declare function renderMap(data: StaticMapInput, options?: StaticMapOptions): {
10
10
  svg: string;
11
11
  project: (coordinates: import("./geometry.js").Position) => [number, number];
12
12
  viewBox: [number, number, number, number];
13
13
  };
14
14
  /** Project canonical layer geometry without constructing an SVG string. */
15
- export declare function getLayerPaths(data: StaticMapInput, options?: SFMapOptions): {
15
+ export declare function getLayerPaths(data: StaticMapInput, options?: StaticMapOptions): {
16
16
  year: import("./types.js").DistrictYear;
17
17
  viewBox: [number, number, number, number];
18
18
  project: (coordinates: import("./geometry.js").Position) => [number, number];
@@ -1,6 +1,6 @@
1
1
  import { prepareAppearance } from './appearance.js';
2
2
  import { validateSwitchPatch } from './features.js';
3
- import { createSFMapWithData, getLayerPathsWithData } from './map-core.js';
3
+ import { createSFMapWithData, getLayerPathsWithData, } from './rendering/svg-map-renderer.js';
4
4
  import { normalizeStaticAnimation } from './static-animation.js';
5
5
  import { staticLabels } from './static-labels.js';
6
6
  import { assertOptions, validateMarkers, validateOverlays } from './validation.js';
@@ -55,15 +55,11 @@ const staticLayerKeys = [
55
55
  'keyRoads',
56
56
  'roadLabels',
57
57
  ];
58
- /** Grouped keys override flat compatibility keys; omitted keys preserve renderer defaults. */
58
+ /** Validate the public grouped contract before preparing internal renderer options. */
59
59
  function expandStaticOptions(options) {
60
60
  assertOptions(options, 'static map', [
61
- ...staticLayerKeys,
62
61
  'layers',
63
62
  'appearance',
64
- 'theme',
65
- 'colors',
66
- 'districtStyle',
67
63
  'width',
68
64
  'height',
69
65
  'padding',
@@ -77,7 +73,7 @@ function expandStaticOptions(options) {
77
73
  'idPrefix',
78
74
  'animation',
79
75
  ]);
80
- for (const key of [...staticLayerKeys, 'labels'])
76
+ for (const key of ['labels'])
81
77
  if (options[key] !== undefined && typeof options[key] !== 'boolean')
82
78
  throw new TypeError(`${key} must be boolean.`);
83
79
  for (const key of ['title', 'idPrefix'])
@@ -117,11 +113,10 @@ function expandStaticOptions(options) {
117
113
  if (options.appearance !== undefined) {
118
114
  assertOptions(options.appearance, 'static appearance', ['theme', 'colors', 'districtStyle']);
119
115
  }
120
- const { layers, appearance, theme, colors, districtStyle, ...flat } = options;
116
+ const { layers, appearance, ...flat } = options;
121
117
  const supplied = (group = {}) => Object.fromEntries(Object.entries(group).filter(([, value]) => value !== undefined));
122
118
  return {
123
119
  ...flat,
124
- ...prepareAppearance({}, { theme, colors, districtStyle }),
125
120
  ...supplied(layers),
126
121
  ...supplied(appearance === undefined ? undefined : prepareAppearance({}, appearance)),
127
122
  };
@@ -2,7 +2,7 @@ import stations from '../data/bart-stations.json' with { type: 'json' };
2
2
  import coast from '../data/coast.json' with { type: 'json' };
3
3
  import districts from '../data/districts-2022.json' with { type: 'json' };
4
4
  import parks from '../data/landmarks.json' with { type: 'json' };
5
- import { createSFMapWithData } from './map-core.js';
5
+ import { createSFMapWithData } from './rendering/svg-map-renderer.js';
6
6
  const districtData = districts;
7
7
  const parkData = parks;
8
8
  const stationData = stations;
@@ -11,7 +11,7 @@ export type DistrictYear = 2002 | 2012 | 2022;
11
11
  export interface DistrictSelection {
12
12
  id: number;
13
13
  year: DistrictYear;
14
- district: import('./map-core.js').DistrictRowData;
14
+ district: import('./rendering/svg-map-renderer.js').DistrictRowData;
15
15
  }
16
16
  export interface DistrictStyle {
17
17
  fill?: string;
@@ -39,10 +39,7 @@ export interface MapOverlay {
39
39
  visible?: boolean;
40
40
  label?: string;
41
41
  }
42
- export interface SFMapOptions {
43
- /** Grouped presentation; supplied keys take precedence over flat compatibility options. */
44
- layers?: StaticMapLayers;
45
- appearance?: StaticMapAppearance;
42
+ export interface SvgRenderOptions {
46
43
  theme?: 'districts' | 'transit';
47
44
  width?: number;
48
45
  height?: number;
@@ -56,7 +53,7 @@ export interface SFMapOptions {
56
53
  neighborhoodLabels?: boolean;
57
54
  districtFills?: boolean;
58
55
  /** Evaluated once per district at construction/style/year updates; call setDistrictStyle again when external data changes. */
59
- districtStyle?: (district: import('./map-core.js').DistrictRowData) => DistrictStyle;
56
+ districtStyle?: (district: import('./rendering/svg-map-renderer.js').DistrictRowData) => DistrictStyle;
60
57
  districtLabels?: boolean;
61
58
  /** Hide all visible text labels while retaining geographic symbols and accessible titles. */
62
59
  labels?: boolean;
@@ -96,7 +93,12 @@ export interface SFMapOptions {
96
93
  }
97
94
  /** Presentation supported by both the static and interactive renderers. */
98
95
  export type StaticMapLayers = InteractiveLayers;
99
- export type StaticMapAppearance = Pick<SFMapOptions, 'theme' | 'colors' | 'districtStyle'>;
96
+ export type StaticMapAppearance = Pick<SvgRenderOptions, 'theme' | 'colors' | 'districtStyle'>;
97
+ /** Public static presentation uses the same grouped vocabulary as the map controller. */
98
+ export type StaticMapOptions = Omit<SvgRenderOptions, keyof StaticMapLayers | keyof StaticMapAppearance> & {
99
+ layers?: StaticMapLayers;
100
+ appearance?: StaticMapAppearance;
101
+ };
100
102
  /** Serializable viewport in the interactive map's fixed 800 × 800 projected space. */
101
103
  export type MapViewport = readonly [x: number, y: number, size: number];
102
104
  export type ExplorerMode = 'neighborhoods' | 'districts' | 'basemap';
@@ -166,8 +168,8 @@ export interface NeighborhoodExplorerOptions extends MapFeatures {
166
168
  source?: NeighborhoodSource;
167
169
  neighborhood?: string;
168
170
  year?: DistrictYear;
169
- theme?: SFMapOptions['theme'];
170
- colors?: SFMapOptions['colors'];
171
+ theme?: SvgRenderOptions['theme'];
172
+ colors?: SvgRenderOptions['colors'];
171
173
  labelStyle?: {
172
174
  fontFamily?: string;
173
175
  fontWeight?: number;
@@ -179,7 +181,7 @@ export interface NeighborhoodExplorerOptions extends MapFeatures {
179
181
  hoverFill?: string;
180
182
  hoverStroke?: string;
181
183
  };
182
- districtStyle?: SFMapOptions['districtStyle'];
184
+ districtStyle?: SvgRenderOptions['districtStyle'];
183
185
  legend?: {
184
186
  builtins?: boolean;
185
187
  hidden?: readonly ('bart' | 'park' | 'highway' | 'road')[];
@@ -245,7 +247,7 @@ export interface NeighborhoodExplorerElement extends HTMLElement {
245
247
  } & CameraOptions): boolean;
246
248
  getSelectedDistrict(): DistrictSelection | null;
247
249
  setDistrictYear(year: DistrictYear, options?: CameraOptions): void;
248
- setDistrictStyle(style: SFMapOptions['districtStyle']): void;
250
+ setDistrictStyle(style: SvgRenderOptions['districtStyle']): void;
249
251
  setSource(source: NeighborhoodSource): void;
250
252
  setMode(mode: ExplorerMode): void;
251
253
  setLabels(visible: boolean): void;