@sportsdataverse/sdvplot 0.1.0 → 0.2.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 (135) hide show
  1. package/NOTICE.md +15 -0
  2. package/README.md +84 -8
  3. package/dist/chartjs-surface.d.ts +2 -2
  4. package/dist/chartjs-surface.js +2 -2
  5. package/dist/chartjs-surface.js.map +1 -1
  6. package/dist/chartjs.d.ts +32 -2
  7. package/dist/chartjs.js +7 -4
  8. package/dist/chartjs.js.map +1 -1
  9. package/dist/chunk-57WFSI4Q.js +128 -0
  10. package/dist/chunk-57WFSI4Q.js.map +1 -0
  11. package/dist/{chunk-KKH6ABMA.js → chunk-7ULOCCFL.js} +94 -4
  12. package/dist/chunk-7ULOCCFL.js.map +1 -0
  13. package/dist/chunk-AMXASQHT.js +120 -0
  14. package/dist/chunk-AMXASQHT.js.map +1 -0
  15. package/dist/chunk-B2MP37SR.js +175 -0
  16. package/dist/chunk-B2MP37SR.js.map +1 -0
  17. package/dist/{chunk-NLPH2VZT.js → chunk-DYMUVE6K.js} +4 -4
  18. package/dist/{chunk-NLPH2VZT.js.map → chunk-DYMUVE6K.js.map} +1 -1
  19. package/dist/{chunk-QY65DUEF.js → chunk-H2C3FMMA.js} +4 -10
  20. package/dist/chunk-H2C3FMMA.js.map +1 -0
  21. package/dist/{chunk-JJFTD4VB.js → chunk-J4P4HDBF.js} +4 -4
  22. package/dist/{chunk-JJFTD4VB.js.map → chunk-J4P4HDBF.js.map} +1 -1
  23. package/dist/chunk-M3XD2OSN.js +132 -0
  24. package/dist/chunk-M3XD2OSN.js.map +1 -0
  25. package/dist/{chunk-OKEE4AH6.js → chunk-OIILARXI.js} +3 -3
  26. package/dist/{chunk-OKEE4AH6.js.map → chunk-OIILARXI.js.map} +1 -1
  27. package/dist/chunk-RPVVJY74.js +170 -0
  28. package/dist/chunk-RPVVJY74.js.map +1 -0
  29. package/dist/chunk-UL5M6KDE.js +205 -0
  30. package/dist/chunk-UL5M6KDE.js.map +1 -0
  31. package/dist/chunk-WQT4IVLX.js +10 -0
  32. package/dist/chunk-WQT4IVLX.js.map +1 -0
  33. package/dist/{chunk-TAXO3VXH.js → chunk-ZAEQXFJM.js} +3 -3
  34. package/dist/{chunk-TAXO3VXH.js.map → chunk-ZAEQXFJM.js.map} +1 -1
  35. package/dist/cytoscape.d.ts +188 -0
  36. package/dist/cytoscape.js +219 -0
  37. package/dist/cytoscape.js.map +1 -0
  38. package/dist/d3.d.ts +3 -3
  39. package/dist/d3.js +6 -6
  40. package/dist/d3.js.map +1 -1
  41. package/dist/deckgl.d.ts +232 -0
  42. package/dist/deckgl.js +197 -0
  43. package/dist/deckgl.js.map +1 -0
  44. package/dist/echarts.js +5 -5
  45. package/dist/g2.d.ts +251 -0
  46. package/dist/g2.js +102 -0
  47. package/dist/g2.js.map +1 -0
  48. package/dist/index.d.ts +2 -1
  49. package/dist/index.js +4 -5
  50. package/dist/lightweight-charts.d.ts +217 -0
  51. package/dist/lightweight-charts.js +155 -0
  52. package/dist/lightweight-charts.js.map +1 -0
  53. package/dist/maplibre.d.ts +271 -0
  54. package/dist/maplibre.js +175 -0
  55. package/dist/maplibre.js.map +1 -0
  56. package/dist/mui.d.ts +338 -0
  57. package/dist/mui.js +184 -0
  58. package/dist/mui.js.map +1 -0
  59. package/dist/nivo.d.ts +193 -0
  60. package/dist/nivo.js +8 -0
  61. package/dist/nivo.js.map +1 -0
  62. package/dist/perspective.d.ts +198 -0
  63. package/dist/perspective.js +171 -0
  64. package/dist/perspective.js.map +1 -0
  65. package/dist/placement-UWkqUzpa.d.ts +32 -0
  66. package/dist/plot.d.ts +7 -5
  67. package/dist/plot.js +9 -8
  68. package/dist/plot.js.map +1 -1
  69. package/dist/plotly.js +5 -5
  70. package/dist/react.d.ts +279 -3
  71. package/dist/react.js +364 -6
  72. package/dist/react.js.map +1 -1
  73. package/dist/recharts.d.ts +162 -0
  74. package/dist/recharts.js +8 -0
  75. package/dist/recharts.js.map +1 -0
  76. package/dist/shadcn.d.ts +83 -0
  77. package/dist/shadcn.js +88 -0
  78. package/dist/shadcn.js.map +1 -0
  79. package/dist/shots.js +3 -2
  80. package/dist/shots.js.map +1 -1
  81. package/dist/surface-TR7YFJW3.js +4 -0
  82. package/dist/surface-TR7YFJW3.js.map +1 -0
  83. package/dist/surfaces-MIG7HOFU.js +6 -0
  84. package/dist/surfaces-MIG7HOFU.js.map +1 -0
  85. package/dist/surfaces-W-odWLpT.d.ts +47 -0
  86. package/dist/svelte/HeadshotMark.svelte +25 -0
  87. package/dist/svelte/HeadshotMark.svelte.d.ts +10 -0
  88. package/dist/svelte/HeadshotMark.svelte.d.ts.map +1 -0
  89. package/dist/svelte/LogoMark.svelte +25 -0
  90. package/dist/svelte/LogoMark.svelte.d.ts +10 -0
  91. package/dist/svelte/LogoMark.svelte.d.ts.map +1 -0
  92. package/dist/svelte/Marks.svelte +25 -0
  93. package/dist/svelte/Marks.svelte.d.ts +10 -0
  94. package/dist/svelte/Marks.svelte.d.ts.map +1 -0
  95. package/dist/svelte/Surface.svelte +29 -0
  96. package/dist/svelte/Surface.svelte.d.ts +9 -0
  97. package/dist/svelte/Surface.svelte.d.ts.map +1 -0
  98. package/dist/svelte/SurfaceBackdrop.svelte +26 -0
  99. package/dist/svelte/SurfaceBackdrop.svelte.d.ts +11 -0
  100. package/dist/svelte/SurfaceBackdrop.svelte.d.ts.map +1 -0
  101. package/dist/svelte/index.d.ts +13 -0
  102. package/dist/svelte/index.d.ts.map +1 -0
  103. package/dist/svelte/index.js +11 -0
  104. package/dist/svelte-core.d.ts +139 -0
  105. package/dist/svelte-core.js +123 -0
  106. package/dist/svelte-core.js.map +1 -0
  107. package/dist/tanstack.d.ts +240 -0
  108. package/dist/tanstack.js +110 -0
  109. package/dist/tanstack.js.map +1 -0
  110. package/dist/testing.js +4 -5
  111. package/dist/testing.js.map +1 -1
  112. package/dist/three.d.ts +223 -0
  113. package/dist/three.js +50 -0
  114. package/dist/three.js.map +1 -0
  115. package/dist/{tiers-B6pUBPcE.d.ts → tiers-BN4md-GB.d.ts} +2 -30
  116. package/dist/unovis.d.ts +127 -0
  117. package/dist/unovis.js +108 -0
  118. package/dist/unovis.js.map +1 -0
  119. package/dist/uplot.d.ts +120 -0
  120. package/dist/uplot.js +110 -0
  121. package/dist/uplot.js.map +1 -0
  122. package/dist/vega.js +5 -5
  123. package/dist/victory.d.ts +107 -0
  124. package/dist/victory.js +38 -0
  125. package/dist/victory.js.map +1 -0
  126. package/package.json +218 -6
  127. package/dist/chunk-KKH6ABMA.js.map +0 -1
  128. package/dist/chunk-QY65DUEF.js.map +0 -1
  129. package/dist/chunk-RMT2GMFH.js +0 -97
  130. package/dist/chunk-RMT2GMFH.js.map +0 -1
  131. package/dist/chunk-SEBO35DV.js +0 -76
  132. package/dist/chunk-SEBO35DV.js.map +0 -1
  133. package/dist/chunk-TDNBAEQB.js +0 -105
  134. package/dist/chunk-TDNBAEQB.js.map +0 -1
  135. package/dist/surfaces-ChaD9nei.d.ts +0 -38
@@ -0,0 +1,271 @@
1
+ import { GeoJSONSourceSpecification, Map, SymbolLayerSpecification, LineLayerSpecification } from 'maplibre-gl';
2
+ import { a as DrawnMark, R as Row } from './_web-D7Aa5Rd8.js';
3
+ import { L as League, S as SeasonInput, V as Variant, I as IdSystem, W as Which } from './types-C7n6gJF3.js';
4
+
5
+ /**
6
+ * The MapLibre GL JS 6 adapter: team logos as a native `symbol` layer (`logoSymbols`, with `addLogoImages` to load
7
+ * each logo once into the map's images), team-coloured travel lines as a `line` layer (`routeLines`), and a free,
8
+ * keyless basemap (`basemap`, OpenFreeMap). Each builder returns plain style JSON: a GeoJSON source and a layer that
9
+ * reads it, for `map.addSource(set.id, set.source)` and `map.addLayer(set.layer)`. Pure: `maplibre-gl` (an optional
10
+ * peer, 6.14 or later) is never imported at runtime, only its types; the map is passed in. For deck.gl layers on a
11
+ * MapLibre map, hand `@sportsdataverse/sdvplot/deckgl`'s `logoIconLayer` / `passArcLayer` props to deck.gl's layers
12
+ * inside `@deck.gl/mapbox`'s `MapboxOverlay`. Synchronous builders: call after `loadLeague(league)` / `preloadAll()`.
13
+ */
14
+
15
+ /** `logoSymbols` options. */
16
+ interface LogoSymbolOptions {
17
+ /** Name of the row column holding each logo's longitude. */
18
+ x: string;
19
+ /** Name of the row column holding each logo's latitude. */
20
+ y: string;
21
+ /** Name of the row column holding the team (abbreviation, name or id, a number or a string; see `idSystem`). */
22
+ team: string;
23
+ /** The league the teams belong to, e.g. "nfl". */
24
+ league: League;
25
+ /** A season (number or string) for every row, or the name of a row column holding each row's season. */
26
+ season?: SeasonInput;
27
+ /** Logo variant, e.g. "dark". Default "default". */
28
+ variant?: Variant;
29
+ /** How the `team` values are read. Default "auto". */
30
+ idSystem?: IdSystem;
31
+ /** The logo's height on screen in px, 1 or more. Default 32. */
32
+ size?: number;
33
+ /** The logos' opacity in [0, 1] (`icon-opacity`). Default 1. */
34
+ alpha?: number;
35
+ /** The source and layer id. Default "sdvplot-logos". */
36
+ id?: string;
37
+ }
38
+ /** A logo feature's properties. */
39
+ interface LogoProperties {
40
+ /** The resolved team id. */
41
+ team: string;
42
+ /** The map image the symbol draws (`icon-image`), one per logo and size. */
43
+ icon: string;
44
+ /** The logo's URL. */
45
+ url: string;
46
+ /** The row's position in the input (left-out rows leave gaps). */
47
+ index: number;
48
+ }
49
+ /** One logo the map needs as an image: `addLogoImages` loads `url` and adds it as `id`, drawn `height` px tall. */
50
+ interface LogoImage {
51
+ id: string;
52
+ url: string;
53
+ height: number;
54
+ }
55
+ /** A GeoJSON point: [longitude, latitude]. */
56
+ interface PointGeometry {
57
+ type: "Point";
58
+ coordinates: [lon: number, lat: number];
59
+ }
60
+ /** A GeoJSON line through [longitude, latitude] positions. */
61
+ interface LineGeometry {
62
+ type: "LineString";
63
+ coordinates: [lon: number, lat: number][];
64
+ }
65
+ /** A GeoJSON feature; its id is the row's index in the input. */
66
+ interface Feature<G, P> {
67
+ type: "Feature";
68
+ id: number;
69
+ geometry: G;
70
+ properties: P;
71
+ }
72
+ /** A GeoJSON source holding a feature collection: `map.addSource(id, source)`. */
73
+ type FeatureSource<G, P> = GeoJSONSourceSpecification & {
74
+ type: "geojson";
75
+ data: {
76
+ type: "FeatureCollection";
77
+ features: Feature<G, P>[];
78
+ };
79
+ };
80
+ /** `logoSymbols`' result: a source and a symbol layer for the map, and the images that layer draws. */
81
+ interface LogoSymbols {
82
+ /** The source id (the layer's own id too). */
83
+ id: string;
84
+ /** One point feature per drawn row; the feature id is the row's index. */
85
+ source: FeatureSource<PointGeometry, LogoProperties>;
86
+ layer: SymbolLayerSpecification;
87
+ /** One per distinct logo: what `addLogoImages` loads. */
88
+ images: LogoImage[];
89
+ }
90
+ /** `routeLines` options. */
91
+ interface RouteLineOptions {
92
+ /** Name of the row column holding each leg's start longitude. */
93
+ x: string;
94
+ /** Name of the row column holding each leg's start latitude. */
95
+ y: string;
96
+ /** Name of the row column holding each leg's end longitude. */
97
+ xend: string;
98
+ /** Name of the row column holding each leg's end latitude. */
99
+ yend: string;
100
+ /** Name of the row column holding the team the leg is coloured by. */
101
+ team: string;
102
+ /** The league the teams belong to, e.g. "nfl". */
103
+ league: League;
104
+ /** The season the teams are resolved for. */
105
+ season?: SeasonInput;
106
+ /** How the `team` values are read. Default "auto". */
107
+ idSystem?: IdSystem;
108
+ /** "primary" (default) or "secondary" colour. */
109
+ which?: Which;
110
+ /**
111
+ * The basemap's background (`basemap("dark").background` under the dark basemap). Given, every team colour keeps 3:1
112
+ * contrast against it and aims to stay distinct from the other teams' (CIE76 ΔE of 20 or more), through the shared
113
+ * team palette, as `teamColorPalette` does in the other adapters.
114
+ */
115
+ background?: string;
116
+ /** The colour of a team that does not resolve, or has no colour. Default "#808080". */
117
+ fallback?: string;
118
+ /** The line's width in px, 1 or more. Default 3. */
119
+ width?: number;
120
+ /** The lines' opacity in [0, 1] (`line-opacity`). Default 1. */
121
+ alpha?: number;
122
+ /** The source and layer id. Default "sdvplot-routes". */
123
+ id?: string;
124
+ }
125
+ /** A route feature's properties. */
126
+ interface RouteProperties {
127
+ /** The resolved team id, or the row's own value as text when it does not resolve. */
128
+ team: string;
129
+ /** The line's colour, `#rrggbb`. */
130
+ color: string;
131
+ /** The row's position in the input (left-out rows leave gaps). */
132
+ index: number;
133
+ }
134
+ /** `routeLines`' result: a source and a line layer for the map. */
135
+ interface RouteLines {
136
+ id: string;
137
+ /** One two-point line per drawn row; the feature id is the row's index. */
138
+ source: FeatureSource<LineGeometry, RouteProperties>;
139
+ layer: LineLayerSpecification;
140
+ }
141
+ /** The map methods `addLogoImages` calls: a MapLibre `Map`. */
142
+ type LogoImageMap = Pick<Map, "loadImage" | "addImage" | "hasImage">;
143
+ /**
144
+ * Each row's team logo at its (longitude, latitude), as a GeoJSON source and a MapLibre `symbol` layer: one point
145
+ * feature per row (its `id` is the row's index; `team`, `icon`, `url` and `index` are its properties), and a layer
146
+ * drawing `icon-image: ["get", "icon"]`, every logo shown even where two overlap. A team with no logo (or one that
147
+ * does not resolve) is left out with one warning; so is a row with a missing, non-numeric or infinite coordinate (a
148
+ * numeric string such as "-94.7" is read as its number). Team values may be numbers or strings (ESPN ids,
149
+ * abbreviations, names). The map needs the logos as images first:
150
+ * `await addLogoImages(map, logos)`, then `map.addSource(logos.id, logos.source)` and `map.addLayer(logos.layer)`.
151
+ *
152
+ * @example
153
+ * ```ts
154
+ * import { loadLeague } from "@sportsdataverse/sdvplot";
155
+ * import { logoSymbols } from "@sportsdataverse/sdvplot/maplibre";
156
+ *
157
+ * await loadLeague("nfl");
158
+ * // Kansas City's home airport, from nflverse's nfldata airports.csv
159
+ * const logos = logoSymbols([{ team: "KC", lon: -94.714, lat: 39.297 }], {
160
+ * x: "lon",
161
+ * y: "lat",
162
+ * team: "team",
163
+ * league: "nfl",
164
+ * size: 40,
165
+ * });
166
+ * // in the browser: await addLogoImages(map, logos); map.addSource(logos.id, logos.source); map.addLayer(logos.layer)
167
+ * logos.source.data.features[0]?.properties.icon;
168
+ * ```
169
+ */
170
+ declare function logoSymbols(rows: readonly Row[], o: LogoSymbolOptions): LogoSymbols;
171
+ /**
172
+ * Loads each logo of `logoSymbols` sets into the map's images, once: `map.loadImage(url)`, then
173
+ * `map.addImage(id, image, { pixelRatio })`, the pixel ratio making the image draw `size` px tall at icon size 1 (and
174
+ * stay sharp on a high-density screen). An image the map already has is skipped, so calling it again adds nothing
175
+ * twice. `map.setStyle` drops the map's images, sources and layers: on the map's `style.load` event, call it again
176
+ * (it adds back only what the new style lacks) and re-add the source and layer. A logo that fails to load is left undrawn with one
177
+ * warning. Resolves when every image is in. Call it before adding the layers that draw them.
178
+ *
179
+ * @example
180
+ * ```ts
181
+ * import { loadLeague } from "@sportsdataverse/sdvplot";
182
+ * import { addLogoImages, logoSymbols } from "@sportsdataverse/sdvplot/maplibre";
183
+ *
184
+ * await loadLeague("nfl");
185
+ * const logos = logoSymbols([{ team: "KC", lon: -94.714, lat: 39.297 }], { x: "lon", y: "lat", team: "team", league: "nfl" });
186
+ * // a stand-in for a MapLibre map (in the browser: the map from `new maplibregl.Map(...)`, once it has loaded)
187
+ * const added: string[] = [];
188
+ * const map = {
189
+ * hasImage: (id: string) => added.includes(id),
190
+ * loadImage: async () => ({ data: { width: 500, height: 500 } as ImageBitmap }),
191
+ * addImage(id: string) {
192
+ * added.push(id);
193
+ * return this;
194
+ * },
195
+ * };
196
+ * await addLogoImages(map as never, logos);
197
+ * added;
198
+ * ```
199
+ */
200
+ declare function addLogoImages(map: LogoImageMap, ...sets: readonly LogoSymbols[]): Promise<void>;
201
+ /**
202
+ * Team-coloured travel legs as a GeoJSON source and a MapLibre `line` layer: one straight line per row from (x, y) to
203
+ * (xend, yend) in longitude and latitude, in the team's colour (`line-color: ["get", "color"]`). Each team keeps one
204
+ * colour, from the shared team palette; with `background` the colours read on the basemap (dark mode) and stay
205
+ * distinct. A team that does not resolve takes `fallback`. A coordinate may be a number or a numeric string, as for
206
+ * `logoSymbols`; a row with a missing, non-numeric or infinite one is left out with one warning. The feature id is the row's index. The same rows feed `./deckgl`'s `passArcLayer` for arcs instead.
207
+ *
208
+ * @example
209
+ * ```ts
210
+ * import { loadLeague } from "@sportsdataverse/sdvplot";
211
+ * import { routeLines } from "@sportsdataverse/sdvplot/maplibre";
212
+ *
213
+ * await loadLeague("nfl");
214
+ * // Kansas City to Buffalo, airport to airport (nflverse nfldata airports.csv): week 11 of 2024
215
+ * const leg = { team: "KC", lon: -94.714, lat: 39.297, lon_end: -78.732, lat_end: 42.94 };
216
+ * const routes = routeLines([leg], { x: "lon", y: "lat", xend: "lon_end", yend: "lat_end", team: "team", league: "nfl" });
217
+ * // in the browser: map.addSource(routes.id, routes.source); map.addLayer(routes.layer)
218
+ * routes.source.data.features[0]?.properties.color;
219
+ * ```
220
+ */
221
+ declare function routeLines(rows: readonly Row[], o: RouteLineOptions): RouteLines;
222
+ /** `basemap`'s result. */
223
+ interface Basemap {
224
+ /** The style URL, for the map's `style` option. */
225
+ style: string;
226
+ /**
227
+ * The provider's credit (HTML). MapLibre shows it by itself from the style's source, in its attribution control (on
228
+ * by default; keep it). Anything else that draws these tiles, or a static export of the map, must show it.
229
+ */
230
+ attribution: string;
231
+ /**
232
+ * The colour of the style's `background` layer, the land (read from the style on 2026-10-27), for `routeLines`' and
233
+ * `passArcLayer`'s `background` (dark mode). Their 3:1 contrast is measured against it, not against the water,
234
+ * which is a little lighter on the dark style (#1b1b1d) and a little darker on the light one (#c2c8ca).
235
+ */
236
+ background: string;
237
+ }
238
+ /**
239
+ * A free basemap that needs no API key: OpenFreeMap's `positron` (light) or `dark` vector style, built from
240
+ * OpenStreetMap data. Pass `style` to `new maplibregl.Map({ style })`. OpenFreeMap's public instance asks for no
241
+ * registration or key and sets no request limit; the credit is required, and MapLibre's attribution control shows it
242
+ * from the style's source (`attribution` is the same text, for anything else). Each map view requests the
243
+ * provider's tiles: build or test pages without a map, or serve a local style.
244
+ *
245
+ * @example
246
+ * ```ts
247
+ * import { basemap } from "@sportsdataverse/sdvplot/maplibre";
248
+ *
249
+ * const { style, background } = basemap("dark");
250
+ * // in the browser: new maplibregl.Map({ container, style, center: [-96, 38], zoom: 3 })
251
+ * [style, background];
252
+ * ```
253
+ */
254
+ declare function basemap(theme?: "light" | "dark"): Basemap;
255
+ /**
256
+ * What `logoSymbols` sets draw, as `[team, longitude, latitude, px, url]` per feature, in order: the module's test
257
+ * hook, as the other adapters' `drawnMarks`.
258
+ *
259
+ * @example
260
+ * ```ts
261
+ * import { loadLeague } from "@sportsdataverse/sdvplot";
262
+ * import { drawnMarks, logoSymbols } from "@sportsdataverse/sdvplot/maplibre";
263
+ *
264
+ * await loadLeague("nfl");
265
+ * // 12 is Kansas City's ESPN team id
266
+ * drawnMarks(logoSymbols([{ team: 12, lon: -94.714, lat: 39.297 }], { x: "lon", y: "lat", team: "team", league: "nfl" }));
267
+ * ```
268
+ */
269
+ declare function drawnMarks(...sets: readonly LogoSymbols[]): DrawnMark[];
270
+
271
+ export { type Basemap, DrawnMark, type Feature, type FeatureSource, type LineGeometry, type LogoImage, type LogoImageMap, type LogoProperties, type LogoSymbolOptions, type LogoSymbols, type PointGeometry, type RouteLineOptions, type RouteLines, type RouteProperties, Row, addLogoImages, basemap, drawnMarks, logoSymbols, routeLines };
@@ -0,0 +1,175 @@
1
+ import { contentId } from './chunk-WQT4IVLX.js';
2
+ import { markPlacements, column, colorPalette } from './chunk-UL5M6KDE.js';
3
+ import { checkAlpha } from './chunk-7ULOCCFL.js';
4
+ import { resolveSync, normValue } from './chunk-ZAEQXFJM.js';
5
+ import './chunk-J6R2LEJ7.js';
6
+ import { warn, InputError } from './chunk-E7G7VSLV.js';
7
+
8
+ // src/maplibre.ts
9
+ var real = (v) => typeof v === "number" && Number.isFinite(v);
10
+ function coord(v) {
11
+ const n = typeof v === "number" ? v : typeof v === "string" && v.trim() !== "" ? Number(v) : Number.NaN;
12
+ return Number.isFinite(n) ? n : null;
13
+ }
14
+ function skippedRows(key, what, teams) {
15
+ if (teams.length === 0) return;
16
+ const shown = teams.slice(0, 10).map(String).join(", ") + (teams.length > 10 ? ` and ${teams.length - 10} more` : "");
17
+ warn(`${key}:${shown}`, `skipped ${teams.length} ${what} with a missing coordinate: ${shown}`);
18
+ }
19
+ function checkPx(name, v) {
20
+ if (!real(v) || v < 1) throw new InputError(`${name} is in px, 1 or more, got ${String(v)}`);
21
+ return v;
22
+ }
23
+ function logoSymbols(rows, o) {
24
+ const size = checkPx("size", o.size ?? 32);
25
+ const opacity = checkAlpha(o.alpha ?? 1);
26
+ const id = o.id ?? "sdvplot-logos";
27
+ const images = /* @__PURE__ */ new Map();
28
+ const placed = markPlacements(rows, "logo", o);
29
+ const at = placed.map((p) => [coord(p.x), coord(p.y)]);
30
+ skippedRows(
31
+ `maplibre:logos:${o.league}:missing`,
32
+ "logo(s)",
33
+ placed.filter((_, k) => at[k]?.includes(null)).map((p) => p.id)
34
+ );
35
+ const features = placed.flatMap((p, k) => {
36
+ const [lon, lat] = at[k];
37
+ if (lon === null || lat === null) return [];
38
+ const icon = `${contentId("sdv-logo", p.url)}-${size}`;
39
+ if (!images.has(icon)) images.set(icon, { id: icon, url: p.url, height: size });
40
+ return [
41
+ {
42
+ type: "Feature",
43
+ id: p.index,
44
+ geometry: { type: "Point", coordinates: [lon, lat] },
45
+ properties: { team: p.id, icon, url: p.url, index: p.index }
46
+ }
47
+ ];
48
+ });
49
+ return {
50
+ id,
51
+ source: { type: "geojson", data: { type: "FeatureCollection", features } },
52
+ layer: {
53
+ id,
54
+ type: "symbol",
55
+ source: id,
56
+ layout: {
57
+ "icon-image": ["get", "icon"],
58
+ "icon-allow-overlap": true,
59
+ "icon-ignore-placement": true
60
+ },
61
+ paint: { "icon-opacity": opacity }
62
+ },
63
+ images: [...images.values()]
64
+ };
65
+ }
66
+ async function addLogoImages(map, ...sets) {
67
+ const want = new Map(sets.flatMap((s) => s.images).map((im) => [im.id, im]));
68
+ await Promise.all(
69
+ [...want.values()].map(async (im) => {
70
+ if (map.hasImage(im.id)) return;
71
+ let image;
72
+ try {
73
+ image = (await map.loadImage(im.url)).data;
74
+ } catch (e) {
75
+ warn(`maplibre:image:${im.url}`, `could not load the logo ${im.url}: ${String(e)}`);
76
+ return;
77
+ }
78
+ if (map.hasImage(im.id)) return;
79
+ map.addImage(im.id, image, { pixelRatio: image.height / im.height });
80
+ })
81
+ );
82
+ }
83
+ var label = (t) => t === null || t === void 0 ? "" : typeof t === "number" ? normValue(t) ?? "" : String(t);
84
+ function routeLines(rows, o) {
85
+ const width = checkPx("width", o.width ?? 3);
86
+ const opacity = checkAlpha(o.alpha ?? 1);
87
+ const id = o.id ?? "sdvplot-routes";
88
+ const cols = [o.x, o.y, o.xend, o.yend].map(
89
+ (c, k) => column(rows, c, ["x", "y", "xend", "yend"][k])
90
+ );
91
+ const teams = column(rows, o.team, "team");
92
+ const at = rows.map((_, i) => cols.map((c) => coord(c[i])));
93
+ const keep = rows.flatMap((_, i) => at[i]?.includes(null) ? [] : [i]);
94
+ skippedRows(
95
+ `maplibre:routes:${o.league}:missing`,
96
+ "route leg(s)",
97
+ rows.flatMap((_, i) => at[i]?.includes(null) ? [teams[i]] : [])
98
+ );
99
+ const kept = keep.map((i) => teams[i]);
100
+ const ro = {
101
+ ...o.season === void 0 ? {} : { season: o.season },
102
+ ...o.idSystem === void 0 ? {} : { idSystem: o.idSystem }
103
+ };
104
+ const resolved = resolveSync(kept, o.league, ro);
105
+ const keys = kept.map((t, j) => resolved[j] ?? `?${label(t)}`);
106
+ const distinct = [...new Set(keys)];
107
+ const known = distinct.filter((k) => !k.startsWith("?"));
108
+ const palette = colorPalette(o.league, known, {
109
+ ...ro,
110
+ idSystem: "team_id",
111
+ ...o.which === void 0 ? {} : { which: o.which },
112
+ ...o.background === void 0 ? {} : { background: o.background },
113
+ ...o.fallback === void 0 ? {} : { fallback: o.fallback }
114
+ });
115
+ const colorOf = new Map(known.map((k, j) => [k, palette[j]]));
116
+ const fallback = o.fallback ?? "#808080";
117
+ const features = keep.map((i, j) => {
118
+ const c = at[i];
119
+ return {
120
+ type: "Feature",
121
+ id: i,
122
+ geometry: {
123
+ type: "LineString",
124
+ coordinates: [
125
+ [c[0], c[1]],
126
+ [c[2], c[3]]
127
+ ]
128
+ },
129
+ properties: {
130
+ team: resolved[j] ?? label(kept[j]),
131
+ color: colorOf.get(keys[j]) ?? fallback,
132
+ index: i
133
+ }
134
+ };
135
+ });
136
+ return {
137
+ id,
138
+ source: { type: "geojson", data: { type: "FeatureCollection", features } },
139
+ layer: {
140
+ id,
141
+ type: "line",
142
+ source: id,
143
+ layout: { "line-cap": "round", "line-join": "round" },
144
+ paint: { "line-color": ["get", "color"], "line-width": width, "line-opacity": opacity }
145
+ }
146
+ };
147
+ }
148
+ var OPENFREEMAP_ATTRIBUTION = '<a href="https://openfreemap.org" target="_blank">OpenFreeMap</a> <a href="https://www.openmaptiles.org/" target="_blank">&copy; OpenMapTiles</a> Data from <a href="https://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>';
149
+ var STYLES = {
150
+ light: { style: "https://tiles.openfreemap.org/styles/positron", background: "#f2f3f0" },
151
+ dark: { style: "https://tiles.openfreemap.org/styles/dark", background: "#0c0c0c" }
152
+ };
153
+ function basemap(theme = "light") {
154
+ const s = STYLES[theme];
155
+ if (s === void 0) throw new InputError(`theme is "light" or "dark", got ${JSON.stringify(theme)}`);
156
+ return { ...s, attribution: OPENFREEMAP_ATTRIBUTION };
157
+ }
158
+ function drawnMarks(...sets) {
159
+ return sets.flatMap((s) => {
160
+ const px = new Map(s.images.map((im) => [im.id, im.height]));
161
+ return s.source.data.features.map(
162
+ (f) => [
163
+ f.properties.team,
164
+ f.geometry.coordinates[0],
165
+ f.geometry.coordinates[1],
166
+ px.get(f.properties.icon),
167
+ f.properties.url
168
+ ]
169
+ );
170
+ });
171
+ }
172
+
173
+ export { addLogoImages, basemap, drawnMarks, logoSymbols, routeLines };
174
+ //# sourceMappingURL=maplibre.js.map
175
+ //# sourceMappingURL=maplibre.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/maplibre.ts"],"names":[],"mappings":"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","file":"maplibre.js","sourcesContent":["/**\n * The MapLibre GL JS 6 adapter: team logos as a native `symbol` layer (`logoSymbols`, with `addLogoImages` to load\n * each logo once into the map's images), team-coloured travel lines as a `line` layer (`routeLines`), and a free,\n * keyless basemap (`basemap`, OpenFreeMap). Each builder returns plain style JSON: a GeoJSON source and a layer that\n * reads it, for `map.addSource(set.id, set.source)` and `map.addLayer(set.layer)`. Pure: `maplibre-gl` (an optional\n * peer, 6.14 or later) is never imported at runtime, only its types; the map is passed in. For deck.gl layers on a\n * MapLibre map, hand `@sportsdataverse/sdvplot/deckgl`'s `logoIconLayer` / `passArcLayer` props to deck.gl's layers\n * inside `@deck.gl/mapbox`'s `MapboxOverlay`. Synchronous builders: call after `loadLeague(league)` / `preloadAll()`.\n */\nimport type {\n GeoJSONSourceSpecification,\n LineLayerSpecification,\n Map as MapLibreMap,\n SymbolLayerSpecification,\n} from \"maplibre-gl\";\nimport { type DrawnMark, type Row, checkAlpha, colorPalette, column, markPlacements } from \"./_web.js\";\nimport { contentId } from \"./content-id.js\";\nimport { InputError, warn } from \"./errors.js\";\nimport { normValue } from \"./normalize.js\";\nimport { type Value, resolveSync } from \"./resolve.js\";\nimport type { IdSystem, League, SeasonInput, Variant, Which } from \"./types.js\";\n\nexport type { DrawnMark, Row } from \"./_web.js\";\n\n/** `logoSymbols` options. */\nexport interface LogoSymbolOptions {\n /** Name of the row column holding each logo's longitude. */\n x: string;\n /** Name of the row column holding each logo's latitude. */\n y: string;\n /** Name of the row column holding the team (abbreviation, name or id, a number or a string; see `idSystem`). */\n team: string;\n /** The league the teams belong to, e.g. \"nfl\". */\n league: League;\n /** A season (number or string) for every row, or the name of a row column holding each row's season. */\n season?: SeasonInput;\n /** Logo variant, e.g. \"dark\". Default \"default\". */\n variant?: Variant;\n /** How the `team` values are read. Default \"auto\". */\n idSystem?: IdSystem;\n /** The logo's height on screen in px, 1 or more. Default 32. */\n size?: number;\n /** The logos' opacity in [0, 1] (`icon-opacity`). Default 1. */\n alpha?: number;\n /** The source and layer id. Default \"sdvplot-logos\". */\n id?: string;\n}\n/** A logo feature's properties. */\nexport interface LogoProperties {\n /** The resolved team id. */\n team: string;\n /** The map image the symbol draws (`icon-image`), one per logo and size. */\n icon: string;\n /** The logo's URL. */\n url: string;\n /** The row's position in the input (left-out rows leave gaps). */\n index: number;\n}\n/** One logo the map needs as an image: `addLogoImages` loads `url` and adds it as `id`, drawn `height` px tall. */\nexport interface LogoImage {\n id: string;\n url: string;\n height: number;\n}\n/** A GeoJSON point: [longitude, latitude]. */\nexport interface PointGeometry {\n type: \"Point\";\n coordinates: [lon: number, lat: number];\n}\n/** A GeoJSON line through [longitude, latitude] positions. */\nexport interface LineGeometry {\n type: \"LineString\";\n coordinates: [lon: number, lat: number][];\n}\n/** A GeoJSON feature; its id is the row's index in the input. */\nexport interface Feature<G, P> {\n type: \"Feature\";\n id: number;\n geometry: G;\n properties: P;\n}\n/** A GeoJSON source holding a feature collection: `map.addSource(id, source)`. */\nexport type FeatureSource<G, P> = GeoJSONSourceSpecification & {\n type: \"geojson\";\n data: { type: \"FeatureCollection\"; features: Feature<G, P>[] };\n};\n/** `logoSymbols`' result: a source and a symbol layer for the map, and the images that layer draws. */\nexport interface LogoSymbols {\n /** The source id (the layer's own id too). */\n id: string;\n /** One point feature per drawn row; the feature id is the row's index. */\n source: FeatureSource<PointGeometry, LogoProperties>;\n layer: SymbolLayerSpecification;\n /** One per distinct logo: what `addLogoImages` loads. */\n images: LogoImage[];\n}\n\n/** `routeLines` options. */\nexport interface RouteLineOptions {\n /** Name of the row column holding each leg's start longitude. */\n x: string;\n /** Name of the row column holding each leg's start latitude. */\n y: string;\n /** Name of the row column holding each leg's end longitude. */\n xend: string;\n /** Name of the row column holding each leg's end latitude. */\n yend: string;\n /** Name of the row column holding the team the leg is coloured by. */\n team: string;\n /** The league the teams belong to, e.g. \"nfl\". */\n league: League;\n /** The season the teams are resolved for. */\n season?: SeasonInput;\n /** How the `team` values are read. Default \"auto\". */\n idSystem?: IdSystem;\n /** \"primary\" (default) or \"secondary\" colour. */\n which?: Which;\n /**\n * The basemap's background (`basemap(\"dark\").background` under the dark basemap). Given, every team colour keeps 3:1\n * contrast against it and aims to stay distinct from the other teams' (CIE76 ΔE of 20 or more), through the shared\n * team palette, as `teamColorPalette` does in the other adapters.\n */\n background?: string;\n /** The colour of a team that does not resolve, or has no colour. Default \"#808080\". */\n fallback?: string;\n /** The line's width in px, 1 or more. Default 3. */\n width?: number;\n /** The lines' opacity in [0, 1] (`line-opacity`). Default 1. */\n alpha?: number;\n /** The source and layer id. Default \"sdvplot-routes\". */\n id?: string;\n}\n/** A route feature's properties. */\nexport interface RouteProperties {\n /** The resolved team id, or the row's own value as text when it does not resolve. */\n team: string;\n /** The line's colour, `#rrggbb`. */\n color: string;\n /** The row's position in the input (left-out rows leave gaps). */\n index: number;\n}\n/** `routeLines`' result: a source and a line layer for the map. */\nexport interface RouteLines {\n id: string;\n /** One two-point line per drawn row; the feature id is the row's index. */\n source: FeatureSource<LineGeometry, RouteProperties>;\n layer: LineLayerSpecification;\n}\n\n/** The map methods `addLogoImages` calls: a MapLibre `Map`. */\nexport type LogoImageMap = Pick<MapLibreMap, \"loadImage\" | \"addImage\" | \"hasImage\">;\n\nconst real = (v: unknown): v is number => typeof v === \"number\" && Number.isFinite(v);\n/**\n * A longitude or latitude as a number: a finite number, or a string that reads as one (\"-94.7\", as a CSV gives it).\n * Anything else (missing, blank, not a number, infinite) is null: the row is left out.\n */\nfunction coord(v: unknown): number | null {\n const n = typeof v === \"number\" ? v : typeof v === \"string\" && v.trim() !== \"\" ? Number(v) : Number.NaN;\n return Number.isFinite(n) ? n : null;\n}\n/** One warning for the rows left out for a coordinate, naming their teams. */\nfunction skippedRows(key: string, what: string, teams: readonly Value[]): void {\n if (teams.length === 0) return;\n const shown =\n teams.slice(0, 10).map(String).join(\", \") + (teams.length > 10 ? ` and ${teams.length - 10} more` : \"\");\n warn(`${key}:${shown}`, `skipped ${teams.length} ${what} with a missing coordinate: ${shown}`);\n}\nfunction checkPx(name: string, v: unknown): number {\n if (!real(v) || v < 1) throw new InputError(`${name} is in px, 1 or more, got ${String(v)}`);\n return v;\n}\n\n/**\n * Each row's team logo at its (longitude, latitude), as a GeoJSON source and a MapLibre `symbol` layer: one point\n * feature per row (its `id` is the row's index; `team`, `icon`, `url` and `index` are its properties), and a layer\n * drawing `icon-image: [\"get\", \"icon\"]`, every logo shown even where two overlap. A team with no logo (or one that\n * does not resolve) is left out with one warning; so is a row with a missing, non-numeric or infinite coordinate (a\n * numeric string such as \"-94.7\" is read as its number). Team values may be numbers or strings (ESPN ids,\n * abbreviations, names). The map needs the logos as images first:\n * `await addLogoImages(map, logos)`, then `map.addSource(logos.id, logos.source)` and `map.addLayer(logos.layer)`.\n *\n * @example\n * ```ts\n * import { loadLeague } from \"@sportsdataverse/sdvplot\";\n * import { logoSymbols } from \"@sportsdataverse/sdvplot/maplibre\";\n *\n * await loadLeague(\"nfl\");\n * // Kansas City's home airport, from nflverse's nfldata airports.csv\n * const logos = logoSymbols([{ team: \"KC\", lon: -94.714, lat: 39.297 }], {\n * x: \"lon\",\n * y: \"lat\",\n * team: \"team\",\n * league: \"nfl\",\n * size: 40,\n * });\n * // in the browser: await addLogoImages(map, logos); map.addSource(logos.id, logos.source); map.addLayer(logos.layer)\n * logos.source.data.features[0]?.properties.icon;\n * ```\n */\nexport function logoSymbols(rows: readonly Row[], o: LogoSymbolOptions): LogoSymbols {\n const size = checkPx(\"size\", o.size ?? 32);\n const opacity = checkAlpha(o.alpha ?? 1);\n const id = o.id ?? \"sdvplot-logos\";\n const images = new Map<string, LogoImage>();\n // placeSync leaves out a null or NaN coordinate; a value that is not a number, or not finite, is left out here\n const placed = markPlacements(rows, \"logo\", o);\n const at = placed.map((p) => [coord(p.x), coord(p.y)] as const);\n skippedRows(\n `maplibre:logos:${o.league}:missing`,\n \"logo(s)\",\n placed.filter((_, k) => at[k]?.includes(null)).map((p) => p.id),\n );\n const features = placed.flatMap((p, k): Feature<PointGeometry, LogoProperties>[] => {\n const [lon, lat] = at[k] as readonly [number | null, number | null];\n if (lon === null || lat === null) return [];\n const icon = `${contentId(\"sdv-logo\", p.url)}-${size}`;\n if (!images.has(icon)) images.set(icon, { id: icon, url: p.url, height: size });\n return [\n {\n type: \"Feature\",\n id: p.index,\n geometry: { type: \"Point\", coordinates: [lon, lat] },\n properties: { team: p.id, icon, url: p.url, index: p.index },\n },\n ];\n });\n return {\n id,\n source: { type: \"geojson\", data: { type: \"FeatureCollection\", features } },\n layer: {\n id,\n type: \"symbol\",\n source: id,\n layout: {\n \"icon-image\": [\"get\", \"icon\"],\n \"icon-allow-overlap\": true,\n \"icon-ignore-placement\": true,\n },\n paint: { \"icon-opacity\": opacity },\n },\n images: [...images.values()],\n };\n}\n\n/**\n * Loads each logo of `logoSymbols` sets into the map's images, once: `map.loadImage(url)`, then\n * `map.addImage(id, image, { pixelRatio })`, the pixel ratio making the image draw `size` px tall at icon size 1 (and\n * stay sharp on a high-density screen). An image the map already has is skipped, so calling it again adds nothing\n * twice. `map.setStyle` drops the map's images, sources and layers: on the map's `style.load` event, call it again\n * (it adds back only what the new style lacks) and re-add the source and layer. A logo that fails to load is left undrawn with one\n * warning. Resolves when every image is in. Call it before adding the layers that draw them.\n *\n * @example\n * ```ts\n * import { loadLeague } from \"@sportsdataverse/sdvplot\";\n * import { addLogoImages, logoSymbols } from \"@sportsdataverse/sdvplot/maplibre\";\n *\n * await loadLeague(\"nfl\");\n * const logos = logoSymbols([{ team: \"KC\", lon: -94.714, lat: 39.297 }], { x: \"lon\", y: \"lat\", team: \"team\", league: \"nfl\" });\n * // a stand-in for a MapLibre map (in the browser: the map from `new maplibregl.Map(...)`, once it has loaded)\n * const added: string[] = [];\n * const map = {\n * hasImage: (id: string) => added.includes(id),\n * loadImage: async () => ({ data: { width: 500, height: 500 } as ImageBitmap }),\n * addImage(id: string) {\n * added.push(id);\n * return this;\n * },\n * };\n * await addLogoImages(map as never, logos);\n * added;\n * ```\n */\nexport async function addLogoImages(map: LogoImageMap, ...sets: readonly LogoSymbols[]): Promise<void> {\n const want = new Map(sets.flatMap((s) => s.images).map((im) => [im.id, im]));\n await Promise.all(\n [...want.values()].map(async (im) => {\n if (map.hasImage(im.id)) return;\n let image: HTMLImageElement | ImageBitmap;\n try {\n image = (await map.loadImage(im.url)).data;\n } catch (e) {\n warn(`maplibre:image:${im.url}`, `could not load the logo ${im.url}: ${String(e)}`);\n return;\n }\n if (map.hasImage(im.id)) return; // another call added it while this one loaded\n map.addImage(im.id, image, { pixelRatio: image.height / im.height });\n }),\n );\n}\n\n/** An unresolved team value as text: a number without \".0\", a missing one empty (as `./deckgl`'s arcs). */\nconst label = (t: unknown): string =>\n t === null || t === undefined ? \"\" : typeof t === \"number\" ? (normValue(t) ?? \"\") : String(t);\n\n/**\n * Team-coloured travel legs as a GeoJSON source and a MapLibre `line` layer: one straight line per row from (x, y) to\n * (xend, yend) in longitude and latitude, in the team's colour (`line-color: [\"get\", \"color\"]`). Each team keeps one\n * colour, from the shared team palette; with `background` the colours read on the basemap (dark mode) and stay\n * distinct. A team that does not resolve takes `fallback`. A coordinate may be a number or a numeric string, as for\n * `logoSymbols`; a row with a missing, non-numeric or infinite one is left out with one warning. The feature id is the row's index. The same rows feed `./deckgl`'s `passArcLayer` for arcs instead.\n *\n * @example\n * ```ts\n * import { loadLeague } from \"@sportsdataverse/sdvplot\";\n * import { routeLines } from \"@sportsdataverse/sdvplot/maplibre\";\n *\n * await loadLeague(\"nfl\");\n * // Kansas City to Buffalo, airport to airport (nflverse nfldata airports.csv): week 11 of 2024\n * const leg = { team: \"KC\", lon: -94.714, lat: 39.297, lon_end: -78.732, lat_end: 42.94 };\n * const routes = routeLines([leg], { x: \"lon\", y: \"lat\", xend: \"lon_end\", yend: \"lat_end\", team: \"team\", league: \"nfl\" });\n * // in the browser: map.addSource(routes.id, routes.source); map.addLayer(routes.layer)\n * routes.source.data.features[0]?.properties.color;\n * ```\n */\nexport function routeLines(rows: readonly Row[], o: RouteLineOptions): RouteLines {\n const width = checkPx(\"width\", o.width ?? 3);\n const opacity = checkAlpha(o.alpha ?? 1);\n const id = o.id ?? \"sdvplot-routes\";\n const cols = [o.x, o.y, o.xend, o.yend].map((c, k) =>\n column(rows, c, [\"x\", \"y\", \"xend\", \"yend\"][k] as string),\n );\n const teams = column(rows, o.team, \"team\");\n // each row's four coordinates as numbers (a numeric string is read as one), or null\n const at = rows.map((_, i) => cols.map((c) => coord(c[i])));\n const keep = rows.flatMap((_, i) => (at[i]?.includes(null) ? [] : [i]));\n skippedRows(\n `maplibre:routes:${o.league}:missing`,\n \"route leg(s)\",\n rows.flatMap((_, i) => (at[i]?.includes(null) ? [teams[i] as Value] : [])),\n );\n const kept = keep.map((i) => teams[i] as Value);\n const ro = {\n ...(o.season === undefined ? {} : { season: o.season }),\n ...(o.idSystem === undefined ? {} : { idSystem: o.idSystem }),\n };\n const resolved = resolveSync(kept, o.league, ro);\n // keyed by the resolved team, so 12 and \"12\" (or \"KC\" and 12) are one team with one colour\n const keys = kept.map((t, j) => resolved[j] ?? `?${label(t)}`);\n const distinct = [...new Set(keys)];\n const known = distinct.filter((k) => !k.startsWith(\"?\"));\n const palette = colorPalette(o.league, known, {\n ...ro,\n idSystem: \"team_id\",\n ...(o.which === undefined ? {} : { which: o.which }),\n ...(o.background === undefined ? {} : { background: o.background }),\n ...(o.fallback === undefined ? {} : { fallback: o.fallback }),\n });\n const colorOf = new Map(known.map((k, j) => [k, palette[j] as string]));\n const fallback = o.fallback ?? \"#808080\";\n const features = keep.map((i, j): Feature<LineGeometry, RouteProperties> => {\n const c = at[i] as number[];\n return {\n type: \"Feature\",\n id: i,\n geometry: {\n type: \"LineString\",\n coordinates: [\n [c[0] as number, c[1] as number],\n [c[2] as number, c[3] as number],\n ],\n },\n properties: {\n team: resolved[j] ?? label(kept[j]),\n color: colorOf.get(keys[j] as string) ?? fallback,\n index: i,\n },\n };\n });\n return {\n id,\n source: { type: \"geojson\", data: { type: \"FeatureCollection\", features } },\n layer: {\n id,\n type: \"line\",\n source: id,\n layout: { \"line-cap\": \"round\", \"line-join\": \"round\" },\n paint: { \"line-color\": [\"get\", \"color\"], \"line-width\": width, \"line-opacity\": opacity },\n },\n };\n}\n\n/** OpenFreeMap's credit, exactly as its TileJSON (`https://tiles.openfreemap.org/planet`) gives it (read 2026-10-27). */\nconst OPENFREEMAP_ATTRIBUTION =\n '<a href=\"https://openfreemap.org\" target=\"_blank\">OpenFreeMap</a> <a href=\"https://www.openmaptiles.org/\" target=\"_blank\">&copy; OpenMapTiles</a> Data from <a href=\"https://www.openstreetmap.org/copyright\" target=\"_blank\">OpenStreetMap</a>';\n\n/** `basemap`'s result. */\nexport interface Basemap {\n /** The style URL, for the map's `style` option. */\n style: string;\n /**\n * The provider's credit (HTML). MapLibre shows it by itself from the style's source, in its attribution control (on\n * by default; keep it). Anything else that draws these tiles, or a static export of the map, must show it.\n */\n attribution: string;\n /**\n * The colour of the style's `background` layer, the land (read from the style on 2026-10-27), for `routeLines`' and\n * `passArcLayer`'s `background` (dark mode). Their 3:1 contrast is measured against it, not against the water,\n * which is a little lighter on the dark style (#1b1b1d) and a little darker on the light one (#c2c8ca).\n */\n background: string;\n}\nconst STYLES = {\n light: { style: \"https://tiles.openfreemap.org/styles/positron\", background: \"#f2f3f0\" },\n dark: { style: \"https://tiles.openfreemap.org/styles/dark\", background: \"#0c0c0c\" },\n} as const;\n\n/**\n * A free basemap that needs no API key: OpenFreeMap's `positron` (light) or `dark` vector style, built from\n * OpenStreetMap data. Pass `style` to `new maplibregl.Map({ style })`. OpenFreeMap's public instance asks for no\n * registration or key and sets no request limit; the credit is required, and MapLibre's attribution control shows it\n * from the style's source (`attribution` is the same text, for anything else). Each map view requests the\n * provider's tiles: build or test pages without a map, or serve a local style.\n *\n * @example\n * ```ts\n * import { basemap } from \"@sportsdataverse/sdvplot/maplibre\";\n *\n * const { style, background } = basemap(\"dark\");\n * // in the browser: new maplibregl.Map({ container, style, center: [-96, 38], zoom: 3 })\n * [style, background];\n * ```\n */\nexport function basemap(theme: \"light\" | \"dark\" = \"light\"): Basemap {\n const s = STYLES[theme];\n if (s === undefined) throw new InputError(`theme is \"light\" or \"dark\", got ${JSON.stringify(theme)}`);\n return { ...s, attribution: OPENFREEMAP_ATTRIBUTION };\n}\n\n/**\n * What `logoSymbols` sets draw, as `[team, longitude, latitude, px, url]` per feature, in order: the module's test\n * hook, as the other adapters' `drawnMarks`.\n *\n * @example\n * ```ts\n * import { loadLeague } from \"@sportsdataverse/sdvplot\";\n * import { drawnMarks, logoSymbols } from \"@sportsdataverse/sdvplot/maplibre\";\n *\n * await loadLeague(\"nfl\");\n * // 12 is Kansas City's ESPN team id\n * drawnMarks(logoSymbols([{ team: 12, lon: -94.714, lat: 39.297 }], { x: \"lon\", y: \"lat\", team: \"team\", league: \"nfl\" }));\n * ```\n */\nexport function drawnMarks(...sets: readonly LogoSymbols[]): DrawnMark[] {\n return sets.flatMap((s) => {\n const px = new Map(s.images.map((im) => [im.id, im.height]));\n return s.source.data.features.map(\n (f): DrawnMark => [\n f.properties.team,\n f.geometry.coordinates[0],\n f.geometry.coordinates[1],\n px.get(f.properties.icon) as number,\n f.properties.url,\n ],\n );\n });\n}\n"]}