@mailwoman/soil 9.4.0 → 10.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.
- package/README.md +118 -116
- package/lib/index.ts +66 -98
- package/lib/paths.ts +24 -0
- package/lib/schema.ts +188 -120
- package/lib/vocabulary.ts +40 -107
- package/out/index.d.ts +52 -70
- package/out/index.d.ts.map +1 -1
- package/out/index.js +24 -72
- package/out/index.js.map +1 -1
- package/out/paths.d.ts +19 -0
- package/out/paths.d.ts.map +1 -0
- package/out/paths.js +21 -0
- package/out/paths.js.map +1 -0
- package/out/schema.d.ts +187 -119
- package/out/schema.d.ts.map +1 -1
- package/out/schema.js +31 -31
- package/out/schema.js.map +1 -1
- package/out/sdk/acquire.d.ts +15 -24
- package/out/sdk/acquire.d.ts.map +1 -1
- package/out/sdk/acquire.js +5 -20
- package/out/sdk/acquire.js.map +1 -1
- package/out/sdk/build-soil.d.ts +67 -70
- package/out/sdk/build-soil.d.ts.map +1 -1
- package/out/sdk/build-soil.js +65 -95
- package/out/sdk/build-soil.js.map +1 -1
- package/out/sdk/cell-tiers.d.ts +7 -19
- package/out/sdk/cell-tiers.d.ts.map +1 -1
- package/out/sdk/cell-tiers.js +19 -38
- package/out/sdk/cell-tiers.js.map +1 -1
- package/out/sdk/cells.d.ts +15 -41
- package/out/sdk/cells.d.ts.map +1 -1
- package/out/sdk/cells.js +11 -38
- package/out/sdk/cells.js.map +1 -1
- package/out/sdk/client.d.ts +23 -48
- package/out/sdk/client.d.ts.map +1 -1
- package/out/sdk/client.js +19 -63
- package/out/sdk/client.js.map +1 -1
- package/out/sdk/download.d.ts +24 -47
- package/out/sdk/download.d.ts.map +1 -1
- package/out/sdk/download.js +14 -54
- package/out/sdk/download.js.map +1 -1
- package/out/sdk/ingest/chunk.d.ts +77 -0
- package/out/sdk/ingest/chunk.d.ts.map +1 -0
- package/out/sdk/{ingest-chunk.js → ingest/chunk.js} +19 -17
- package/out/sdk/ingest/chunk.js.map +1 -0
- package/out/sdk/ingest/worker.d.ts +9 -0
- package/out/sdk/ingest/worker.d.ts.map +1 -0
- package/out/{scripts/ingest-chunk.js → sdk/ingest/worker.js} +11 -10
- package/out/sdk/ingest/worker.js.map +1 -0
- package/out/sdk/ingest.d.ts +40 -52
- package/out/sdk/ingest.d.ts.map +1 -1
- package/out/sdk/ingest.js +18 -61
- package/out/sdk/ingest.js.map +1 -1
- package/out/sdk/measure-resolutions.d.ts +5 -16
- package/out/sdk/measure-resolutions.d.ts.map +1 -1
- package/out/sdk/measure-resolutions.js +3 -15
- package/out/sdk/measure-resolutions.js.map +1 -1
- package/out/sdk/reduce.d.ts +33 -59
- package/out/sdk/reduce.d.ts.map +1 -1
- package/out/sdk/reduce.js +47 -78
- package/out/sdk/reduce.js.map +1 -1
- package/out/sdk/survey-area.d.ts +16 -48
- package/out/sdk/survey-area.d.ts.map +1 -1
- package/out/sdk/survey-area.js +33 -77
- package/out/sdk/survey-area.js.map +1 -1
- package/out/sdk/tabular.d.ts +32 -31
- package/out/sdk/tabular.d.ts.map +1 -1
- package/out/sdk/tabular.js +58 -56
- package/out/sdk/tabular.js.map +1 -1
- package/out/sdk/test-kit.d.ts +57 -0
- package/out/sdk/test-kit.d.ts.map +1 -0
- package/out/{test-kit.js → sdk/test-kit.js} +18 -39
- package/out/sdk/test-kit.js.map +1 -0
- package/out/sdk/verify.d.ts +15 -51
- package/out/sdk/verify.d.ts.map +1 -1
- package/out/sdk/verify.js +19 -77
- package/out/sdk/verify.js.map +1 -1
- package/out/vocabulary.d.ts +37 -103
- package/out/vocabulary.d.ts.map +1 -1
- package/out/vocabulary.js +34 -107
- package/out/vocabulary.js.map +1 -1
- package/package.json +36 -190
- package/{lib/sdk → sdk}/acquire.ts +17 -27
- package/{lib/sdk → sdk}/build-soil.ts +114 -128
- package/{lib/sdk → sdk}/cell-tiers.ts +20 -39
- package/{lib/sdk → sdk}/cells.ts +17 -43
- package/sdk/client.ts +147 -0
- package/sdk/download.ts +131 -0
- package/{lib/sdk/ingest-chunk.ts → sdk/ingest/chunk.ts} +34 -26
- package/{lib/scripts/ingest-chunk.ts → sdk/ingest/worker.ts} +10 -9
- package/sdk/ingest.ts +253 -0
- package/{lib/sdk → sdk}/measure-resolutions.ts +5 -16
- package/sdk/reduce.ts +344 -0
- package/{lib/sdk → sdk}/survey-area.ts +39 -83
- package/{lib/sdk → sdk}/tabular.ts +62 -59
- package/{lib → sdk}/test-kit.ts +18 -40
- package/{lib/sdk → sdk}/verify.ts +29 -85
- package/lib/sdk/client.ts +0 -184
- package/lib/sdk/download.ts +0 -161
- package/lib/sdk/index.ts +0 -20
- package/lib/sdk/ingest.ts +0 -278
- package/lib/sdk/reduce.ts +0 -375
- package/out/scripts/ingest-chunk.d.ts +0 -11
- package/out/scripts/ingest-chunk.d.ts.map +0 -1
- package/out/scripts/ingest-chunk.js.map +0 -1
- package/out/sdk/index.d.ts +0 -20
- package/out/sdk/index.d.ts.map +0 -1
- package/out/sdk/index.js +0 -20
- package/out/sdk/index.js.map +0 -1
- package/out/sdk/ingest-chunk.d.ts +0 -73
- package/out/sdk/ingest-chunk.d.ts.map +0 -1
- package/out/sdk/ingest-chunk.js.map +0 -1
- package/out/test-kit.d.ts +0 -79
- package/out/test-kit.d.ts.map +0 -1
- package/out/test-kit.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# `@mailwoman/soil`
|
|
2
2
|
|
|
3
|
-
USDA NRCS SSURGO soil survey as a sealed spatial layer
|
|
3
|
+
This package provides the USDA NRCS SSURGO soil survey as a sealed spatial layer. It covers acquisition,
|
|
4
|
+
the `soil.db` build, and the reader.
|
|
4
5
|
|
|
5
|
-
The layer answers one question
|
|
6
|
-
location
|
|
7
|
-
|
|
6
|
+
The layer answers one question: **what does the soil survey assign to the map unit covering this
|
|
7
|
+
location?** It answers with a distribution instead of a single class, because three measurements require
|
|
8
|
+
that shape.
|
|
8
9
|
|
|
9
10
|
## What it stores, and why it is a distribution
|
|
10
11
|
|
|
@@ -15,42 +16,43 @@ it is what three measurements force.
|
|
|
15
16
|
| `IA153` delineations smaller than one resolution-9 cell | 15,350 of 17,966 (**85.4%**) |
|
|
16
17
|
| NRCS's own dominant-condition share `muaggatt.niccdcdpct`, observed minimum | **2%** |
|
|
17
18
|
|
|
18
|
-
No affordable cell size removes the mixture, because the mixture is the survey's own finding
|
|
19
|
-
map units (38.0%) are complexes, associations or undifferentiated groups
|
|
20
|
-
soils are intermingled and cannot be separated at the mapping scale. NRCS itself ships its
|
|
21
|
-
dominant-condition class beside the share that class covers
|
|
19
|
+
No affordable cell size removes the mixture, because the mixture is the survey's own finding. 128,499
|
|
20
|
+
map units (38.0%) are complexes, associations or undifferentiated groups. With those designations NRCS
|
|
21
|
+
states that the soils are intermingled and cannot be separated at the mapping scale. NRCS itself ships its
|
|
22
|
+
dominant-condition class beside the share that class covers, and `soil_capability_cell` reproduces that
|
|
22
23
|
pattern at cell grain.
|
|
23
24
|
|
|
24
|
-
**One artifact
|
|
25
|
-
|
|
26
|
-
one aggregation, one set of provenance rows,
|
|
25
|
+
**One artifact serves two consumers.** A result-level observation reads `top_class` with
|
|
26
|
+
`top_class_share`. A bulk per-cell signal reads `class_shares` plus the four absence shares as one axis.
|
|
27
|
+
Both come from one acquisition, one aggregation, and one set of provenance rows, so the two consumers
|
|
28
|
+
cannot disagree about the ground.
|
|
27
29
|
|
|
28
30
|
## Four absences, and the one positive negative
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
The layer never represents an absence with a small number. A consumer can tell apart five readings:
|
|
31
33
|
|
|
32
34
|
| reading | where it lives |
|
|
33
35
|
| ---------------------------------------------------- | -------------------------------------------------- |
|
|
34
|
-
| the survey rated this land as precluding cultivation | class `"8"` in `class_shares
|
|
36
|
+
| the survey rated this land as precluding cultivation | class `"8"` in `class_shares`, a determination |
|
|
35
37
|
| the survey did not rate it | `unrated_share` |
|
|
36
38
|
| the rating does not apply to it (rock, water) | `notrateable_share` |
|
|
37
39
|
| the polygon exists, the soil mapping does not | `nodata_share` (`NOTCOM`, `NOTPUB`, access denied) |
|
|
38
40
|
| there is no survey here at all | **no `layer_coverage` row, and no summary row** |
|
|
39
41
|
|
|
40
|
-
Class 8 is a determination and is a class share like any other.
|
|
41
|
-
well-formed wrong answer, and 67,547 national components carry it. The irrigated rating
|
|
42
|
-
|
|
43
|
-
where irrigation is a considered use, so
|
|
42
|
+
Class 8 is a determination and is a class share like any other. A merge with the absence readings
|
|
43
|
+
produces a well-formed wrong answer, and 67,547 national components carry it. The irrigated rating shows
|
|
44
|
+
the same problem at a larger scale. `irrcapcl` is NULL on 85.1% of national components because it is
|
|
45
|
+
populated only where irrigation is a considered use, so the layer stores it and never reduces it.
|
|
44
46
|
|
|
45
|
-
`other_share` carries the truncated minority tail, so the five shares always sum to 1 and a reader can
|
|
46
|
-
see how much was folded away
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
`other_share` carries the truncated minority tail, so the five shares always sum to 1, and a reader can
|
|
48
|
+
see how much was folded away without inferring it from a gap. `mapped_share` says how much of the cell
|
|
49
|
+
any delineation covers. Without it, the unmapped remainder of a cell at a survey-area edge would silently
|
|
50
|
+
shrink every class share.
|
|
49
51
|
|
|
50
52
|
## The resolution, measured
|
|
51
53
|
|
|
52
|
-
The index resolution
|
|
53
|
-
1,532.5 km
|
|
54
|
+
The index resolution was chosen by measurement. The measurements below come from `IA153`, which has
|
|
55
|
+
17,966 delineations over 1,532.5 km² and a median delineation of 24,863 m²:
|
|
54
56
|
|
|
55
57
|
| res | touched cells | whole | partial | partial share | whole after compaction | (cell, delineation) pairs | mean delineations/cell | top class under half |
|
|
56
58
|
| --- | ------------: | -----: | ------: | ------------: | ---------------------: | ------------------------: | ---------------------: | -------------------: |
|
|
@@ -59,87 +61,86 @@ The index resolution is a measurement, not an argument. Measured on `IA153` —
|
|
|
59
61
|
| 9 | 15,136 | 369 | 14,767 | **97.6%** | 315 | 80,956 | 5.35 | **30.7%** |
|
|
60
62
|
| 10 | 104,508 | 13,691 | 90,817 | **86.9%** | 11,537 | 268,408 | 2.57 | **18.2%** |
|
|
61
63
|
|
|
62
|
-
**The `partial` share
|
|
63
|
-
|
|
64
|
-
`compactCells` collapses long uniform interiors. Soil delineations are
|
|
65
|
-
index answers almost no probe on its own at any candidate resolution**, and compaction
|
|
66
|
-
|
|
67
|
-
interiors collapse by orders of magnitude. At resolution 7
|
|
64
|
+
**The `partial` share is the reverse of the flood layer's, as the survey predicted, and the reversal is
|
|
65
|
+
complete.** Flood polygons are large relative to their cells, so most cells fall wholly inside one zone and
|
|
66
|
+
`compactCells` collapses long uniform interiors. Soil delineations are small relative to their cells, so
|
|
67
|
+
**the containment index answers almost no probe on its own at any candidate resolution**, and compaction
|
|
68
|
+
saves almost no space. At resolution 9, 369 whole cells compact to 315, a 14.6% reduction, while the flood
|
|
69
|
+
layer's interiors collapse by orders of magnitude. At resolution 7 there are zero whole cells to collapse.
|
|
68
70
|
|
|
69
|
-
|
|
70
|
-
relying on the index the way the flood layer can. The unsimplified geometry is still
|
|
71
|
-
|
|
71
|
+
For that reason this layer carries the reduced `soil_capability_cell` **alongside** the index instead of
|
|
72
|
+
relying on the index the way the flood layer can. The unsimplified geometry is still stored,
|
|
73
|
+
and the reduction weights by it, but probes are answered from the reduced table.
|
|
72
74
|
|
|
73
|
-
**The two numbers move in opposite directions, and only one of them
|
|
74
|
-
is 87–100% at every candidate, so it cannot
|
|
75
|
-
mixture number can, and
|
|
75
|
+
**The two numbers move in opposite directions, and only one of them distinguishes the candidates.** The
|
|
76
|
+
`partial` share is 87–100% at every candidate, so it cannot select a resolution here, and that is itself
|
|
77
|
+
a finding. The mixture number can select one, and the choice rests on it.
|
|
76
78
|
|
|
77
|
-
**Resolution 9 is the choice.**
|
|
78
|
-
layer's cells finds these without a conversion
|
|
79
|
-
authority's own aggregation
|
|
80
|
-
|
|
81
|
-
2.7% at resolution 9. Resolution 11 was excluded before measuring
|
|
82
|
-
|
|
79
|
+
**Resolution 9 is the choice.** `poi.db` keys its rows at resolution 9, so a reader that already holds
|
|
80
|
+
another layer's cells finds these without a conversion. Its mixture share (30.7%) is well inside the range
|
|
81
|
+
of the authority's own aggregation. Resolution 10 costs 6.9× the cells (104,508 against 15,136 for one
|
|
82
|
+
county) to move the mixture from 30.7% to 18.2%, and it leaves 5.2% of its cells carrying no class at all,
|
|
83
|
+
against 2.7% at resolution 9. Resolution 11 was excluded before measuring, because it would leave 2.1% of
|
|
84
|
+
`IA153`'s delineations smaller than a cell at roughly 49× the resolution-9 cell count.
|
|
83
85
|
|
|
84
86
|
For comparison, NRCS's own map-unit-grain `niccdcdpct` reads below half on 3.3% of national map units.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
+
Aggregation to a resolution-9 cell multiplies that roughly ninefold, which quantifies the cost of the cell
|
|
88
|
+
grain.
|
|
87
89
|
|
|
88
90
|
## Acquisition
|
|
89
91
|
|
|
90
|
-
- **Soil Data Access** (`sdmdataaccess.nrcs.usda.gov/Tabular/post.rest`)
|
|
91
|
-
the point-intersection check, through `APIClient`.
|
|
92
|
-
answer and 1.807 s for a point intersection.
|
|
93
|
-
- **Survey-area archives** (`websoilsurvey.sc.egov.usda.gov/DSD/Download/Cache/SSA`)
|
|
94
|
-
raw `fetch`, streamed to disk
|
|
92
|
+
- **Soil Data Access** (`sdmdataaccess.nrcs.usda.gov/Tabular/post.rest`) provides the survey-area
|
|
93
|
+
catalogue and the point-intersection check, through `APIClient`. It is anonymous and needs no key. It
|
|
94
|
+
measured 0.374 s for a tabular answer and 1.807 s for a point intersection.
|
|
95
|
+
- **Survey-area archives** (`websoilsurvey.sc.egov.usda.gov/DSD/Download/Cache/SSA`) are file transfers on
|
|
96
|
+
raw `fetch`, streamed to disk. The call site documents that choice.
|
|
95
97
|
|
|
96
|
-
|
|
98
|
+
The code handles three measured behaviors:
|
|
97
99
|
|
|
98
100
|
1. **Failures come back as XML, including on a timeout.** A bad column, a blocked query and a
|
|
99
|
-
server-side timeout all return an OGC `ServiceExceptionReport`, and the timeout arrives
|
|
101
|
+
server-side timeout all return an OGC `ServiceExceptionReport`, and the timeout arrives with HTTP 200.
|
|
100
102
|
Every response is read as text and checked for the report before anything parses it as JSON.
|
|
101
103
|
2. **The download host answers `HEAD` with 405 and ignores `Range`.** A request with `Range: bytes=0-0`
|
|
102
104
|
returned HTTP 200 and transferred the whole 27,598,377 bytes. Freshness comes from
|
|
103
|
-
`sacatalog.saverest`, which
|
|
104
|
-
not a 404.
|
|
105
|
+
`sacatalog.saverest`, which the archive's filename also embeds. A wrong date returns HTTP **400** (and never 404).
|
|
105
106
|
3. **The tabular export carries embedded newlines.** `sacatlog.txt` holds 594 newline bytes and exactly
|
|
106
|
-
|
|
107
|
-
dictionary itself
|
|
107
|
+
one record, because `fgdcmetadata` is a 43,251-character XML document. `mstabcol.txt`, the column
|
|
108
|
+
dictionary itself, holds 913 newlines and 865 records. The reader is quote-aware throughout.
|
|
108
109
|
|
|
109
110
|
**The archive ships its own schema and its own vocabulary.** `mstab.txt` maps a logical table to the file
|
|
110
|
-
that holds it (`component` → `comp.txt
|
|
111
|
-
position
|
|
112
|
-
definition
|
|
113
|
-
classifications, the six component kinds. The layer reads its domain
|
|
114
|
-
|
|
111
|
+
that holds it (`component` → `comp.txt`, which cannot be guessed). `mstabcol.txt` gives every column's
|
|
112
|
+
position. `msdomdet.txt` carries each `Choice` column's declared members **with NRCS's own prose
|
|
113
|
+
definition**: capability classes 1 through 8, subclasses `c`/`e`/`s`/`w`, the 28 conditional farmland
|
|
114
|
+
classifications, and the six component kinds. The layer reads its domain from the file it ingested instead
|
|
115
|
+
of transcribing it, stores it in `soil_vocabulary`, and throws on a value outside it.
|
|
115
116
|
|
|
116
117
|
## License, and where the grant comes from
|
|
117
118
|
|
|
118
|
-
data.gov's entry carries `usa.gov/publicdomain/label/1.0/`, which redirects to a page that declines
|
|
119
|
-
blanket grant and tells the reader to check with the agency. The
|
|
120
|
-
|
|
119
|
+
data.gov's entry carries `usa.gov/publicdomain/label/1.0/`, which redirects to a page that declines to
|
|
120
|
+
make a blanket grant and tells the reader to check with the agency. The strongest available agency
|
|
121
|
+
statement is **the FGDC metadata that NRCS ships inside every archive**, and its use constraints say:
|
|
121
122
|
|
|
122
123
|
> This is public information and may be interpreted by organizations, agencies, units of government, or
|
|
123
124
|
> others based on needs; however, they are responsible for the appropriate application.
|
|
124
125
|
|
|
125
|
-
The build asserts that sentence is present **per survey area**.
|
|
126
|
-
carry it
|
|
127
|
-
checked. The acknowledgement the same metadata
|
|
128
|
-
_U.S. Department of Agriculture, Natural Resources Conservation Service._
|
|
126
|
+
The build asserts that the sentence is present **per survey area**. If an area's use constraints no longer
|
|
127
|
+
carry it, the license has changed, and a build that accepted the change would ship an artifact under terms
|
|
128
|
+
nobody checked. The acknowledgement that the same metadata requests is stored in
|
|
129
|
+
`layer_manifest.attribution`: _U.S. Department of Agriculture, Natural Resources Conservation Service._
|
|
129
130
|
|
|
130
131
|
## Two dates, and they are not the same fact
|
|
131
132
|
|
|
132
|
-
`sacatalog.saverest` is the refresh. NRCS runs
|
|
133
|
-
|
|
133
|
+
`sacatalog.saverest` is the refresh date. NRCS runs one coordinated Annual Soils Refresh each October 1.
|
|
134
|
+
The catalogue grouped by year returns 2016: 1, 2025: 3,323, 2026: 56, so a region's areas share a
|
|
134
135
|
vintage. **The field survey underneath is far older.** `IA153` carries a 2025-09-09 refresh over a
|
|
135
136
|
_Soil Survey of Polk County, Iowa_ published in **1960** at 1:15,840, and the dataset's own
|
|
136
|
-
time-period-of-content ends at the refresh. A consumer
|
|
137
|
-
sixty-five years.
|
|
137
|
+
time-period-of-content ends at the refresh. A consumer that reads the refresh date as the survey date is
|
|
138
|
+
wrong by sixty-five years.
|
|
138
139
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
The layer stores both dates separately per survey area, together with the title of the source for the
|
|
141
|
+
older date, so a reader can check it. It keeps two scales separate for the same reason.
|
|
142
|
+
`legend.projectscale` (12,000 for `IA153`) is the scale at which the map units were digitized. The source
|
|
143
|
+
citation's own `srcscale` (15,840) is the scale at which the ground was surveyed.
|
|
143
144
|
|
|
144
145
|
## What a reading may claim
|
|
145
146
|
|
|
@@ -149,20 +150,20 @@ and never
|
|
|
149
150
|
|
|
150
151
|
> this land can (or cannot) be farmed.
|
|
151
152
|
|
|
152
|
-
NRCS says the second reading is wrong
|
|
153
|
+
The metadata NRCS ships says the second reading is wrong. It states that the data "do not eliminate the need for
|
|
153
154
|
onsite sampling, testing, and detailed study of specific sites for intensive uses. Thus, these data and
|
|
154
155
|
their interpretations are intended for planning purposes only." Every reading carries the product's own
|
|
155
156
|
limits for that reason.
|
|
156
157
|
|
|
157
|
-
**The farmland vocabulary is conditional, and two of its categories
|
|
158
|
-
values carry an "if"
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
artifact.
|
|
158
|
+
**The farmland vocabulary is conditional, and two of its categories mean different things in different
|
|
159
|
+
states.** 24 of its 28 declared values carry an "if", such as `Prime farmland if drained` and `Prime
|
|
160
|
+
farmland if irrigated and reclaimed of excess salts and sodium`. The layer therefore stores the string
|
|
161
|
+
whole, because a boolean `arable` column would be this layer's invention. 7 CFR 657.5 defines prime and
|
|
162
|
+
unique farmland nationally, while §657.5(c) and (d) assign statewide and local importance to state and
|
|
163
|
+
local agencies. `Farmland of statewide importance` in Iowa and in Georgia are therefore different claims.
|
|
164
|
+
`soil_map_unit.farmland_scope` carries that distinction into the artifact.
|
|
164
165
|
|
|
165
|
-
##
|
|
166
|
+
## Build
|
|
166
167
|
|
|
167
168
|
```bash
|
|
168
169
|
# The smoke rung: one real survey area, end to end.
|
|
@@ -171,51 +172,52 @@ mailwoman gazetteer build soil --area IA153 --verify
|
|
|
171
172
|
# The pilot: every published Iowa survey area.
|
|
172
173
|
mailwoman gazetteer build soil --region IA --verify
|
|
173
174
|
|
|
174
|
-
# The resolution measurement. Reports a table
|
|
175
|
+
# The resolution measurement. Reports a table rather than an artifact.
|
|
175
176
|
mailwoman gazetteer build soil --area IA153 --measure-resolutions 7,8,9,10
|
|
176
177
|
```
|
|
177
178
|
|
|
178
|
-
The build
|
|
179
|
-
|
|
180
|
-
answer. A per-part zero-cell guard
|
|
181
|
-
reproducible. Both live in `@mailwoman/spatial`'s `h3/polygon-cells.ts
|
|
182
|
-
because the
|
|
179
|
+
The build bounds its h3 usage by running one child process per range of a survey area's own FIDs. h3's
|
|
180
|
+
WASM heap cannot be reset from JavaScript, and it reports an exhausted allocator as a successful empty
|
|
181
|
+
answer. A per-part zero-cell guard rejects that answer, and the process bound makes the build
|
|
182
|
+
reproducible. Both live in `@mailwoman/spatial`'s `h3/polygon-cells.ts` and are shared with
|
|
183
|
+
`@mailwoman/flood`, because the failure modes belong to h3-js and not to either product.
|
|
183
184
|
|
|
184
185
|
## Verification
|
|
185
186
|
|
|
186
|
-
`--verify` runs both halves. The positive half
|
|
187
|
-
points drawn deterministically from the artifact,
|
|
188
|
-
derived
|
|
189
|
-
class. Disagreements
|
|
190
|
-
centimeter from a long edge can be meters from every vertex, and the flood layer's one
|
|
191
|
-
1.58 m to vertices and 0.009 m to edges.
|
|
187
|
+
`--verify` runs both halves. The positive half asks Soil Data Access again which map unit covers a sample
|
|
188
|
+
of points drawn deterministically from the artifact, and it compares **map unit against map unit**.
|
|
189
|
+
The derived-class comparison would let a wrong delineation agree by accident whenever two
|
|
190
|
+
neighbors share a class. Disagreements report the distance to the nearest **edge** instead of the nearest
|
|
191
|
+
vertex. A point a centimeter from a long edge can be meters from every vertex, and the flood layer's one
|
|
192
|
+
near-miss measured 1.58 m to vertices and 0.009 m to edges.
|
|
192
193
|
|
|
193
194
|
The negative half samples points in every neighboring state, two of them close to the Iowa border, and
|
|
194
|
-
requires `unknown`
|
|
195
|
-
|
|
195
|
+
requires `unknown` with no coverage row. A low-capability reading fails the check. The positive half alone would pass
|
|
196
|
+
on an artifact that answered class 8 for the whole planet.
|
|
196
197
|
|
|
197
198
|
## The observation
|
|
198
199
|
|
|
199
|
-
|
|
200
|
-
The reading reaches a caller as one additive `QueryIntentMarker` with
|
|
201
|
-
`mechanism: "layer:soil_capability"
|
|
202
|
-
family,
|
|
203
|
-
|
|
204
|
-
byte-identical to one with it,
|
|
200
|
+
The observation is off by default. It turns on when `$MAILWOMAN_DATA_ROOT/db/soil/soil.db` exists, and
|
|
201
|
+
the file's presence is the only switch. The reading reaches a caller as one additive `QueryIntentMarker` with
|
|
202
|
+
`code: "authority_designation"` and `mechanism: "layer:soil_capability"`. The flood layer's marker uses the
|
|
203
|
+
same code in the same `layer` family, and this layer has a rule of its own. The class always travels with
|
|
204
|
+
the share it rests on. This marker leaves ranking, abstention and every existing result field unchanged. A test checks
|
|
205
|
+
that a geocode without the layer is byte-identical to one with it, apart from the marker.
|
|
205
206
|
|
|
206
207
|
## Not this layer's job
|
|
207
208
|
|
|
208
|
-
- **
|
|
209
|
+
- **The database holds no raster.** gSSURGO and gNATSGO are the gridded derivatives at 10 m per state and
|
|
209
210
|
30 m for CONUS, in a projected CRS, distributed through a host that refuses anonymous programmatic
|
|
210
|
-
download.
|
|
211
|
-
per-cell class summary shape and store that, never the grid.
|
|
212
|
-
- **
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
`"1"` planted once in 18 years through `"18"`
|
|
217
|
-
while **`"0"` is No Data**. A reader
|
|
218
|
-
|
|
219
|
-
for either state
|
|
220
|
-
|
|
221
|
-
|
|
211
|
+
download. If a builder ever uses one, the raster rule applies: bin it at build time into the same
|
|
212
|
+
per-cell class summary shape and store that summary, never the grid.
|
|
213
|
+
- **The layer excludes the Cropland Data Layer.** That product is CC0 and measured, but it answers a
|
|
214
|
+
different question, since it reports observed cover in one season and not capability. Its accuracy
|
|
215
|
+
caveats are unread, and ingesting it would mean a raster ingest into a repository with no raster
|
|
216
|
+
tooling. Whoever builds it will face a reversed meaning of zero in the source's own encoding. The
|
|
217
|
+
derived Crop Frequency Layer's value domain runs from `"1"` (planted once in 18 years) through `"18"`
|
|
218
|
+
(planted every year), then **`"255"` means planted zero times**, while **`"0"` is No Data**. A reader
|
|
219
|
+
that takes 0 as "never planted" reads _we have no data here_ as _nothing was ever grown here_, which is
|
|
220
|
+
the opposite of the truth. This vocabulary uses no numeric sentinel for either state and should not
|
|
221
|
+
start.
|
|
222
|
+
- **The layer computes no suitability score.** It repeats what an authority states, in the authority's
|
|
223
|
+
vocabulary, with the authority's dates. The consumer converts that value into a number.
|