@kahwee/sf-map-svg 4.5.0 → 5.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.
- package/CHANGELOG.md +11 -0
- package/README.md +32 -0
- package/dist/data/README.md +7 -0
- package/dist/src/cards.js +1 -1
- package/dist/src/configuration.d.ts +2 -0
- package/dist/src/configuration.js +30 -0
- package/dist/src/controller-types.d.ts +11 -15
- package/dist/src/explorer-core.js +10 -5
- package/dist/src/geometry.d.ts +6 -1
- package/dist/src/geometry.js +80 -0
- package/dist/src/guide-static.d.ts +1 -1
- package/dist/src/layers.js +1 -1
- package/dist/src/map.d.ts +1 -1
- package/dist/src/map.js +22 -12
- package/dist/src/marker-catalog.d.ts +1 -1
- package/dist/src/marker-catalog.js +11 -5
- package/dist/src/static-labels.js +3 -3
- package/dist/src/static.d.ts +2 -2
- package/dist/src/types.d.ts +12 -9
- package/dist/src/validation.js +7 -5
- package/docs/API.md +43 -5
- package/docs/EXAMPLES.md +29 -0
- package/docs/api-audit.md +22 -5
- package/docs/consumer-integration.md +47 -1
- package/docs/developer-guide.md +29 -7
- package/docs/guide-bundle-report.md +4 -4
- package/docs/migration-v5.md +100 -0
- package/llms-full.txt +339 -52
- package/llms.txt +2 -1
- package/package.json +2 -1
package/docs/API.md
CHANGED
|
@@ -4,6 +4,41 @@ Start with the [static and interactive examples](../README.md). Exact option
|
|
|
4
4
|
and event types are exported by [src/api.ts](../src/api.ts) and defined in
|
|
5
5
|
[src/types.ts](../src/types.ts) and [src/controller-types.ts](../src/controller-types.ts).
|
|
6
6
|
|
|
7
|
+
## Geographic queries
|
|
8
|
+
|
|
9
|
+
New in v5. See the [migration guide](migration-v5.md) for upgrade steps.
|
|
10
|
+
|
|
11
|
+
The data-free `@kahwee/sf-map-svg/geometry` entry exports `geometryBounds(geometry)`
|
|
12
|
+
and `geometryContainsPoint(geometry, [longitude, latitude])`. They work offline
|
|
13
|
+
with the same readonly GeoJSON geometries used by the renderer, without a DOM.
|
|
14
|
+
The same entry exports the `Geometry`, `Position`, and `Bounds` types, so geometry
|
|
15
|
+
consumers need no second import path for their function input and result types.
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { geometryBounds, geometryContainsPoint } from '@kahwee/sf-map-svg/geometry';
|
|
19
|
+
import { neighborhoods } from '@kahwee/sf-map-svg/data/realtor';
|
|
20
|
+
|
|
21
|
+
const location = [-122.42, 37.76] as const;
|
|
22
|
+
const matches = neighborhoods.features.filter((feature) =>
|
|
23
|
+
geometryContainsPoint(feature.geometry, location),
|
|
24
|
+
);
|
|
25
|
+
const bounds = matches[0] ? geometryBounds(matches[0].geometry) : null;
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Bounds are `[west, south, east, north]` in degrees, computed from coordinates,
|
|
29
|
+
not cached feature metadata. Empty or absent geometry returns `null`.
|
|
30
|
+
Hit testing includes Polygon, MultiPolygon, and polygon members of nested
|
|
31
|
+
GeometryCollections. Hole interiors are excluded; outer and hole boundaries
|
|
32
|
+
are included, so a shared boundary may match multiple features. Ring winding
|
|
33
|
+
does not matter; open rings are closed implicitly. Other geometry types do
|
|
34
|
+
not produce polygon hits. Empty or absent geometry returns `false`.
|
|
35
|
+
|
|
36
|
+
Both helpers reject nonfinite or out-of-range longitude/latitude with
|
|
37
|
+
`RangeError`; altitude is ignored. They assume valid planar GeoJSON topology
|
|
38
|
+
and straight segments in longitude/latitude, suitable for these SF datasets.
|
|
39
|
+
They do not repair polygons, apply a boundary tolerance, or unwrap antimeridian
|
|
40
|
+
crossings. A bounds match alone does not establish polygon containment.
|
|
41
|
+
|
|
7
42
|
## Static maps
|
|
8
43
|
|
|
9
44
|
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.
|
|
@@ -85,16 +120,16 @@ District style callbacks are evaluated once per district at construction and on
|
|
|
85
120
|
|
|
86
121
|
## Live configuration and inspection
|
|
87
122
|
|
|
88
|
-
`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
|
|
123
|
+
`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`, `setMode(mode)`, and `setSource(source)` preserve the camera. Use `map.camera.reset()` or `map.camera.fit(geometry)` when movement is intended. The former `{ resetView }` setter argument is removed in v5. 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.
|
|
89
124
|
|
|
90
|
-
|
|
125
|
+
Controller patches merge nested token and feature records. Omitted and `undefined` values retain current values at every depth; `false` disables boolean switches. Use `resetConfiguration(groups?)` to remove presentation overrides explicitly. The supported groups are `features`, `layers`, `controls`, and `appearance`; omitting the argument resets all four. Resets restore built-in defaults, preserve mode/source/year/labels and camera, and do not restore constructor overrides. Unknown groups fail before changing state. Static construction options and low-level element methods retain their separately documented contracts.
|
|
91
126
|
|
|
92
127
|
```ts
|
|
93
128
|
map.configure({
|
|
94
129
|
appearance: { colors: { water: '#142d45' }, labelStyle: { fontFamily: 'Georgia,serif' } },
|
|
95
130
|
layers: { bartStations: false },
|
|
96
131
|
});
|
|
97
|
-
map.
|
|
132
|
+
map.resetConfiguration(['appearance']); // restore built-in defaults
|
|
98
133
|
|
|
99
134
|
const overrides = map.getConfiguration();
|
|
100
135
|
const effective = map.getResolvedConfiguration();
|
|
@@ -126,11 +161,14 @@ map.selectFeature({ kind: 'neighborhood', source: 'realtor', id: 'inner-mission'
|
|
|
126
161
|
map.selectFeature({ kind: 'district', year: 2022, id: 3 });
|
|
127
162
|
```
|
|
128
163
|
|
|
129
|
-
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.
|
|
164
|
+
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. The `selectMarker`, `selectNeighborhood`, and `selectDistrict` shortcuts select exact IDs in the current geography. Controller `selectNeighborhood` no longer resolves display names or aliases; resolve those with a source-specific data lookup first.
|
|
130
165
|
|
|
131
166
|
## Coordinates and units
|
|
132
167
|
|
|
133
|
-
|
|
168
|
+
Version 5 uses tuple inputs and exports the named `ScreenPoint` /
|
|
169
|
+
`ProjectToScreen` types. The former two-number call is removed.
|
|
170
|
+
|
|
171
|
+
GeoJSON and static projection inputs use `[longitude, latitude]` in WGS84; markers use `{ position: [longitude, latitude] }`. Static `project` returns SVG units. `projectToScreen([longitude, latitude])` accepts the same readonly GeoJSON position as static `project` and returns `ScreenPoint` (`{ x, y, visible }`) in CSS pixels relative to the mounted canvas. Altitude is ignored. Mount the map before calling; invalid Mercator coordinates throw, and calls after controller destruction throw. `visible` indicates viewport inclusion, not whether another element covers the point. Camera viewports use the fixed 800 × 800 projected map space, independent of canvas size. Pan deltas use screen pixels, converted using the mounted canvas width. Marker radius uses screen pixels in browser maps and SVG units in static output.
|
|
134
172
|
|
|
135
173
|
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.
|
|
136
174
|
|
package/docs/EXAMPLES.md
CHANGED
|
@@ -105,3 +105,32 @@ API** includes executable motion, reduced-motion, and progressive-shell checks.
|
|
|
105
105
|
See [consumer integration](consumer-integration.md) for server and browser recipes.
|
|
106
106
|
|
|
107
107
|
For the controller and explicit data imports, see [the v3 migration guide](migration-v3.md).
|
|
108
|
+
|
|
109
|
+
## Find areas containing a location
|
|
110
|
+
|
|
111
|
+
New in v5. This example runs in
|
|
112
|
+
Node or a browser without mounting a map and imports only the SFAR definition:
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
import { geometryBounds, geometryContainsPoint } from '@kahwee/sf-map-svg/geometry';
|
|
116
|
+
import { neighborhoods } from '@kahwee/sf-map-svg/data/realtor';
|
|
117
|
+
|
|
118
|
+
const location = [-122.42, 37.76] as const;
|
|
119
|
+
const matches = neighborhoods.features.filter((feature) =>
|
|
120
|
+
geometryContainsPoint(feature.geometry, location),
|
|
121
|
+
);
|
|
122
|
+
for (const feature of matches) {
|
|
123
|
+
console.log(feature.id, feature.properties.canonicalName, geometryBounds(feature.geometry));
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Keep all matches when the location lies on a shared boundary. An empty result
|
|
128
|
+
means no supplied polygon contains the location; it does not trigger a switch
|
|
129
|
+
to another neighborhood definition. For an existing map using the same realtor
|
|
130
|
+
source, pass a chosen match to
|
|
131
|
+
`map.selectFeature({ kind: 'neighborhood', source: 'realtor', id: feature.id })`.
|
|
132
|
+
To fit it, use `map.camera.fit(feature.geometry)`. Geographic bounds cannot be
|
|
133
|
+
passed directly to `camera.set`, which takes projected `[x, y, size]`.
|
|
134
|
+
|
|
135
|
+
See [query semantics and limitations](API.md#geographic-queries) and the
|
|
136
|
+
[HTML overlay recipe](consumer-integration.md#position-html-overlays).
|
package/docs/api-audit.md
CHANGED
|
@@ -20,8 +20,8 @@ unbounded geographic data can never fail.
|
|
|
20
20
|
| Viewport marker rendering | Individual pin visuals allocate on first visibility or selection; offscreen pins and cluster symbols detach with screen-space overscan. Uncreated records receive current labels/styles on first materialization. Full catalog/picker and cached node identities remain available; selected/focused pins stay mounted. Pan reuses global cluster membership and nodes until zoom, data, selection, focus, or clustering changes. |
|
|
21
21
|
| Marker entrance and reduced motion | Entrances begin on first visibility, are independent of camera motion, and cancel on preference changes or disposal, including pending entrances. Cached pins do not restart entrances on reentry. Stable IDs avoid repeated entrances on filtering. |
|
|
22
22
|
| Hiding touch control while gestures are enabled | Returns touch scrolling to the page. Other toolbar controls remain independent. |
|
|
23
|
-
| Convenience mode/source setters | Share the atomic configuration path.
|
|
24
|
-
| Runtime patch omitted/false/undefined |
|
|
23
|
+
| Convenience mode/source setters | Share the atomic configuration path. Setters preserve the camera; explicit camera methods move it. Unavailable geography leaves state unchanged. |
|
|
24
|
+
| Runtime patch omitted/false/undefined | Controller patches ignore omitted/undefined values at every record depth; false disables boolean switches. Nested feature records merge. Explicit resetConfiguration clears presentation groups. Low-level element setters retain their separate contract. |
|
|
25
25
|
| Compact shell and full attribution | Rejected before replacement. Use `createGuideController` for full attribution. Control overrides are honored and may deliberately alter layout. |
|
|
26
26
|
| District style callbacks | Prepare and copy every style before committing. Hover/selection reuse the results; call `setDistrictStyle()` to refresh changed external data. Reentrant updates or destruction supersede pending work. |
|
|
27
27
|
| Rapid district year changes | Both outgoing layers are tracked, inert, and removed on interruption, completion, reduced-motion changes, or destruction. Zero duration creates no transition copies. |
|
|
@@ -58,15 +58,32 @@ exceptions are not transactional validation errors and do not roll back an alrea
|
|
|
58
58
|
committed change. Direct mutation of the returned DOM can invalidate invariants;
|
|
59
59
|
use the public methods and `overlayElement` extension point.
|
|
60
60
|
|
|
61
|
-
Static
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
Static flat and grouped options reject unknown keys and invalid values before
|
|
62
|
+
rendering. Grouped options take precedence over flat compatibility options;
|
|
63
|
+
static and interactive schemas retain their documented differences. Host layout, delayed images, fonts, and application cards need their
|
|
64
64
|
own CLS tests. The scale bar is an approximate local Mercator scale, not survey
|
|
65
65
|
instrumentation. Coincident locations use a place chooser rather than spiderfying. Geographic
|
|
66
66
|
source correctness remains governed by SOURCES.md and existing topology tests.
|
|
67
67
|
|
|
68
68
|
## Extending the contract
|
|
69
69
|
|
|
70
|
+
### V5 consistency contract
|
|
71
|
+
|
|
72
|
+
- Geometry helpers export their input/result types alongside functions and use
|
|
73
|
+
geometry-first arguments with readonly GeoJSON positions.
|
|
74
|
+
- Marker positions, static projection, and screen projection all use
|
|
75
|
+
`[longitude, latitude]`. Screen projection returns CSS pixels; camera viewports
|
|
76
|
+
remain projected map units and pan deltas remain CSS pixels.
|
|
77
|
+
- Controller `configure`, `setMode`, and `setSource` preserve the camera. Camera
|
|
78
|
+
movement is explicit through camera methods or selection's `fit` option.
|
|
79
|
+
- Controller patches ignore omitted/undefined values at every depth and merge
|
|
80
|
+
nested records. `resetConfiguration` clears named presentation groups to
|
|
81
|
+
built-in defaults without changing geographic choices or camera.
|
|
82
|
+
- Controller neighborhood selection requires exact IDs. Prefer source/year-qualified
|
|
83
|
+
`selectFeature`; name lookup belongs in source-specific dataset helpers.
|
|
84
|
+
- Construction-time neighborhood lookup and low-level element APIs retain their
|
|
85
|
+
separately documented contracts. See [v5 migration](migration-v5.md).
|
|
86
|
+
|
|
70
87
|
For every new option, define its default, disable/reset semantics, precedence,
|
|
71
88
|
validation boundary, cleanup owner, and effect on keyboard focus and selection.
|
|
72
89
|
Test invalid updates against an existing live map, not only clean construction.
|
|
@@ -35,7 +35,7 @@ For a fixed-width embed, use shared inputs on the server and in the browser:
|
|
|
35
35
|
```ts
|
|
36
36
|
// Shared inputs: coordinates, IDs, radii, colors and layer choices must match.
|
|
37
37
|
const guideMarkers = [
|
|
38
|
-
{ id: 'ferry', label: 'Ferry Building',
|
|
38
|
+
{ id: 'ferry', label: 'Ferry Building', position: [-122.3937, 37.7955] as const, radius: 7 },
|
|
39
39
|
];
|
|
40
40
|
const guideLayers = { neighborhoodLabels: true, roadLabels: true };
|
|
41
41
|
const guideColors = { label: '#183e58', landmark: '#426641' };
|
|
@@ -125,3 +125,49 @@ const binding = bindMapCards(map, document.querySelector('#place-cards'), {
|
|
|
125
125
|
Style `[data-map-selected]` with an outline or background change that remains legible in both themes. Use a visible focus outline on card buttons and links. Hover/focus only previews the location: it preserves selection and camera. Map selection reveals the card without shifting keyboard focus; set `scrollOnSelect: false` if your application owns scrolling. Match cards and markers by stable IDs, and keep hidden/filtered places out of both catalogs when that is your intended filter behavior.
|
|
126
126
|
|
|
127
127
|
Run `pnpm demo` and open `examples/generated/linked-cards.html` for the complete pattern. Storybook **Maps / Linked place cards** checks hover, keyboard selection, card reveal, both palettes, and disposal.
|
|
128
|
+
|
|
129
|
+
## Position HTML overlays
|
|
130
|
+
|
|
131
|
+
In v5, pass the same readonly `[longitude, latitude]` position used by static
|
|
132
|
+
projection. The former two-number call is removed; see the
|
|
133
|
+
[migration guide](migration-v5.md).
|
|
134
|
+
|
|
135
|
+
Call this helper after mounting the map in a container with nonzero width:
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
import type { MapController } from '@kahwee/sf-map-svg';
|
|
139
|
+
import type { Position } from '@kahwee/sf-map-svg/geometry';
|
|
140
|
+
|
|
141
|
+
function attachCallout(map: MapController, location: Position, label: string) {
|
|
142
|
+
const callout = document.createElement('span');
|
|
143
|
+
callout.textContent = label;
|
|
144
|
+
callout.style.cssText = 'position:absolute;pointer-events:none;transform:translate(-50%,-100%)';
|
|
145
|
+
const update = () => {
|
|
146
|
+
const point = map.projectToScreen(location);
|
|
147
|
+
callout.hidden = !point.visible;
|
|
148
|
+
callout.style.left = `${point.x}px`;
|
|
149
|
+
callout.style.top = `${point.y}px`;
|
|
150
|
+
};
|
|
151
|
+
update();
|
|
152
|
+
map.overlayElement.append(callout);
|
|
153
|
+
const stopViewport = map.on('viewportchange', update);
|
|
154
|
+
const stopResize = map.on('mapresize', update);
|
|
155
|
+
return () => {
|
|
156
|
+
stopViewport();
|
|
157
|
+
stopResize();
|
|
158
|
+
callout.remove();
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Use `const detach = attachCallout(map, [-122.3937, 37.7955], 'Ferry Building')`
|
|
164
|
+
with your mounted controller. Call `detach()` before `map.destroy()` on unmount.
|
|
165
|
+
Keep the container visible while projecting; an unmounted or zero-width canvas
|
|
166
|
+
throws. Controller calls after destruction also throw. Camera moves and resize
|
|
167
|
+
both require repositioning; a one-time projection becomes stale.
|
|
168
|
+
|
|
169
|
+
`x` and `y` are CSS pixels relative to `overlayElement`, not page coordinates or
|
|
170
|
+
SVG units. `visible` tests inclusion in the map viewport; it does not account
|
|
171
|
+
for occlusion by other content. This noninteractive label uses `textContent`
|
|
172
|
+
and leaves pointer input to the map. Interactive callouts need their own
|
|
173
|
+
keyboard, focus, and dismissal behavior. See [coordinates and units](API.md#coordinates-and-units).
|
package/docs/developer-guide.md
CHANGED
|
@@ -10,6 +10,7 @@ Choose the renderer and geography separately. `renderMap` produces a string with
|
|
|
10
10
|
| Explore districts or several neighborhood definitions | `/map`: `createMap` | `/data/full`: `fullMapData` |
|
|
11
11
|
| Embed a smaller neighborhood guide | `/guide/map`: `createGuideController` | Included lightweight guide geography |
|
|
12
12
|
| Use the guide data with the general controller | `/map`: `createMap` | `/guide/data`: `guideMapData` |
|
|
13
|
+
| Query bounds or polygon containment without a DOM | `/geometry` | Caller-supplied geometry or explicit dataset imports |
|
|
13
14
|
| Look up names without mounting a map | `/data/lookup` | Source-aware lookup helpers |
|
|
14
15
|
|
|
15
16
|
Imports starting with `/` in this table are package suffixes, such as `@kahwee/sf-map-svg/static`. Node 24+ is required for server rendering. Browser examples need a DOM and a bundler that supports JSON modules. This is an ESM package.
|
|
@@ -96,22 +97,43 @@ SFAR realtor neighborhoods are the default when supplied. If only an alternative
|
|
|
96
97
|
| Camera | `map.camera.set/pan/zoom/fit/reset(...)` |
|
|
97
98
|
| Palette, typography, control styling | `map.configure({ appearance })`; retains camera, selection and focus |
|
|
98
99
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
`
|
|
100
|
+
Controller patches merge nested token and feature records. Omitted and `undefined`
|
|
101
|
+
values retain existing values. Use `false` to disable a boolean switch and
|
|
102
|
+
`resetConfiguration()` to clear presentation overrides explicitly:
|
|
102
103
|
|
|
103
104
|
```ts
|
|
104
105
|
map.configure({ layers: { neighborhoodLabels: false } });
|
|
105
|
-
map.configure({ layers: { neighborhoodLabels: undefined } }); // follow mode again
|
|
106
106
|
map.configure({ features: { motion: false } });
|
|
107
|
-
map.
|
|
107
|
+
map.resetConfiguration(['layers']); // follow mode-derived defaults again
|
|
108
|
+
map.resetConfiguration(['appearance']); // built-in palette and typography
|
|
108
109
|
```
|
|
109
110
|
|
|
110
|
-
|
|
111
|
+
Reset groups are `features`, `layers`, `controls`, and `appearance`; omitting
|
|
112
|
+
the argument resets all four. Resets preserve geographic choices and camera,
|
|
113
|
+
and do not restore constructor overrides. Static maps accept grouped layers and
|
|
114
|
+
the shared appearance keys (`theme`, `colors`, `districtStyle`) at construction.
|
|
115
|
+
See [the API contract](API.md#live-configuration-and-inspection).
|
|
116
|
+
|
|
117
|
+
`getConfiguration()` returns a detached snapshot of geographic choices and
|
|
118
|
+
presentation overrides. Use `getResolvedConfiguration()` for effective layers
|
|
119
|
+
and `getCapabilities()` for supplied geography. Invalid patches preserve state.
|
|
120
|
+
`setMode`, `setSource`, and `configure` all preserve the camera; request movement
|
|
121
|
+
with `camera.reset()` or `camera.fit(geometry)`. See the
|
|
122
|
+
[v5 migration guide](migration-v5.md) for changed call forms.
|
|
123
|
+
|
|
124
|
+
## Geographic queries
|
|
125
|
+
|
|
126
|
+
The v5 `/geometry` helpers `geometryBounds(geometry)` and
|
|
127
|
+
`geometryContainsPoint(geometry, position)` work without a DOM or bundled
|
|
128
|
+
geography. Import `Bounds`, `Geometry`, and `Position` types from the same entry.
|
|
129
|
+
Use containment for area lookup and bounds for geographic extents; pass the
|
|
130
|
+
original geometry to `camera.fit`, rather than geographic bounds to `camera.set`.
|
|
131
|
+
See the [worked example](EXAMPLES.md#find-areas-containing-a-location) and
|
|
132
|
+
[full query semantics](API.md#geographic-queries).
|
|
111
133
|
|
|
112
134
|
## Markers, routes and camera coordinates
|
|
113
135
|
|
|
114
|
-
|
|
136
|
+
Markers use `position: [longitude, latitude]`. GeoJSON coordinates and the static projection use `[longitude, latitude]`, in WGS84. Camera viewports use `[x, y, size]` in the fixed 800 × 800 projected map space; they are not geographic bounds. `camera.pan` takes deltas in CSS pixels and converts them using the mounted canvas width. `projectToScreen([lng, lat])` returns CSS pixels relative to the map canvas, plus viewport visibility. The former two-number call is removed in v5.
|
|
115
137
|
|
|
116
138
|
Marker and overlay IDs must be unique. Keep marker IDs stable across updates so retained markers preserve DOM nodes, focus and entrance animations. Use `null` to clear a selection; selection methods return a boolean. Selection events can contain null values. `map.on()` returns an unsubscribe function.
|
|
117
139
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Guide bundle size report
|
|
2
2
|
|
|
3
|
-
Generated 2026-10-
|
|
3
|
+
Generated 2026-10-20 by `pnpm report:guide` with Vite production minification and gzip compression. Each emitted JS chunk is compressed independently. The report measures the explicit-data root and the optional `@kahwee/sf-map-svg/guide` preset.
|
|
4
4
|
|
|
5
5
|
| Entry | Initial JS, raw | Initial JS, gzip | Explicit detail JS, gzip |
|
|
6
6
|
| --- | ---: | ---: | ---: |
|
|
7
|
-
| Renderer root (explicit data) |
|
|
8
|
-
| Static renderer | 33.
|
|
9
|
-
| Guide preset |
|
|
7
|
+
| Renderer root (explicit data) | 124.2 KB | 36.5 KB | — |
|
|
8
|
+
| Static renderer | 33.4 KB | 10.3 KB | — |
|
|
9
|
+
| Guide preset | 508.4 KB | 122.3 KB | 467.0 KB |
|
|
10
10
|
|
|
11
11
|
**500 KB target:** met.
|
|
12
12
|
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Migrating to v5
|
|
2
|
+
|
|
3
|
+
Version 5.0.0 makes coordinate inputs, controller updates, and reset behavior
|
|
4
|
+
consistent. It requires Node 24+ and retains offline rendering, explicit data
|
|
5
|
+
imports, zero runtime dependencies, and the existing default map appearance.
|
|
6
|
+
The version bump prepares the next release; it does not publish the package.
|
|
7
|
+
|
|
8
|
+
## Coordinates use GeoJSON positions
|
|
9
|
+
|
|
10
|
+
Markers now require `position: [longitude, latitude]`. The old `lng` and `lat`
|
|
11
|
+
properties are rejected. `projectToScreen` takes one readonly position; its
|
|
12
|
+
old two-number form is removed. Altitude is accepted and ignored by projection.
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
// v4
|
|
16
|
+
const oldMarker = { id: 'ferry', lng: -122.3937, lat: 37.7955 };
|
|
17
|
+
// v5
|
|
18
|
+
const marker = { id: 'ferry', position: [-122.3937, 37.7955] as const };
|
|
19
|
+
map.setMarkers([marker]);
|
|
20
|
+
const point = map.projectToScreen(marker.position);
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Use `Position` from `/geometry` for reusable positions and `ScreenPoint` or
|
|
24
|
+
`ProjectToScreen` from the root or `/map` for screen-projection contracts.
|
|
25
|
+
`x` and `y` remain canvas-relative CSS pixels; `visible` means inside the
|
|
26
|
+
viewport, not unobscured. Mount the map in a nonzero-width container first.
|
|
27
|
+
Camera viewports still use projected `[x, y, size]`; camera pan deltas are CSS
|
|
28
|
+
pixels. Geographic bounds are not camera viewports.
|
|
29
|
+
|
|
30
|
+
## Controller updates preserve the camera
|
|
31
|
+
|
|
32
|
+
`configure`, `setMode`, and `setSource` preserve the camera. The second
|
|
33
|
+
`{ resetView }` argument to the controller convenience setters is removed.
|
|
34
|
+
To reproduce an old automatic reset, request it explicitly:
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
map.setMode('neighborhoods');
|
|
38
|
+
map.camera.reset({ animate: false });
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Selection still fits by default. Use `{ fit: false }` to change selection
|
|
42
|
+
without moving the camera, or `camera.fit(geometry)` for an explicit fit.
|
|
43
|
+
|
|
44
|
+
## Reset presentation explicitly
|
|
45
|
+
|
|
46
|
+
In controller `configure` patches, omitted and `undefined` values are no-ops at
|
|
47
|
+
every record depth. Nested token and feature records merge; an explicit boolean
|
|
48
|
+
replaces a feature record and `false` disables a boolean switch.
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
map.configure({ appearance: { colors: { water: '#142d45' } } });
|
|
52
|
+
map.configure({ appearance: undefined }); // keeps the current appearance
|
|
53
|
+
map.resetConfiguration(['appearance']); // restores built-in appearance defaults
|
|
54
|
+
map.resetConfiguration(['layers', 'controls']); // removes explicit overrides
|
|
55
|
+
map.resetConfiguration(); // resets all four presentation groups
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The reset groups are `features`, `layers`, `controls`, and `appearance`.
|
|
59
|
+
Resetting a group does not restore initial constructor overrides, change the
|
|
60
|
+
camera, or change mode/source/year/labels. It restores built-in defaults and
|
|
61
|
+
mode-derived layer choices. Supply geographic choices explicitly through
|
|
62
|
+
`configure`. To remove a single token while retaining other overrides, build
|
|
63
|
+
the desired group, reset that group, then configure the retained values.
|
|
64
|
+
Each call commits atomically; the two calls are separate updates.
|
|
65
|
+
|
|
66
|
+
These rules describe `MapController` from `createMap`, `createGuideController`,
|
|
67
|
+
and `mountGuideController`. Low-level element factories retain their separate
|
|
68
|
+
`setFeatures` / `setLayers` APIs; use controllers for the grouped update contract.
|
|
69
|
+
|
|
70
|
+
## Select neighborhoods by exact identity
|
|
71
|
+
|
|
72
|
+
Controller `selectNeighborhood(id)` accepts an exact ID in the current source,
|
|
73
|
+
rather than a display name or alias. Unknown IDs return `false` without changing
|
|
74
|
+
selection. Resolve a human-entered name using the source-specific lookup first.
|
|
75
|
+
For integrations, prefer the source-qualified identity:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import { getRealtorNeighborhood } from '@kahwee/sf-map-svg/data/realtor';
|
|
79
|
+
|
|
80
|
+
const feature = getRealtorNeighborhood('Inner Mission');
|
|
81
|
+
if (feature) {
|
|
82
|
+
map.selectFeature({ kind: 'neighborhood', source: 'realtor', id: feature.id }, { fit: false });
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`selectFeature` requires the current supplied source/year and never switches
|
|
87
|
+
geography implicitly. Marker IDs and numeric district IDs remain unchanged.
|
|
88
|
+
The construction-time `neighborhood` option and low-level element lookup APIs
|
|
89
|
+
continue to resolve names; the stricter ID contract applies to controller
|
|
90
|
+
selection methods. Existing selection event names remain available alongside
|
|
91
|
+
`selectionchange`.
|
|
92
|
+
|
|
93
|
+
## Offline geometry queries
|
|
94
|
+
|
|
95
|
+
`geometryBounds(geometry)` and `geometryContainsPoint(geometry, position)` are
|
|
96
|
+
new in v5. They accept readonly GeoJSON without a DOM or geographic imports.
|
|
97
|
+
Bounds are `[west, south, east, north]` or `null` for empty geometry. Containment
|
|
98
|
+
respects holes, includes polygon boundaries, and supports islands and nested
|
|
99
|
+
collections. Shared edges can match more than one feature. See the
|
|
100
|
+
[query contract](API.md#geographic-queries) for validation and planar limitations.
|