@invana/graph-datasets 0.0.1

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.
@@ -0,0 +1,577 @@
1
+ /**
2
+ * Les Misérables character co-occurrence network.
3
+ *
4
+ * Character relationships from Victor Hugo's *Les Misérables* novel —
5
+ * 77 characters, 254 undirected co-occurrence edges weighted by how many
6
+ * scenes they share. Each character belongs to one of 11 "groups" (loosely:
7
+ * the cluster of characters they appear with most). Useful as a small but
8
+ * structurally-rich force-directed layout demo.
9
+ *
10
+ * Source: Donald E. Knuth, *The Stanford GraphBase: A Platform for
11
+ * Combinatorial Computing*, 1993. Adapted from the D3.js examples.
12
+ *
13
+ * Export shape is structurally compatible with `GraphData` from
14
+ * `@invana/graph` — pass it straight to `graph.setData()`.
15
+ *
16
+ * @example
17
+ * import { lesMiserables } from '@invana/graph-datasets';
18
+ * graph.setData(lesMiserables);
19
+ */
20
+ interface LesMiserablesNodeData {
21
+ /** Co-occurrence cluster (0–10). Used for colour-by-group in stories. */
22
+ group: number;
23
+ }
24
+ interface LesMiserablesEdgeData {
25
+ /** Number of scenes the two characters share (edge weight, 1–31). */
26
+ value: number;
27
+ }
28
+ interface LesMiserablesNode {
29
+ id: string;
30
+ data: LesMiserablesNodeData;
31
+ }
32
+ interface LesMiserablesEdge {
33
+ id: string;
34
+ source: string;
35
+ target: string;
36
+ data: LesMiserablesEdgeData;
37
+ }
38
+ interface LesMiserablesData {
39
+ nodes: LesMiserablesNode[];
40
+ edges: LesMiserablesEdge[];
41
+ }
42
+ /** Les Misérables co-occurrence network — pass straight to `graph.setData()`. */
43
+ declare const lesMiserables: LesMiserablesData;
44
+
45
+ /**
46
+ * Procedurally-generated tree, for force-layout stress tests and
47
+ * tree-shaped demos.
48
+ *
49
+ * Node `i + 1`'s parent is node `floor(sqrt(i))`, which yields a
50
+ * branchy, square-root-balanced tree in O(n) time with no RNG.
51
+ *
52
+ * The export uses a minimal `{ index }` / `{ source, target }` shape.
53
+ * Map it onto `GraphNode` / `GraphEdge` at the call site (see the
54
+ * `RandomTree` story for an example).
55
+ *
56
+ * @example
57
+ * import { generateRandomTree } from '@invana/graph-datasets';
58
+ * const tree = generateRandomTree(500);
59
+ */
60
+ interface RandomTreeNode {
61
+ index: number;
62
+ }
63
+ interface RandomTreeEdge {
64
+ source: number;
65
+ target: number;
66
+ }
67
+ interface RandomTreeData {
68
+ nodes: RandomTreeNode[];
69
+ edges: RandomTreeEdge[];
70
+ }
71
+ declare const generateRandomTree: (numNodes: number) => RandomTreeData;
72
+
73
+ /**
74
+ * The classic **Flare** software hierarchy — the same dataset used in
75
+ * d3-hierarchy's tidy / radial / treemap examples.
76
+ *
77
+ * Two shapes are exposed:
78
+ * - `flareHierarchy` — the original nested `{name, value?, children?}` tree.
79
+ * - `flareAsGraph()` — a flat `{nodes, edges}` projection that drops
80
+ * straight into `GraphLayer.setData`. Each edge points from parent to
81
+ * child, so a hierarchy layout (e.g. `D3HierarchyLayout`) can read the
82
+ * topology directly off `edge.source → edge.target`.
83
+ *
84
+ * @example
85
+ * import { flareAsGraph } from '@invana/graph-datasets';
86
+ * graphLayer.setData(flareAsGraph());
87
+ */
88
+ /** Node shape in the original Flare hierarchy. Leaves carry `value`; inner
89
+ * nodes carry `children`. The root has neither field guaranteed. */
90
+ interface FlareNode {
91
+ name: string;
92
+ value?: number;
93
+ children?: FlareNode[];
94
+ }
95
+ /** A single node in the flat projection. The id is the slash-joined path
96
+ * from the root (e.g. `flare/analytics/cluster/AgglomerativeCluster`) so
97
+ * it survives duplicate `name` values across branches. */
98
+ interface FlareGraphNode {
99
+ id: string;
100
+ data: {
101
+ /** Original `name` field. Convenient for labels once those land. */
102
+ name: string;
103
+ /** Depth from root. Root is 0. */
104
+ depth: number;
105
+ /** True iff this node has no children. */
106
+ isLeaf: boolean;
107
+ /** Original `value` field, if present (only on leaves). */
108
+ value?: number;
109
+ /**
110
+ * Top-level category — the depth-1 ancestor's name (e.g. `analytics`,
111
+ * `animate`, `data`, ...). Depth-1 nodes carry their own name here;
112
+ * the root (depth 0) carries `undefined`. Drives categorical colour
113
+ * in the bubble-chart layout, since every leaf inherits the same
114
+ * `group` as every other leaf under the same top-level branch.
115
+ */
116
+ group?: string;
117
+ };
118
+ }
119
+ /** A single parent→child edge in the flat projection. */
120
+ interface FlareGraphEdge {
121
+ id: string;
122
+ source: string;
123
+ target: string;
124
+ }
125
+ /** Output of {@link flareAsGraph}. */
126
+ interface FlareGraphData {
127
+ nodes: FlareGraphNode[];
128
+ edges: FlareGraphEdge[];
129
+ }
130
+ /** The original Flare hierarchy in its nested form. */
131
+ declare const flareHierarchy: FlareNode;
132
+ /**
133
+ * Flatten {@link flareHierarchy} to a `{nodes, edges}` shape compatible with
134
+ * `GraphLayer.setData`. BFS-traverses the tree, assigning slash-joined path
135
+ * ids so duplicate names across branches stay distinct.
136
+ */
137
+ declare function flareAsGraph(): FlareGraphData;
138
+
139
+ /**
140
+ * Flare **with synthetic class-imports** — companion to {@link flareAsGraph}
141
+ * for the d3 *Hierarchical Edge Bundling* demo
142
+ * (https://observablehq.com/@d3/hierarchical-edge-bundling/2).
143
+ *
144
+ * The Observable demo ships a flat `flare-imports.json` where every leaf
145
+ * class carries an `imports: string[]` list of other class names it
146
+ * "depends on". We don't bundle that file (no network fetch available at
147
+ * dataset-build time); instead we generate a deterministic synthetic
148
+ * imports graph over the existing nested `flare.json`, biased so that:
149
+ *
150
+ * - each leaf imports a small handful of other leaves (1–5);
151
+ * - ~70% of imports stay inside the leaf's depth-1 package
152
+ * (`flare.analytics`, `flare.vis`, ...); the rest reach across packages.
153
+ *
154
+ * That bias is what *makes* hierarchical edge bundling worth looking at:
155
+ * intra-package edges form dense, tightly-bundled arcs through one parent;
156
+ * inter-package edges sweep across the centre. Visually it reproduces the
157
+ * d3 demo faithfully; only the specific source/target pairs differ.
158
+ *
159
+ * The generator is seeded by the leaf's id, so the same call always
160
+ * produces the same edges — stories stay snapshot-stable across reloads.
161
+ *
162
+ * @example
163
+ * import { flareImportsAsGraph } from '@invana/graph-datasets';
164
+ * const { nodes, treeEdges, importEdges } = flareImportsAsGraph();
165
+ * graph.setData({ nodes, edges: treeEdges });
166
+ * await new D3HierarchyLayout({ mode: 'radial-cluster' }).apply(graph);
167
+ * // ... then swap to importEdges and render with `pathType: 'bundle'`.
168
+ */
169
+
170
+ /** A synthetic leaf→leaf import edge. */
171
+ interface FlareImportEdge {
172
+ id: string;
173
+ source: string;
174
+ target: string;
175
+ }
176
+ /** Output of {@link flareImportsAsGraph}. */
177
+ interface FlareImportsGraphData {
178
+ nodes: FlareGraphNode[];
179
+ /** Parent→child edges from the flare hierarchy. Feed these to the layout. */
180
+ treeEdges: FlareGraphEdge[];
181
+ /** Synthetic leaf→leaf import edges. Render these as bundled curves. */
182
+ importEdges: FlareImportEdge[];
183
+ }
184
+ /** Options for {@link flareImportsAsGraph}. */
185
+ interface FlareImportsOptions {
186
+ /** Minimum import out-degree per leaf. Default `1`. */
187
+ readonly minImportsPerLeaf?: number;
188
+ /** Maximum import out-degree per leaf. Default `5`. */
189
+ readonly maxImportsPerLeaf?: number;
190
+ /**
191
+ * Probability that a generated import targets a leaf in the same depth-1
192
+ * package as its source (vs a leaf in any other package). Default `0.7`
193
+ * — produces a dense-intra-package + sparse-cross-package mix that
194
+ * bundles cleanly through the hierarchy.
195
+ */
196
+ readonly intraGroupBias?: number;
197
+ }
198
+ /**
199
+ * Build the Flare hierarchy plus a deterministic synthetic imports graph
200
+ * over its leaves. See module doc for the generation policy.
201
+ */
202
+ declare function flareImportsAsGraph(opts?: FlareImportsOptions): FlareImportsGraphData;
203
+
204
+ /**
205
+ * **H-1B 2019** — USCIS H-1B employer petition counts for fiscal year 2019,
206
+ * aggregated into a **State → City → Employer** hierarchy. The same dataset
207
+ * used in the d3 [`pack-rollup`](https://observablehq.com/@d3/pack-rollup/2)
208
+ * example. Source: [USCIS H-1B Data Hub](https://www.uscis.gov/h-1b-data-hub).
209
+ *
210
+ * Aggregation was done offline (see `tools/build-h1b2019.ts` or the
211
+ * data-import note below) so the package ships a single static JSON instead
212
+ * of a 1.8 MB CSV that would need parsing at import time. The leaf `value` is
213
+ * the sum of all four petition outcomes for that employer / city / state:
214
+ *
215
+ * value = InitialApprovals + InitialDenials
216
+ * + ContinuingApprovals + ContinuingDenials
217
+ *
218
+ * matching d3.rollup's:
219
+ *
220
+ * d3.rollup(rows, D => d3.sum(D, d => d.IA + d.ID + d.CA + d.CD),
221
+ * d => d.State, d => d.City, d => d.Employer)
222
+ *
223
+ * Employers with a zero total are pruned during aggregation — d3.pack treats
224
+ * zero-value leaves as 0-radius circles, so removing them avoids cluttering
225
+ * the layout with invisible nodes.
226
+ *
227
+ * Two shapes are exposed, mirroring the Flare API:
228
+ * - `h1b2019Hierarchy` — the original nested `{name, value?, children?}` tree.
229
+ * - `h1b2019AsGraph()` — flat `{nodes, edges}` projection, ready for
230
+ * `GraphLayer.setData`.
231
+ *
232
+ * @example
233
+ * import { h1b2019AsGraph } from '@invana/graph-datasets';
234
+ * graphLayer.setData(h1b2019AsGraph());
235
+ */
236
+ /**
237
+ * Node shape in the rolled-up H-1B hierarchy. Identical structure to
238
+ * {@link FlareNode}: leaves carry `value`, inner nodes (root, states, cities)
239
+ * carry `children`.
240
+ */
241
+ interface H1B2019Node {
242
+ name: string;
243
+ value?: number;
244
+ children?: H1B2019Node[];
245
+ }
246
+ /**
247
+ * A single node in the flat projection. The id is the slash-joined path
248
+ * from the root (e.g. `H-1B 2019/CA/SAN JOSE/GOOGLE LLC`) so duplicate
249
+ * employer / city names across states stay distinct.
250
+ */
251
+ interface H1B2019GraphNode {
252
+ id: string;
253
+ data: {
254
+ /** Original `name` field — state abbreviation, city name, or employer. */
255
+ name: string;
256
+ /** Depth from root. Root = 0, state = 1, city = 2, employer = 3. */
257
+ depth: number;
258
+ /** True iff this node has no children (i.e. an employer leaf). */
259
+ isLeaf: boolean;
260
+ /** Sum of petition counts for this leaf. Inner nodes omit it. */
261
+ value?: number;
262
+ /**
263
+ * Top-level category — the depth-1 ancestor's name (the U.S. state
264
+ * abbreviation, e.g. `CA`, `NY`, `TX`). Depth-1 nodes carry their own
265
+ * name here; the root (depth 0) carries `undefined`. Drives categorical
266
+ * colouring by state for bubble-chart-style renders.
267
+ */
268
+ group?: string;
269
+ };
270
+ }
271
+ /** A single parent→child edge in the flat projection. */
272
+ interface H1B2019GraphEdge {
273
+ id: string;
274
+ source: string;
275
+ target: string;
276
+ }
277
+ /** Output of {@link h1b2019AsGraph}. */
278
+ interface H1B2019GraphData {
279
+ nodes: H1B2019GraphNode[];
280
+ edges: H1B2019GraphEdge[];
281
+ }
282
+ /** The H-1B 2019 hierarchy in its nested form. */
283
+ declare const h1b2019Hierarchy: H1B2019Node;
284
+ /**
285
+ * Flatten {@link h1b2019Hierarchy} to a `{nodes, edges}` shape compatible with
286
+ * `GraphLayer.setData`. BFS-traverses the tree, assigning slash-joined path
287
+ * ids so duplicate names across branches stay distinct.
288
+ *
289
+ * Returns ~55 states + ~3 000 cities + ~22 000 employer leaves; allocating
290
+ * each call is cheap (one pass over a 1 MB JSON), so consumers can re-call
291
+ * after filtering settings change without caching.
292
+ */
293
+ declare function h1b2019AsGraph(): H1B2019GraphData;
294
+
295
+ /**
296
+ * The classic **Tree of Life** — a small phylogenetic tree of 145 prokaryote +
297
+ * eukaryote species partitioned into the three top-level kingdoms (Bacteria,
298
+ * Eukaryota, Archaea). It's the dataset behind d3's
299
+ * [Tree of Life](https://observablehq.com/@d3/tree-of-life) example, sourced
300
+ * from Ciccarelli et al. (2006).
301
+ *
302
+ * Two shapes are exposed:
303
+ * - `lifeTreeHierarchy` — the parsed Newick tree as a `{name, length, children?}`
304
+ * nested object.
305
+ * - `lifeTreeAsGraph()` — a flat `{nodes, edges}` projection that drops
306
+ * straight into `GraphLayer.setData`. Each edge points from parent to
307
+ * child; each node carries its inherited `kingdom` so consumers can colour
308
+ * sub-trees by domain of life.
309
+ *
310
+ * @example
311
+ * import { lifeTreeAsGraph } from '@invana/graph-datasets';
312
+ * graphLayer.setData(lifeTreeAsGraph());
313
+ */
314
+ /** The three domains of life. Set on every node beneath a top-level clade. */
315
+ type LifeTreeKingdom = 'Bacteria' | 'Eukaryota' | 'Archaea';
316
+ /** Node shape in the parsed Newick hierarchy. */
317
+ interface LifeTreeNode {
318
+ /** Clade or species name. May be empty for anonymous internal nodes. */
319
+ name: string;
320
+ /** Branch length to parent (substitution rate). `undefined` on the root. */
321
+ length?: number;
322
+ /** Child clades. Leaves omit this field. */
323
+ children?: LifeTreeNode[];
324
+ }
325
+ /** A single node in the flat projection. */
326
+ interface LifeTreeGraphNode {
327
+ id: string;
328
+ data: {
329
+ /** Original Newick name. Empty for anonymous internal clades. */
330
+ name: string;
331
+ /** Depth from root. Root is 0. */
332
+ depth: number;
333
+ /** True iff this node has no children. */
334
+ isLeaf: boolean;
335
+ /** Branch length to parent (substitution rate). `undefined` on the root. */
336
+ length?: number;
337
+ /**
338
+ * Top-level domain of life. Inherited from the nearest ancestor whose name
339
+ * is `'Bacteria'`, `'Eukaryota'`, or `'Archaea'`. The root and its two
340
+ * pre-domain wrapper nodes have no kingdom.
341
+ */
342
+ kingdom?: LifeTreeKingdom;
343
+ };
344
+ }
345
+ /** A single parent→child edge in the flat projection. */
346
+ interface LifeTreeGraphEdge {
347
+ id: string;
348
+ source: string;
349
+ target: string;
350
+ }
351
+ /** Output of {@link lifeTreeAsGraph}. */
352
+ interface LifeTreeGraphData {
353
+ nodes: LifeTreeGraphNode[];
354
+ edges: LifeTreeGraphEdge[];
355
+ }
356
+ /** The parsed Tree of Life as a nested hierarchy. Computed once. */
357
+ declare const lifeTreeHierarchy: LifeTreeNode;
358
+ /**
359
+ * Flatten {@link lifeTreeHierarchy} to a `{nodes, edges}` shape compatible
360
+ * with `GraphLayer.setData`. BFS-traverses the tree, assigning slash-joined
361
+ * path ids so anonymous internal clades — and duplicate names across branches
362
+ * — stay distinct. Each node carries its inherited `kingdom`.
363
+ *
364
+ * The Newick source reuses generic clade labels (e.g. `Bacteria_subclade`)
365
+ * across many sibling positions, so a plain slash-path can collide. When that
366
+ * happens, the colliding child gets a numeric suffix (`_2`, `_3`, …) so every
367
+ * emitted id is unique while the readable path is preserved in the common
368
+ * case.
369
+ */
370
+ declare function lifeTreeAsGraph(): LifeTreeGraphData;
371
+
372
+ /**
373
+ * UK energy-flow dataset — the canonical d3-sankey example fixture.
374
+ *
375
+ * 48 nodes, 68 weighted links representing 2050 UK energy flows from
376
+ * supply (coal reserves, oil reserves, nuclear, renewables, imports) →
377
+ * intermediate carriers (oil, gas, electricity grid, solid / liquid /
378
+ * gaseous fuels) → end use (industry, heating, transport, losses).
379
+ *
380
+ * Source: DECC 2050 Pathways (UK Department of Energy and Climate Change),
381
+ * via Tom Counsell's d3-sankey example used since 2012. Reproduced from
382
+ * `https://bost.ocks.org/mike/sankey/energy.json`. Distributed by the
383
+ * original author under the same MIT terms as `d3-sankey`.
384
+ *
385
+ * Two shapes are exposed:
386
+ * - `ukEnergyFlow` — the original `{nodes:[{name}], links:[{source,target,value}]}`
387
+ * shape (numeric indices on the links), suitable for direct use with
388
+ * `d3.sankey()`.
389
+ * - `ukEnergyFlowAsGraph()` — a `{nodes, edges}` projection ready for
390
+ * `GraphLayer.setData`. Node ids are the original `name` field; edge
391
+ * ids are `<source>--<target>` (no duplicates in this dataset).
392
+ *
393
+ * @example
394
+ * import { ukEnergyFlowAsGraph } from '@invana/graph-datasets';
395
+ * graphLayer.setData(ukEnergyFlowAsGraph());
396
+ */
397
+ /** Original (numeric-index) node shape. */
398
+ interface UkEnergyFlowNode {
399
+ name: string;
400
+ }
401
+ /** Original (numeric-index) link shape — `source` / `target` are indices
402
+ * into the `nodes` array. */
403
+ interface UkEnergyFlowLink {
404
+ source: number;
405
+ target: number;
406
+ value: number;
407
+ }
408
+ /** Original `{nodes, links}` shape (matches d3-sankey's expected input). */
409
+ interface UkEnergyFlow {
410
+ nodes: UkEnergyFlowNode[];
411
+ links: UkEnergyFlowLink[];
412
+ }
413
+ /** Node in the flat `{nodes, edges}` projection. */
414
+ interface UkEnergyFlowGraphNode {
415
+ id: string;
416
+ data: {
417
+ /** Original `name` field — used by Sankey labels. */
418
+ name: string;
419
+ /**
420
+ * Categorical bucket derived from the node name's first whitespace-
421
+ * separated word (`'Solar PV'` becomes `'Solar'`, `'Coal reserves'`
422
+ * becomes `'Coal'`). Mirrors the d3 example's first-word replace key
423
+ * so a 10-colour ordinal scale produces the same grouping as the
424
+ * canonical Observable port.
425
+ */
426
+ category: string;
427
+ };
428
+ }
429
+ /** Edge in the flat projection. `value` is the flow magnitude (TWh). */
430
+ interface UkEnergyFlowGraphEdge {
431
+ id: string;
432
+ source: string;
433
+ target: string;
434
+ data: {
435
+ value: number;
436
+ };
437
+ }
438
+ /** Output of {@link ukEnergyFlowAsGraph}. */
439
+ interface UkEnergyFlowGraphData {
440
+ nodes: UkEnergyFlowGraphNode[];
441
+ edges: UkEnergyFlowGraphEdge[];
442
+ }
443
+ /** Original dataset, untouched. Pass straight to `d3.sankey()` if you want
444
+ * to drive the layout yourself; otherwise use {@link ukEnergyFlowAsGraph}. */
445
+ declare const ukEnergyFlow: UkEnergyFlow;
446
+ /**
447
+ * Project {@link ukEnergyFlow} to `{nodes, edges}` for `GraphLayer.setData`.
448
+ *
449
+ * The mapping:
450
+ * - Numeric link endpoints → string ids (the node `name`).
451
+ * - Each node carries `data.category` for colour grouping.
452
+ * - Edge ids are `<source>--<target>`; the source dataset has no duplicate
453
+ * pairs, so no extra disambiguation is needed.
454
+ */
455
+ declare function ukEnergyFlowAsGraph(): UkEnergyFlowGraphData;
456
+
457
+ /**
458
+ * Old Faithful geyser eruptions — 272 measurements.
459
+ *
460
+ * Each record pairs an eruption's duration in minutes (`eruptions`, 1.5–5.1)
461
+ * with the time in minutes until the next eruption (`waiting`, 43–96). The
462
+ * dataset is famously bimodal — two distinct (short, short-wait) and
463
+ * (long, long-wait) clusters — which makes it a canonical demo for 2D
464
+ * density estimators. This is the source dataset behind the Observable
465
+ * [`@d3/density-contours`](https://observablehq.com/@d3/density-contours)
466
+ * example.
467
+ *
468
+ * Source: W. Härdle (1991), *Smoothing Techniques with Implementation in S*,
469
+ * New York: Springer. Public domain; bundled with R as `datasets::faithful`.
470
+ *
471
+ * The exported `oldFaithful` is a nodes-only `GraphData` ready for
472
+ * `GraphLayer.setData()` — each point becomes a node positioned at
473
+ * `(waiting, eruptions * 20)` so that the two axes have comparable spread in
474
+ * world units (waiting ≈ 53 units of range, scaled eruptions ≈ 72 units).
475
+ * No edges.
476
+ *
477
+ * @example
478
+ * import { oldFaithful } from '@invana/graph-datasets';
479
+ * graph.setData(oldFaithful);
480
+ */
481
+ interface OldFaithfulPoint {
482
+ /** Eruption duration in minutes. */
483
+ eruptions: number;
484
+ /** Minutes elapsed before the next eruption. */
485
+ waiting: number;
486
+ }
487
+ interface OldFaithfulNodeData extends OldFaithfulPoint {
488
+ /**
489
+ * Bimodal cluster the point most likely belongs to — `'short'` for the
490
+ * brief eruption / short-wait cluster, `'long'` for the long-duration /
491
+ * long-wait cluster. Determined by a 3-minute split on `eruptions`.
492
+ * Useful for colour-by-cluster stories.
493
+ */
494
+ cluster: 'short' | 'long';
495
+ }
496
+ interface OldFaithfulNode {
497
+ id: string;
498
+ data: OldFaithfulNodeData;
499
+ position: {
500
+ x: number;
501
+ y: number;
502
+ };
503
+ }
504
+ interface OldFaithfulGraphData {
505
+ nodes: OldFaithfulNode[];
506
+ edges: never[];
507
+ }
508
+ declare const oldFaithful: OldFaithfulGraphData;
509
+
510
+ /**
511
+ * `air-routes` — world airports + land outline geo data.
512
+ *
513
+ * Companion dataset for the maplibre stories. Mirrors the data referenced
514
+ * by Observable's
515
+ * [`@d3/world-airports`](https://observablehq.com/@d3/world-airports) —
516
+ * `airports.csv` (Natural Earth airports point set) and a 1:50m
517
+ * land-only TopoJSON for the basemap stroke.
518
+ *
519
+ * See `air-routes/README.md` for sources, licensing, and the exact files
520
+ * the JSON in this folder was derived from.
521
+ *
522
+ * @example
523
+ * ```ts
524
+ * import { airports, landTopology } from '@invana/graph-datasets';
525
+ *
526
+ * for (const a of airports) {
527
+ * const { x, y } = mapLayer.project([a.lng, a.lat]);
528
+ * // ...
529
+ * }
530
+ * ```
531
+ */
532
+ /** A single airport point. */
533
+ interface Airport {
534
+ /** Airport name from the source CSV (e.g. `"London Heathrow Airport"`). */
535
+ name: string;
536
+ /** Longitude in degrees, WGS-84. */
537
+ lng: number;
538
+ /** Latitude in degrees, WGS-84. */
539
+ lat: number;
540
+ }
541
+ /**
542
+ * Loose TopoJSON shape — the world-50m land outline used by the d3
543
+ * notebook. Typed structurally (no `topojson-specification` dep) since
544
+ * consumers either pass this straight to `topojson-client.feature(...)`
545
+ * or pluck `.objects.land` directly.
546
+ */
547
+ interface LandTopology {
548
+ type: 'Topology';
549
+ bbox?: [number, number, number, number];
550
+ transform?: {
551
+ scale: [number, number];
552
+ translate: [number, number];
553
+ };
554
+ arcs: number[][][];
555
+ objects: {
556
+ land: {
557
+ type: 'MultiPolygon' | 'GeometryCollection';
558
+ arcs?: unknown;
559
+ geometries?: unknown[];
560
+ };
561
+ };
562
+ }
563
+ /**
564
+ * All `2980` airport points from `airports.csv`. Order matches the source
565
+ * CSV; ids are not assigned here because the source has no stable id
566
+ * column — story code typically synthesizes ids from the array index
567
+ * (`'ap-' + i`).
568
+ */
569
+ declare const airports: Airport[];
570
+ /**
571
+ * 1:50m world land outline as TopoJSON. Pair with `topojson-client.feature`
572
+ * to materialize the GeoJSON `MultiPolygon`, then sample with `d3-geo`'s
573
+ * `geoPath` over a custom canvas projection.
574
+ */
575
+ declare const landTopology: LandTopology;
576
+
577
+ export { type Airport, type FlareGraphData, type FlareGraphEdge, type FlareGraphNode, type FlareImportEdge, type FlareImportsGraphData, type FlareImportsOptions, type FlareNode, type H1B2019GraphData, type H1B2019GraphEdge, type H1B2019GraphNode, type H1B2019Node, type LandTopology, type LesMiserablesData, type LesMiserablesEdge, type LesMiserablesEdgeData, type LesMiserablesNode, type LesMiserablesNodeData, type LifeTreeGraphData, type LifeTreeGraphEdge, type LifeTreeGraphNode, type LifeTreeKingdom, type LifeTreeNode, type OldFaithfulGraphData, type OldFaithfulNode, type OldFaithfulNodeData, type OldFaithfulPoint, type RandomTreeData, type RandomTreeEdge, type RandomTreeNode, type UkEnergyFlow, type UkEnergyFlowGraphData, type UkEnergyFlowGraphEdge, type UkEnergyFlowGraphNode, type UkEnergyFlowLink, type UkEnergyFlowNode, airports, flareAsGraph, flareHierarchy, flareImportsAsGraph, generateRandomTree, h1b2019AsGraph, h1b2019Hierarchy, landTopology, lesMiserables, lifeTreeAsGraph, lifeTreeHierarchy, oldFaithful, ukEnergyFlow, ukEnergyFlowAsGraph };