@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/lib/index.ts
CHANGED
|
@@ -3,43 +3,10 @@
|
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
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`.
|
|
6
|
+
* Synchronous reader for `soil.db` that returns the soil survey's capability-class distribution at a coordinate.
|
|
40
7
|
*/
|
|
41
8
|
|
|
42
|
-
import { parseJSONStrict } from "@mailwoman/core/json"
|
|
9
|
+
import { parseJSONStrict, stringifyJSON } from "@mailwoman/core/json"
|
|
43
10
|
import {
|
|
44
11
|
assertCoverageNotEmpty,
|
|
45
12
|
singleManifestRow,
|
|
@@ -51,6 +18,7 @@ import { shortCellToInt, type H3Cell } from "@mailwoman/spatial"
|
|
|
51
18
|
import { readCoverageAt } from "@mailwoman/spatial/h3/coverage"
|
|
52
19
|
import { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
53
20
|
import { latLngToCell } from "h3-js"
|
|
21
|
+
import type { PathBuilderLike } from "path-ts"
|
|
54
22
|
|
|
55
23
|
import type { SoilDatabase } from "#schema"
|
|
56
24
|
import { SOIL_LAYER_NAME_PREFIX, SSURGO_PRODUCT_LIMITS } from "#vocabulary"
|
|
@@ -58,80 +26,92 @@ import { SOIL_LAYER_NAME_PREFIX, SSURGO_PRODUCT_LIMITS } from "#vocabulary"
|
|
|
58
26
|
export { FarmlandScope, farmlandScope, SSURGO_PRODUCT_LIMITS } from "#vocabulary"
|
|
59
27
|
|
|
60
28
|
/**
|
|
61
|
-
*
|
|
29
|
+
* The kinds of answer the layer gives for a coordinate, where `DesignatedNoRating`
|
|
30
|
+
* and `Unknown` must stay apart.
|
|
62
31
|
*/
|
|
63
32
|
export const SoilReadingKind = {
|
|
64
33
|
/**
|
|
65
|
-
* The survey mapped this location and assigns at least one capability class
|
|
34
|
+
* The survey mapped this location and assigns at least one capability class.
|
|
66
35
|
*/
|
|
67
36
|
Designated: "designated",
|
|
68
37
|
/**
|
|
69
|
-
* The survey mapped this location and
|
|
38
|
+
* The survey mapped this location and assigned no rating.
|
|
39
|
+
* Every share is reported as an absence share.
|
|
70
40
|
*/
|
|
71
41
|
DesignatedNoRating: "designated_no_rating",
|
|
72
42
|
/**
|
|
73
|
-
*
|
|
43
|
+
* The layer has no coverage for this location.
|
|
44
|
+
* This does not imply low capability.
|
|
74
45
|
*/
|
|
75
46
|
Unknown: "unknown",
|
|
76
47
|
} as const
|
|
77
48
|
|
|
49
|
+
/**
|
|
50
|
+
* One of the {@link SoilReadingKind} values.
|
|
51
|
+
*/
|
|
78
52
|
export type SoilReadingKind = (typeof SoilReadingKind)[keyof typeof SoilReadingKind]
|
|
79
53
|
|
|
80
54
|
/**
|
|
81
|
-
* The
|
|
55
|
+
* The capability-class distribution of one cell.
|
|
82
56
|
*/
|
|
83
57
|
export interface SoilCapabilityDistribution {
|
|
84
58
|
/**
|
|
85
|
-
* The authority's class codes mapped to their area-weighted
|
|
59
|
+
* The authority's class codes mapped to their area-weighted shares, largest first.
|
|
86
60
|
*/
|
|
87
61
|
classShares: Record<string, number>
|
|
88
62
|
/**
|
|
89
|
-
*
|
|
63
|
+
* The share of mapped soil components that have a NULL rating.
|
|
90
64
|
*/
|
|
91
65
|
unratedShare: number
|
|
92
66
|
/**
|
|
93
|
-
*
|
|
67
|
+
* The share of miscellaneous areas that the rating does not apply to.
|
|
94
68
|
*/
|
|
95
69
|
notRateableShare: number
|
|
96
70
|
/**
|
|
97
|
-
*
|
|
71
|
+
* The share of polygons that have no soil mapping.
|
|
98
72
|
*/
|
|
99
73
|
noDataShare: number
|
|
100
74
|
/**
|
|
101
|
-
* The
|
|
75
|
+
* The share of the truncated minority classes.
|
|
76
|
+
* The class shares and the four other shares sum to 1.
|
|
102
77
|
*/
|
|
103
78
|
otherShare: number
|
|
104
79
|
/**
|
|
105
|
-
*
|
|
80
|
+
* The fraction of the cell covered by any delineation.
|
|
81
|
+
* It falls below 1 at a survey-area edge.
|
|
106
82
|
*/
|
|
107
83
|
mappedShare: number
|
|
108
84
|
/**
|
|
109
|
-
* The
|
|
85
|
+
* The class with the largest share, absent when the cell has no class.
|
|
86
|
+
*
|
|
87
|
+
* A caller that reads `topClass` should also report `topClassShare`,
|
|
88
|
+
* because the top class can hold a small plurality.
|
|
110
89
|
*/
|
|
111
90
|
topClass?: string
|
|
112
91
|
topClassShare?: number
|
|
113
92
|
/**
|
|
114
|
-
*
|
|
93
|
+
* The weighting that produced these shares.
|
|
115
94
|
*/
|
|
116
95
|
weighting: string
|
|
117
96
|
/**
|
|
118
|
-
*
|
|
97
|
+
* The number of delineations that reached the cell.
|
|
119
98
|
*/
|
|
120
99
|
delineations: number
|
|
121
100
|
}
|
|
122
101
|
|
|
123
102
|
/**
|
|
124
|
-
* One survey area
|
|
103
|
+
* One survey area in the layer.
|
|
125
104
|
*/
|
|
126
105
|
export interface SoilSurveyAreaRecord {
|
|
127
106
|
areaSymbol: string
|
|
128
107
|
areaName: string
|
|
129
108
|
/**
|
|
130
|
-
* The
|
|
109
|
+
* The date on which this version of the survey data was established.
|
|
131
110
|
*/
|
|
132
111
|
saverest: string
|
|
133
112
|
/**
|
|
134
|
-
* The
|
|
113
|
+
* The field survey date.
|
|
114
|
+
* It can be much older than `saverest`.
|
|
135
115
|
*/
|
|
136
116
|
surveySourceDate: string | null
|
|
137
117
|
surveySourceTitle: string | null
|
|
@@ -140,69 +120,71 @@ export interface SoilSurveyAreaRecord {
|
|
|
140
120
|
}
|
|
141
121
|
|
|
142
122
|
/**
|
|
143
|
-
* One reading,
|
|
123
|
+
* One reading at a coordinate, with the provenance a caller needs to check it.
|
|
144
124
|
*/
|
|
145
125
|
export interface SoilCapabilityReading {
|
|
146
126
|
kind: SoilReadingKind
|
|
147
127
|
/**
|
|
148
|
-
* The cell's distribution
|
|
128
|
+
* The cell's distribution, present on both designated kinds and absent on `unknown`.
|
|
149
129
|
*/
|
|
150
130
|
distribution?: SoilCapabilityDistribution
|
|
151
131
|
/**
|
|
152
|
-
* The authority's
|
|
132
|
+
* The authority's definition of the top class, from its vocabulary.
|
|
153
133
|
*/
|
|
154
134
|
topClassDefinition?: string
|
|
155
135
|
/**
|
|
156
|
-
* The survey area
|
|
136
|
+
* The survey area that covers the location.
|
|
157
137
|
*/
|
|
158
138
|
surveyArea?: SoilSurveyAreaRecord
|
|
159
139
|
/**
|
|
160
|
-
* The coverage row that
|
|
140
|
+
* The coverage row that supports the reading, when one exists.
|
|
161
141
|
*/
|
|
162
142
|
coverage?: CoverageCell & { h3CellIndex: string; resolution: number }
|
|
163
143
|
/**
|
|
164
|
-
* The index cell
|
|
144
|
+
* The H3 index cell that the lookup probed.
|
|
165
145
|
*/
|
|
166
146
|
indexCellIndex: string
|
|
167
147
|
/**
|
|
168
|
-
*
|
|
148
|
+
* The authority's own statements of what the product does not cover.
|
|
149
|
+
*
|
|
150
|
+
* Every reading includes them because the survey supports planning only
|
|
151
|
+
* and does not replace onsite study.
|
|
169
152
|
*/
|
|
170
153
|
limits: ReadonlyArray<string>
|
|
171
154
|
}
|
|
172
155
|
|
|
173
156
|
/**
|
|
174
|
-
* The layer's identity, read once
|
|
157
|
+
* The layer's identity, read once when the database opens.
|
|
175
158
|
*/
|
|
176
159
|
export interface SoilLayerIdentity {
|
|
177
160
|
manifest: LayerManifest
|
|
178
161
|
indexResolution: number
|
|
179
162
|
coverageResolution: number
|
|
180
163
|
/**
|
|
181
|
-
* The survey areas the layer covers,
|
|
164
|
+
* The survey areas the layer covers, ordered by area symbol.
|
|
182
165
|
*/
|
|
183
166
|
surveyAreas: SoilSurveyAreaRecord[]
|
|
184
167
|
/**
|
|
185
|
-
* The class codes the layer's
|
|
168
|
+
* The class codes that the layer's vocabulary declares.
|
|
186
169
|
*/
|
|
187
170
|
classCodes: string[]
|
|
188
171
|
/**
|
|
189
|
-
* The weighting every stored share
|
|
172
|
+
* The code of the weighting used for every stored share, with its description.
|
|
190
173
|
*/
|
|
191
174
|
weighting: { code: string; description: string }
|
|
192
175
|
databasePath: string
|
|
193
176
|
}
|
|
194
177
|
|
|
178
|
+
/**
|
|
179
|
+
* Options for {@link SoilCapabilityLookup}.
|
|
180
|
+
*/
|
|
195
181
|
export interface SoilCapabilityLookupOptions {
|
|
196
|
-
databasePath:
|
|
182
|
+
databasePath: PathBuilderLike
|
|
197
183
|
}
|
|
198
184
|
|
|
199
185
|
/**
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
* Everything that would make the reader answer a well-formed wrong thing is refused at CONSTRUCTION rather than at
|
|
203
|
-
* query time: a manifest naming a different product, a coverage table with no rows, a vocabulary with no classes. Each
|
|
204
|
-
* of those would otherwise present as a reader that simply always answers `unknown`, which on a receipt is
|
|
205
|
-
* indistinguishable from a region the authority genuinely has not surveyed.
|
|
186
|
+
* Reads a sealed `soil.db`, throwing at construction on a manifest for a different product,
|
|
187
|
+
* an empty coverage table, an empty class vocabulary, or a missing share weighting.
|
|
206
188
|
*/
|
|
207
189
|
export class SoilCapabilityLookup implements Disposable {
|
|
208
190
|
readonly identity: SoilLayerIdentity
|
|
@@ -240,15 +222,14 @@ export class SoilCapabilityLookup implements Disposable {
|
|
|
240
222
|
}
|
|
241
223
|
|
|
242
224
|
/**
|
|
243
|
-
*
|
|
225
|
+
* Returns what the soil survey assigns at this coordinate.
|
|
244
226
|
*/
|
|
245
227
|
public lookup(latitude: number, longitude: number): SoilCapabilityReading {
|
|
246
228
|
const indexCell = latLngToCell(latitude, longitude, this.identity.indexResolution) as H3Cell
|
|
247
229
|
const coverage = this.#readCoverage(indexCell)
|
|
248
230
|
|
|
249
|
-
//
|
|
250
|
-
//
|
|
251
|
-
// summary, so a summary row without a coverage row would state a determination outside the authority's footprint.
|
|
231
|
+
// Every answer is a per-cell summary, so a cell outside the coverage table
|
|
232
|
+
// is unknown even if a summary row exists.
|
|
252
233
|
if (!coverage) {
|
|
253
234
|
return {
|
|
254
235
|
kind: SoilReadingKind.Unknown,
|
|
@@ -273,9 +254,8 @@ export class SoilCapabilityLookup implements Disposable {
|
|
|
273
254
|
| undefined
|
|
274
255
|
|
|
275
256
|
if (!row) {
|
|
276
|
-
//
|
|
277
|
-
//
|
|
278
|
-
// this location may be outside the delineations it covers.
|
|
257
|
+
// At a survey-area edge the coarser coverage cell is covered and this index cell
|
|
258
|
+
// has no delineation, so the location may be outside the survey.
|
|
279
259
|
return {
|
|
280
260
|
kind: SoilReadingKind.Unknown,
|
|
281
261
|
coverage,
|
|
@@ -315,24 +295,16 @@ export class SoilCapabilityLookup implements Disposable {
|
|
|
315
295
|
this.#database.destroy()
|
|
316
296
|
}
|
|
317
297
|
|
|
318
|
-
/**
|
|
319
|
-
* The coverage row for the index cell's parent at the coverage resolution.
|
|
320
|
-
*/
|
|
321
298
|
#readCoverage(indexCell: H3Cell): (CoverageCell & { h3CellIndex: string; resolution: number }) | undefined {
|
|
322
299
|
return readCoverageAt(this.#selectCoverage, indexCell, this.identity.coverageResolution)
|
|
323
300
|
}
|
|
324
301
|
|
|
325
302
|
/**
|
|
326
|
-
*
|
|
327
|
-
*
|
|
328
|
-
* A rectangle rather than the outline, and that is honest about what it is: the answer names WHICH published survey
|
|
329
|
-
* the reading came from, and two neighbouring counties' rectangles overlap at their corners. The reading itself does
|
|
330
|
-
* not depend on it — the cell row is the answer — so a corner ambiguity costs a label rather than a determination.
|
|
303
|
+
* Returns the first survey area whose bounding rectangle contains the coordinate.
|
|
304
|
+
* overlapping rectangles may give the wrong area near a corner.
|
|
331
305
|
*
|
|
332
|
-
*
|
|
333
|
-
*
|
|
334
|
-
* holds this wants a bounding-box index; it is left as a scan because a structure sized for a set this build does not
|
|
335
|
-
* hold would be untested at the size it was built for.
|
|
306
|
+
* This affects only the `surveyArea` label.
|
|
307
|
+
* A spatial index would be needed to avoid the linear scan for thousands of areas.
|
|
336
308
|
*/
|
|
337
309
|
#surveyAreaAt(latitude: number, longitude: number): SoilSurveyAreaRecord | undefined {
|
|
338
310
|
for (const [index, bounds] of this.#bounds.entries()) {
|
|
@@ -350,12 +322,9 @@ export class SoilCapabilityLookup implements Disposable {
|
|
|
350
322
|
}
|
|
351
323
|
}
|
|
352
324
|
|
|
353
|
-
/**
|
|
354
|
-
* Read and check the layer's identity.
|
|
355
|
-
*/
|
|
356
325
|
function readIdentity(
|
|
357
326
|
database: DatabaseClient<SoilDatabase>,
|
|
358
|
-
databasePath:
|
|
327
|
+
databasePath: PathBuilderLike
|
|
359
328
|
): {
|
|
360
329
|
identity: SoilLayerIdentity
|
|
361
330
|
definitions: Map<string, string>
|
|
@@ -365,15 +334,14 @@ function readIdentity(
|
|
|
365
334
|
Record<string, string | number | null>
|
|
366
335
|
>
|
|
367
336
|
|
|
368
|
-
// The name
|
|
369
|
-
//
|
|
370
|
-
// vocabulary per artifact, over whichever survey areas were built.
|
|
337
|
+
// The layer name ends with the region the build covers, so the reader checks only the prefix
|
|
338
|
+
// rather than `parseManifestRows`, which compares whole names.
|
|
371
339
|
const row = singleManifestRow(manifestRows, `soil reader: ${databasePath}`)
|
|
372
340
|
const name = String(row.name)
|
|
373
341
|
|
|
374
342
|
if (!name.startsWith(SOIL_LAYER_NAME_PREFIX)) {
|
|
375
343
|
throw new Error(
|
|
376
|
-
`soil reader: ${databasePath} is layer ${
|
|
344
|
+
`soil reader: ${databasePath} is layer ${stringifyJSON(name)}, which is not a ${stringifyJSON(SOIL_LAYER_NAME_PREFIX)} layer — one authority, one product, one rating vocabulary per artifact`
|
|
377
345
|
)
|
|
378
346
|
}
|
|
379
347
|
|
|
@@ -461,7 +429,7 @@ function readIdentity(
|
|
|
461
429
|
})),
|
|
462
430
|
classCodes: [...definitions.keys()],
|
|
463
431
|
weighting: { code: weighting.code, description: weighting.definition },
|
|
464
|
-
databasePath,
|
|
432
|
+
databasePath: databasePath.toString(),
|
|
465
433
|
},
|
|
466
434
|
definitions,
|
|
467
435
|
bounds: areaRows.map((area) => ({
|
package/lib/paths.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* Where the soil layer database and its download caches live under the data root's `db/` group.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { databaseRootPath, dataRootPath } from "@mailwoman/core/data-root"
|
|
10
|
+
import type { PathBuilder, PathBuilderLike } from "path-ts"
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* `<dataRoot>/db/soil`.
|
|
14
|
+
*/
|
|
15
|
+
export function soilDatabaseRoot(dataRoot: PathBuilderLike): PathBuilder {
|
|
16
|
+
return databaseRootPath(dataRoot)("soil")
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* `$MAILWOMAN_DATA_ROOT/db/soil`, read when a path is requested.
|
|
21
|
+
*
|
|
22
|
+
* @see {@link soilDatabaseRoot} for another data root.
|
|
23
|
+
*/
|
|
24
|
+
export const soilDatabasePath: PathBuilder = soilDatabaseRoot(dataRootPath())
|