@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.
- package/dist/index.d.ts +577 -0
- package/dist/index.js +1460 -0
- package/dist/index.js.map +1 -0
- package/dist/usecase-demos/index.d.ts +357 -0
- package/dist/usecase-demos/index.js +25708 -0
- package/dist/usecase-demos/index.js.map +1 -0
- package/package.json +49 -0
package/dist/index.d.ts
ADDED
|
@@ -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 };
|