@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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"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"}
package/out/index.js ADDED
@@ -0,0 +1,255 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * The `soil.db` reader — what the soil survey assigns at a coordinate, and on what basis.
7
+ *
8
+ * THREE ANSWERS, AND KEEPING THEM APART IS THE WHOLE JOB.
9
+ *
10
+ * 1. `designated` — the survey mapped this location and the cell's class distribution is the answer.
11
+ * 2. `designated_no_rating` — the survey mapped this location and rated nothing there. A cell that is
12
+ * 100% `unrated_share` or `notrateable_share` is `designated`-complete and carries no capability
13
+ * reading whatsoever, and that is not a corner case: 17.1% of national components carry no capability
14
+ * rating.
15
+ * 3. `unknown` — no coverage row. Outside any published survey area, or inside one where the polygon
16
+ * exists and the soil mapping behind it does not.
17
+ *
18
+ * READINGS 2 AND 3 LOOK THE SAME FROM A CLASS CODE AND ARE OPPOSITE ANSWERS FROM THE READER. A layer that
19
+ * could not tell them apart would report unmapped ground as unrated ground, which is one of the four
20
+ * absences this whole layer exists to keep separate.
21
+ *
22
+ * THE ANSWER IS A DISTRIBUTION, AND THE TOP CLASS ALWAYS ARRIVES WITH THE SHARE IT RESTS ON. NRCS's own
23
+ * `muaggatt` ships `niccdcd` beside `niccdcdpct` for exactly this reason, with an observed minimum of 2%.
24
+ * A caller that wants one class may take `topClass`; it cannot take it without also being handed
25
+ * `topClassShare`, because a 2% plurality and an 85% majority are different claims.
26
+ *
27
+ * NEITHER READING IS A STATEMENT ABOUT WHETHER THE LAND CAN BE FARMED. The layer reports what the soil
28
+ * survey assigns to the map unit covering a location, which is a fact about the map. NRCS states that its
29
+ * data "do not eliminate the need for onsite sampling, testing, and detailed study of specific sites for
30
+ * intensive uses" and are "intended for planning purposes only" — so `limits` carries the authority's own
31
+ * exclusions on every answer.
32
+ *
33
+ * THE PROBE IS ONE PRIMARY-KEY READ. The reduction is single-resolution and one row per cell, which is
34
+ * what makes it the spine key: a coordinate becomes a cell, the cell becomes a row, and the geometry tier
35
+ * underneath is never touched at read time. The unsimplified rings are there for a caller that wants to
36
+ * re-derive the claim, not for the probe.
37
+ *
38
+ * THE READER IS SYNCHRONOUS AND USES RAW PREPARED STATEMENTS, matching the resolution ladder's existing
39
+ * shape. The DDL that created these tables IS Kysely — see `schema.ts`.
40
+ */
41
+ import { assertCoverageNotEmpty, singleManifestRow, toLayerManifest, } from "@mailwoman/core/layers";
42
+ import { parseJSONStrict } from "@mailwoman/core/objects";
43
+ import { shortCellToInt } from "@mailwoman/spatial";
44
+ import { readCoverageAt } from "@mailwoman/spatial/h3/coverage";
45
+ import { DatabaseClient } from "@mailwoman/sqlite/client";
46
+ import { latLngToCell } from "h3-js";
47
+ import { SOIL_LAYER_NAME_PREFIX, SSURGO_PRODUCT_LIMITS } from "#vocabulary";
48
+ export { FarmlandScope, farmlandScope, SSURGO_PRODUCT_LIMITS } from "#vocabulary";
49
+ /**
50
+ * What the layer can say about a coordinate.
51
+ */
52
+ export const SoilReadingKind = {
53
+ /**
54
+ * The survey mapped this location and assigns at least one capability class here.
55
+ */
56
+ Designated: "designated",
57
+ /**
58
+ * The survey mapped this location and rated nothing here — every share is an absence share.
59
+ */
60
+ DesignatedNoRating: "designated_no_rating",
61
+ /**
62
+ * No coverage row. Unmapped by this authority, and never a low-capability reading.
63
+ */
64
+ Unknown: "unknown",
65
+ };
66
+ /**
67
+ * Read a sealed `soil.db`.
68
+ *
69
+ * Everything that would make the reader answer a well-formed wrong thing is refused at CONSTRUCTION rather than at
70
+ * query time: a manifest naming a different product, a coverage table with no rows, a vocabulary with no classes. Each
71
+ * of those would otherwise present as a reader that simply always answers `unknown`, which on a receipt is
72
+ * indistinguishable from a region the authority genuinely has not surveyed.
73
+ */
74
+ export class SoilCapabilityLookup {
75
+ identity;
76
+ #database;
77
+ #selectCell;
78
+ #selectCoverage;
79
+ #definitions;
80
+ #surveyAreaByBounds;
81
+ #bounds;
82
+ constructor(options) {
83
+ this.#database = new DatabaseClient(options.databasePath, { readOnly: true });
84
+ try {
85
+ const identity = readIdentity(this.#database, options.databasePath);
86
+ this.identity = identity.identity;
87
+ this.#definitions = identity.definitions;
88
+ this.#surveyAreaByBounds = identity.identity.surveyAreas;
89
+ this.#bounds = identity.bounds;
90
+ }
91
+ catch (error) {
92
+ this.#database.destroy();
93
+ throw error;
94
+ }
95
+ this.#selectCell = this.#database.prepare("SELECT class_shares, unrated_share, notrateable_share, nodata_share, other_share, mapped_share, top_class, top_class_share, weighting, delineations FROM soil_capability_cell WHERE h3_cell = ?");
96
+ this.#selectCoverage = this.#database.prepare("SELECT h3_cell, completeness, basis, observed_rows FROM layer_coverage WHERE h3_cell = ?");
97
+ }
98
+ /**
99
+ * What the soil survey assigns at this coordinate.
100
+ */
101
+ lookup(latitude, longitude) {
102
+ const indexCell = latLngToCell(latitude, longitude, this.identity.indexResolution);
103
+ const coverage = this.#readCoverage(indexCell);
104
+ // COVERAGE QUALIFIES THE READING, and without it there is nothing to report. Unlike a polygon hit — which is a
105
+ // determination at a location and needs no coverage row to be true — every answer this layer gives is a per-cell
106
+ // summary, so a summary row without a coverage row would state a determination outside the authority's footprint.
107
+ if (!coverage) {
108
+ return {
109
+ kind: SoilReadingKind.Unknown,
110
+ indexCellIndex: indexCell,
111
+ limits: SSURGO_PRODUCT_LIMITS,
112
+ };
113
+ }
114
+ const row = this.#selectCell.get(shortCellToInt(indexCell));
115
+ if (!row) {
116
+ // A coverage row without a summary row means the coverage cell is designated and this finer cell holds nothing —
117
+ // the survey-area edge. Unknown rather than "no rating": the authority's statement covers the coverage cell, and
118
+ // this location may be outside the delineations it covers.
119
+ return {
120
+ kind: SoilReadingKind.Unknown,
121
+ coverage,
122
+ indexCellIndex: indexCell,
123
+ limits: SSURGO_PRODUCT_LIMITS,
124
+ };
125
+ }
126
+ const distribution = {
127
+ classShares: parseJSONStrict(row.class_shares),
128
+ unratedShare: row.unrated_share,
129
+ notRateableShare: row.notrateable_share,
130
+ noDataShare: row.nodata_share,
131
+ otherShare: row.other_share,
132
+ mappedShare: row.mapped_share,
133
+ ...(row.top_class ? { topClass: row.top_class } : {}),
134
+ ...(row.top_class_share === null ? {} : { topClassShare: row.top_class_share }),
135
+ weighting: row.weighting,
136
+ delineations: row.delineations,
137
+ };
138
+ const surveyArea = this.#surveyAreaAt(latitude, longitude);
139
+ const definition = row.top_class ? this.#definitions.get(row.top_class) : undefined;
140
+ return {
141
+ kind: row.top_class ? SoilReadingKind.Designated : SoilReadingKind.DesignatedNoRating,
142
+ distribution,
143
+ ...(definition ? { topClassDefinition: definition } : {}),
144
+ ...(surveyArea ? { surveyArea } : {}),
145
+ coverage,
146
+ indexCellIndex: indexCell,
147
+ limits: SSURGO_PRODUCT_LIMITS,
148
+ };
149
+ }
150
+ [Symbol.dispose]() {
151
+ this.#database.destroy();
152
+ }
153
+ /**
154
+ * The coverage row for the index cell's parent at the coverage resolution.
155
+ */
156
+ #readCoverage(indexCell) {
157
+ return readCoverageAt(this.#selectCoverage, indexCell, this.identity.coverageResolution);
158
+ }
159
+ /**
160
+ * Which survey area a coordinate falls in, by the delineation bounds each area's row carries.
161
+ *
162
+ * A rectangle rather than the outline, and that is honest about what it is: the answer names WHICH published survey
163
+ * the reading came from, and two neighbouring counties' rectangles overlap at their corners. The reading itself does
164
+ * not depend on it — the cell row is the answer — so a corner ambiguity costs a label rather than a determination.
165
+ *
166
+ * A LINEAR SCAN, WHICH THE PILOT'S 99 SURVEY AREAS MAKE FREE AND A NATIONAL BUILD WOULD NOT. It returns on the first
167
+ * containing rectangle, so the pilot costs a few dozen comparisons per geocode. At the 3,380 survey areas the country
168
+ * holds this wants a bounding-box index; it is left as a scan because a structure sized for a set this build does not
169
+ * hold would be untested at the size it was built for.
170
+ */
171
+ #surveyAreaAt(latitude, longitude) {
172
+ for (const [index, bounds] of this.#bounds.entries()) {
173
+ if (longitude >= bounds.minLon &&
174
+ longitude <= bounds.maxLon &&
175
+ latitude >= bounds.minLat &&
176
+ latitude <= bounds.maxLat) {
177
+ return this.#surveyAreaByBounds[index];
178
+ }
179
+ }
180
+ return undefined;
181
+ }
182
+ }
183
+ /**
184
+ * Read and check the layer's identity.
185
+ */
186
+ function readIdentity(database, databasePath) {
187
+ const manifestRows = database.prepare("SELECT * FROM layer_manifest").all();
188
+ // The name's SUFFIX names the region a build covers, so the reader checks the prefix rather than a whole name — which
189
+ // is why it asserts its own identity instead of taking `parseManifestRows`: one authority, one product, one rating
190
+ // vocabulary per artifact, over whichever survey areas were built.
191
+ const row = singleManifestRow(manifestRows, `soil reader: ${databasePath}`);
192
+ const name = String(row.name);
193
+ if (!name.startsWith(SOIL_LAYER_NAME_PREFIX)) {
194
+ throw new Error(`soil reader: ${databasePath} is layer ${JSON.stringify(name)}, which is not a ${JSON.stringify(SOIL_LAYER_NAME_PREFIX)} layer — one authority, one product, one rating vocabulary per artifact`);
195
+ }
196
+ const manifest = toLayerManifest(row);
197
+ const spineKeys = manifest.spineKeys;
198
+ if (!spineKeys.h3) {
199
+ throw new Error(`soil reader: ${databasePath} declares no h3 spine key`);
200
+ }
201
+ const coverageCount = database.prepare("SELECT count(*) AS n FROM layer_coverage").get().n;
202
+ assertCoverageNotEmpty(coverageCount, `soil reader: ${databasePath}`, "a region the authority has not surveyed");
203
+ const areaRows = database
204
+ .prepare("SELECT areasymbol, areaname, saverest, survey_source_date, survey_source_title, source_scale, mapping_scale, min_lat, min_lon, max_lat, max_lon, coverage_resolution FROM soil_survey_area ORDER BY areasymbol")
205
+ .all();
206
+ if (!areaRows.length) {
207
+ throw new Error(`soil reader: ${databasePath} names no survey area — the footprint is the claim, and an artifact that cannot name which surveys it holds cannot support one`);
208
+ }
209
+ const coverageResolutions = new Set(areaRows.map((area) => area.coverage_resolution));
210
+ if (coverageResolutions.size !== 1) {
211
+ throw new Error(`soil reader: ${databasePath}'s survey areas declare coverage resolutions ${[...coverageResolutions].join(", ")} — a probe derives its coverage cell from one resolution, and a mixed table would answer some locations from the wrong parent`);
212
+ }
213
+ const definitions = new Map();
214
+ for (const vocabularyRow of database
215
+ .prepare("SELECT code, definition FROM soil_vocabulary WHERE domain = 'capability_class' ORDER BY sequence")
216
+ .all()) {
217
+ definitions.set(vocabularyRow.code, vocabularyRow.definition);
218
+ }
219
+ if (!definitions.size) {
220
+ throw new Error(`soil reader: ${databasePath} declares no capability-class vocabulary — an answer could not be checked against the authority's own domain`);
221
+ }
222
+ const weighting = database
223
+ .prepare("SELECT code, definition FROM soil_vocabulary WHERE domain = 'share_weighting'")
224
+ .get();
225
+ if (!weighting) {
226
+ throw new Error(`soil reader: ${databasePath} records no share weighting — the shares would arrive without the one fact needed to know what they are shares OF`);
227
+ }
228
+ return {
229
+ identity: {
230
+ manifest,
231
+ indexResolution: spineKeys.h3.resolution,
232
+ coverageResolution: [...coverageResolutions][0],
233
+ surveyAreas: areaRows.map((area) => ({
234
+ areaSymbol: area.areasymbol,
235
+ areaName: area.areaname,
236
+ saverest: area.saverest,
237
+ surveySourceDate: area.survey_source_date,
238
+ surveySourceTitle: area.survey_source_title,
239
+ sourceScale: area.source_scale,
240
+ mappingScale: area.mapping_scale,
241
+ })),
242
+ classCodes: [...definitions.keys()],
243
+ weighting: { code: weighting.code, description: weighting.definition },
244
+ databasePath,
245
+ },
246
+ definitions,
247
+ bounds: areaRows.map((area) => ({
248
+ minLat: area.min_lat,
249
+ minLon: area.min_lon,
250
+ maxLat: area.max_lat,
251
+ maxLon: area.max_lon,
252
+ })),
253
+ };
254
+ }
255
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,369 @@
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 type { LayerContractDatabase } from "@mailwoman/core/layers";
35
+ import { type Kysely } from "kysely";
36
+ /**
37
+ * Whether an H3 cell lies wholly inside one map-unit delineation, or is crossed by its boundary.
38
+ */
39
+ export declare const SoilCellContainment: {
40
+ /**
41
+ * Every point in the cell is inside the delineation. Answered from the index alone, with no geometry read.
42
+ */
43
+ readonly Whole: "whole";
44
+ /**
45
+ * The delineation's boundary crosses the cell. The index has narrowed the candidates; the point test decides.
46
+ */
47
+ readonly Partial: "partial";
48
+ };
49
+ export type SoilCellContainment = (typeof SoilCellContainment)[keyof typeof SoilCellContainment];
50
+ /**
51
+ * One map-unit delineation, verbatim. A plain rowid table: it holds a geometry blob, which is the one shape `WITHOUT
52
+ * ROWID` hurts.
53
+ */
54
+ export interface SoilMapUnitAreaTable {
55
+ /**
56
+ * `<areasymbol>:<ordinal>` — the survey area plus this delineation's position in the authority's own shapefile order.
57
+ * SSURGO publishes no per-delineation key of its own (`mukey` names the MAP UNIT, and one map unit has many
58
+ * delineations), so the ordinal is what makes a row nameable at all. Text, so a source that starts publishing a
59
+ * non-numeric id needs no schema change.
60
+ */
61
+ area_id: string;
62
+ /**
63
+ * The map unit this delineation belongs to — NRCS's own key, and the join to every attribute.
64
+ */
65
+ mukey: string;
66
+ areasymbol: string;
67
+ min_lat: number;
68
+ min_lon: number;
69
+ max_lat: number;
70
+ max_lon: number;
71
+ /**
72
+ * The authority's ring coordinates, unsimplified — see `@mailwoman/spatial`'s ring blob for the layout.
73
+ */
74
+ rings: Uint8Array;
75
+ }
76
+ /**
77
+ * Per (cell, delineation): does the delineation cover the whole cell, or only part of it?
78
+ *
79
+ * Keyed on the delineation rather than on the map unit, because the reduction weights by the area a DELINEATION covers
80
+ * and two delineations of one map unit reaching the same cell cover different ground.
81
+ */
82
+ export interface SoilMapUnitCellTable {
83
+ /**
84
+ * 48-bit short H3 cell. Mixed-resolution: `whole` rows are compacted parent-ward, `partial` rows stay at the index
85
+ * resolution.
86
+ */
87
+ h3_cell: number;
88
+ /**
89
+ * The resolution this row's cell was captured at. A short cell does not name its own resolution, and a table that
90
+ * mixes them cannot be probed without it.
91
+ */
92
+ resolution: number;
93
+ area_id: string;
94
+ /**
95
+ * One of {@link SoilCellContainment}.
96
+ */
97
+ containment: string;
98
+ }
99
+ /**
100
+ * One SSURGO map unit — the attribute row every delineation joins to.
101
+ */
102
+ export interface SoilMapUnitTable {
103
+ mukey: string;
104
+ areasymbol: string;
105
+ /**
106
+ * The map unit symbol. `NOTCOM` and `NOTPUB` are MEANINGFUL values here, not codes to skip: they name a polygon the
107
+ * authority drew with no soil mapping behind it.
108
+ */
109
+ musym: string;
110
+ muname: string;
111
+ /**
112
+ * `Consociation` | `Complex` | `Association` | `Undifferentiated group`, from the authority's declared domain. A
113
+ * complex is NRCS's statement that two or more soils are intermingled and cannot be separated at the mapping scale —
114
+ * the mixture is the survey's finding rather than this layer's loss.
115
+ */
116
+ mukind: string | null;
117
+ mustatus: string | null;
118
+ /**
119
+ * The full conditional string, verbatim. NULL is not "not prime farmland": `Not prime farmland` is itself a declared
120
+ * value, and NULL means the map unit carries no farmland classification at all.
121
+ */
122
+ farmlndcl: string | null;
123
+ /**
124
+ * Which of {@link FarmlandScope} `farmlndcl` falls under — federal criteria travel between states, delegated ones do
125
+ * not. Derived once at build time so a consumer never has to re-read 7 CFR 657.5 to know whether two rows are
126
+ * comparable.
127
+ */
128
+ farmland_scope: string;
129
+ /**
130
+ * NRCS's OWN dominant-condition capability class for the map unit, carried rather than recomputed.
131
+ */
132
+ niccdcd: string | null;
133
+ /**
134
+ * And the share that class actually covers. The pair is the pattern this layer's cell reduction reproduces at cell
135
+ * grain, so carrying both makes the two comparable.
136
+ */
137
+ niccdcdpct: number | null;
138
+ /**
139
+ * Whether this map unit is a polygon with NO soil mapping behind it — `NOTCOM`, `NOTPUB`, access denied, or a map
140
+ * unit carrying no components at all. Such a map unit contributes to `nodata_share` and NEVER to a class share.
141
+ */
142
+ no_mapping: number;
143
+ }
144
+ /**
145
+ * A map unit's components, because a map unit is a mixture.
146
+ */
147
+ export interface SoilComponentTable {
148
+ cokey: string;
149
+ mukey: string;
150
+ /**
151
+ * The component's representative percentage of its map unit — the weight the reduction aggregates by.
152
+ */
153
+ comppct_r: number;
154
+ compname: string | null;
155
+ /**
156
+ * `Miscellaneous area` is what separates NOT-RATEABLE from UNRATED: a rock outcrop or a water body is a component the
157
+ * capability rating does not apply to, while an unrated series is one the survey did not rate.
158
+ */
159
+ compkind: string | null;
160
+ /**
161
+ * Nonirrigated Land Capability Class, `"1"`–`"8"`. NULL means NOT RATED, never class 8.
162
+ */
163
+ nirrcapcl: string | null;
164
+ /**
165
+ * Subclass `c` | `e` | `s` | `w`.
166
+ */
167
+ nirrcapscl: string | null;
168
+ /**
169
+ * The irrigated rating. NULL on 85.1% of national components, because it is populated only where irrigation is a
170
+ * considered use — so its absence is a statement about the rating's applicability rather than about the land, and it
171
+ * is carried but never reduced.
172
+ */
173
+ irrcapcl: string | null;
174
+ irrcapscl: string | null;
175
+ /**
176
+ * The NCCPI v3.0 overall index in [0, 1], under its own rule name. Never blended with the capability class.
177
+ */
178
+ nccpi_v3: number | null;
179
+ }
180
+ /**
181
+ * THE SHARED ARTIFACT BOTH CONSUMERS READ: one row per cell, the index reduced once.
182
+ *
183
+ * The result-level observation takes {@link SoilCapabilityCellTable.top_class} with the share it rests on; #1683's
184
+ * affordance vector takes `class_shares` plus the four absence shares as its axis. One artifact, one aggregation, one
185
+ * set of provenance rows, and no possibility of the two consumers disagreeing about what the ground is.
186
+ */
187
+ export interface SoilCapabilityCellTable {
188
+ /**
189
+ * 48-bit short H3 cell at the declared index resolution. SINGLE-resolution, unlike {@link SoilMapUnitCellTable}: this
190
+ * is the table a consumer joins on, and a mixed-resolution join key is not one.
191
+ */
192
+ h3_cell: number;
193
+ /**
194
+ * JSON: the authority's class codes mapped to their area-weighted share, sorted by descending share. Shares above the
195
+ * declared truncation floor only; the remainder is in `other_share`.
196
+ */
197
+ class_shares: string;
198
+ /**
199
+ * Mapped soil components carrying a NULL rating — the survey did not rate them.
200
+ */
201
+ unrated_share: number;
202
+ /**
203
+ * Miscellaneous areas (rock outcrop, water) the rating does not apply to.
204
+ */
205
+ notrateable_share: number;
206
+ /**
207
+ * `NOTCOM`, `NOTPUB` and access-denied map units: a polygon the authority drew, with no soil mapping behind it.
208
+ */
209
+ nodata_share: number;
210
+ /**
211
+ * The truncated minority tail. Stored explicitly so the five shares always sum to 1 and a reader can see how much was
212
+ * folded away rather than inferring it from a gap.
213
+ */
214
+ other_share: number;
215
+ /**
216
+ * The fraction of the CELL covered by any map-unit delineation at all.
217
+ *
218
+ * The five shares above are normalized over this, so they sum to 1 exactly. A cell at the edge of a survey area is
219
+ * partly outside every delineation, and without this column that unmapped remainder would silently deflate every
220
+ * class share — an absence represented as a small number, which is the one thing this schema exists to prevent. A
221
+ * cell wholly inside the mapped area reads 1.
222
+ */
223
+ mapped_share: number;
224
+ /**
225
+ * The largest class share, and the share it rests on — the result-level consumer's reading, and NRCS's own
226
+ * `niccdcd`/`niccdcdpct` pattern at cell grain. NULL when the cell carries no class at all, which is a real answer: a
227
+ * cell that is 100% `unrated_share` is complete and holds no capability reading whatsoever.
228
+ */
229
+ top_class: string | null;
230
+ top_class_share: number | null;
231
+ /**
232
+ * Which weighting produced the shares — {@link SOIL_SHARE_WEIGHTING}. Stored per row rather than only in the
233
+ * manifest, because a later build at a different weighting must not read as the same claim.
234
+ */
235
+ weighting: string;
236
+ /**
237
+ * How many delineations reached this cell. The denominator behind every share above, and the number that separates a
238
+ * confident single-delineation cell from a crowded one.
239
+ */
240
+ delineations: number;
241
+ }
242
+ /**
243
+ * The authority's mapped footprint, one row per published survey area — derived from the survey-area OUTLINE and each
244
+ * area's own metadata, NEVER from the rated polygons.
245
+ *
246
+ * Deriving it from the rated polygons is the error §3.2 of the survey describes: `NOTCOM` and access-denied map units
247
+ * are INSIDE the footprint and carry no rating, so a footprint taken from the rated set would report them as unmapped
248
+ * when the authority has in fact declared exactly what they are.
249
+ */
250
+ export interface SoilSurveyAreaTable {
251
+ areasymbol: string;
252
+ areaname: string;
253
+ /**
254
+ * The version-established date from `sacatalog.saverest` — the refresh, and the manifest's vintage.
255
+ */
256
+ saverest: string;
257
+ saversion: number | null;
258
+ /**
259
+ * The OLDEST source citation date in the area's own FGDC lineage — the field survey the republished polygons rest on.
260
+ *
261
+ * This is a different fact from `saverest` and keeping them apart is the point: `IA153` carries a 2025-09-09 refresh
262
+ * over a field survey published in 1960, and the dataset's own time-period-of-content ends at the refresh, so a
263
+ * consumer reading that as survey currency reads it wrong.
264
+ */
265
+ survey_source_date: string | null;
266
+ /**
267
+ * The title of the source `survey_source_date` came from, so the date is checkable rather than assertible.
268
+ */
269
+ survey_source_title: string | null;
270
+ /**
271
+ * The scale of that original source — 15840 for `IA153`'s 1960 survey.
272
+ */
273
+ source_scale: number | null;
274
+ /**
275
+ * The scale the map units were DIGITIZED at, from `legend.projectscale` — 12000 for `IA153`. A different number from
276
+ * `source_scale` and a different fact: one is how finely the ground was walked, the other how finely it was drawn.
277
+ */
278
+ mapping_scale: number | null;
279
+ /**
280
+ * The area the authority publishes for the survey area, in acres. The independent witness the ring-area check
281
+ * compares against.
282
+ */
283
+ area_acres: number | null;
284
+ min_lat: number;
285
+ min_lon: number;
286
+ max_lat: number;
287
+ max_lon: number;
288
+ /**
289
+ * How many `layer_coverage` rows this survey area produced, and at what resolution.
290
+ */
291
+ coverage_cells: number;
292
+ coverage_resolution: number;
293
+ }
294
+ /**
295
+ * The authority's declared domain for one `Choice` column, read out of the `msdomdet.txt` the archive ships.
296
+ *
297
+ * Stored so a reader can refuse a code the layer was never built to hold, and so the authority's own prose definition
298
+ * of "capability class 3" travels with the artifact instead of living in a handbook the reader has to go find.
299
+ */
300
+ export interface SoilVocabularyTable {
301
+ /**
302
+ * The domain name as NRCS spells it — `capability_class`, `capability_subclass`, `farmland_classification`,
303
+ * `component_kind`, `mapunit_kind`.
304
+ */
305
+ domain: string;
306
+ /**
307
+ * The value as it appears in the data.
308
+ */
309
+ code: string;
310
+ /**
311
+ * The authority's own definition of it.
312
+ */
313
+ definition: string;
314
+ /**
315
+ * Ordering within the domain, as the authority declares it.
316
+ */
317
+ sequence: number;
318
+ }
319
+ /**
320
+ * Pass to `new DatabaseClient<SoilDatabase>(...)`.
321
+ */
322
+ export interface SoilDatabase extends LayerContractDatabase {
323
+ soil_map_unit_area: SoilMapUnitAreaTable;
324
+ soil_map_unit_cell: SoilMapUnitCellTable;
325
+ soil_map_unit: SoilMapUnitTable;
326
+ soil_component: SoilComponentTable;
327
+ soil_capability_cell: SoilCapabilityCellTable;
328
+ soil_survey_area: SoilSurveyAreaTable;
329
+ soil_vocabulary: SoilVocabularyTable;
330
+ }
331
+ /**
332
+ * The slice of a Kysely handle the DDL touches. Kysely is invariant in its schema parameter, so naming only the members
333
+ * these functions call lets a caller pass its own wider handle.
334
+ */
335
+ export type SoilSchemaHandle = Pick<Kysely<SoilDatabase>, "schema">;
336
+ /**
337
+ * Create `soil_map_unit_area`. A PLAIN rowid table on purpose — the `rings` blob is exactly the payload `WITHOUT ROWID`
338
+ * penalizes.
339
+ */
340
+ export declare function createSoilMapUnitAreaTable(db: SoilSchemaHandle): Promise<void>;
341
+ /**
342
+ * Create `soil_map_unit_cell` — the containment index. Small fixed-width rows probed by their exact primary key.
343
+ */
344
+ export declare function createSoilMapUnitCellTable(db: SoilSchemaHandle): Promise<void>;
345
+ /**
346
+ * Create `soil_map_unit`.
347
+ */
348
+ export declare function createSoilMapUnitTable(db: SoilSchemaHandle): Promise<void>;
349
+ /**
350
+ * Create `soil_component`.
351
+ */
352
+ export declare function createSoilComponentTable(db: SoilSchemaHandle): Promise<void>;
353
+ /**
354
+ * Create `soil_capability_cell` — the reduction both consumers read.
355
+ */
356
+ export declare function createSoilCapabilityCellTable(db: SoilSchemaHandle): Promise<void>;
357
+ /**
358
+ * Create `soil_survey_area`.
359
+ */
360
+ export declare function createSoilSurveyAreaTable(db: SoilSchemaHandle): Promise<void>;
361
+ /**
362
+ * Create `soil_vocabulary`.
363
+ */
364
+ export declare function createSoilVocabularyTable(db: SoilSchemaHandle): Promise<void>;
365
+ /**
366
+ * Every domain table this layer owns, in dependency order.
367
+ */
368
+ export declare function createSoilTables(db: SoilSchemaHandle): Promise<void>;
369
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../lib/schema.ts"],"names":[],"mappings":"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"}