@mailwoman/soil 9.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +221 -0
  2. package/lib/index.ts +474 -0
  3. package/lib/schema.ts +493 -0
  4. package/lib/scripts/ingest-chunk.ts +47 -0
  5. package/lib/sdk/acquire.ts +124 -0
  6. package/lib/sdk/build-soil.ts +778 -0
  7. package/lib/sdk/cell-tiers.ts +310 -0
  8. package/lib/sdk/cells.ts +224 -0
  9. package/lib/sdk/client.ts +273 -0
  10. package/lib/sdk/download.ts +161 -0
  11. package/lib/sdk/index.ts +20 -0
  12. package/lib/sdk/ingest-chunk.ts +182 -0
  13. package/lib/sdk/ingest.ts +271 -0
  14. package/lib/sdk/measure-resolutions.ts +81 -0
  15. package/lib/sdk/reduce.ts +375 -0
  16. package/lib/sdk/survey-area.ts +467 -0
  17. package/lib/sdk/tabular.ts +296 -0
  18. package/lib/sdk/verify.ts +369 -0
  19. package/lib/test-kit.ts +255 -0
  20. package/lib/vocabulary.ts +241 -0
  21. package/out/index.d.ts +199 -0
  22. package/out/index.d.ts.map +1 -0
  23. package/out/index.js +255 -0
  24. package/out/index.js.map +1 -0
  25. package/out/schema.d.ts +369 -0
  26. package/out/schema.d.ts.map +1 -0
  27. package/out/schema.js +186 -0
  28. package/out/schema.js.map +1 -0
  29. package/out/scripts/ingest-chunk.d.ts +11 -0
  30. package/out/scripts/ingest-chunk.d.ts.map +1 -0
  31. package/out/scripts/ingest-chunk.js +42 -0
  32. package/out/scripts/ingest-chunk.js.map +1 -0
  33. package/out/sdk/acquire.d.ts +60 -0
  34. package/out/sdk/acquire.d.ts.map +1 -0
  35. package/out/sdk/acquire.js +74 -0
  36. package/out/sdk/acquire.js.map +1 -0
  37. package/out/sdk/build-soil.d.ts +197 -0
  38. package/out/sdk/build-soil.d.ts.map +1 -0
  39. package/out/sdk/build-soil.js +434 -0
  40. package/out/sdk/build-soil.js.map +1 -0
  41. package/out/sdk/cell-tiers.d.ts +47 -0
  42. package/out/sdk/cell-tiers.d.ts.map +1 -0
  43. package/out/sdk/cell-tiers.js +194 -0
  44. package/out/sdk/cell-tiers.js.map +1 -0
  45. package/out/sdk/cells.d.ts +118 -0
  46. package/out/sdk/cells.d.ts.map +1 -0
  47. package/out/sdk/cells.js +140 -0
  48. package/out/sdk/cells.js.map +1 -0
  49. package/out/sdk/client.d.ts +120 -0
  50. package/out/sdk/client.d.ts.map +1 -0
  51. package/out/sdk/client.js +214 -0
  52. package/out/sdk/client.js.map +1 -0
  53. package/out/sdk/download.d.ts +84 -0
  54. package/out/sdk/download.d.ts.map +1 -0
  55. package/out/sdk/download.js +117 -0
  56. package/out/sdk/download.js.map +1 -0
  57. package/out/sdk/index.d.ts +20 -0
  58. package/out/sdk/index.d.ts.map +1 -0
  59. package/out/sdk/index.js +20 -0
  60. package/out/sdk/index.js.map +1 -0
  61. package/out/sdk/ingest-chunk.d.ts +73 -0
  62. package/out/sdk/ingest-chunk.d.ts.map +1 -0
  63. package/out/sdk/ingest-chunk.js +98 -0
  64. package/out/sdk/ingest-chunk.js.map +1 -0
  65. package/out/sdk/ingest.d.ts +132 -0
  66. package/out/sdk/ingest.d.ts.map +1 -0
  67. package/out/sdk/ingest.js +163 -0
  68. package/out/sdk/ingest.js.map +1 -0
  69. package/out/sdk/measure-resolutions.d.ts +39 -0
  70. package/out/sdk/measure-resolutions.d.ts.map +1 -0
  71. package/out/sdk/measure-resolutions.js +52 -0
  72. package/out/sdk/measure-resolutions.js.map +1 -0
  73. package/out/sdk/reduce.d.ts +122 -0
  74. package/out/sdk/reduce.d.ts.map +1 -0
  75. package/out/sdk/reduce.js +266 -0
  76. package/out/sdk/reduce.js.map +1 -0
  77. package/out/sdk/survey-area.d.ts +105 -0
  78. package/out/sdk/survey-area.d.ts.map +1 -0
  79. package/out/sdk/survey-area.js +346 -0
  80. package/out/sdk/survey-area.js.map +1 -0
  81. package/out/sdk/tabular.d.ts +106 -0
  82. package/out/sdk/tabular.d.ts.map +1 -0
  83. package/out/sdk/tabular.js +194 -0
  84. package/out/sdk/tabular.js.map +1 -0
  85. package/out/sdk/verify.d.ts +135 -0
  86. package/out/sdk/verify.d.ts.map +1 -0
  87. package/out/sdk/verify.js +221 -0
  88. package/out/sdk/verify.js.map +1 -0
  89. package/out/test-kit.d.ts +80 -0
  90. package/out/test-kit.d.ts.map +1 -0
  91. package/out/test-kit.js +228 -0
  92. package/out/test-kit.js.map +1 -0
  93. package/out/vocabulary.d.ts +179 -0
  94. package/out/vocabulary.d.ts.map +1 -0
  95. package/out/vocabulary.js +215 -0
  96. package/out/vocabulary.js.map +1 -0
  97. package/package.json +276 -0
package/out/schema.js ADDED
@@ -0,0 +1,186 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Typed schema for `soil.db` — the polygon truth table, the containment index above it, and the ONE
7
+ * reduction both consumers read, plus the layer-contract tables from `@mailwoman/core/layers`.
8
+ *
9
+ * THREE CELL-FACING TABLES ARE ONE PIPELINE, NOT THREE SOURCES. {@link SoilMapUnitAreaTable} holds what
10
+ * the authority drew, unsimplified. {@link SoilMapUnitCellTable} says which cells each delineation
11
+ * reaches and whether it fills them. {@link SoilCapabilityCellTable} is that index reduced ONCE, at build
12
+ * time, into a per-cell distribution. A `partial` cell's contribution to the reduction is weighted by the
13
+ * area it actually covers, which is why the truth table keeps unsimplified rings: simplify them and the
14
+ * weights change silently.
15
+ *
16
+ * THE REDUCTION STORES A DISTRIBUTION, NEVER A WINNER, AND THAT IS FORCED BY MEASUREMENT RATHER THAN
17
+ * PREFERENCE. 84.0% of the 339,191 national map units hold two or more components; in 16.8% the largest
18
+ * component covers under half the map unit; and 85.4% of `IA153`'s 17,966 delineations are smaller than
19
+ * one resolution-9 cell, so no affordable cell size removes the mixture. NRCS itself ships
20
+ * `muaggatt.niccdcd` — its own dominant-condition capability class — beside `niccdcdpct`, the share that
21
+ * class actually covers, with an observed minimum of 2%. This table reproduces that pattern at cell grain
22
+ * rather than inventing one.
23
+ *
24
+ * AN ABSENCE IS NEVER A SMALL NUMBER. Four separate shares name why the rest of a cell carries no class,
25
+ * and class 8 is NOT among them: class 8 is a determination — the survey looked and rated the land as
26
+ * precluding commercial plant production — so it is a class share like any other. Folding a `NOTCOM`
27
+ * polygon, a water body and an unrated series into "not arable" would produce a well-formed wrong answer,
28
+ * which is what the four shares exist to make impossible.
29
+ *
30
+ * `WITHOUT ROWID` ON THE CELL TABLES AND NEVER ON THE GEOMETRY TABLE. Small fixed-width rows probed by
31
+ * their exact primary key belong in the B-tree; a row carrying a geometry blob does not — clustering it
32
+ * into the B-tree makes every index page a geometry page.
33
+ */
34
+ import { sql } from "kysely";
35
+ /**
36
+ * Whether an H3 cell lies wholly inside one map-unit delineation, or is crossed by its boundary.
37
+ */
38
+ export const SoilCellContainment = {
39
+ /**
40
+ * Every point in the cell is inside the delineation. Answered from the index alone, with no geometry read.
41
+ */
42
+ Whole: "whole",
43
+ /**
44
+ * The delineation's boundary crosses the cell. The index has narrowed the candidates; the point test decides.
45
+ */
46
+ Partial: "partial",
47
+ };
48
+ /**
49
+ * Create `soil_map_unit_area`. A PLAIN rowid table on purpose — the `rings` blob is exactly the payload `WITHOUT ROWID`
50
+ * penalizes.
51
+ */
52
+ export async function createSoilMapUnitAreaTable(db) {
53
+ await db.schema
54
+ .createTable("soil_map_unit_area")
55
+ .addColumn("area_id", "text", (c) => c.primaryKey())
56
+ .addColumn("mukey", "text", (c) => c.notNull())
57
+ .addColumn("areasymbol", "text", (c) => c.notNull())
58
+ .addColumn("min_lat", "real", (c) => c.notNull())
59
+ .addColumn("min_lon", "real", (c) => c.notNull())
60
+ .addColumn("max_lat", "real", (c) => c.notNull())
61
+ .addColumn("max_lon", "real", (c) => c.notNull())
62
+ .addColumn("rings", "blob", (c) => c.notNull())
63
+ .execute();
64
+ }
65
+ /**
66
+ * Create `soil_map_unit_cell` — the containment index. Small fixed-width rows probed by their exact primary key.
67
+ */
68
+ export async function createSoilMapUnitCellTable(db) {
69
+ await db.schema
70
+ .createTable("soil_map_unit_cell")
71
+ .addColumn("h3_cell", "integer", (c) => c.notNull())
72
+ .addColumn("resolution", "integer", (c) => c.notNull())
73
+ .addColumn("area_id", "text", (c) => c.notNull())
74
+ .addColumn("containment", "text", (c) => c.notNull())
75
+ .addPrimaryKeyConstraint("soil_map_unit_cell_pk", ["h3_cell", "area_id"])
76
+ // `WITHOUT ROWID` has no first-class builder; the raw modifier is the idiomatic fallback.
77
+ .modifyEnd(sql `without rowid`)
78
+ .execute();
79
+ }
80
+ /**
81
+ * Create `soil_map_unit`.
82
+ */
83
+ export async function createSoilMapUnitTable(db) {
84
+ await db.schema
85
+ .createTable("soil_map_unit")
86
+ .addColumn("mukey", "text", (c) => c.primaryKey())
87
+ .addColumn("areasymbol", "text", (c) => c.notNull())
88
+ .addColumn("musym", "text", (c) => c.notNull())
89
+ .addColumn("muname", "text", (c) => c.notNull())
90
+ .addColumn("mukind", "text")
91
+ .addColumn("mustatus", "text")
92
+ .addColumn("farmlndcl", "text")
93
+ .addColumn("farmland_scope", "text", (c) => c.notNull())
94
+ .addColumn("niccdcd", "text")
95
+ .addColumn("niccdcdpct", "integer")
96
+ .addColumn("no_mapping", "integer", (c) => c.notNull())
97
+ .execute();
98
+ }
99
+ /**
100
+ * Create `soil_component`.
101
+ */
102
+ export async function createSoilComponentTable(db) {
103
+ await db.schema
104
+ .createTable("soil_component")
105
+ .addColumn("cokey", "text", (c) => c.primaryKey())
106
+ .addColumn("mukey", "text", (c) => c.notNull())
107
+ .addColumn("comppct_r", "integer", (c) => c.notNull())
108
+ .addColumn("compname", "text")
109
+ .addColumn("compkind", "text")
110
+ .addColumn("nirrcapcl", "text")
111
+ .addColumn("nirrcapscl", "text")
112
+ .addColumn("irrcapcl", "text")
113
+ .addColumn("irrcapscl", "text")
114
+ .addColumn("nccpi_v3", "real")
115
+ .execute();
116
+ }
117
+ /**
118
+ * Create `soil_capability_cell` — the reduction both consumers read.
119
+ */
120
+ export async function createSoilCapabilityCellTable(db) {
121
+ await db.schema
122
+ .createTable("soil_capability_cell")
123
+ .addColumn("h3_cell", "integer", (c) => c.primaryKey())
124
+ .addColumn("class_shares", "text", (c) => c.notNull())
125
+ .addColumn("unrated_share", "real", (c) => c.notNull())
126
+ .addColumn("notrateable_share", "real", (c) => c.notNull())
127
+ .addColumn("nodata_share", "real", (c) => c.notNull())
128
+ .addColumn("other_share", "real", (c) => c.notNull())
129
+ .addColumn("mapped_share", "real", (c) => c.notNull())
130
+ .addColumn("top_class", "text")
131
+ .addColumn("top_class_share", "real")
132
+ .addColumn("weighting", "text", (c) => c.notNull())
133
+ .addColumn("delineations", "integer", (c) => c.notNull())
134
+ .modifyEnd(sql `without rowid`)
135
+ .execute();
136
+ }
137
+ /**
138
+ * Create `soil_survey_area`.
139
+ */
140
+ export async function createSoilSurveyAreaTable(db) {
141
+ await db.schema
142
+ .createTable("soil_survey_area")
143
+ .addColumn("areasymbol", "text", (c) => c.primaryKey())
144
+ .addColumn("areaname", "text", (c) => c.notNull())
145
+ .addColumn("saverest", "text", (c) => c.notNull())
146
+ .addColumn("saversion", "integer")
147
+ .addColumn("survey_source_date", "text")
148
+ .addColumn("survey_source_title", "text")
149
+ .addColumn("source_scale", "integer")
150
+ .addColumn("mapping_scale", "integer")
151
+ .addColumn("area_acres", "integer")
152
+ .addColumn("min_lat", "real", (c) => c.notNull())
153
+ .addColumn("min_lon", "real", (c) => c.notNull())
154
+ .addColumn("max_lat", "real", (c) => c.notNull())
155
+ .addColumn("max_lon", "real", (c) => c.notNull())
156
+ .addColumn("coverage_cells", "integer", (c) => c.notNull())
157
+ .addColumn("coverage_resolution", "integer", (c) => c.notNull())
158
+ .execute();
159
+ }
160
+ /**
161
+ * Create `soil_vocabulary`.
162
+ */
163
+ export async function createSoilVocabularyTable(db) {
164
+ await db.schema
165
+ .createTable("soil_vocabulary")
166
+ .addColumn("domain", "text", (c) => c.notNull())
167
+ .addColumn("code", "text", (c) => c.notNull())
168
+ .addColumn("definition", "text", (c) => c.notNull())
169
+ .addColumn("sequence", "integer", (c) => c.notNull())
170
+ .addPrimaryKeyConstraint("soil_vocabulary_pk", ["domain", "code"])
171
+ .modifyEnd(sql `without rowid`)
172
+ .execute();
173
+ }
174
+ /**
175
+ * Every domain table this layer owns, in dependency order.
176
+ */
177
+ export async function createSoilTables(db) {
178
+ await createSoilMapUnitAreaTable(db);
179
+ await createSoilMapUnitCellTable(db);
180
+ await createSoilMapUnitTable(db);
181
+ await createSoilComponentTable(db);
182
+ await createSoilCapabilityCellTable(db);
183
+ await createSoilSurveyAreaTable(db);
184
+ await createSoilVocabularyTable(db);
185
+ }
186
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../lib/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * One chunk of the soil ingest, as its own process — spawned by `buildSoilDatabase`, never run by hand.
7
+ * The process boundary and the stdout contract live with `runIngestChunkScript`; what stays here is only
8
+ * this product's flags and its feature-source constructor.
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=ingest-chunk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ingest-chunk.d.ts","sourceRoot":"","sources":["../../lib/scripts/ingest-chunk.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * One chunk of the soil ingest, as its own process — spawned by `buildSoilDatabase`, never run by hand.
7
+ * The process boundary and the stdout contract live with `runIngestChunkScript`; what stays here is only
8
+ * this product's flags and its feature-source constructor.
9
+ */
10
+ import { requiredArgument } from "@mailwoman/core/scripting/arguments";
11
+ import { INGEST_CHUNK_FLAGS, runIngestChunkScript } from "@mailwoman/core/scripting/ingest-chunk-script";
12
+ import { createShapefileFeatureSource } from "#sdk/ingest";
13
+ import { ingestSoilChunk } from "#sdk/ingest-chunk";
14
+ await runIngestChunkScript({
15
+ context: "soil ingest-chunk",
16
+ options: {
17
+ ...INGEST_CHUNK_FLAGS,
18
+ shapefile: { type: "string" },
19
+ "area-symbol": { type: "string" },
20
+ "fid-from": { type: "string" },
21
+ "fid-to": { type: "string" },
22
+ "no-mapping-mukeys": { type: "string" },
23
+ },
24
+ run: async (database, values, chunk) => ingestSoilChunk(database, {
25
+ source: await createShapefileFeatureSource({
26
+ shapefilePath: requiredArgument("soil ingest-chunk", "shapefile", values.shapefile),
27
+ areaSymbol: requiredArgument("soil ingest-chunk", "area-symbol", values["area-symbol"]),
28
+ fidFrom: Number(requiredArgument("soil ingest-chunk", "fid-from", values["fid-from"])),
29
+ fidTo: Number(requiredArgument("soil ingest-chunk", "fid-to", values["fid-to"])),
30
+ // A range's own count is not knowable up front — `ogrinfo` reports the layer's total and nothing narrower — so
31
+ // the chunk asserts nothing about its size and the PARENT checks the per-area sum against the shapefile's.
32
+ declaredFeatureCount: 0,
33
+ }),
34
+ indexResolution: chunk.indexResolution,
35
+ coverageResolution: chunk.coverageResolution,
36
+ // An empty string is an empty set, not "every map unit": a build where nothing lacks soil mapping passes one, and
37
+ // `"".split(",")` yields one empty element that has to be dropped rather than joined against as a mukey.
38
+ noMappingMukeys: new Set((values["no-mapping-mukeys"] ?? "").split(",").filter((mukey) => mukey.length > 0)),
39
+ onProgress: chunk.onProgress,
40
+ }),
41
+ });
42
+ //# sourceMappingURL=ingest-chunk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ingest-chunk.js","sourceRoot":"","sources":["../../lib/scripts/ingest-chunk.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAA;AACtE,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,+CAA+C,CAAA;AAIxG,OAAO,EAAE,4BAA4B,EAAE,MAAM,aAAa,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAEnD,MAAM,oBAAoB,CAAC;IAC1B,OAAO,EAAE,mBAAmB;IAC5B,OAAO,EAAE;QACR,GAAG,kBAAkB;QACrB,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC7B,aAAa,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACjC,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC9B,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC5B,mBAAmB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;KACvC;IACD,GAAG,EAAE,KAAK,EAAE,QAAsC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CACpE,eAAe,CAAC,QAAQ,EAAE;QACzB,MAAM,EAAE,MAAM,4BAA4B,CAAC;YAC1C,aAAa,EAAE,gBAAgB,CAAC,mBAAmB,EAAE,WAAW,EAAE,MAAM,CAAC,SAAS,CAAC;YACnF,UAAU,EAAE,gBAAgB,CAAC,mBAAmB,EAAE,aAAa,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC;YACvF,OAAO,EAAE,MAAM,CAAC,gBAAgB,CAAC,mBAAmB,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;YACtF,KAAK,EAAE,MAAM,CAAC,gBAAgB,CAAC,mBAAmB,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;YAChF,+GAA+G;YAC/G,2GAA2G;YAC3G,oBAAoB,EAAE,CAAC;SACvB,CAAC;QACF,eAAe,EAAE,KAAK,CAAC,eAAe;QACtC,kBAAkB,EAAE,KAAK,CAAC,kBAAkB;QAC5C,kHAAkH;QAClH,yGAAyG;QACzG,eAAe,EAAE,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC5G,UAAU,EAAE,KAAK,CAAC,UAAU;KAC5B,CAAC;CACH,CAAC,CAAA"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Acquisition, end to end: which survey areas a region holds, their archives, and the inputs the builder
7
+ * takes.
8
+ *
9
+ * THE FRESHNESS QUESTION IS ANSWERED BY THE TABULAR SERVICE, NOT BY THE FILE HOST. `sacatalog.saverest` is
10
+ * the version-established date, and it is also what the archive's filename embeds — so one catalogue call
11
+ * both decides what to download and names the file. The download host cannot answer it: it refuses `HEAD`
12
+ * with 405 and ignores `Range`, so a length probe there is a full transfer.
13
+ *
14
+ * THE VINTAGE IS THE REFRESH THE BUILD INGESTED, AND IT IS ONE DATE FOR THE WHOLE ARTIFACT. NRCS performs
15
+ * ONE coordinated Annual Soils Refresh, each October 1; grouping `sacatalog` by year of `saverest` returns
16
+ * 2016: 1, 2025: 3,323, 2026: 56. So a region's areas share a refresh and the manifest can carry one
17
+ * `source_vintage` — the LATEST of the areas built, because that is the date after which nothing in the
18
+ * artifact changed. Every area's own date is kept per row in `soil_survey_area`, and so is the far older
19
+ * field-survey date, which is the number a currency claim actually turns on.
20
+ */
21
+ import type { SurveyAreaInput } from "#sdk/build-soil";
22
+ import type { SoilDataAccessClient, SurveyAreaCatalogEntry } from "#sdk/client";
23
+ import { type SurveyAreaArchive } from "#sdk/download";
24
+ export interface AcquireRegionOptions {
25
+ client: SoilDataAccessClient;
26
+ /**
27
+ * The survey-area symbol prefix — a state code (`IA`) for a whole state, or a full symbol (`IA153`) for the
28
+ * single-area rung.
29
+ */
30
+ prefix: string;
31
+ /**
32
+ * Where vintages are kept.
33
+ */
34
+ cacheRoot: string;
35
+ /**
36
+ * Build only these symbols out of the ones the catalogue returns. Absent means all of them.
37
+ */
38
+ only?: ReadonlyArray<string>;
39
+ onProgress?: (message: string) => void;
40
+ }
41
+ /**
42
+ * What one region's acquisition produced.
43
+ */
44
+ export interface AcquiredRegion {
45
+ catalog: SurveyAreaCatalogEntry[];
46
+ archives: SurveyAreaArchive[];
47
+ areas: SurveyAreaInput[];
48
+ /**
49
+ * The refresh the artifact carries — the latest version date among the areas built.
50
+ */
51
+ sourceVintage: string;
52
+ }
53
+ /**
54
+ * Acquire every survey area a prefix names, and turn them into builder inputs.
55
+ *
56
+ * @throws {Error} When the catalogue holds nothing for the prefix, when `only` names a symbol the catalogue does not
57
+ * carry, or when any area's archive, metadata or shapefile refuses.
58
+ */
59
+ export declare function acquireRegion(options: AcquireRegionOptions): Promise<AcquiredRegion>;
60
+ //# sourceMappingURL=acquire.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acquire.d.ts","sourceRoot":"","sources":["../../lib/sdk/acquire.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACtD,OAAO,KAAK,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAC/E,OAAO,EAAsB,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAI1E,MAAM,WAAW,oBAAoB;IACpC,MAAM,EAAE,oBAAoB,CAAA;IAC5B;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAA;IACd;;OAEG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB;;OAEG;IACH,IAAI,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IAC5B,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACtC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,sBAAsB,EAAE,CAAA;IACjC,QAAQ,EAAE,iBAAiB,EAAE,CAAA;IAC7B,KAAK,EAAE,eAAe,EAAE,CAAA;IACxB;;OAEG;IACH,aAAa,EAAE,MAAM,CAAA;CACrB;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,cAAc,CAAC,CA2D1F"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Acquisition, end to end: which survey areas a region holds, their archives, and the inputs the builder
7
+ * takes.
8
+ *
9
+ * THE FRESHNESS QUESTION IS ANSWERED BY THE TABULAR SERVICE, NOT BY THE FILE HOST. `sacatalog.saverest` is
10
+ * the version-established date, and it is also what the archive's filename embeds — so one catalogue call
11
+ * both decides what to download and names the file. The download host cannot answer it: it refuses `HEAD`
12
+ * with 405 and ignores `Range`, so a length probe there is a full transfer.
13
+ *
14
+ * THE VINTAGE IS THE REFRESH THE BUILD INGESTED, AND IT IS ONE DATE FOR THE WHOLE ARTIFACT. NRCS performs
15
+ * ONE coordinated Annual Soils Refresh, each October 1; grouping `sacatalog` by year of `saverest` returns
16
+ * 2016: 1, 2025: 3,323, 2026: 56. So a region's areas share a refresh and the manifest can carry one
17
+ * `source_vintage` — the LATEST of the areas built, because that is the date after which nothing in the
18
+ * artifact changed. Every area's own date is kept per row in `soil_survey_area`, and so is the far older
19
+ * field-survey date, which is the number a currency claim actually turns on.
20
+ */
21
+ import { downloadSurveyArea } from "#sdk/download";
22
+ import { mapUnitShapefile, readSoilSourceIdentity, surveyAreaShapefile } from "#sdk/ingest";
23
+ import { readSurveyAreaAttributes, readSurveyAreaOutline } from "#sdk/survey-area";
24
+ /**
25
+ * Acquire every survey area a prefix names, and turn them into builder inputs.
26
+ *
27
+ * @throws {Error} When the catalogue holds nothing for the prefix, when `only` names a symbol the catalogue does not
28
+ * carry, or when any area's archive, metadata or shapefile refuses.
29
+ */
30
+ export async function acquireRegion(options) {
31
+ const catalog = await options.client.readSurveyAreaCatalog(options.prefix);
32
+ const wanted = options.only ? new Set(options.only.map((symbol) => symbol.toUpperCase())) : undefined;
33
+ const selected = wanted ? catalog.filter((entry) => wanted.has(entry.areasymbol.toUpperCase())) : catalog;
34
+ if (wanted) {
35
+ const found = new Set(selected.map((entry) => entry.areasymbol.toUpperCase()));
36
+ const missing = [...wanted].filter((symbol) => !found.has(symbol));
37
+ if (missing.length) {
38
+ throw new Error(`soil acquire: the catalogue holds no survey area named ${missing.join(", ")} — building the rest would quietly answer a smaller question than the one asked`);
39
+ }
40
+ }
41
+ options.onProgress?.(`${selected.length} survey area(s) from the catalogue`);
42
+ const archives = [];
43
+ const areas = [];
44
+ for (const entry of selected) {
45
+ const archive = await downloadSurveyArea({
46
+ areaSymbol: entry.areasymbol,
47
+ versionDate: entry.saverest,
48
+ cacheRoot: options.cacheRoot,
49
+ ...(options.onProgress ? { onProgress: options.onProgress } : {}),
50
+ });
51
+ archives.push(archive);
52
+ const attributes = await readSurveyAreaAttributes(archive.tabularDirectory, entry.areasymbol);
53
+ const shapefilePath = mapUnitShapefile(archive.spatialDirectory, entry.areasymbol);
54
+ const identity = await readSoilSourceIdentity({ shapefilePath });
55
+ const outline = await readSurveyAreaOutline(surveyAreaShapefile(archive.spatialDirectory, entry.areasymbol));
56
+ options.onProgress?.(`${entry.areasymbol}: ${identity.featureCount.toLocaleString()} delineations · ${attributes.mapUnits.length} map units · ` +
57
+ `${attributes.components.length} components · refresh ${attributes.saverest} · field survey ${attributes.surveySourceDate ?? "unstated"}`);
58
+ areas.push({
59
+ attributes,
60
+ shapefilePath,
61
+ outline,
62
+ declaredFeatureCount: identity.featureCount,
63
+ });
64
+ }
65
+ // The LATEST refresh among the areas built, because that is the date after which nothing in this artifact changed.
66
+ // Taking the earliest would claim a currency the newest area does not have; taking today's date would claim one no
67
+ // area has.
68
+ const sourceVintage = selected
69
+ .map((entry) => entry.saverest)
70
+ .toSorted()
71
+ .at(-1);
72
+ return { catalog, archives, areas, sourceVintage };
73
+ }
74
+ //# sourceMappingURL=acquire.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acquire.js","sourceRoot":"","sources":["../../lib/sdk/acquire.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,197 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Build `soil.db` — the sealed polygon layer, from the survey areas NRCS publishes.
7
+ *
8
+ * THE ACCUMULATION IS IN SQL, NOT IN A MAP. Classification is per delineation and shared with the
9
+ * resolution measurement, but where the touches GO differs on purpose: the measuring instrument holds
10
+ * them in memory because it is comparing candidate resolutions in one pass, and the builder streams them
11
+ * into a temporary table because memory has to stay flat in row count. A polygon layer's touches
12
+ * outnumber its features, and Iowa is 99 survey areas.
13
+ *
14
+ * ABSENCE IS NO ROW, IN EVERY TABLE. Land outside a published survey area gets no `layer_coverage` row and
15
+ * no summary row — never a zero, never an empty histogram. Inside a published area the coverage row says
16
+ * `designated` at completeness 1.0, because NRCS declares its own mapping complete for those areas at its
17
+ * own scale; a coverage cell reached ONLY by `NOTCOM` and access-denied polygons gets no row either,
18
+ * because the polygon exists and the soil mapping behind it does not.
19
+ *
20
+ * A COVERAGE ROW LICENSES ONLY THAT THE AUTHORITY MAPPED HERE. The reading is the class distribution, and
21
+ * a cell that is 100% `unrated_share` is `designated`-complete and carries no capability reading
22
+ * whatsoever. That pairing is not a corner case: 17.1% of national components carry no capability rating,
23
+ * and for the irrigated rating the figure is 85.1%.
24
+ *
25
+ * THE AREA CROSS-CHECK USES THE AUTHORITY'S OWN PUBLISHED FIGURE. `legend.areaacres` is what NRCS states
26
+ * the survey area covers — 378,800 acres for `IA153`, which is 1,532.9 km² against the 1,532.5 km² an
27
+ * independent projected measurement of the same shapefile reports. Comparing the spherical ring sum
28
+ * against it catches a hole read as an exterior ring, whose absence is silent: such a polygon is
29
+ * well-formed and simply answers "inside" for ground the authority did not map.
30
+ */
31
+ import { type AreaAgreementReading } from "@mailwoman/core/layers";
32
+ import { type ParsedGeometry } from "@mailwoman/spatial";
33
+ import type { SoilFeatureSource } from "#sdk/ingest";
34
+ import type { SoilChunkResult } from "#sdk/ingest-chunk";
35
+ import type { SurveyAreaAttributes } from "#sdk/survey-area";
36
+ /**
37
+ * Schema version of the domain tables. Bumped when a column changes meaning, never for an added column a reader can
38
+ * ignore.
39
+ */
40
+ export declare const SOIL_SCHEMA_VERSION = 1;
41
+ /**
42
+ * Delineation ids per chunk process.
43
+ *
44
+ * Sized against the ceiling the flood layer measured rather than guessed: single-process runs over that product died
45
+ * after roughly 510,000 and 798,000 features as h3's WASM heap fragmented. 100,000 leaves five times that margin, and
46
+ * the cost of a smaller number is one interpreter start per chunk. Iowa's largest survey area holds well under it, so
47
+ * on this build the bound costs one process per area — which is what makes an area's failure nameable.
48
+ */
49
+ export declare const DEFAULT_CHUNK_SIZE = 100000;
50
+ /**
51
+ * One survey area, ready to build: where its geometry is, and what its tabular export said.
52
+ */
53
+ export interface SurveyAreaInput {
54
+ attributes: SurveyAreaAttributes;
55
+ /**
56
+ * The map-unit polygon shapefile.
57
+ */
58
+ shapefilePath?: string;
59
+ /**
60
+ * The survey area's own outline, already read.
61
+ */
62
+ outline: ParsedGeometry;
63
+ /**
64
+ * An in-process feature source. Correct for a fixture and for anything small; the batched path builds one of these
65
+ * per chunk, so the two share one implementation.
66
+ */
67
+ source?: SoilFeatureSource;
68
+ /**
69
+ * The delineation count the shapefile declares, when the caller has already read it.
70
+ */
71
+ declaredFeatureCount?: number;
72
+ }
73
+ export interface BuildSoilOptions {
74
+ /**
75
+ * The survey areas to build, in the order they should be ingested.
76
+ */
77
+ areas: ReadonlyArray<SurveyAreaInput>;
78
+ /**
79
+ * The region the layer name carries — the pilot's is `ia`.
80
+ */
81
+ region: string;
82
+ /**
83
+ * Where the sealed artifact lands. The build writes beside it and swaps.
84
+ */
85
+ out: string;
86
+ /**
87
+ * The refresh the build ingested — `layer_manifest.version` and `source_vintage`.
88
+ */
89
+ sourceVintage: string;
90
+ buildCmd: string;
91
+ buildSHA: string;
92
+ /**
93
+ * ISO-8601, supplied by the caller. Never generated here: the contract says so, and a library-generated timestamp
94
+ * makes two builds of the same inputs differ.
95
+ */
96
+ createdAt: string;
97
+ /**
98
+ * The resolution the cell index and the reduction are built at — chosen from the measurement.
99
+ */
100
+ indexResolution: number;
101
+ /**
102
+ * The resolution `layer_coverage` rows are keyed at. Must be coarser than the index resolution.
103
+ */
104
+ coverageResolution: number;
105
+ /**
106
+ * Delineation ids per chunk process. See {@link DEFAULT_CHUNK_SIZE}.
107
+ */
108
+ chunkSize?: number;
109
+ /**
110
+ * Run the ingest IN THIS PROCESS rather than spawning chunk children. Only for fixtures, which carry no shapefile for
111
+ * a child to open.
112
+ */
113
+ inProcess?: boolean;
114
+ onProgress?: (message: string) => void;
115
+ }
116
+ export interface BuildSoilResult {
117
+ out: string;
118
+ region: string;
119
+ surveyAreas: number;
120
+ delineations: number;
121
+ mapUnits: number;
122
+ components: number;
123
+ indexResolution: number;
124
+ coverageResolution: number;
125
+ wholeCellRows: number;
126
+ partialCellRows: number;
127
+ /**
128
+ * `partialCellRows / (wholeCellRows + partialCellRows)` over the STORED index rows. The whole side is compacted, so
129
+ * this is not the same number the resolution was chosen on and is reported separately.
130
+ */
131
+ storedPartialShare: number;
132
+ capabilityCells: number;
133
+ /**
134
+ * Cells the lattice was used for, rather than the whole-cell fast path.
135
+ */
136
+ sampledCells: number;
137
+ /**
138
+ * Cells whose top class covers less than half the cell — the §4.7 number, taken off the shipping artifact rather than
139
+ * out of a separate harness.
140
+ */
141
+ topClassUnderHalfCells: number;
142
+ topClassUnderHalfShare: number;
143
+ /**
144
+ * Cells with NO class at all: the survey mapped them and rated nothing there.
145
+ */
146
+ classlessCells: number;
147
+ /**
148
+ * Cells the index touched that no lattice point landed inside. Dropped rather than stored as an all-zero
149
+ * distribution, and counted because a large number would mean the lattice is too coarse for this geometry.
150
+ */
151
+ unsampledCells: number;
152
+ meanDelineationsPerCell: number;
153
+ coarsenedFeatures: number;
154
+ storedResolutions: number[];
155
+ coverageCells: number;
156
+ /**
157
+ * Coverage cells inside a survey-area outline that no delineation with soil mapping reached. Reported rather than
158
+ * smoothed over: SSURGO is wall-to-wall inside a published area, so a large number means the outline and the
159
+ * delineations disagree.
160
+ */
161
+ coverageCellsWithoutMapping: number;
162
+ /**
163
+ * The area readings in square kilometres against the authority's own published figure, with the witness stated. The
164
+ * `known` count of areas that publish an acreage is what a receipt reads the absence against.
165
+ */
166
+ area: AreaAgreementReading;
167
+ sizeBytes: number;
168
+ }
169
+ /**
170
+ * Build the layer.
171
+ *
172
+ * @throws {Error} On a delineation the classifier refuses, a streamed count that disagrees with the shapefile's own
173
+ * declaration, an area total that disagrees with the authority's published acreage, or a set of outlines that yields
174
+ * no interior coverage cell at all.
175
+ */
176
+ export declare function buildSoilDatabase(options: BuildSoilOptions): Promise<BuildSoilResult>;
177
+ /**
178
+ * What the whole ingest produced, however many processes it took.
179
+ */
180
+ interface StreamResult {
181
+ delineations: number;
182
+ coarsened: number;
183
+ byArea: Map<string, number>;
184
+ observedByCoverageCell: Map<number, number>;
185
+ mappedByCoverageCell: Map<number, number>;
186
+ nestedM2: number;
187
+ allExteriorM2: number;
188
+ }
189
+ /**
190
+ * Add up what the chunks reported.
191
+ *
192
+ * Exported for its own test: the coverage-cell arithmetic is the one part of the batched path a fixture build cannot
193
+ * reach, and getting it wrong produces a well-formed artifact that under-reports how many delineations a cell holds.
194
+ */
195
+ export declare function aggregateChunks(chunks: ReadonlyArray<SoilChunkResult>): StreamResult;
196
+ export {};
197
+ //# sourceMappingURL=build-soil.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-soil.d.ts","sourceRoot":"","sources":["../../lib/sdk/build-soil.ts"],"names":[],"mappings":"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"}