@mailwoman/tiger 4.16.2 → 5.1.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 (69) hide show
  1. package/block-group.ts +2 -3
  2. package/class-code.ts +268 -318
  3. package/constants.ts +2 -4
  4. package/county-subdivision.ts +3 -4
  5. package/county.ts +2 -3
  6. package/files.ts +1 -0
  7. package/functional-status.ts +4 -6
  8. package/geoid.ts +20 -18
  9. package/legal-statistical-area.ts +4 -6
  10. package/out/block-group.d.ts +2 -3
  11. package/out/block-group.d.ts.map +1 -1
  12. package/out/class-code.d.ts +268 -318
  13. package/out/class-code.d.ts.map +1 -1
  14. package/out/class-code.js +268 -318
  15. package/out/class-code.js.map +1 -1
  16. package/out/constants.d.ts +2 -4
  17. package/out/constants.d.ts.map +1 -1
  18. package/out/county-subdivision.d.ts +3 -4
  19. package/out/county-subdivision.d.ts.map +1 -1
  20. package/out/county.d.ts +2 -3
  21. package/out/county.d.ts.map +1 -1
  22. package/out/county.js.map +1 -1
  23. package/out/files.d.ts.map +1 -1
  24. package/out/files.js.map +1 -1
  25. package/out/functional-status.d.ts +4 -6
  26. package/out/functional-status.d.ts.map +1 -1
  27. package/out/functional-status.js +4 -6
  28. package/out/functional-status.js.map +1 -1
  29. package/out/geoid.d.ts +15 -15
  30. package/out/geoid.d.ts.map +1 -1
  31. package/out/geoid.js +3 -4
  32. package/out/geoid.js.map +1 -1
  33. package/out/legal-statistical-area.d.ts +4 -6
  34. package/out/legal-statistical-area.d.ts.map +1 -1
  35. package/out/legal-statistical-area.js +4 -6
  36. package/out/legal-statistical-area.js.map +1 -1
  37. package/out/sdk/fetch.d.ts +3 -6
  38. package/out/sdk/fetch.d.ts.map +1 -1
  39. package/out/sdk/fetch.js +3 -4
  40. package/out/sdk/fetch.js.map +1 -1
  41. package/out/sdk/redistricting.d.ts +1 -2
  42. package/out/sdk/redistricting.d.ts.map +1 -1
  43. package/out/sdk/redistricting.js +3 -4
  44. package/out/sdk/redistricting.js.map +1 -1
  45. package/out/sdk/schema.d.ts +12 -12
  46. package/out/sdk/schema.js +9 -9
  47. package/out/sdk/state/block-operations.d.ts +1 -2
  48. package/out/sdk/state/block-operations.d.ts.map +1 -1
  49. package/out/sdk/state/block-operations.js +1 -2
  50. package/out/sdk/state/block-operations.js.map +1 -1
  51. package/out/state.d.ts +10 -12
  52. package/out/state.d.ts.map +1 -1
  53. package/out/state.js +8 -10
  54. package/out/state.js.map +1 -1
  55. package/out/tabulation-block.d.ts +2 -2
  56. package/out/tabulation-block.d.ts.map +1 -1
  57. package/out/tabulation-block.js.map +1 -1
  58. package/out/tract.d.ts +2 -2
  59. package/out/tract.d.ts.map +1 -1
  60. package/out/tract.js.map +1 -1
  61. package/out/tsconfig.tsbuildinfo +1 -1
  62. package/package.json +18 -18
  63. package/sdk/fetch.ts +21 -8
  64. package/sdk/redistricting.ts +14 -4
  65. package/sdk/schema.ts +12 -12
  66. package/sdk/state/block-operations.ts +1 -2
  67. package/state.ts +11 -12
  68. package/tabulation-block.ts +3 -2
  69. package/tract.ts +3 -2
package/package.json CHANGED
@@ -1,7 +1,16 @@
1
1
  {
2
2
  "name": "@mailwoman/tiger",
3
- "version": "4.16.2",
3
+ "version": "5.1.0",
4
4
  "description": "US Census TIGER/Line data processing and analysis.",
5
+ "keywords": [
6
+ "census",
7
+ "geospatial",
8
+ "tiger"
9
+ ],
10
+ "homepage": "https://github.com/sister-software/mailwoman",
11
+ "bugs": {
12
+ "url": "https://github.com/sister-software/mailwoman/issues"
13
+ },
5
14
  "license": "AGPL-3.0-only",
6
15
  "contributors": [
7
16
  {
@@ -14,13 +23,6 @@
14
23
  "url": "https://github.com/sister-software/mailwoman.git",
15
24
  "directory": "tiger"
16
25
  },
17
- "bugs": {
18
- "url": "https://github.com/sister-software/mailwoman/issues"
19
- },
20
- "homepage": "https://github.com/sister-software/mailwoman",
21
- "scripts": {
22
- "test": "node ./out/sdk/TIGERService.test.js"
23
- },
24
26
  "type": "module",
25
27
  "exports": {
26
28
  "./package.json": "./package.json",
@@ -29,9 +31,15 @@
29
31
  "./sdk": "./out/sdk/index.js",
30
32
  ".": "./out/index.js"
31
33
  },
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "scripts": {
38
+ "test": "node ./out/sdk/TIGERService.test.js"
39
+ },
32
40
  "dependencies": {
33
- "@mailwoman/core": "4.16.2",
34
- "@mailwoman/spatial": "4.16.2",
41
+ "@mailwoman/core": "5.1.0",
42
+ "@mailwoman/spatial": "5.1.0",
35
43
  "@turf/boolean-contains": "^7.3.5",
36
44
  "kysely": "^0.29.2",
37
45
  "shapefile-parser": "^1.0.3",
@@ -39,13 +47,5 @@
39
47
  },
40
48
  "engines": {
41
49
  "node": ">=24.18.0"
42
- },
43
- "keywords": [
44
- "census",
45
- "tiger",
46
- "geospatial"
47
- ],
48
- "publishConfig": {
49
- "access": "public"
50
50
  }
51
51
  }
package/sdk/fetch.ts CHANGED
@@ -24,8 +24,6 @@
24
24
  * inserts. Re-running a state replaces its rows.
25
25
  */
26
26
 
27
- import { DatabaseClient } from "@mailwoman/core/kysley/client"
28
- import { mailwomanDataRoot } from "@mailwoman/core/utils"
29
27
  import { spawn } from "node:child_process"
30
28
  import { createWriteStream, existsSync } from "node:fs"
31
29
  import { mkdir, rename } from "node:fs/promises"
@@ -34,6 +32,10 @@ import { createInterface } from "node:readline"
34
32
  import { DatabaseSync } from "node:sqlite"
35
33
  import { Readable } from "node:stream"
36
34
  import { pipeline } from "node:stream/promises"
35
+
36
+ import { DatabaseClient } from "@mailwoman/core/kysley/client"
37
+ import { mailwomanDataRoot } from "@mailwoman/core/utils"
38
+
37
39
  import type { TIGERBlockTable, TIGERDatabase, TIGERPlaceTable, TIGERStreetTable } from "./schema.js"
38
40
  import { initializeTIGERSchema, TIGER_PRAGMAS } from "./schema.js"
39
41
 
@@ -41,8 +43,7 @@ const CENSUS_HOST = "https://www2.census.gov"
41
43
  const DEFAULT_DATA_ROOT = mailwomanDataRoot()
42
44
 
43
45
  /**
44
- * Supported TIGER levels. `tabblock20` is per state + carries geometry; `place`/`addrfeat` are
45
- * attribute-only.
46
+ * Supported TIGER levels. `tabblock20` is per state + carries geometry; `place`/`addrfeat` are attribute-only.
46
47
  */
47
48
  export type TIGERFetchLevel = "tabblock20" | "place" | "addrfeat"
48
49
 
@@ -65,8 +66,7 @@ export interface FetchTIGEROptions {
65
66
  /** Vintage. Default 2020 for blocks (matches the 2020 P.L.), 2024 for place/addrfeat (current). */
66
67
  vintage?: number
67
68
  /**
68
- * Output SQLite path. Default `<dataRoot>/tiger/tiger.db` (the name the corpus `tiger` adapter
69
- * reads).
69
+ * Output SQLite path. Default `<dataRoot>/tiger/tiger.db` (the name the corpus `tiger` adapter reads).
70
70
  */
71
71
  outPath?: string
72
72
  /** Download cache + default output root. */
@@ -91,6 +91,7 @@ export interface FetchTIGERResult {
91
91
  /** The isp-nexus column map for `tabblock20`. Geometry rides along implicitly. */
92
92
  function blockSelectSQL(layer: string, county?: string): string {
93
93
  const where = county ? ` WHERE COUNTYFP20 = '${county}'` : ""
94
+
94
95
  return (
95
96
  `SELECT GEOID20 AS GEOID, STATEFP20 AS state_code, COUNTYFP20 AS county_code, ` +
96
97
  `SUBSTR(GEOID20, 6, 6) AS tract_code, ` +
@@ -171,6 +172,7 @@ async function downloadIfNeeded(url: string, dest: string): Promise<boolean> {
171
172
  if (existsSync(dest)) {
172
173
  try {
173
174
  await runCapture("unzip", ["-tq", dest])
175
+
174
176
  return true
175
177
  } catch {
176
178
  // corrupt cache — re-download
@@ -178,26 +180,30 @@ async function downloadIfNeeded(url: string, dest: string): Promise<boolean> {
178
180
  }
179
181
  const tmp = dest + ".tmp"
180
182
  const res = await fetch(url, { redirect: "follow" })
183
+
181
184
  if (!res.ok || !res.body) throw new Error(`HTTP ${res.status} fetching ${url}`)
182
185
  await pipeline(Readable.fromWeb(res.body as Parameters<typeof Readable.fromWeb>[0]), createWriteStream(tmp))
183
186
  await rename(tmp, dest)
187
+
184
188
  return false
185
189
  }
186
190
 
187
191
  /** Scrape the ADDRFEAT directory listing for a state's county FIPS codes. */
188
192
  async function discoverCounties(state: string, vintage: number): Promise<string[]> {
189
193
  const res = await fetch(`${CENSUS_HOST}/geo/tiger/TIGER${vintage}/ADDRFEAT/`, { redirect: "follow" })
194
+
190
195
  if (!res.ok) throw new Error(`HTTP ${res.status} listing ADDRFEAT for vintage ${vintage}`)
191
196
  const html = await res.text()
192
197
  const re = new RegExp(`tl_${vintage}_${state}(\\d{3})_addrfeat\\.zip`, "g")
193
198
  const counties = new Set<string>()
199
+
194
200
  for (let m = re.exec(html); m; m = re.exec(html)) counties.add(m[1]!)
201
+
195
202
  return [...counties].sort()
196
203
  }
197
204
 
198
205
  /**
199
- * Fetch one state's TIGER data at `level` into a SQLite DB. Yields progress; returns the final
200
- * tally.
206
+ * Fetch one state's TIGER data at `level` into a SQLite DB. Yields progress; returns the final tally.
201
207
  */
202
208
  export async function* fetchTIGER(options: FetchTIGEROptions): AsyncGenerator<FetchTIGEREvent, FetchTIGERResult> {
203
209
  const level = options.level ?? "tabblock20"
@@ -218,6 +224,7 @@ export async function* fetchTIGER(options: FetchTIGEROptions): AsyncGenerator<Fe
218
224
 
219
225
  // Source units: one (per-state) for block/place; one per county for addrfeat.
220
226
  const geoCodes = level === "addrfeat" ? await discoverCounties(state, vintage) : [""]
227
+
221
228
  if (level === "addrfeat" && geoCodes.length === 0) {
222
229
  throw new Error(`No ADDRFEAT counties found for state ${state} vintage ${vintage}`)
223
230
  }
@@ -296,14 +303,18 @@ export async function* fetchTIGER(options: FetchTIGEROptions): AsyncGenerator<Fe
296
303
  for await (const line of createInterface({ input: child.stdout, crlfDelay: Infinity })) {
297
304
  if (!line) continue
298
305
  let feat: { properties?: Record<string, unknown>; geometry?: unknown }
306
+
299
307
  try {
300
308
  feat = JSON.parse(line)
301
309
  } catch {
302
310
  continue
303
311
  }
312
+
304
313
  if (!feat.properties) continue
314
+
305
315
  if (level === "tabblock20" && !feat.geometry) continue
306
316
  batch.push(buildRow(level, feat.properties, feat.geometry, state))
317
+
307
318
  if (batch.length >= batchSize) {
308
319
  await flush()
309
320
  yield { phase: "load", inserted, total: 0 }
@@ -312,11 +323,13 @@ export async function* fetchTIGER(options: FetchTIGEROptions): AsyncGenerator<Fe
312
323
  await flush()
313
324
 
314
325
  const code = await exited
326
+
315
327
  if (code !== 0) throw new Error(`ogr2ogr exited ${code} on ${layer}: ${stderr.slice(0, 500)}`)
316
328
  yield { phase: "load", inserted, total: 0 }
317
329
  }
318
330
 
319
331
  db.exec("PRAGMA wal_checkpoint(TRUNCATE);")
332
+
320
333
  return { outPath, table, inserted }
321
334
  } finally {
322
335
  await kdb.destroy()
@@ -16,8 +16,6 @@
16
16
  * https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/
17
17
  */
18
18
 
19
- import { DatabaseClient } from "@mailwoman/core/kysley/client"
20
- import { mailwomanDataRoot } from "@mailwoman/core/utils"
21
19
  import { spawn } from "node:child_process"
22
20
  import { createReadStream, createWriteStream, existsSync } from "node:fs"
23
21
  import { mkdir, rename } from "node:fs/promises"
@@ -26,6 +24,10 @@ import { createInterface } from "node:readline"
26
24
  import { DatabaseSync } from "node:sqlite"
27
25
  import { Readable } from "node:stream"
28
26
  import { pipeline } from "node:stream/promises"
27
+
28
+ import { DatabaseClient } from "@mailwoman/core/kysley/client"
29
+ import { mailwomanDataRoot } from "@mailwoman/core/utils"
30
+
29
31
  import { AdminLevel1CodeToAbbreviation, StateName, type AdminLevel1Code } from "../state.js"
30
32
  import { initializeTIGERSchema, TIGER_PRAGMAS, type PLBlockTable, type TIGERDatabase } from "./schema.js"
31
33
 
@@ -100,6 +102,7 @@ async function downloadIfNeeded(url: string, dest: string): Promise<boolean> {
100
102
  if (existsSync(dest)) {
101
103
  try {
102
104
  await runCapture("unzip", ["-tq", dest])
105
+
103
106
  return true
104
107
  } catch {
105
108
  // corrupt cache — re-download
@@ -107,9 +110,11 @@ async function downloadIfNeeded(url: string, dest: string): Promise<boolean> {
107
110
  }
108
111
  const tmp = dest + ".tmp"
109
112
  const res = await fetch(url, { redirect: "follow" })
113
+
110
114
  if (!res.ok || !res.body) throw new Error(`HTTP ${res.status} fetching ${url}`)
111
115
  await pipeline(Readable.fromWeb(res.body as Parameters<typeof Readable.fromWeb>[0]), createWriteStream(tmp))
112
116
  await rename(tmp, dest)
117
+
113
118
  return false
114
119
  }
115
120
 
@@ -120,8 +125,7 @@ async function eachLine(path: string, fn: (line: string) => void): Promise<void>
120
125
  }
121
126
 
122
127
  /**
123
- * Fetch one state's P.L. 94-171 block race counts into `pl_block`. Yields progress; returns the
124
- * tally.
128
+ * Fetch one state's P.L. 94-171 block race counts into `pl_block`. Yields progress; returns the tally.
125
129
  */
126
130
  export async function* fetchRedistricting(
127
131
  options: FetchRedistrictingOptions
@@ -132,6 +136,7 @@ export async function* fetchRedistricting(
132
136
  const state = options.stateFIPS
133
137
 
134
138
  const abbr = AdminLevel1CodeToAbbreviation[state as AdminLevel1Code]
139
+
135
140
  if (!abbr) throw new Error(`Unknown state FIPS "${state}"`)
136
141
  const stateName = StateName[abbr as keyof typeof StateName]
137
142
  const dirName = stateName.replace(/ /g, "_")
@@ -160,8 +165,10 @@ export async function* fetchRedistricting(
160
165
  const logToGeoid = new Map<string, string>()
161
166
  await eachLine(geoPath, (line) => {
162
167
  const f = line.split("|")
168
+
163
169
  if (f[GEO_SUMLEV] !== "750") return
164
170
  const geoid = f[GEO_GEOCODE] ?? ""
171
+
165
172
  if (!geoid.startsWith(prefix)) return
166
173
  logToGeoid.set(f[GEO_LOGRECNO] ?? "", geoid)
167
174
  })
@@ -195,6 +202,7 @@ export async function* fetchRedistricting(
195
202
  if (!line) continue
196
203
  const f = line.split("|")
197
204
  const geoid = logToGeoid.get(f[SEG_LOGRECNO] ?? "")
205
+
198
206
  if (!geoid) continue
199
207
  batch.push({
200
208
  GEOID: geoid,
@@ -208,6 +216,7 @@ export async function* fetchRedistricting(
208
216
  other: Number(f[CATEGORY_INDEX.other] ?? 0),
209
217
  multi: Number(f[CATEGORY_INDEX.multi] ?? 0),
210
218
  })
219
+
211
220
  if (batch.length >= batchSize) {
212
221
  await flush()
213
222
  yield { phase: "load", inserted, total }
@@ -217,6 +226,7 @@ export async function* fetchRedistricting(
217
226
 
218
227
  yield { phase: "load", inserted, total }
219
228
  db.exec("PRAGMA wal_checkpoint(TRUNCATE);")
229
+
220
230
  return { outPath, table: "pl_block", inserted }
221
231
  } finally {
222
232
  await kdb.destroy()
package/sdk/schema.ts CHANGED
@@ -30,9 +30,9 @@ export interface TIGERBlockTable {
30
30
  }
31
31
 
32
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`.
33
+ * Kysely row type for `pl_block` — Census 2020 P.L. 94-171 table P2 (Hispanic-or-Latino by race), one row per
34
+ * tabulation block, keyed on the same 15-char `GEOID` as {@link TIGERBlockTable}. The eight category columns partition
35
+ * `pop_total`.
36
36
  */
37
37
  export interface PLBlockTable {
38
38
  GEOID: string
@@ -78,9 +78,9 @@ export interface TIGERDatabase {
78
78
  export type { Generated }
79
79
 
80
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.
81
+ * Build-tuning PRAGMAs, run raw before any table is created (`page_size`/`auto_vacuum` only take effect on an empty DB,
82
+ * and PRAGMA has no Kysely builder). The consumer execs this, then calls {@link initializeTIGERSchema} for the tables +
83
+ * indexes.
84
84
  */
85
85
  export const TIGER_PRAGMAS = /* sql */ `
86
86
  PRAGMA auto_vacuum = INCREMENTAL;
@@ -90,13 +90,13 @@ PRAGMA journal_mode = WAL;
90
90
  `
91
91
 
92
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.
93
+ * Create the TIGER tables + indexes via the Kysely schema-builder (the house idiom). Idempotent (`IF NOT EXISTS`). Pass
94
+ * a {@link DatabaseClient} (or any `Kysely`) over the TIGER DB; run {@link TIGER_PRAGMAS} first. `us_state`/`tract`
95
+ * aren't in {@link TIGERDatabase} (created here but not queried via Kysely) — `createTable` takes any table name, so
96
+ * that's fine.
97
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.
98
+ * The `text(N)` length hints in the prior raw DDL were documentary only (SQLite uses TEXT affinity regardless); the
99
+ * lengths live on the {@link TIGERBlockTable} interface instead.
100
100
  */
101
101
  export async function initializeTIGERSchema(db: Kysely<TIGERDatabase>): Promise<void> {
102
102
  await db.schema
@@ -51,8 +51,7 @@ export function parseTIGERBlockFromRow({ serializedGeometry, ...properties }: TI
51
51
  }
52
52
 
53
53
  /**
54
- * Given a collection of rows from the TIGER tabulated block table, parses them into a GeoJSON
55
- * feature collection.
54
+ * Given a collection of rows from the TIGER tabulated block table, parses them into a GeoJSON feature collection.
56
55
  *
57
56
  * @internal
58
57
  */
package/state.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  import type { GeoFeature, MultiPolygonLiteral } from "@mailwoman/spatial"
8
+
8
9
  import { GeoIDPart } from "./geoid.js"
9
10
  import type { TIGERTabulatedBlockProperties } from "./tabulation-block.js"
10
11
 
@@ -180,10 +181,10 @@ export enum FIPSStateCode {
180
181
  * @category FIPS
181
182
  * @category Territories
182
183
  * @category Census
183
- * @see {@linkcode FIPSStateCode} for states.
184
- * @see {@linkcode AdminLevel1Code} for a combined set of states and territories.
185
184
  * @minLength 2
186
185
  * @maxLength 2
186
+ * @see {@linkcode FIPSStateCode} for states.
187
+ * @see {@linkcode AdminLevel1Code} for a combined set of states and territories.
187
188
  */
188
189
  export enum FIPSTerritoryCode {
189
190
  /**
@@ -214,11 +215,11 @@ export enum FIPSTerritoryCode {
214
215
  * @category States
215
216
  * @category Territories
216
217
  * @category Census
217
- * @see {@linkcode FIPSStateCode} for states.
218
- * @see {@linkcode FIPSTerritoryCode} for territories.
219
218
  * @minLength 2
220
219
  * @maxLength 2
221
220
  * @public
221
+ * @see {@linkcode FIPSStateCode} for states.
222
+ * @see {@linkcode FIPSTerritoryCode} for territories.
222
223
  */
223
224
  export const AdminLevel1Code = {
224
225
  ...FIPSStateCode,
@@ -434,8 +435,8 @@ export enum StateName {
434
435
  //#region State Utilities
435
436
 
436
437
  /**
437
- * Predicate for checking if a string is a proper abbreviation for a US State or territory, rather
438
- * than a random 2-letter string.
438
+ * Predicate for checking if a string is a proper abbreviation for a US State or territory, rather than a random
439
+ * 2-letter string.
439
440
  *
440
441
  * @see {@link isStateAbbreviation} for a specific check for US States.
441
442
  * @see {@link isStateTerritoryAbbreviation} for a specific check for US Territories.
@@ -447,8 +448,7 @@ export function isStateLevelAbbreviation(input: unknown): input is AdminLevel1Ab
447
448
  }
448
449
 
449
450
  /**
450
- * Predicate for checking if a string is a proper abbreviation for a US State, rather than a random
451
- * 2-letter string.
451
+ * Predicate for checking if a string is a proper abbreviation for a US State, rather than a random 2-letter string.
452
452
  *
453
453
  * @see {@link isStateLevelAbbreviation} for a general check for US States and Territories.
454
454
  * @see {@link isStateTerritoryAbbreviation} for a specific check for US Territories.
@@ -460,8 +460,7 @@ export function isStateAbbreviation(input: unknown): input is StateAbbreviation
460
460
  }
461
461
 
462
462
  /**
463
- * Predicate for checking if a string is a proper abbreviation for a US Territory, rather than a
464
- * random 2-letter string.
463
+ * Predicate for checking if a string is a proper abbreviation for a US Territory, rather than a random 2-letter string.
465
464
  *
466
465
  * @see {@link isStateLevelAbbreviation} for a general check for US States and Territories.
467
466
  * @see {@link isStateAbbreviation} for a specific check for US States.
@@ -540,8 +539,8 @@ export interface TIGERState {
540
539
  [GeoIDPart.State]: AdminLevel1Code
541
540
 
542
541
  /**
543
- * The geometry of the tabulated block, typically a polygon, but may be a multi-polygon for blocks
544
- * with holes, or islands.
542
+ * The geometry of the tabulated block, typically a polygon, but may be a multi-polygon for blocks with holes, or
543
+ * islands.
545
544
  *
546
545
  * @title Geometry
547
546
  */
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  import { type MultiPolygonLiteral } from "@mailwoman/spatial"
8
+
8
9
  import { TIGERProperty, type TIGERPropertyRecord } from "./constants.js"
9
10
  import { GeoIDPart, type FIPSBlockGeoID, type ParsedGeoIDBlockLevel } from "./geoid.js"
10
11
 
@@ -59,8 +60,8 @@ export interface TIGERTabulatedBlock extends ParsedGeoIDBlockLevel, TIGERTabulat
59
60
  GEOID: FIPSBlockGeoID
60
61
 
61
62
  /**
62
- * The geometry of the tabulated block, typically a polygon, but may be a multi-polygon for blocks
63
- * with holes, or islands.
63
+ * The geometry of the tabulated block, typically a polygon, but may be a multi-polygon for blocks with holes, or
64
+ * islands.
64
65
  *
65
66
  * @title Geometry
66
67
  */
package/tract.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  import { type GeoFeature, type MultiPolygonLiteral } from "@mailwoman/spatial"
8
+
8
9
  import { TIGERProperty, type TIGERPropertyRecord } from "./constants.js"
9
10
  import type { FIPSTractGeoID, GeoIDPart, ParsedGeoIDTractLevel } from "./geoid.js"
10
11
 
@@ -21,8 +22,8 @@ export interface TIGERTract extends ParsedGeoIDTractLevel {
21
22
  GEOID: FIPSTractGeoID
22
23
 
23
24
  /**
24
- * The geometry of the tabulated block, typically a polygon, but may be a multi-polygon for blocks
25
- * with holes, or islands.
25
+ * The geometry of the tabulated block, typically a polygon, but may be a multi-polygon for blocks with holes, or
26
+ * islands.
26
27
  *
27
28
  * @title Geometry
28
29
  */