@mailwoman/tiger 1.0.0 → 4.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/sdk/fetch.d.ts +72 -0
- package/out/sdk/fetch.d.ts.map +1 -1
- package/out/sdk/fetch.js +267 -0
- package/out/sdk/fetch.js.map +1 -1
- package/out/sdk/index.d.ts +3 -0
- package/out/sdk/index.d.ts.map +1 -1
- package/out/sdk/index.js +3 -0
- package/out/sdk/index.js.map +1 -1
- package/out/sdk/redistricting.d.ts +57 -0
- package/out/sdk/redistricting.d.ts.map +1 -1
- package/out/sdk/redistricting.js +184 -0
- package/out/sdk/redistricting.js.map +1 -1
- package/out/sdk/schema.d.ts +88 -0
- package/out/sdk/schema.d.ts.map +1 -1
- package/out/sdk/schema.js +130 -0
- package/out/sdk/schema.js.map +1 -1
- package/out/sdk/state/TIGERTabulatedBlockEntity.d.ts +1 -1
- package/out/tsconfig.tsbuildinfo +1 -1
- package/package.json +13 -3
- package/sdk/fetch.ts +323 -0
- package/sdk/index.ts +3 -0
- package/sdk/redistricting.ts +223 -0
- package/sdk/schema.ts +203 -0
- package/sdk/initialize.sql +0 -161
package/sdk/schema.ts
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software.
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* TIGER SQLite schema, as a string so it loads in both `tsx` source mode and the compiled CLI
|
|
7
|
+
* (`tsc` doesn't copy `.sql` assets into `out/`). Geometry is GeoJSON text — NOT SpatiaLite. The
|
|
8
|
+
* prior SpatiaLite path (`load_extension` of a hardcoded macOS dylib + WKB `GEOM` columns) never
|
|
9
|
+
* loaded on Linux; plain text keeps the build dependency-free and `node:sqlite`-native (read it
|
|
10
|
+
* back with `JSON.parse`).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { sql, type Generated, type Kysely } from "kysely"
|
|
14
|
+
|
|
15
|
+
/** Kysely row type for `tabblock20`. Geometry is a GeoJSON string. */
|
|
16
|
+
export interface TIGERBlockTable {
|
|
17
|
+
GEOID: string
|
|
18
|
+
state_code: string
|
|
19
|
+
county_code: string
|
|
20
|
+
tract_code: string
|
|
21
|
+
block_group_code: string
|
|
22
|
+
block_code: string
|
|
23
|
+
urbanized_area_code: string | null
|
|
24
|
+
urban_rural_code: string | null
|
|
25
|
+
housing_unit_count: number
|
|
26
|
+
land_area_sqm: number
|
|
27
|
+
water_area_sqm: number
|
|
28
|
+
population: number
|
|
29
|
+
geometry: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Kysely row type for `pl_block` — Census 2020 P.L. 94-171 table P2 (Hispanic-or-Latino by race),
|
|
34
|
+
* one row per tabulation block, keyed on the same 15-char `GEOID` as {@link TIGERBlockTable}. The
|
|
35
|
+
* eight category columns partition `pop_total`.
|
|
36
|
+
*/
|
|
37
|
+
export interface PLBlockTable {
|
|
38
|
+
GEOID: string
|
|
39
|
+
pop_total: number
|
|
40
|
+
hispanic: number
|
|
41
|
+
white: number
|
|
42
|
+
black: number
|
|
43
|
+
aian: number
|
|
44
|
+
asian: number
|
|
45
|
+
nhpi: number
|
|
46
|
+
other: number
|
|
47
|
+
multi: number
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Kysely row type for `tiger_streets` (ADDRFEAT — named street segments + ZIPs, per county). */
|
|
51
|
+
export interface TIGERStreetTable {
|
|
52
|
+
linearid: string
|
|
53
|
+
fullname: string
|
|
54
|
+
zipl: string | null
|
|
55
|
+
zipr: string | null
|
|
56
|
+
statefp: string
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Kysely row type for `tiger_places` (PLACE — incorporated/census places, per state). */
|
|
60
|
+
export interface TIGERPlaceTable {
|
|
61
|
+
geoid: string
|
|
62
|
+
name: string
|
|
63
|
+
statefp: string
|
|
64
|
+
lsad: string | null
|
|
65
|
+
namelsad: string | null
|
|
66
|
+
classfp: string | null
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** The TIGER database schema, for `new DatabaseClient<TIGERDatabase>(...)`. */
|
|
70
|
+
export interface TIGERDatabase {
|
|
71
|
+
tabblock20: TIGERBlockTable
|
|
72
|
+
pl_block: PLBlockTable
|
|
73
|
+
tiger_streets: TIGERStreetTable
|
|
74
|
+
tiger_places: TIGERPlaceTable
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Marker so callers can opt into `Generated` columns later without importing kysely here. */
|
|
78
|
+
export type { Generated }
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Build-tuning PRAGMAs, run raw before any table is created (`page_size`/`auto_vacuum` only take
|
|
82
|
+
* effect on an empty DB, and PRAGMA has no Kysely builder). The consumer execs this, then calls
|
|
83
|
+
* {@link initializeTIGERSchema} for the tables + indexes.
|
|
84
|
+
*/
|
|
85
|
+
export const TIGER_PRAGMAS = /* sql */ `
|
|
86
|
+
PRAGMA auto_vacuum = INCREMENTAL;
|
|
87
|
+
PRAGMA page_size = 4096;
|
|
88
|
+
PRAGMA cache_size = 10000;
|
|
89
|
+
PRAGMA journal_mode = WAL;
|
|
90
|
+
`
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Create the TIGER tables + indexes via the Kysely schema-builder (the house idiom). Idempotent
|
|
94
|
+
* (`IF NOT EXISTS`). Pass a {@link DatabaseClient} (or any `Kysely`) over the TIGER DB; run
|
|
95
|
+
* {@link TIGER_PRAGMAS} first. `us_state`/`tract` aren't in {@link TIGERDatabase} (created here but
|
|
96
|
+
* not queried via Kysely) — `createTable` takes any table name, so that's fine.
|
|
97
|
+
*
|
|
98
|
+
* The `text(N)` length hints in the prior raw DDL were documentary only (SQLite uses TEXT affinity
|
|
99
|
+
* regardless); the lengths live on the {@link TIGERBlockTable} interface instead.
|
|
100
|
+
*/
|
|
101
|
+
export async function initializeTIGERSchema(db: Kysely<TIGERDatabase>): Promise<void> {
|
|
102
|
+
await db.schema
|
|
103
|
+
.createTable("us_state")
|
|
104
|
+
.ifNotExists()
|
|
105
|
+
.addColumn("state_code", "text", (c) => c.primaryKey().notNull())
|
|
106
|
+
.addColumn("abbreviation", "text", (c) => c.notNull())
|
|
107
|
+
.addColumn("display_name", "text", (c) => c.notNull())
|
|
108
|
+
.addColumn("geometry", "text", (c) => c.notNull())
|
|
109
|
+
.execute()
|
|
110
|
+
|
|
111
|
+
await db.schema
|
|
112
|
+
.createTable("tract")
|
|
113
|
+
.ifNotExists()
|
|
114
|
+
.addColumn("GEOID", "text", (c) => c.primaryKey().notNull())
|
|
115
|
+
.addColumn("state_code", "text", (c) => c.notNull())
|
|
116
|
+
.addColumn("county_code", "text", (c) => c.notNull())
|
|
117
|
+
.addColumn("tract_code", "text", (c) => c.notNull())
|
|
118
|
+
.addColumn("geometry", "text", (c) => c.notNull())
|
|
119
|
+
.execute()
|
|
120
|
+
|
|
121
|
+
await db.schema
|
|
122
|
+
.createTable("tabblock20")
|
|
123
|
+
.ifNotExists()
|
|
124
|
+
.addColumn("GEOID", "text", (c) => c.primaryKey().notNull())
|
|
125
|
+
.addColumn("state_code", "text", (c) => c.notNull())
|
|
126
|
+
.addColumn("county_code", "text", (c) => c.notNull())
|
|
127
|
+
.addColumn("tract_code", "text", (c) => c.notNull())
|
|
128
|
+
.addColumn("block_group_code", "text", (c) => c.notNull())
|
|
129
|
+
.addColumn("block_code", "text", (c) => c.notNull())
|
|
130
|
+
.addColumn("urbanized_area_code", "text")
|
|
131
|
+
.addColumn("urban_rural_code", "text")
|
|
132
|
+
.addColumn("housing_unit_count", "integer", (c) => c.notNull())
|
|
133
|
+
.addColumn("land_area_sqm", "integer", (c) => c.notNull())
|
|
134
|
+
.addColumn("water_area_sqm", "integer", (c) => c.notNull())
|
|
135
|
+
.addColumn("population", "integer", (c) => c.notNull())
|
|
136
|
+
.addColumn("geometry", "text", (c) => c.notNull())
|
|
137
|
+
.execute()
|
|
138
|
+
|
|
139
|
+
// No index on GEOID alone — it's the PRIMARY KEY, which already carries a unique index. The prior
|
|
140
|
+
// schema's idx_tabblock20_geoid duplicated that for nothing (double insert cost, double footprint).
|
|
141
|
+
await db.schema.createIndex("idx_tabblock20_state_code").ifNotExists().on("tabblock20").column("state_code").execute()
|
|
142
|
+
await db.schema
|
|
143
|
+
.createIndex("idx_tabblock20_state_county")
|
|
144
|
+
.ifNotExists()
|
|
145
|
+
.on("tabblock20")
|
|
146
|
+
.columns(["state_code", "county_code"])
|
|
147
|
+
.execute()
|
|
148
|
+
await db.schema
|
|
149
|
+
.createIndex("idx_tabblock20_state_county_tract")
|
|
150
|
+
.ifNotExists()
|
|
151
|
+
.on("tabblock20")
|
|
152
|
+
.columns(["state_code", "county_code", "tract_code"])
|
|
153
|
+
.execute()
|
|
154
|
+
await db.schema.createIndex("idx_tabblock20_population").ifNotExists().on("tabblock20").column("population").execute()
|
|
155
|
+
|
|
156
|
+
await db.schema
|
|
157
|
+
.createTable("pl_block")
|
|
158
|
+
.ifNotExists()
|
|
159
|
+
.addColumn("GEOID", "text", (c) => c.primaryKey().notNull())
|
|
160
|
+
.addColumn("pop_total", "integer", (c) => c.notNull())
|
|
161
|
+
.addColumn("hispanic", "integer", (c) => c.notNull())
|
|
162
|
+
.addColumn("white", "integer", (c) => c.notNull())
|
|
163
|
+
.addColumn("black", "integer", (c) => c.notNull())
|
|
164
|
+
.addColumn("aian", "integer", (c) => c.notNull())
|
|
165
|
+
.addColumn("asian", "integer", (c) => c.notNull())
|
|
166
|
+
.addColumn("nhpi", "integer", (c) => c.notNull())
|
|
167
|
+
.addColumn("other", "integer", (c) => c.notNull())
|
|
168
|
+
.addColumn("multi", "integer", (c) => c.notNull())
|
|
169
|
+
// pl_block is small (no geometry) and always probed by its GEOID PK (1:1 join to tabblock20), so
|
|
170
|
+
// cluster it WITHOUT ROWID — one B-tree probe per join, no separate rowid + PK-index pair.
|
|
171
|
+
.modifyEnd(sql`without rowid`)
|
|
172
|
+
.execute()
|
|
173
|
+
|
|
174
|
+
await db.schema
|
|
175
|
+
.createTable("tiger_streets")
|
|
176
|
+
.ifNotExists()
|
|
177
|
+
.addColumn("linearid", "text", (c) => c.notNull())
|
|
178
|
+
.addColumn("fullname", "text", (c) => c.notNull())
|
|
179
|
+
.addColumn("zipl", "text")
|
|
180
|
+
.addColumn("zipr", "text")
|
|
181
|
+
.addColumn("statefp", "text", (c) => c.notNull())
|
|
182
|
+
.execute()
|
|
183
|
+
await db.schema.createIndex("idx_tiger_streets_statefp").ifNotExists().on("tiger_streets").column("statefp").execute()
|
|
184
|
+
await db.schema
|
|
185
|
+
.createIndex("idx_tiger_streets_linearid")
|
|
186
|
+
.ifNotExists()
|
|
187
|
+
.on("tiger_streets")
|
|
188
|
+
.column("linearid")
|
|
189
|
+
.execute()
|
|
190
|
+
|
|
191
|
+
await db.schema
|
|
192
|
+
.createTable("tiger_places")
|
|
193
|
+
.ifNotExists()
|
|
194
|
+
.addColumn("geoid", "text", (c) => c.notNull())
|
|
195
|
+
.addColumn("name", "text", (c) => c.notNull())
|
|
196
|
+
.addColumn("statefp", "text", (c) => c.notNull())
|
|
197
|
+
.addColumn("lsad", "text")
|
|
198
|
+
.addColumn("namelsad", "text")
|
|
199
|
+
.addColumn("classfp", "text")
|
|
200
|
+
.execute()
|
|
201
|
+
await db.schema.createIndex("idx_tiger_places_statefp").ifNotExists().on("tiger_places").column("statefp").execute()
|
|
202
|
+
await db.schema.createIndex("idx_tiger_places_geoid").ifNotExists().on("tiger_places").column("geoid").execute()
|
|
203
|
+
}
|
package/sdk/initialize.sql
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
-- @copyright Sister Software.
|
|
2
|
-
-- @license AGPL-3.0
|
|
3
|
-
-- @author Teffen Ellis, et al.
|
|
4
|
-
-- @file TIGER SQLite3 Database Initialization Script
|
|
5
|
-
|
|
6
|
-
PRAGMA auto_vacuum = FULL;
|
|
7
|
-
PRAGMA synchronous = FULL;
|
|
8
|
-
PRAGMA locking_mode = EXCLUSIVE;
|
|
9
|
-
PRAGMA page_size = 4096;
|
|
10
|
-
PRAGMA cache_size = 10000;
|
|
11
|
-
|
|
12
|
-
SELECT load_extension('/opt/homebrew/opt/libspatialite/lib/mod_spatialite.dylib');
|
|
13
|
-
SELECT InitSpatialMetadata();
|
|
14
|
-
|
|
15
|
-
--#region US States
|
|
16
|
-
|
|
17
|
-
CREATE TABLE "us_state" (
|
|
18
|
-
"state_code" text(2) PRIMARY KEY NOT NULL,
|
|
19
|
-
"abbreviation" text NOT NULL,
|
|
20
|
-
"display_name" text NOT NULL
|
|
21
|
-
);
|
|
22
|
-
|
|
23
|
-
SELECT AddGeometryColumn('us_state', 'GEOM', 4326, 'MultiPolygon', 2, 1);
|
|
24
|
-
SELECT CreateSpatialIndex('us_state', 'GEOM');
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
CREATE UNIQUE INDEX "idx_us_state_code" ON "us_state" (
|
|
28
|
-
"state_code"
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
CREATE UNIQUE INDEX "idx_us_state_abbreviation" ON "us_state" (
|
|
32
|
-
"abbreviation"
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
CREATE UNIQUE INDEX "idx_us_state_display_name" ON "us_state" (
|
|
36
|
-
"display_name"
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
--#endregion
|
|
40
|
-
|
|
41
|
-
--#region Tracts
|
|
42
|
-
|
|
43
|
-
CREATE TABLE "tract" (
|
|
44
|
-
"GEOID" text(11) PRIMARY KEY NOT NULL,
|
|
45
|
-
"state_code" text(2) NOT NULL,
|
|
46
|
-
"county_code" text(3) NOT NULL,
|
|
47
|
-
"county_sub_division_code" text(5) NOT NULL,
|
|
48
|
-
"tract_code" text(6) NOT NULL
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
SELECT AddGeometryColumn('tract', 'GEOM', 4326, 'MultiPolygon', 2, 1);
|
|
52
|
-
SELECT CreateSpatialIndex('tract', 'GEOM');
|
|
53
|
-
|
|
54
|
-
--#endregion
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
--#region Tabulated Blocks
|
|
58
|
-
|
|
59
|
-
CREATE TABLE "tabblock20" (
|
|
60
|
-
"GEOID" text(15) PRIMARY KEY NOT NULL,
|
|
61
|
-
"state_code" text(2) NOT NULL,
|
|
62
|
-
"county_code" text(3) NOT NULL,
|
|
63
|
-
"county_sub_division_code" text(5) NOT NULL,
|
|
64
|
-
"tract_code" text(6) NOT NULL,
|
|
65
|
-
"block_group_code" text(1) NOT NULL,
|
|
66
|
-
"block_code" text(4) NOT NULL,
|
|
67
|
-
"urbanized_area_code" text(5),
|
|
68
|
-
"urban_rural_code" text(1),
|
|
69
|
-
"housing_unit_count" integer NOT NULL,
|
|
70
|
-
"land_area_sqm" integer NOT NULL,
|
|
71
|
-
"water_area_sqm" integer NOT NULL,
|
|
72
|
-
"population" integer NOT NULL
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
SELECT AddGeometryColumn('tabblock20', 'GEOM', 4326, 'MultiPolygon', 2, 1);
|
|
76
|
-
SELECT CreateSpatialIndex('tabblock20', 'GEOM');
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
CREATE UNIQUE INDEX "idx_tabblock20_geoid" ON "tabblock20" (
|
|
80
|
-
"GEOID"
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
CREATE INDEX "idx_tabblock20_state_code" ON "tabblock20" (
|
|
84
|
-
"state_code"
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
CREATE INDEX "idx_tabblock20_county_code" ON "tabblock20" (
|
|
88
|
-
"county_code"
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
CREATE INDEX "idx_tabblock20_county_sub_division_code" ON "tabblock20" (
|
|
92
|
-
"county_sub_division_code"
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
CREATE INDEX "idx_tabblock20_tract_code" ON "tabblock20" (
|
|
96
|
-
"tract_code"
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
CREATE INDEX "idx_tabblock20_block_group_code" ON "tabblock20" (
|
|
100
|
-
"block_group_code"
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
CREATE INDEX "idx_tabblock20_block_code" ON "tabblock20" (
|
|
104
|
-
"block_code"
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
CREATE INDEX "idx_tabblock20_urbanized_area_code" ON "tabblock20" (
|
|
108
|
-
"urbanized_area_code"
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
CREATE INDEX "idx_tabblock20_urban_rural_code" ON "tabblock20" (
|
|
112
|
-
"urban_rural_code"
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
CREATE INDEX "idx_tabblock20_housing_unit_count" ON "tabblock20" (
|
|
116
|
-
"housing_unit_count"
|
|
117
|
-
);
|
|
118
|
-
|
|
119
|
-
CREATE INDEX "idx_tabblock20_land_area_sqm" ON "tabblock20" (
|
|
120
|
-
"land_area_sqm"
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
CREATE INDEX "idx_tabblock20_water_area_sqm" ON "tabblock20" (
|
|
124
|
-
"water_area_sqm"
|
|
125
|
-
);
|
|
126
|
-
|
|
127
|
-
CREATE INDEX "idx_tabblock20_population" ON "tabblock20" (
|
|
128
|
-
"population"
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
CREATE INDEX "idx_tabblock20_state_code_county_code" ON "tabblock20" (
|
|
132
|
-
"state_code",
|
|
133
|
-
"county_code"
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
CREATE INDEX "idx_tabblock20_state_code_county_code_tract_code" ON "tabblock20" (
|
|
137
|
-
"state_code",
|
|
138
|
-
"county_code",
|
|
139
|
-
"tract_code"
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
CREATE INDEX "idx_tabblock20_state_code_county_code_tract_code_block_group_code" ON "tabblock20" (
|
|
143
|
-
"state_code",
|
|
144
|
-
"county_code",
|
|
145
|
-
"tract_code",
|
|
146
|
-
"block_group_code"
|
|
147
|
-
);
|
|
148
|
-
|
|
149
|
-
CREATE INDEX "idx_tabblock20_state_code_county_code_tract_code_block_group_code_block_code" ON "tabblock20" (
|
|
150
|
-
"state_code",
|
|
151
|
-
"county_code",
|
|
152
|
-
"tract_code",
|
|
153
|
-
"block_group_code",
|
|
154
|
-
"block_code"
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
--#endregion
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|