@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.
Files changed (115) hide show
  1. package/README.md +118 -116
  2. package/lib/index.ts +66 -98
  3. package/lib/paths.ts +24 -0
  4. package/lib/schema.ts +188 -120
  5. package/lib/vocabulary.ts +40 -107
  6. package/out/index.d.ts +52 -70
  7. package/out/index.d.ts.map +1 -1
  8. package/out/index.js +24 -72
  9. package/out/index.js.map +1 -1
  10. package/out/paths.d.ts +19 -0
  11. package/out/paths.d.ts.map +1 -0
  12. package/out/paths.js +21 -0
  13. package/out/paths.js.map +1 -0
  14. package/out/schema.d.ts +187 -119
  15. package/out/schema.d.ts.map +1 -1
  16. package/out/schema.js +31 -31
  17. package/out/schema.js.map +1 -1
  18. package/out/sdk/acquire.d.ts +15 -24
  19. package/out/sdk/acquire.d.ts.map +1 -1
  20. package/out/sdk/acquire.js +5 -20
  21. package/out/sdk/acquire.js.map +1 -1
  22. package/out/sdk/build-soil.d.ts +67 -70
  23. package/out/sdk/build-soil.d.ts.map +1 -1
  24. package/out/sdk/build-soil.js +65 -95
  25. package/out/sdk/build-soil.js.map +1 -1
  26. package/out/sdk/cell-tiers.d.ts +7 -19
  27. package/out/sdk/cell-tiers.d.ts.map +1 -1
  28. package/out/sdk/cell-tiers.js +19 -38
  29. package/out/sdk/cell-tiers.js.map +1 -1
  30. package/out/sdk/cells.d.ts +15 -41
  31. package/out/sdk/cells.d.ts.map +1 -1
  32. package/out/sdk/cells.js +11 -38
  33. package/out/sdk/cells.js.map +1 -1
  34. package/out/sdk/client.d.ts +23 -48
  35. package/out/sdk/client.d.ts.map +1 -1
  36. package/out/sdk/client.js +19 -63
  37. package/out/sdk/client.js.map +1 -1
  38. package/out/sdk/download.d.ts +24 -47
  39. package/out/sdk/download.d.ts.map +1 -1
  40. package/out/sdk/download.js +14 -54
  41. package/out/sdk/download.js.map +1 -1
  42. package/out/sdk/ingest/chunk.d.ts +77 -0
  43. package/out/sdk/ingest/chunk.d.ts.map +1 -0
  44. package/out/sdk/{ingest-chunk.js → ingest/chunk.js} +19 -17
  45. package/out/sdk/ingest/chunk.js.map +1 -0
  46. package/out/sdk/ingest/worker.d.ts +9 -0
  47. package/out/sdk/ingest/worker.d.ts.map +1 -0
  48. package/out/{scripts/ingest-chunk.js → sdk/ingest/worker.js} +11 -10
  49. package/out/sdk/ingest/worker.js.map +1 -0
  50. package/out/sdk/ingest.d.ts +40 -52
  51. package/out/sdk/ingest.d.ts.map +1 -1
  52. package/out/sdk/ingest.js +18 -61
  53. package/out/sdk/ingest.js.map +1 -1
  54. package/out/sdk/measure-resolutions.d.ts +5 -16
  55. package/out/sdk/measure-resolutions.d.ts.map +1 -1
  56. package/out/sdk/measure-resolutions.js +3 -15
  57. package/out/sdk/measure-resolutions.js.map +1 -1
  58. package/out/sdk/reduce.d.ts +33 -59
  59. package/out/sdk/reduce.d.ts.map +1 -1
  60. package/out/sdk/reduce.js +47 -78
  61. package/out/sdk/reduce.js.map +1 -1
  62. package/out/sdk/survey-area.d.ts +16 -48
  63. package/out/sdk/survey-area.d.ts.map +1 -1
  64. package/out/sdk/survey-area.js +33 -77
  65. package/out/sdk/survey-area.js.map +1 -1
  66. package/out/sdk/tabular.d.ts +32 -31
  67. package/out/sdk/tabular.d.ts.map +1 -1
  68. package/out/sdk/tabular.js +58 -56
  69. package/out/sdk/tabular.js.map +1 -1
  70. package/out/sdk/test-kit.d.ts +57 -0
  71. package/out/sdk/test-kit.d.ts.map +1 -0
  72. package/out/{test-kit.js → sdk/test-kit.js} +18 -39
  73. package/out/sdk/test-kit.js.map +1 -0
  74. package/out/sdk/verify.d.ts +15 -51
  75. package/out/sdk/verify.d.ts.map +1 -1
  76. package/out/sdk/verify.js +19 -77
  77. package/out/sdk/verify.js.map +1 -1
  78. package/out/vocabulary.d.ts +37 -103
  79. package/out/vocabulary.d.ts.map +1 -1
  80. package/out/vocabulary.js +34 -107
  81. package/out/vocabulary.js.map +1 -1
  82. package/package.json +36 -190
  83. package/{lib/sdk → sdk}/acquire.ts +17 -27
  84. package/{lib/sdk → sdk}/build-soil.ts +114 -128
  85. package/{lib/sdk → sdk}/cell-tiers.ts +20 -39
  86. package/{lib/sdk → sdk}/cells.ts +17 -43
  87. package/sdk/client.ts +147 -0
  88. package/sdk/download.ts +131 -0
  89. package/{lib/sdk/ingest-chunk.ts → sdk/ingest/chunk.ts} +34 -26
  90. package/{lib/scripts/ingest-chunk.ts → sdk/ingest/worker.ts} +10 -9
  91. package/sdk/ingest.ts +253 -0
  92. package/{lib/sdk → sdk}/measure-resolutions.ts +5 -16
  93. package/sdk/reduce.ts +344 -0
  94. package/{lib/sdk → sdk}/survey-area.ts +39 -83
  95. package/{lib/sdk → sdk}/tabular.ts +62 -59
  96. package/{lib → sdk}/test-kit.ts +18 -40
  97. package/{lib/sdk → sdk}/verify.ts +29 -85
  98. package/lib/sdk/client.ts +0 -184
  99. package/lib/sdk/download.ts +0 -161
  100. package/lib/sdk/index.ts +0 -20
  101. package/lib/sdk/ingest.ts +0 -278
  102. package/lib/sdk/reduce.ts +0 -375
  103. package/out/scripts/ingest-chunk.d.ts +0 -11
  104. package/out/scripts/ingest-chunk.d.ts.map +0 -1
  105. package/out/scripts/ingest-chunk.js.map +0 -1
  106. package/out/sdk/index.d.ts +0 -20
  107. package/out/sdk/index.d.ts.map +0 -1
  108. package/out/sdk/index.js +0 -20
  109. package/out/sdk/index.js.map +0 -1
  110. package/out/sdk/ingest-chunk.d.ts +0 -73
  111. package/out/sdk/ingest-chunk.d.ts.map +0 -1
  112. package/out/sdk/ingest-chunk.js.map +0 -1
  113. package/out/test-kit.d.ts +0 -79
  114. package/out/test-kit.d.ts.map +0 -1
  115. package/out/test-kit.js.map +0 -1
package/out/sdk/reduce.js CHANGED
@@ -3,69 +3,42 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * The reduction: the containment index, read once, into one per-cell distribution both consumers share.
6
+ * Reduces the delineations that reach a cell into that cell's capability-class distribution.
7
7
  *
8
- * A DISTRIBUTION, NEVER A WINNER, AND THAT IS FORCED BY MEASUREMENT. 84.0% of the 339,191 national map
9
- * units hold two or more components; in 16.8% the largest component covers under half the map unit; and
10
- * 85.4% of `IA153`'s delineations are smaller than one resolution-9 cell. No affordable cell size removes
11
- * the mixture — it is a property of the survey, whose own `mukind` says so: 128,499 map units (38.0%) are
12
- * complexes, associations or undifferentiated groups, which is NRCS stating that the soils are
13
- * intermingled and cannot be separated at the mapping scale. A winner class would satisfy the result-level
14
- * consumer and starve the signal consumer, which needs a magnitude to vary over.
15
- *
16
- * THE WEIGHT IS A UNIFORM-AREA LATTICE OVER THE CELL, AND THE GRAIN IS CHOSEN AGAINST THE AUTHORITY'S OWN.
17
- * A cell's children at {@link WEIGHT_LATTICE_DEPTH} levels finer have equal area by construction, so
18
- * counting which delineation covers each child centre estimates covered area without a polygon clip. At
19
- * depth 2 that is 49 children — 2.04% per child, which is the finest share NRCS's own
20
- * `muaggatt.niccdcdpct` ever reports (observed minimum: 2%). Resolving finer than the authority publishes
21
- * would be precision this layer cannot source.
22
- *
23
- * A WHOLE CELL SKIPS THE LATTICE ENTIRELY, and that is exact rather than an optimization: a cell lying
24
- * wholly inside one delineation is covered by that delineation and by nothing else, so its distribution is
25
- * that map unit's component split and its `mapped_share` is 1.
26
- *
27
- * `mapped_share` EXISTS BECAUSE A SURVEY-AREA EDGE CELL IS PARTLY OUTSIDE EVERY DELINEATION. Without it,
28
- * the unmapped remainder would silently deflate every class share — an absence represented as a small
29
- * number, which is the one thing this schema exists to prevent. The five shares are normalized over the
30
- * mapped part, so they sum to 1 exactly, and `mapped_share` says how much of the cell that was.
31
- *
32
- * CLASS 8 IS A CLASS SHARE, NOT AN ABSENCE. It is a determination — the survey looked and rated the land
33
- * as precluding commercial plant production, and 67,547 national components carry it. Folding it in with
34
- * `NOTCOM`, a water body and an unrated series would produce a well-formed wrong answer, and separating
35
- * the four absences from the one positive negative is the whole reason this table has five columns rather
36
- * than one.
8
+ * The result is a distribution because most map units mix several soil components.
9
+ * The survey cannot separate those components at its mapping scale. Shares are normalized over the mapped part of the cell.
10
+ * `mapped_share` records how large that part is. Capability class 8 is a rated class, so it is stored as a
11
+ * class share. The unrated share, the no-data share and the not-rateable share each have their own column.
37
12
  */
38
- import { parseJSONStrict } from "@mailwoman/core/json";
13
+ import { parseJSONStrict, stringifyJSON } from "@mailwoman/core/json";
39
14
  import { pointInEncodedRings } from "@mailwoman/spatial";
40
15
  import { cellToChildren, cellToLatLng } from "h3-js";
41
16
  import { SOIL_SHARE_WEIGHTING } from "#vocabulary";
42
17
  /**
43
- * How many resolution levels finer than the index the weighting lattice runs.
18
+ * The number of H3 levels below the index resolution at which the weighting lattice samples.
44
19
  *
45
- * Two, giving 49 children per cell and a 2.04% share granularity. That is deliberately matched to the finest share the
46
- * authority itself publishes — `muaggatt.niccdcdpct`'s observed minimum is 2% — because a lattice finer than the
47
- * source's own reporting grain buys precision this layer cannot source, at 7× the cost per level.
20
+ * Children at a finer resolution have equal area, so counting which delineation
21
+ * covers each child center estimates the covered area.
22
+ * A depth of 2 gives 49 children, or about 2% per child.
23
+ *
24
+ * NRCS publishes component shares no finer than 2%, and each extra level costs seven times as much.
48
25
  */
49
26
  export const WEIGHT_LATTICE_DEPTH = 2;
50
27
  /**
51
- * Class shares below this are folded into `other_share` rather than stored.
28
+ * The class share below which a class is added to `other_share` instead of stored.
52
29
  *
53
- * One percent, which sits BELOW the lattice's own 2.04% granularity, so nothing a single child cell produces is
54
- * truncated — what lands here is the long tail that component percentages create inside a child (a 1%-weight component
55
- * inside one child cell contributes 0.02%). Truncating a long tail is legitimate; doing it silently is not, which is
56
- * why the remainder is stored explicitly and the shares still sum to 1.
30
+ * The floor is below the lattice's 2% step, so it only removes the small shares
31
+ * that minor components contribute.
32
+ * The reducer adds them to `other_share` so all shares sum to 1.
57
33
  */
58
34
  export const CLASS_SHARE_FLOOR = 0.01;
59
35
  /**
60
- * Turn one map unit and its components into the per-unit-area profile the reduction folds in.
36
+ * Builds the profile of one map unit from its components.
61
37
  *
62
- * A `no_mapping` map unit contributes wholly to `nodata` and NEVER to a class: it is a polygon the authority drew with
63
- * no soil mapping behind it, and reading it as a low class would be the reassuring wrong number §3.2 of the survey is
64
- * about.
38
+ * A `no_mapping` map unit contributes only to `noData`, because it has no soil mapping.
65
39
  *
66
- * The split across components is by `comppct_r`, the component's representative percentage of its map unit, normalized
67
- * by the total actually present rather than assumed to be 100 — measured on `IA153` all 152 map units sum to exactly
68
- * 100, and a national build must not depend on that holding everywhere.
40
+ * Components are weighted by `comppct_r`, the component's representative percentage of its map unit.
41
+ * The weights are divided by their actual total, because the percentages may not sum to 100.
69
42
  */
70
43
  export function mapUnitProfile(mapUnit, components) {
71
44
  if (mapUnit.no_mapping) {
@@ -75,10 +48,8 @@ export function mapUnitProfile(mapUnit, components) {
75
48
  for (const component of components) {
76
49
  total += component.comppct_r;
77
50
  }
78
- // A map unit whose components carry no weight at all publishes no readable proportion, so nothing can be apportioned
79
- // from it. It is marked `no_mapping` upstream for exactly this reason; reaching here with a zero total means the
80
- // upstream check and this one disagree, and answering with an empty distribution would silently drop the delineation's
81
- // area out of every share.
51
+ // Components with zero total weight give no proportions, so the map unit counts as no data.
52
+ // An empty distribution would drop the delineation's area from every share.
82
53
  if (total <= 0) {
83
54
  return { classShares: new Map(), unrated: 0, notRateable: 0, noData: 1 };
84
55
  }
@@ -93,10 +64,8 @@ export function mapUnitProfile(mapUnit, components) {
93
64
  classShares.set(component.nirrcapcl, (classShares.get(component.nirrcapcl) ?? 0) + weight);
94
65
  continue;
95
66
  }
96
- // A NULL rating means the survey did not rate this component, and WHY it did not is what separates the two buckets.
97
- // A miscellaneous area is a non-soil area — rock outcrop, water — that the capability rating does not apply to; a
98
- // named soil with no rating is one the survey chose not to rate. Read as one number they would both say "not
99
- // arable", which neither of them says.
67
+ // A miscellaneous area, such as rock outcrop or water, cannot take a capability rating.
68
+ // Any other component with a NULL rating is a soil that the survey did not rate.
100
69
  if (component.compkind === "Miscellaneous area") {
101
70
  notRateable += weight;
102
71
  }
@@ -107,11 +76,11 @@ export function mapUnitProfile(mapUnit, components) {
107
76
  return { classShares, unrated, notRateable, noData: 0 };
108
77
  }
109
78
  /**
110
- * Reduce one cell.
79
+ * Reduces one cell.
111
80
  *
112
- * @throws {Error} When a candidate names a map unit the profile map does not hold. A missing profile means the
113
- * attribute join is short, and answering with the remaining candidates would report a well-formed distribution over
114
- * part of the cell.
81
+ * @throws {Error} When a candidate's map unit has no profile.
82
+ * A missing profile means the attribute join is incomplete.
83
+ * The remaining candidates would describe only part of the cell.
115
84
  */
116
85
  export function reduceCell(cell, resolution, candidates, profiles, h3Cell) {
117
86
  const weights = new Map();
@@ -119,8 +88,7 @@ export function reduceCell(cell, resolution, candidates, profiles, h3Cell) {
119
88
  let mappedShare = 1;
120
89
  const whole = candidates.length === 1 ? candidates.find((candidate) => candidate.containment === "whole") : undefined;
121
90
  if (whole) {
122
- // Exactly one delineation, and it covers the cell entirely. Nothing else can reach it, so the lattice would return
123
- // the same answer at 49 times the cost.
91
+ // A single delineation covers the whole cell, so the lattice would give the same answer.
124
92
  weights.set(whole.mukey, 1);
125
93
  }
126
94
  else {
@@ -136,10 +104,10 @@ export function reduceCell(cell, resolution, candidates, profiles, h3Cell) {
136
104
  weights.set(owner.mukey, (weights.get(owner.mukey) ?? 0) + 1);
137
105
  }
138
106
  if (!covered) {
139
- // Every child centre fell outside every delineation reaching the cell. The cell IS touched — the index says so —
140
- // but no lattice point landed inside, which happens when a sliver clips a corner. Reporting shares over nothing
141
- // would divide by zero; reporting a mapped share of zero is the truthful answer, and the row is dropped by the
142
- // caller rather than stored as an all-zero distribution.
107
+ // No child center fell inside a delineation.
108
+ // A sliver can clip only a corner.
109
+ // This row has a mapped share of zero.
110
+ // The caller drops it.
143
111
  return {
144
112
  row: emptyRow(h3Cell, candidates.length),
145
113
  topClassUnderHalf: false,
@@ -154,10 +122,10 @@ export function reduceCell(cell, resolution, candidates, profiles, h3Cell) {
154
122
  return assembleRow(h3Cell, weights, profiles, mappedShare, candidates.length, sampled);
155
123
  }
156
124
  /**
157
- * The delineation covering a point, or `undefined` where none does.
125
+ * Returns the delineation that covers a point, or `undefined` when none does.
158
126
  *
159
- * The bounding box is the prefilter the geometry table stores precisely so the ray cast runs on the few delineations
160
- * that could contain the point rather than on every delineation reaching the cell.
127
+ * A bounding-box test runs first so that the ray cast runs only on delineations
128
+ * that could contain the point.
161
129
  */
162
130
  function candidateAt(candidates, latitude, longitude) {
163
131
  for (const candidate of candidates) {
@@ -173,7 +141,7 @@ function candidateAt(candidates, latitude, longitude) {
173
141
  return undefined;
174
142
  }
175
143
  /**
176
- * Fold the per-map-unit weights through their profiles into the stored row.
144
+ * Combines the per-map-unit weights with their profiles into the stored row.
177
145
  */
178
146
  function assembleRow(h3Cell, weights, profiles, mappedShare, delineations, sampled) {
179
147
  const classShares = new Map();
@@ -192,8 +160,7 @@ function assembleRow(h3Cell, weights, profiles, mappedShare, delineations, sampl
192
160
  notRateable += profile.notRateable * weight;
193
161
  noData += profile.noData * weight;
194
162
  }
195
- // The floor truncates the long tail component percentages create inside a lattice child. The remainder is stored
196
- // rather than dropped, so the five shares sum to 1 and a reader can see how much was folded away.
163
+ // Classes below the floor go into `other_share` so that the shares still sum to 1.
197
164
  let other = 0;
198
165
  const kept = [];
199
166
  for (const [code, share] of classShares) {
@@ -209,7 +176,7 @@ function assembleRow(h3Cell, weights, profiles, mappedShare, delineations, sampl
209
176
  return {
210
177
  row: {
211
178
  h3_cell: h3Cell,
212
- class_shares: JSON.stringify(Object.fromEntries(kept.map(([code, share]) => [code, round(share)]))),
179
+ class_shares: stringifyJSON(Object.fromEntries(kept.map(([code, share]) => [code, round(share)]))),
213
180
  unrated_share: round(unrated),
214
181
  notrateable_share: round(notRateable),
215
182
  nodata_share: round(noData),
@@ -225,8 +192,10 @@ function assembleRow(h3Cell, weights, profiles, mappedShare, delineations, sampl
225
192
  };
226
193
  }
227
194
  /**
228
- * A cell no lattice point landed inside. `mapped_share` zero says exactly that, and the caller drops it rather than
229
- * storing an all-zero distribution that would read as a surveyed cell holding nothing.
195
+ * Returns the row for a cell that no lattice point landed in.
196
+ *
197
+ * The caller drops a row with a zero `mapped_share`, because storing it would
198
+ * look like a surveyed cell with no soil.
230
199
  */
231
200
  function emptyRow(h3Cell, delineations) {
232
201
  return {
@@ -244,16 +213,16 @@ function emptyRow(h3Cell, delineations) {
244
213
  };
245
214
  }
246
215
  /**
247
- * Six decimals — a millionth of a cell, far below the lattice's own 2% granularity, and enough that the stored shares
248
- * still sum to 1 within a rounding error a reader can see is rounding.
216
+ * The number of decimals kept in a stored share, far finer than the lattice's 2% step.
249
217
  */
250
218
  const SHARE_DECIMALS = 6;
251
219
  function round(value) {
252
220
  return Number(value.toFixed(SHARE_DECIMALS));
253
221
  }
254
222
  /**
255
- * The sum of a stored row's five shares. Exported because the invariant it checks — that they sum to 1 — is what makes
256
- * `other_share` required rather than decorative, and a test that could not state the sum could not pin it.
223
+ * Returns the sum of a stored row's class shares and four other shares.
224
+ *
225
+ * Tests use it to check that the shares sum to 1.
257
226
  */
258
227
  export function shareTotal(row) {
259
228
  const classes = parseJSONStrict(row.class_shares);
@@ -1 +1 @@
1
- {"version":3,"file":"reduce.js","sourceRoot":"","sources":["../../lib/sdk/reduce.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"reduce.js","sourceRoot":"","sources":["../../sdk/reduce.ts"],"names":[],"mappings":"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"}
@@ -2,40 +2,14 @@
2
2
  * @copyright Sister Software
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
- *
6
- * One survey area's attributes, its own metadata, and its mapped footprint.
7
- *
8
- * THE FOOTPRINT IS THE SURVEY-AREA OUTLINE, NEVER THE UNION OF THE RATED POLYGONS. `NOTCOM`,
9
- * access-denied and `NOTPUB` map units are INSIDE the footprint and carry no rating, so a footprint taken
10
- * from the rated set would report them as unmapped when the authority has declared exactly what they are.
11
- * The archive ships the outline as its own shapefile — `soilsa_a_<areasymbol>.shp`, one feature — which is
12
- * why this layer never has to reconstruct it.
13
- *
14
- * THE REFRESH DATE IS NOT THE SURVEY DATE, AND CONFLATING THEM IS THE CURRENCY LIE THIS FILE EXISTS TO
15
- * PREVENT. `IA153` carries `saverest` 2025-09-09 and version 28, and the FGDC lineage inside the same
16
- * archive cites `Soil Survey of Polk County, Iowa`, 1:15,840, **1960**. The dataset's own
17
- * time-period-of-content runs 1998-09-22 to 2025-09-09, so a consumer reading that as survey currency
18
- * reads it wrong by sixty-five years. Both dates are stored, apart, with the title the older one came
19
- * from so it is checkable rather than assertible.
20
- *
21
- * TWO SCALES, ALSO DIFFERENT FACTS. `legend.projectscale` is 12,000 for `IA153` — the scale the map units
22
- * were digitized at. The 1960 source citation's own `srcscale` is 15,840 — the scale the ground was
23
- * walked at. Storing one as the other would answer the enlargement caveat's question wrongly.
24
- *
25
- * THE LICENCE IS CHECKED PER SURVEY AREA, against the `useconst` element of the metadata that area ships.
26
- * An area whose use constraints no longer say "This is public information" is a licence change, and a
27
- * build that absorbed one would ship an artifact under terms nobody checked. The text is boilerplate
28
- * repeated across SSURGO, which is why asserting it is cheap and why a change in it is loud.
29
5
  */
30
6
  import type { ParsedGeometry } from "@mailwoman/spatial";
31
7
  import type { PathBuilderLike } from "path-ts";
32
8
  import type { SoilComponentTable, SoilMapUnitTable } from "#schema";
33
9
  import { type DomainMember } from "#sdk/tabular";
34
10
  /**
35
- * The declared domains this layer validates against, and stores.
36
- *
37
- * `capability_class` is shared by `nirrcapcl`, `irrcapcl` and `muaggatt.niccdcd`, which is why one domain covers three
38
- * columns.
11
+ * The declared domains this layer validates against and stores; `capability_class`
12
+ * covers `nirrcapcl`, `irrcapcl` and `muaggatt.niccdcd`.
39
13
  */
40
14
  export declare const STORED_DOMAINS: readonly ["capability_class", "capability_subclass", "farmland_classification", "component_kind", "mapunit_kind", "mapunit_status"];
41
15
  /**
@@ -51,8 +25,8 @@ export interface SurveyAreaAttributes {
51
25
  sourceScale: number | null;
52
26
  mappingScale: number | null;
53
27
  /**
54
- * The area the authority publishes for this survey area, in acres — the independent witness the ring-area check
55
- * compares against.
28
+ * The area the authority publishes for this survey area, in acres —
29
+ * the independent witness the ring-area check compares against.
56
30
  */
57
31
  areaAcres: number | null;
58
32
  mapUnits: SoilMapUnitTable[];
@@ -62,44 +36,38 @@ export interface SurveyAreaAttributes {
62
36
  /**
63
37
  * Read one survey area's tabular export.
64
38
  *
65
- * @throws {Error} When the metadata's use constraints no longer carry the public-information sentence, when a `Choice`
66
- * column holds a value outside the authority's own declared domain, or when the export declares no legend row.
39
+ * @throws {Error} When the metadata's use constraints no longer include the
40
+ * public-information sentence, when a `Choice` column holds a value outside the
41
+ * authority's own declared domain, or when the export declares no legend row.
67
42
  */
68
43
  export declare function readSurveyAreaAttributes(tabularDirectory: PathBuilderLike, areaSymbol: string): Promise<SurveyAreaAttributes>;
69
44
  /**
70
- * What the shipped FGDC metadata says about this survey area's dates and its licence.
45
+ * What the shipped FGDC metadata records about this survey area's dates and its license.
71
46
  */
72
47
  export interface FGDCMetadata {
73
48
  /**
74
- * The citation's own `pubdate`, as an ISO date — the refresh.
49
+ * The citation's own `pubdate` as an ISO date — the refresh rather than the survey date.
75
50
  */
76
51
  publicationDate: string;
77
52
  /**
78
- * The OLDEST source citation date in the lineage, as an ISO date or a bare year.
53
+ * The oldest source citation date in the lineage, as an ISO date or a bare year.
79
54
  */
80
55
  oldestSourceDate: string | null;
81
56
  oldestSourceTitle: string | null;
82
57
  oldestSourceScale: number | null;
83
58
  }
84
59
  /**
85
- * Read the metadata NRCS ships inside the archive.
86
- *
87
- * Targeted extraction rather than a general XML parse, and NOT for want of a parser — `@mailwoman/core` ships
88
- * `htmlparser2`. A parser RECOVERS an unclosed element by giving it the rest of the document as its content, and the
89
- * two values below that throw would then stamp the artifact with that content instead. {@link elementText} answers
90
- * `undefined` for an element it cannot read, which is what makes the throw reachable. Every value this reader cannot
91
- * find is reported as `null` EXCEPT the publication date and the licence sentence, which throw — those two decide the
92
- * artifact's vintage and whether it may be shipped at all, and neither has a safe default.
60
+ * Read the metadata nrcs ships inside the archive.
93
61
  *
94
- * @throws {Error} When the metadata carries no publication date, or its use constraints no longer carry the
95
- * public-information sentence.
62
+ * @throws {Error} When the metadata has no publication date, or its use constraints
63
+ * no longer include the public-information sentence.
96
64
  */
97
65
  export declare function readFGDCMetadata(xml: string, areaSymbol: string): FGDCMetadata;
98
66
  /**
99
67
  * Read the survey area's own outline shapefile as a GeoJSON geometry.
100
68
  *
101
- * @throws {Error} When the shapefile holds anything other than exactly one feature. Taking the first of several would
102
- * silently choose which ground the coverage claim is about.
69
+ * @throws {Error} When the shapefile holds anything other than exactly one feature.
70
+ * The function would silently choose which ground the coverage claim describes if it took the first feature.
103
71
  */
104
- export declare function readSurveyAreaOutline(shapefilePath: string): Promise<ParsedGeometry>;
72
+ export declare function readSurveyAreaOutline(shapefilePath: PathBuilderLike): Promise<ParsedGeometry>;
105
73
  //# sourceMappingURL=survey-area.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"survey-area.d.ts","sourceRoot":"","sources":["../../lib/sdk/survey-area.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAE9C,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AACnE,OAAO,EAAsE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAA;AAUpH;;;;;GAKG;AACH,eAAO,MAAM,cAAc,qIAOjB,CAAA;AAEV;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACpC,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B;;;OAGG;IACH,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,EAAE,gBAAgB,EAAE,CAAA;IAC5B,UAAU,EAAE,kBAAkB,EAAE,CAAA;IAChC,OAAO,EAAE,YAAY,EAAE,CAAA;CACvB;AAED;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC7C,gBAAgB,EAAE,eAAe,EACjC,UAAU,EAAE,MAAM,GAChB,OAAO,CAAC,oBAAoB,CAAC,CAkI/B;AA2ED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC5B;;OAEG;IACH,eAAe,EAAE,MAAM,CAAA;IACvB;;OAEG;IACH,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;CAChC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,YAAY,CA0B9E;AAuFD;;;;;GAKG;AACH,wBAAsB,qBAAqB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAqB1F"}
1
+ {"version":3,"file":"survey-area.d.ts","sourceRoot":"","sources":["../../sdk/survey-area.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAE9C,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AACnE,OAAO,EAAsE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAA;AAUpH;;;GAGG;AACH,eAAO,MAAM,cAAc,YAC1B,kBAAkB,EAClB,qBAAqB,EACrB,yBAAyB,EACzB,gBAAgB,EAChB,cAAc,EACd,gBAAgB,CACP,CAAA;AAEV;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACpC,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B;;;OAGG;IACH,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,EAAE,gBAAgB,EAAE,CAAA;IAC5B,UAAU,EAAE,kBAAkB,EAAE,CAAA;IAChC,OAAO,EAAE,YAAY,EAAE,CAAA;CACvB;AAED;;;;;;GAMG;AACH,wBAAsB,wBAAwB,CAC7C,gBAAgB,EAAE,eAAe,EACjC,UAAU,EAAE,MAAM,GAChB,OAAO,CAAC,oBAAoB,CAAC,CAkI/B;AAmED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC5B;;OAEG;IACH,eAAe,EAAE,MAAM,CAAA;IACvB;;OAEG;IACH,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;CAChC;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,YAAY,CA0B9E;AAkFD;;;;;GAKG;AACH,wBAAsB,qBAAqB,CAAC,aAAa,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAqBnG"}
@@ -2,40 +2,15 @@
2
2
  * @copyright Sister Software
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
- *
6
- * One survey area's attributes, its own metadata, and its mapped footprint.
7
- *
8
- * THE FOOTPRINT IS THE SURVEY-AREA OUTLINE, NEVER THE UNION OF THE RATED POLYGONS. `NOTCOM`,
9
- * access-denied and `NOTPUB` map units are INSIDE the footprint and carry no rating, so a footprint taken
10
- * from the rated set would report them as unmapped when the authority has declared exactly what they are.
11
- * The archive ships the outline as its own shapefile — `soilsa_a_<areasymbol>.shp`, one feature — which is
12
- * why this layer never has to reconstruct it.
13
- *
14
- * THE REFRESH DATE IS NOT THE SURVEY DATE, AND CONFLATING THEM IS THE CURRENCY LIE THIS FILE EXISTS TO
15
- * PREVENT. `IA153` carries `saverest` 2025-09-09 and version 28, and the FGDC lineage inside the same
16
- * archive cites `Soil Survey of Polk County, Iowa`, 1:15,840, **1960**. The dataset's own
17
- * time-period-of-content runs 1998-09-22 to 2025-09-09, so a consumer reading that as survey currency
18
- * reads it wrong by sixty-five years. Both dates are stored, apart, with the title the older one came
19
- * from so it is checkable rather than assertible.
20
- *
21
- * TWO SCALES, ALSO DIFFERENT FACTS. `legend.projectscale` is 12,000 for `IA153` — the scale the map units
22
- * were digitized at. The 1960 source citation's own `srcscale` is 15,840 — the scale the ground was
23
- * walked at. Storing one as the other would answer the enlargement caveat's question wrongly.
24
- *
25
- * THE LICENCE IS CHECKED PER SURVEY AREA, against the `useconst` element of the metadata that area ships.
26
- * An area whose use constraints no longer say "This is public information" is a licence change, and a
27
- * build that absorbed one would ship an artifact under terms nobody checked. The text is boilerplate
28
- * repeated across SSURGO, which is why asserting it is cheap and why a change in it is loud.
29
5
  */
30
- import { parseJSONStrict } from "@mailwoman/core/json";
6
+ import { parseJSONStrict, stringifyJSON } from "@mailwoman/core/json";
31
7
  import { runFile } from "@mailwoman/core/process";
8
+ import { normalizeWhitespace } from "@mailwoman/core/strings/format";
32
9
  import { domainCodes, readDeclaredDomains, readTable, readTabularDictionary } from "#sdk/tabular";
33
10
  import { COINTERP_OVERALL_RULE_DEPTH, farmlandScope, NCCPI_V3_RULE_NAME, SSURGO_NO_MAPPING_NAMES, SSURGO_NO_MAPPING_SYMBOLS, SSURGO_PUBLIC_INFORMATION_SENTENCE, } from "#vocabulary";
34
11
  /**
35
- * The declared domains this layer validates against, and stores.
36
- *
37
- * `capability_class` is shared by `nirrcapcl`, `irrcapcl` and `muaggatt.niccdcd`, which is why one domain covers three
38
- * columns.
12
+ * The declared domains this layer validates against and stores; `capability_class`
13
+ * covers `nirrcapcl`, `irrcapcl` and `muaggatt.niccdcd`.
39
14
  */
40
15
  export const STORED_DOMAINS = [
41
16
  "capability_class",
@@ -48,8 +23,9 @@ export const STORED_DOMAINS = [
48
23
  /**
49
24
  * Read one survey area's tabular export.
50
25
  *
51
- * @throws {Error} When the metadata's use constraints no longer carry the public-information sentence, when a `Choice`
52
- * column holds a value outside the authority's own declared domain, or when the export declares no legend row.
26
+ * @throws {Error} When the metadata's use constraints no longer include the
27
+ * public-information sentence, when a `Choice` column holds a value outside the
28
+ * authority's own declared domain, or when the export declares no legend row.
53
29
  */
54
30
  export async function readSurveyAreaAttributes(tabularDirectory, areaSymbol) {
55
31
  const dictionary = await readTabularDictionary(tabularDirectory);
@@ -114,8 +90,8 @@ export async function readSurveyAreaAttributes(tabularDirectory, areaSymbol) {
114
90
  return {
115
91
  cokey: row.cokey,
116
92
  mukey: row.mukey,
117
- // A blank `comppct_r` is a component with no declared weight. Zero is the truthful reading — it contributes
118
- // nothing to a weighted share — and it is recorded rather than dropped, so the component still appears.
93
+ // A blank `comppct_r` is a component with no declared weight, recorded as zero
94
+ // rather than dropped so the component still appears.
119
95
  comppct_r: row.comppct_r ? Number(row.comppct_r) : 0,
120
96
  compname: nullable(row.compname),
121
97
  compkind: nullable(row.compkind),
@@ -163,11 +139,8 @@ export async function readSurveyAreaAttributes(tabularDirectory, areaSymbol) {
163
139
  /**
164
140
  * A polygon the authority drew with no soil mapping behind it.
165
141
  *
166
- * Three signals rather than one, because the source encodes the same fact three ways and each on its own has a gap: the
167
- * symbol (`NOTCOM`, `NOTPUB`), the name (`Area not surveyed, access denied`), and the structural case of a map unit
168
- * carrying NO components at all. A map unit with no components has nothing to rate whatever it is called, and reading
169
- * it as "rated nothing" rather than "no mapping" would put it in `unrated_share` — a claim that the survey looked and
170
- * declined, when it did not look.
142
+ * A map unit with no components has no component to rate, so it reads as no mapping
143
+ * rather than assigned no rating.
171
144
  */
172
145
  function isNoMapping(musym, muname, componentCount) {
173
146
  if (SSURGO_NO_MAPPING_SYMBOLS.has(musym.toUpperCase()))
@@ -179,28 +152,23 @@ function isNoMapping(musym, muname, componentCount) {
179
152
  /**
180
153
  * Refuse a value outside the authority's own declared domain.
181
154
  *
182
- * An unknown code is a source-schema change, which is the event a reader most needs to hear about; coercing it to a
183
- * nearest neighbour or to NULL converts "the source changed" into "there is nothing here". A BLANK is not a violation:
184
- * NULL is a real state in every one of these columns and means something specific — for `nirrcapcl` it means the survey
185
- * did not rate the component, which is not class 8.
155
+ * A blank is a real NULL state rather than a violation, recording that the
156
+ * survey did not rate the component.
186
157
  */
187
158
  function assertDeclared(declared, value, domain, where) {
188
159
  if (!value)
189
160
  return;
190
161
  if (declared.has(value))
191
162
  return;
192
- throw new Error(`soil survey area: ${where} holds ${JSON.stringify(value)}, which is not in the authority's declared ${domain} domain (${declared.size} members, read from the archive's own msdomdet.txt) — an unknown code is a source-schema change, and coercing it would turn "the source changed" into "there is nothing here"`);
163
+ throw new Error(`soil survey area: ${where} holds ${stringifyJSON(value)}, which is not in the authority's declared ${domain} domain (${declared.size} members, read from the archive's own msdomdet.txt) — an unknown code is a source-schema change, and coercing it would turn "the source changed" into "there is nothing here"`);
193
164
  }
194
165
  function nullable(value) {
195
166
  return value || null;
196
167
  }
197
168
  /**
198
- * The NCCPI v3.0 overall index per component.
169
+ * The nccpi v3.0 overall index per component.
199
170
  *
200
- * `cointerp` is the largest table in the export — 157,063 rows for `IA153`, read in 0.36 s — and the overall rule is
201
- * one row per component at {@link COINTERP_OVERALL_RULE_DEPTH}: 369 of 369 components on `IA153`, of which 327 carry a
202
- * value. Sub-rules at greater depths are the submodels (corn, soybeans, small grains, cotton), which this layer does
203
- * not carry.
171
+ * Sub-rules at greater depths are submodels this layer does not include.
204
172
  */
205
173
  async function readNCCPI(tabularDirectory, dictionary) {
206
174
  const rows = await readTable(tabularDirectory, dictionary, "cointerp", [
@@ -222,22 +190,15 @@ async function readNCCPI(tabularDirectory, dictionary) {
222
190
  return byCokey;
223
191
  }
224
192
  /**
225
- * Read the metadata NRCS ships inside the archive.
226
- *
227
- * Targeted extraction rather than a general XML parse, and NOT for want of a parser — `@mailwoman/core` ships
228
- * `htmlparser2`. A parser RECOVERS an unclosed element by giving it the rest of the document as its content, and the
229
- * two values below that throw would then stamp the artifact with that content instead. {@link elementText} answers
230
- * `undefined` for an element it cannot read, which is what makes the throw reachable. Every value this reader cannot
231
- * find is reported as `null` EXCEPT the publication date and the licence sentence, which throw — those two decide the
232
- * artifact's vintage and whether it may be shipped at all, and neither has a safe default.
193
+ * Read the metadata nrcs ships inside the archive.
233
194
  *
234
- * @throws {Error} When the metadata carries no publication date, or its use constraints no longer carry the
235
- * public-information sentence.
195
+ * @throws {Error} When the metadata has no publication date, or its use constraints
196
+ * no longer include the public-information sentence.
236
197
  */
237
198
  export function readFGDCMetadata(xml, areaSymbol) {
238
199
  const useConstraints = elementText(xml, "useconst");
239
200
  if (!useConstraints?.includes(SSURGO_PUBLIC_INFORMATION_SENTENCE)) {
240
- throw new Error(`soil survey area: ${areaSymbol}'s FGDC use constraints do not carry ${JSON.stringify(SSURGO_PUBLIC_INFORMATION_SENTENCE)} — that sentence is the grant this layer ships on, so a survey area without it must not be built into a distributable artifact`);
201
+ throw new Error(`soil survey area: ${areaSymbol}'s FGDC use constraints do not carry ${stringifyJSON(SSURGO_PUBLIC_INFORMATION_SENTENCE)} — that sentence is the grant this layer ships on, so a survey area without it must not be built into a distributable artifact`);
241
202
  }
242
203
  const publicationDate = elementText(xml, "pubdate");
243
204
  if (!publicationDate) {
@@ -253,33 +214,30 @@ export function readFGDCMetadata(xml, areaSymbol) {
253
214
  };
254
215
  }
255
216
  /**
256
- * The lineage's source citations: what the polygons rest on, and when each was made.
217
+ * The lineage's source citations — what the polygons rest on and when each was made.
257
218
  */
258
219
  function readSourceCitations(xml) {
259
220
  const citations = [];
260
221
  for (const body of elementBlocks(xml, "srcinfo")) {
261
- // `caldate` for a single date, `begdate` for a range. A range's END is when the source stopped being collected;
262
- // its BEGINNING is when the ground was first looked at, which is the fact this layer is carrying.
222
+ // `caldate` stores a single date.
223
+ // `begdate` stores a range's start date in this layer.
263
224
  const date = elementText(body, "caldate") ?? elementText(body, "begdate");
264
225
  if (!date)
265
226
  continue;
266
227
  const scale = elementText(body, "srcscale");
267
228
  citations.push({
268
229
  date: date.trim(),
269
- title: (elementText(body, "title") ?? "").replaceAll(/\s+/gu, " ").trim(),
230
+ title: normalizeWhitespace(elementText(body, "title") ?? ""),
270
231
  scale: scale ? Number(scale) : null,
271
232
  });
272
233
  }
273
234
  return citations;
274
235
  }
275
236
  /**
276
- * The text of the first `<name>` element, whitespace left alone.
237
+ * The text of the first `<name>` element, with whitespace preserved.
277
238
  *
278
- * INDEX SCANS RATHER THAN A REGEX, AND THAT IS A CORRECTNESS CHOICE RATHER THAN A SPEED ONE. The obvious form — ``new
279
- * RegExp(`<${name}>([\\s\\S]*?)</${name}>`)`` — backtracks polynomially on a document whose opening tag has no closing
280
- * partner: the lazy run re-scans to the end from every candidate start. The input here is a 43,251-character document
281
- * that arrived over the network inside a downloaded archive, so "a malformed one cannot happen" is not a claim this
282
- * reader gets to make. Two `indexOf` calls answer the same question in one pass.
239
+ * Two `indexOf` calls avoid the polynomial backtracking a regex takes on a document
240
+ * whose opening tag has no closing partner.
283
241
  */
284
242
  function elementText(xml, name) {
285
243
  const open = `<${name}>`;
@@ -288,13 +246,11 @@ function elementText(xml, name) {
288
246
  return undefined;
289
247
  const from = start + open.length;
290
248
  const end = xml.indexOf(`</${name}>`, from);
291
- // An element with no closing tag is unreadable, not empty — the same answer an absent element gets, because both
292
- // mean the value could not be read rather than that it is blank.
249
+ // An element with no closing tag is unreadable rather than empty, the same answer an absent element gets.
293
250
  return end === -1 ? undefined : xml.slice(from, end);
294
251
  }
295
252
  /**
296
- * Every `<name>` element's inner text, in document order. The repeating counterpart of {@link elementText}, and linear
297
- * for the same reason.
253
+ * Every `<name>` element's inner text in document order, the repeating counterpart of {@link elementText}.
298
254
  */
299
255
  function elementBlocks(xml, name) {
300
256
  const open = `<${name}>`;
@@ -314,8 +270,8 @@ function elementBlocks(xml, name) {
314
270
  }
315
271
  }
316
272
  /**
317
- * FGDC dates arrive as `YYYY` or `YYYYMMDD`. Both are kept as they are meant — a bare year is a bare year, and padding
318
- * it to January 1 would invent a precision the citation does not claim.
273
+ * A bare year stays bare, since padding it to January 1 would invent a precision
274
+ * the citation does not claim.
319
275
  */
320
276
  function normalizeFGDCDate(value) {
321
277
  const trimmed = value.trim();
@@ -325,8 +281,8 @@ function normalizeFGDCDate(value) {
325
281
  /**
326
282
  * Read the survey area's own outline shapefile as a GeoJSON geometry.
327
283
  *
328
- * @throws {Error} When the shapefile holds anything other than exactly one feature. Taking the first of several would
329
- * silently choose which ground the coverage claim is about.
284
+ * @throws {Error} When the shapefile holds anything other than exactly one feature.
285
+ * The function would silently choose which ground the coverage claim describes if it took the first feature.
330
286
  */
331
287
  export async function readSurveyAreaOutline(shapefilePath) {
332
288
  const { stdout } = await runFile("ogr2ogr", ["-f", "GeoJSON", "/vsistdout/", "-t_srs", "EPSG:4326", shapefilePath], {