@bedrock-engineer/bro-xml-parser 0.2.0 → 0.3.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 (47) hide show
  1. package/dist/browser.d.ts +1 -1
  2. package/dist/browser.d.ts.map +1 -1
  3. package/dist/browser.js.map +1 -1
  4. package/dist/node.d.ts +1 -1
  5. package/dist/node.d.ts.map +1 -1
  6. package/dist/node.js.map +1 -1
  7. package/dist/resolvers/bhrg-resolvers.d.ts +3 -18
  8. package/dist/resolvers/bhrg-resolvers.d.ts.map +1 -1
  9. package/dist/resolvers/bhrg-resolvers.js +294 -41
  10. package/dist/resolvers/bhrg-resolvers.js.map +1 -1
  11. package/dist/resolvers/bore-resolver-utils.d.ts +21 -2
  12. package/dist/resolvers/bore-resolver-utils.d.ts.map +1 -1
  13. package/dist/resolvers/bore-resolver-utils.js +69 -22
  14. package/dist/resolvers/bore-resolver-utils.js.map +1 -1
  15. package/dist/resolvers/bore-resolvers.d.ts +34 -17
  16. package/dist/resolvers/bore-resolvers.d.ts.map +1 -1
  17. package/dist/resolvers/bore-resolvers.js +342 -175
  18. package/dist/resolvers/bore-resolvers.js.map +1 -1
  19. package/dist/resolvers/cpt-resolvers.d.ts +1 -8
  20. package/dist/resolvers/cpt-resolvers.d.ts.map +1 -1
  21. package/dist/resolvers/cpt-resolvers.js +2 -29
  22. package/dist/resolvers/cpt-resolvers.js.map +1 -1
  23. package/dist/resolvers/type-resolvers.d.ts +12 -4
  24. package/dist/resolvers/type-resolvers.d.ts.map +1 -1
  25. package/dist/resolvers/type-resolvers.js +12 -4
  26. package/dist/resolvers/type-resolvers.js.map +1 -1
  27. package/dist/resolvers.d.ts +3 -2
  28. package/dist/resolvers.d.ts.map +1 -1
  29. package/dist/resolvers.js +4 -2
  30. package/dist/resolvers.js.map +1 -1
  31. package/dist/schemas/bhrg-schema.d.ts.map +1 -1
  32. package/dist/schemas/bhrg-schema.js +35 -8
  33. package/dist/schemas/bhrg-schema.js.map +1 -1
  34. package/dist/schemas/bore-schema.d.ts.map +1 -1
  35. package/dist/schemas/bore-schema.js +46 -11
  36. package/dist/schemas/bore-schema.js.map +1 -1
  37. package/dist/schemas/common-fields.d.ts +16 -0
  38. package/dist/schemas/common-fields.d.ts.map +1 -0
  39. package/dist/schemas/common-fields.js +31 -0
  40. package/dist/schemas/common-fields.js.map +1 -0
  41. package/dist/schemas/cpt-schema.d.ts.map +1 -1
  42. package/dist/schemas/cpt-schema.js +20 -10
  43. package/dist/schemas/cpt-schema.js.map +1 -1
  44. package/dist/types/index.d.ts +465 -49
  45. package/dist/types/index.d.ts.map +1 -1
  46. package/dist/types/index.js.map +1 -1
  47. package/package.json +3 -2
@@ -4,7 +4,7 @@
4
4
  * Functions to parse and convert bore-specific data structures
5
5
  */
6
6
  // Import shared utilities
7
- import { createXPathTextGetter, createNamespaceResolver, createLayerParser, getTextContent, findChildElement, extractArray, parseCSVPairs, parseCSVRows, } from "./bore-resolver-utils.js";
7
+ import { createXPathTextGetter, createNamespaceResolver, extractOptionalFields, getTextContent, findChildElement, extractArray, parseCSVPairs, parseCSVRows, } from "./bore-resolver-utils.js";
8
8
  // Import type resolvers
9
9
  import { parseBoolean, parseFloat, parseDate } from "./type-resolvers.js";
10
10
  /**
@@ -35,164 +35,186 @@ function parseDispersedInhomogeneity(text) {
35
35
  * - Tertiary soil constituent
36
36
  * - Grain shape properties for coarse fractions
37
37
  */
38
- export const processBHRGTLayerData = createLayerParser({
39
- layerXPath: ".//bhrgtcom:layer",
40
- requiredFields: {
41
- upperBoundary: "./bhrgtcom:upperBoundary",
42
- lowerBoundary: "./bhrgtcom:lowerBoundary",
43
- soilName: "./bhrgtcom:soil/bhrgtcom:geotechnicalSoilName",
44
- soilNameKey: "geotechnicalSoilName",
38
+ // Layer-level fields shared by soil and rock layers (outside <soil>/<rock>).
39
+ const BHRGT_BASE_FIELDS = [
40
+ { xpath: "./bhrgtcom:upperBoundaryDetermination", key: "upperBoundaryDetermination" },
41
+ { xpath: "./bhrgtcom:lowerBoundaryDetermination", key: "lowerBoundaryDetermination" },
42
+ { xpath: "./bhrgtcom:anthropogenic", key: "anthropogenic", transform: parseBoolean },
43
+ { xpath: "./bhrgtcom:slant", key: "slant", transform: parseBoolean },
44
+ { xpath: "./bhrgtcom:bedding", key: "bedding", omitIfEmpty: true },
45
+ {
46
+ xpath: "./bhrgtcom:compositeLayer",
47
+ key: "compositeLayer",
48
+ transform: parseBoolean,
49
+ omitIfEmpty: true,
50
+ },
51
+ { xpath: "./bhrgtcom:activityType", key: "activityType", omitIfEmpty: true },
52
+ { xpath: "./bhrgtcom:bedded", key: "bedded", transform: parseBoolean },
53
+ {
54
+ xpath: "./bhrgtcom:internalStructureIntact",
55
+ key: "internalStructureIntact",
56
+ transform: parseBoolean,
57
+ },
58
+ { xpath: "./bhrgtcom:specialMaterial", key: "specialMaterial", omitIfEmpty: true },
59
+ ];
60
+ // Soil-description fields (inside <soil>) for soil layers only.
61
+ const BHRGT_SOIL_FIELDS = [
62
+ { xpath: "./bhrgtcom:soil/bhrgtcom:soilNameNEN5104", key: "soilNameNEN5104", omitIfEmpty: true },
63
+ {
64
+ xpath: "./bhrgtcom:soil/bhrgtcom:gravelContentClassNEN5104",
65
+ key: "gravelContentClassNEN5104",
66
+ omitIfEmpty: true,
67
+ },
68
+ {
69
+ xpath: "./bhrgtcom:soil/bhrgtcom:organicMatterContentClassNEN5104",
70
+ key: "organicMatterContentClassNEN5104",
71
+ omitIfEmpty: true,
72
+ },
73
+ { xpath: "./bhrgtcom:soil/bhrgtcom:tertiaryConstituent", key: "tertiaryConstituent" },
74
+ { xpath: "./bhrgtcom:soil/bhrgtcom:colour", key: "color", omitIfEmpty: true },
75
+ {
76
+ xpath: "./bhrgtcom:soil/bhrgtcom:dispersedInhomogeneity",
77
+ key: "dispersedInhomogeneity",
78
+ transform: parseDispersedInhomogeneity,
79
+ },
80
+ { xpath: "./bhrgtcom:soil/bhrgtcom:organicMatterContentClass", key: "organicMatterContentClass" },
81
+ { xpath: "./bhrgtcom:soil/bhrgtcom:carbonateContentClass", key: "carbonateContentClass" },
82
+ { xpath: "./bhrgtcom:soil/bhrgtcom:sandMedianClass", key: "sandMedianClass" },
83
+ {
84
+ xpath: "./bhrgtcom:soil/bhrgtcom:gravelMedianClass",
85
+ key: "gravelMedianClass",
86
+ omitIfEmpty: true,
87
+ },
88
+ {
89
+ xpath: "./bhrgtcom:soil/bhrgtcom:geotechnicalDepositionalCharacteristic",
90
+ key: "geotechnicalDepositionalCharacteristic",
91
+ omitIfEmpty: true,
45
92
  },
46
- optionalFields: [
47
- // Layer-level fields (outside <soil>)
48
- {
49
- xpath: "./bhrgtcom:upperBoundaryDetermination",
50
- key: "upperBoundaryDetermination",
51
- },
52
- {
53
- xpath: "./bhrgtcom:lowerBoundaryDetermination",
54
- key: "lowerBoundaryDetermination",
55
- },
56
- {
57
- xpath: "./bhrgtcom:anthropogenic",
58
- key: "anthropogenic",
59
- transform: parseBoolean,
60
- },
61
- {
62
- xpath: "./bhrgtcom:slant",
63
- key: "slant",
64
- transform: parseBoolean,
65
- },
66
- {
67
- xpath: "./bhrgtcom:bedding",
68
- key: "bedding",
69
- omitIfEmpty: true,
70
- },
71
- {
72
- xpath: "./bhrgtcom:compositeLayer",
73
- key: "compositeLayer",
74
- transform: parseBoolean,
75
- omitIfEmpty: true,
76
- },
77
- {
78
- xpath: "./bhrgtcom:activityType",
79
- key: "activityType",
80
- omitIfEmpty: true,
81
- },
82
- // Soil-level fields (inside <soil>)
83
- {
84
- xpath: "./bhrgtcom:soil/bhrgtcom:soilNameNEN5104",
85
- key: "soilNameNEN5104",
86
- omitIfEmpty: true,
87
- },
88
- {
89
- xpath: "./bhrgtcom:soil/bhrgtcom:gravelContentClassNEN5104",
90
- key: "gravelContentClassNEN5104",
91
- omitIfEmpty: true,
92
- },
93
- {
94
- xpath: "./bhrgtcom:soil/bhrgtcom:organicMatterContentClassNEN5104",
95
- key: "organicMatterContentClassNEN5104",
96
- omitIfEmpty: true,
97
- },
98
- {
99
- xpath: "./bhrgtcom:soil/bhrgtcom:tertiaryConstituent",
100
- key: "tertiaryConstituent",
101
- },
102
- {
103
- xpath: "./bhrgtcom:soil/bhrgtcom:colour",
104
- key: "color",
105
- omitIfEmpty: true,
106
- },
107
- {
108
- xpath: "./bhrgtcom:soil/bhrgtcom:dispersedInhomogeneity",
109
- key: "dispersedInhomogeneity",
110
- transform: parseDispersedInhomogeneity,
111
- },
112
- {
113
- xpath: "./bhrgtcom:soil/bhrgtcom:organicMatterContentClass",
114
- key: "organicMatterContentClass",
115
- },
116
- {
117
- xpath: "./bhrgtcom:soil/bhrgtcom:carbonateContentClass",
118
- key: "carbonateContentClass",
119
- },
120
- {
121
- xpath: "./bhrgtcom:soil/bhrgtcom:sandMedianClass",
122
- key: "sandMedianClass",
123
- },
124
- {
125
- xpath: "./bhrgtcom:soil/bhrgtcom:gravelMedianClass",
126
- key: "gravelMedianClass",
127
- omitIfEmpty: true,
128
- },
129
- {
130
- xpath: "./bhrgtcom:soil/bhrgtcom:geotechnicalDepositionalCharacteristic",
131
- key: "geotechnicalDepositionalCharacteristic",
132
- omitIfEmpty: true,
133
- },
134
- {
135
- xpath: "./bhrgtcom:soil/bhrgtcom:interbedding",
136
- key: "interbedding",
137
- omitIfEmpty: true,
138
- },
139
- // Layer structure properties (at layer level)
140
- {
141
- xpath: "./bhrgtcom:bedded",
142
- key: "bedded",
143
- transform: parseBoolean,
144
- },
145
- {
146
- xpath: "./bhrgtcom:internalStructureIntact",
147
- key: "internalStructureIntact",
148
- transform: parseBoolean,
149
- },
150
- // Soil structure properties (inside <soil>)
151
- {
152
- xpath: "./bhrgtcom:soil/bhrgtcom:mixed",
153
- key: "mixed",
154
- transform: parseBoolean,
155
- },
156
- {
157
- xpath: "./bhrgtcom:soil/bhrgtcom:mottled",
158
- key: "mottled",
159
- transform: parseBoolean,
160
- },
161
- // Fine-grained soil consistency
162
- {
163
- xpath: "./bhrgtcom:soil/bhrgtcom:fineSoilConsistency",
164
- key: "fineSoilConsistency",
165
- },
166
- // Organic soil properties
167
- {
168
- xpath: "./bhrgtcom:soil/bhrgtcom:organicSoilConsistency",
169
- key: "organicSoilConsistency",
170
- },
171
- {
172
- xpath: "./bhrgtcom:soil/bhrgtcom:organicSoilTexture",
173
- key: "organicSoilTexture",
174
- },
175
- {
176
- xpath: "./bhrgtcom:soil/bhrgtcom:peatTensileStrength",
177
- key: "peatTensileStrength",
178
- },
179
- ],
180
- // Extract nested grainshape object
181
- postProcess: (layerNode, layer, adapter, namespaces) => {
182
- const nsResolver = createNamespaceResolver(namespaces);
93
+ { xpath: "./bhrgtcom:soil/bhrgtcom:interbedding", key: "interbedding", omitIfEmpty: true },
94
+ { xpath: "./bhrgtcom:soil/bhrgtcom:mixed", key: "mixed", transform: parseBoolean },
95
+ { xpath: "./bhrgtcom:soil/bhrgtcom:mottled", key: "mottled", transform: parseBoolean },
96
+ { xpath: "./bhrgtcom:soil/bhrgtcom:fineSoilConsistency", key: "fineSoilConsistency" },
97
+ { xpath: "./bhrgtcom:soil/bhrgtcom:organicSoilConsistency", key: "organicSoilConsistency" },
98
+ { xpath: "./bhrgtcom:soil/bhrgtcom:organicSoilTexture", key: "organicSoilTexture" },
99
+ { xpath: "./bhrgtcom:soil/bhrgtcom:peatTensileStrength", key: "peatTensileStrength" },
100
+ { xpath: "./bhrgtcom:soil/bhrgtcom:crossBedding", key: "crossBedding", omitIfEmpty: true },
101
+ { xpath: "./bhrgtcom:soil/bhrgtcom:gradedBedding", key: "gradedBedding", omitIfEmpty: true },
102
+ { xpath: "./bhrgtcom:soil/bhrgtcom:mixingType", key: "mixingType", omitIfEmpty: true },
103
+ {
104
+ xpath: "./bhrgtcom:soil/bhrgtcom:fineGravelContentClass",
105
+ key: "fineGravelContentClass",
106
+ omitIfEmpty: true,
107
+ },
108
+ {
109
+ xpath: "./bhrgtcom:soil/bhrgtcom:mediumCoarseGravelContentClass",
110
+ key: "mediumCoarseGravelContentClass",
111
+ omitIfEmpty: true,
112
+ },
113
+ {
114
+ xpath: "./bhrgtcom:soil/bhrgtcom:veryCoarseGravelContentClass",
115
+ key: "veryCoarseGravelContentClass",
116
+ omitIfEmpty: true,
117
+ },
118
+ {
119
+ xpath: "./bhrgtcom:soil/bhrgtcom:sandSortingNEN5104",
120
+ key: "sandSortingNEN5104",
121
+ omitIfEmpty: true,
122
+ },
123
+ { xpath: "./bhrgtcom:soil/bhrgtcom:peatType", key: "peatType", omitIfEmpty: true },
124
+ { xpath: "./bhrgtcom:soil/bhrgtcom:depositionalAge", key: "depositionalAge", omitIfEmpty: true },
125
+ ];
126
+ /**
127
+ * Parse a BHR-GT `rock` element into a RockDescription.
128
+ */
129
+ function parseRockDescription(rockNode, adapter, namespaces) {
130
+ const nsResolver = createNamespaceResolver(namespaces);
131
+ const getText = createXPathTextGetter(rockNode, adapter, namespaces);
132
+ const textList = (xpath) => adapter
133
+ .evaluateXPathAll(rockNode, xpath, nsResolver)
134
+ .map((n) => n.textContent?.trim() ?? "")
135
+ .filter((s) => s !== "");
136
+ const rock = {
137
+ rockType: getText("./bhrgtcom:rockType"),
138
+ cementType: getText("./bhrgtcom:cementType"),
139
+ colour: getText("./bhrgtcom:colour"),
140
+ tertiaryRockConstituent: textList("./bhrgtcom:tertiaryRockConstituent"),
141
+ interbedding: getText("./bhrgtcom:interbedding"),
142
+ dispersedInhomogeneity: textList("./bhrgtcom:dispersedInhomogeneity"),
143
+ carbonateContentClass: getText("./bhrgtcom:carbonateContentClass"),
144
+ crossBedding: getText("./bhrgtcom:crossBedding"),
145
+ gradedBedding: getText("./bhrgtcom:gradedBedding"),
146
+ voidsPresent: getText("./bhrgtcom:voidsPresent"),
147
+ voidDistribution: getText("./bhrgtcom:voidDistribution"),
148
+ stability: getText("./bhrgtcom:stability"),
149
+ strengthClass: getText("./bhrgtcom:strengthClass"),
150
+ weathered: getText("./bhrgtcom:weathered"),
151
+ };
152
+ const weatheringNode = adapter.evaluateXPath(rockNode, "./bhrgtcom:weatheringDegree", nsResolver);
153
+ if (weatheringNode) {
154
+ const getW = createXPathTextGetter(weatheringNode, adapter, namespaces);
155
+ rock.weatheringDegree = {
156
+ discolouration: getW("./bhrgtcom:discolouration"),
157
+ disintegration: getW("./bhrgtcom:disintegration"),
158
+ decomposition: getW("./bhrgtcom:decomposition"),
159
+ };
160
+ }
161
+ return rock;
162
+ }
163
+ /**
164
+ * Parse BHR-GT descriptive-log layers into a discriminated union of soil / rock
165
+ * layers. A layer describes either soil or rock; we branch on which is present
166
+ * so rock layers aren't forced through the soil shape (and vice versa).
167
+ */
168
+ export function processBHRGTLayerData(_value, context) {
169
+ const { node, adapter, namespaces } = context;
170
+ const nsResolver = createNamespaceResolver(namespaces);
171
+ const layerNodes = adapter.evaluateXPathAll(node, ".//bhrgtcom:layer", nsResolver);
172
+ const layers = [];
173
+ for (const layerNode of layerNodes) {
174
+ const getText = createXPathTextGetter(layerNode, adapter, namespaces);
175
+ const upperBoundary = parseFloat(getText("./bhrgtcom:upperBoundary"));
176
+ const lowerBoundary = parseFloat(getText("./bhrgtcom:lowerBoundary"));
177
+ if (upperBoundary === null || lowerBoundary === null) {
178
+ continue;
179
+ }
180
+ // Spread extracted optionals first, then the guaranteed fields, so the
181
+ // required boundaries/discriminant keep their exact (non-optional) types.
182
+ const base = {
183
+ ...extractOptionalFields(BHRGT_BASE_FIELDS, layerNode, adapter, namespaces),
184
+ upperBoundary,
185
+ lowerBoundary,
186
+ };
187
+ // A layer describes rock or soil - branch on which element is present.
188
+ const rockNode = adapter.evaluateXPath(layerNode, "./bhrgtcom:rock", nsResolver);
189
+ if (rockNode) {
190
+ layers.push({
191
+ ...base,
192
+ material: "rock",
193
+ rock: parseRockDescription(rockNode, adapter, namespaces),
194
+ });
195
+ continue;
196
+ }
197
+ const soilLayer = {
198
+ ...base,
199
+ ...extractOptionalFields(BHRGT_SOIL_FIELDS, layerNode, adapter, namespaces),
200
+ material: "soil",
201
+ geotechnicalSoilName: getText("./bhrgtcom:soil/bhrgtcom:geotechnicalSoilName") ?? "",
202
+ };
183
203
  const grainshapeNode = adapter.evaluateXPath(layerNode, "./bhrgtcom:soil/bhrgtcom:grainshape", nsResolver);
184
204
  if (grainshapeNode) {
185
- const getText = createXPathTextGetter(grainshapeNode, adapter, namespaces);
205
+ const getGrain = createXPathTextGetter(grainshapeNode, adapter, namespaces);
186
206
  const grainshape = {
187
- sizeFraction: getText("./bhrgtcom:sizeFraction"),
188
- angularity: getText("./bhrgtcom:angularity"),
189
- sphericity: getText("./bhrgtcom:sphericity"),
190
- roughness: getText("./bhrgtcom:roughness"),
207
+ sizeFraction: getGrain("./bhrgtcom:sizeFraction"),
208
+ angularity: getGrain("./bhrgtcom:angularity"),
209
+ sphericity: getGrain("./bhrgtcom:sphericity"),
210
+ roughness: getGrain("./bhrgtcom:roughness"),
191
211
  };
192
- layer.grainshape = grainshape;
212
+ soilLayer.grainshape = grainshape;
193
213
  }
194
- },
195
- });
214
+ layers.push(soilLayer);
215
+ }
216
+ return layers;
217
+ }
196
218
  /**
197
219
  * Empty structure constants for determinations
198
220
  */
@@ -232,7 +254,11 @@ const EMPTY_SATURATED_PERMEABILITY = {
232
254
  waterDegassed: null,
233
255
  temperature: null,
234
256
  maximumGradient: null,
257
+ ringWaterRepellent: null,
258
+ waterContentAfterwards: null,
259
+ materialIrregularity: [],
235
260
  saturatedPermeabilityAtSpecificDensity: [],
261
+ saturatedPermeabilityAtSpecificLoad: [],
236
262
  };
237
263
  /**
238
264
  * Parse water content determination from XML node
@@ -329,7 +355,33 @@ function parseParticleSizeDistributionDetermination(node, adapter, namespaces) {
329
355
  ? findChildElement(basicDistNode, "./bhrgtcom:standardDistributionFractionSmaller63um", adapter, namespaces)
330
356
  : null;
331
357
  const standardSmallerBase = "./bhrgtcom:basicParticleSizeDistribution/bhrgtcom:standardDistributionFractionSmaller63um";
358
+ // Finer >63μm resolution — an alternative to standardDistributionFractionLarger63um.
359
+ const detailedLargerNode = basicDistNode
360
+ ? findChildElement(basicDistNode, "./bhrgtcom:detailedDistributionFractionLarger63um", adapter, namespaces)
361
+ : null;
362
+ const detailedLargerBase = "./bhrgtcom:basicParticleSizeDistribution/bhrgtcom:detailedDistributionFractionLarger63um";
363
+ const fracDL = (qname) => detailedLargerNode ? parseFloat(getText(`${detailedLargerBase}/${qname}`)) : null;
332
364
  return {
365
+ usedOpticalModel: getText("./bhrgtcom:usedOpticalModel"),
366
+ fraction63to75um: fracDL("bhrgtcom:fraction63to75um"),
367
+ fraction75to90um: fracDL("bhrgtcom:fraction75to90um"),
368
+ fraction90to106um: fracDL("bhrgtcom:fraction90to106um"),
369
+ fraction106to125um: fracDL("bhrgtcom:fraction106to125um"),
370
+ fraction125to150um: fracDL("bhrgtcom:fraction125to150um"),
371
+ fraction150to180um: fracDL("bhrgtcom:fraction150to180um"),
372
+ fraction180to212um: fracDL("bhrgtcom:fraction180to212um"),
373
+ fraction212to250um: fracDL("bhrgtcom:fraction212to250um"),
374
+ // eslint-disable-next-line @typescript-eslint/naming-convention
375
+ fraction4to5_6mm: fracDL("bhrgtcom:fraction4to5_6mm"),
376
+ // eslint-disable-next-line @typescript-eslint/naming-convention
377
+ fraction5_6to8mm: fracDL("bhrgtcom:fraction5_6to8mm"),
378
+ // eslint-disable-next-line @typescript-eslint/naming-convention
379
+ fraction8to11_2mm: fracDL("bhrgtcom:fraction8to11_2mm"),
380
+ // eslint-disable-next-line @typescript-eslint/naming-convention
381
+ fraction11_2to16mm: fracDL("bhrgtcom:fraction11_2to16mm"),
382
+ fraction16to20mm: fracDL("bhrgtcom:fraction16to20mm"),
383
+ // eslint-disable-next-line @typescript-eslint/naming-convention
384
+ fraction20to31_5mm: fracDL("bhrgtcom:fraction20to31_5mm"),
333
385
  determinationProcedure: getText("./bhrgtcom:determinationProcedure"),
334
386
  determinationMethod: getText("./bhrgtcom:determinationMethod"),
335
387
  fractionDistribution: getText("./bhrgtcom:fractionDistribution"),
@@ -520,6 +572,8 @@ function parseSettlementCharacteristicsDetermination(node, adapter, namespaces)
520
572
  constantHeight: parseBoolean(getSatText("./bhrgtcom:constantHeight")),
521
573
  specimenHeightAfterwards: parseFloat(getSatText("./bhrgtcom:specimenHeightAfterwards")),
522
574
  disturbanceInduced: parseBoolean(getSatText("./bhrgtcom:disturbanceInduced")),
575
+ maximumStressDifference: parseFloat(getSatText("./bhrgtcom:maximumStressDifference")),
576
+ maximumStrain: parseFloat(getSatText("./bhrgtcom:maximumStrain")),
523
577
  };
524
578
  }
525
579
  return {
@@ -553,6 +607,18 @@ function parseSaturatedPermeabilityDetermination(node, adapter, namespaces) {
553
607
  ? { dryVolumetricMassDensity, saturatedPermeability }
554
608
  : null;
555
609
  }, adapter, namespaces);
610
+ const saturatedPermeabilityAtSpecificLoad = extractArray(determinationNode, "./bhrgtcom:saturatedPermeabilityAtSpecificLoad", (_loadNode, getLoadText) => {
611
+ const load = parseFloat(getLoadText("./bhrgtcom:load"));
612
+ const saturatedPermeability = parseFloat(getLoadText("./bhrgtcom:saturatedPermeability"));
613
+ return load !== null || saturatedPermeability !== null
614
+ ? { load, saturatedPermeability }
615
+ : null;
616
+ }, adapter, namespaces);
617
+ const nsResolver = createNamespaceResolver(namespaces);
618
+ const materialIrregularity = adapter
619
+ .evaluateXPathAll(determinationNode, "./bhrgtcom:materialIrregularity", nsResolver)
620
+ .map((n) => n.textContent?.trim() ?? "")
621
+ .filter((s) => s !== "");
556
622
  return {
557
623
  determinationProcedure: getText("./bhrgtcom:determinationProcedure"),
558
624
  determinationMethod: getText("./bhrgtcom:determinationMethod"),
@@ -564,7 +630,11 @@ function parseSaturatedPermeabilityDetermination(node, adapter, namespaces) {
564
630
  waterDegassed: parseBoolean(getText("./bhrgtcom:waterDegassed")),
565
631
  temperature: parseFloat(getText("./bhrgtcom:temperature")),
566
632
  maximumGradient: parseFloat(getText("./bhrgtcom:maximumGradient")),
633
+ ringWaterRepellent: getText("./bhrgtcom:ringWaterRepellent"),
634
+ waterContentAfterwards: parseFloat(getText("./bhrgtcom:waterContentAfterwards")),
635
+ materialIrregularity,
567
636
  saturatedPermeabilityAtSpecificDensity,
637
+ saturatedPermeabilityAtSpecificLoad,
568
638
  };
569
639
  }
570
640
  /**
@@ -649,6 +719,7 @@ function parseShearStressChangeDuringLoadingDeterminations(node, adapter, namesp
649
719
  effectivePressure: parseFloat(getSatText("./bhrgtcom:effectivePressure")),
650
720
  skemptonBCoefficient: parseFloat(getSatText("./bhrgtcom:skemptonB_Coefficient")),
651
721
  disturbanceInduced: parseBoolean(getSatText("./bhrgtcom:disturbanceInduced")),
722
+ stressDifference: parseFloat(getSatText("./bhrgtcom:stressDifference")),
652
723
  };
653
724
  }
654
725
  // Parse consolidation stage (optional nested object with time-series data)
@@ -729,6 +800,8 @@ function parseMaximumUndrainedShearStrengthDetermination(node, adapter, namespac
729
800
  verticallyDetermined: parseBoolean(getText("./bhrgtcom:verticallyDetermined")),
730
801
  sampleMoistness: getText("./bhrgtcom:sampleMoistness"),
731
802
  maximumUndrainedShearStrength: parseFloat(getText("./bhrgtcom:maximumUndrainedShearStrength")),
803
+ lowestMaximumUndrainedShearStrength: parseFloat(getText("./bhrgtcom:lowestMaximumUndrainedShearStrength")),
804
+ highestMaximumUndrainedShearStrength: parseFloat(getText("./bhrgtcom:highestMaximumUndrainedShearStrength")),
732
805
  };
733
806
  }
734
807
  /**
@@ -1047,6 +1120,17 @@ export function processSampledIntervals(_value, context) {
1047
1120
  };
1048
1121
  interval.sampler = sampler;
1049
1122
  }
1123
+ // Check for nested coreRecovery element (rock coring)
1124
+ const coreRecoveryNode = adapter.evaluateXPath(intervalNode, "./bhrgtcom:coreRecovery", nsResolver);
1125
+ if (coreRecoveryNode) {
1126
+ const getCoreText = createXPathTextGetter(coreRecoveryNode, adapter, namespaces);
1127
+ interval.coreRecovery = {
1128
+ totalCoreRecovery: parseFloat(getCoreText("./bhrgtcom:totalCoreRecovery")),
1129
+ solidCoreRecovery: parseFloat(getCoreText("./bhrgtcom:solidCoreRecovery")),
1130
+ rockQualityDesignation: parseFloat(getCoreText("./bhrgtcom:rockQualityDesignation")),
1131
+ fieldDetermined: parseBoolean(getCoreText("./bhrgtcom:fieldDetermined")),
1132
+ };
1133
+ }
1050
1134
  intervals.push(interval);
1051
1135
  }
1052
1136
  return intervals;
@@ -1073,6 +1157,8 @@ export function processCompletedIntervals(_value, context) {
1073
1157
  beginDepth,
1074
1158
  endDepth,
1075
1159
  permanentCasingPresent: parseBoolean(getText("./bhrgtcom:permanentCasingPresent")),
1160
+ diameterPermanentCasing: parseFloat(getText("./bhrgtcom:diameterPermanentCasing")),
1161
+ materialPermanentCasing: getText("./bhrgtcom:materialPermanentCasing"),
1076
1162
  backfillMaterial: getText("./bhrgtcom:backfillMaterial"),
1077
1163
  backfillMaterialWashed: parseBoolean(getText("./bhrgtcom:backfillMaterialWashed")),
1078
1164
  backfillMaterialCertified: parseBoolean(getText("./bhrgtcom:backfillMaterialCertified")),
@@ -1080,6 +1166,78 @@ export function processCompletedIntervals(_value, context) {
1080
1166
  }
1081
1167
  return intervals;
1082
1168
  }
1169
+ /**
1170
+ * Process excavated layers from boring element
1171
+ *
1172
+ * Layers removed by excavation before/during boring.
1173
+ */
1174
+ export function processExcavatedLayers(_value, context) {
1175
+ const { element, adapter, namespaces } = context;
1176
+ const nsResolver = createNamespaceResolver(namespaces);
1177
+ const layerNodes = adapter.evaluateXPathAll(element, "./dsbhrgt:boring/bhrgtcom:excavatedLayer", nsResolver);
1178
+ const layers = [];
1179
+ for (const layerNode of layerNodes) {
1180
+ const getText = createXPathTextGetter(layerNode, adapter, namespaces);
1181
+ const upperBoundary = parseFloat(getText("./bhrgtcom:upperBoundary"));
1182
+ const lowerBoundary = parseFloat(getText("./bhrgtcom:lowerBoundary"));
1183
+ if (upperBoundary === null || lowerBoundary === null) {
1184
+ continue;
1185
+ }
1186
+ layers.push({
1187
+ upperBoundary,
1188
+ lowerBoundary,
1189
+ excavatedMaterial: getText("./bhrgtcom:excavatedMaterial"),
1190
+ });
1191
+ }
1192
+ return layers;
1193
+ }
1194
+ /**
1195
+ * Process boring velocity profile from boring element
1196
+ *
1197
+ * Series of (elapsed time, depth) points describing drilling progress.
1198
+ */
1199
+ export function processBoringVelocity(_value, context) {
1200
+ const { element, adapter, namespaces } = context;
1201
+ const nsResolver = createNamespaceResolver(namespaces);
1202
+ const velocityNodes = adapter.evaluateXPathAll(element, "./dsbhrgt:boring/bhrgtcom:boringVelocity", nsResolver);
1203
+ return velocityNodes.map((node) => {
1204
+ const getText = createXPathTextGetter(node, adapter, namespaces);
1205
+ return {
1206
+ elapsedTime: parseFloat(getText("./bhrgtcom:elapsedTime")),
1207
+ depth: parseFloat(getText("./bhrgtcom:depth")),
1208
+ };
1209
+ });
1210
+ }
1211
+ /**
1212
+ * Process fluid mud layer from document element
1213
+ *
1214
+ * Single optional fluid mud (slib) layer recorded at the borehole.
1215
+ */
1216
+ export function processFluidMudLayer(_value, context) {
1217
+ const { element, adapter, namespaces } = context;
1218
+ const nsResolver = createNamespaceResolver(namespaces);
1219
+ const node = adapter.evaluateXPath(element, "./dsbhrgt:fluidMudLayer", nsResolver);
1220
+ if (!node) {
1221
+ return undefined;
1222
+ }
1223
+ const getText = createXPathTextGetter(node, adapter, namespaces);
1224
+ return {
1225
+ thickness: parseFloat(getText("./bhrgtcom:thickness")),
1226
+ colour: getText("./bhrgtcom:colour"),
1227
+ upperBoundaryPositioningMethod: getText("./bhrgtcom:upperBoundaryPositioningMethod"),
1228
+ lowerBoundaryPositioningMethod: getText("./bhrgtcom:lowerBoundaryPositioningMethod"),
1229
+ };
1230
+ }
1231
+ /**
1232
+ * Resolve an organisation identifier (KvK number, or European company
1233
+ * registration number as a fallback) from an organisation element.
1234
+ */
1235
+ export function resolveOrganisationId(_value, context) {
1236
+ const { node, adapter, namespaces } = context;
1237
+ const getText = createXPathTextGetter(node, adapter, namespaces);
1238
+ return (getText("./brocom:chamberOfCommerceNumber") ??
1239
+ getText("./brocom:europeanCompanyRegistrationNumber"));
1240
+ }
1083
1241
  /**
1084
1242
  * Process not described intervals from boreholeSampleDescription element
1085
1243
  *
@@ -1107,34 +1265,43 @@ export function processNotDescribedIntervals(_value, context) {
1107
1265
  return intervals;
1108
1266
  }
1109
1267
  /**
1110
- * Process registration history from document element
1268
+ * Process post-sedimentary discontinuities from the descriptive borehole log
1111
1269
  *
1112
- * Extracts BRO registration history information.
1270
+ * Fractures/fault planes crossing described intervals (e.g. in rock).
1113
1271
  */
1114
- export function processRegistrationHistory(_value, context) {
1272
+ export function processPostSedimentaryDiscontinuities(_value, context) {
1115
1273
  const { element, adapter, namespaces } = context;
1116
1274
  const nsResolver = createNamespaceResolver(namespaces);
1117
- // Find registrationHistory element (in default namespace dsbhrgt)
1118
- const historyNode = adapter.evaluateXPath(element, "./dsbhrgt:registrationHistory", nsResolver);
1119
- if (!historyNode) {
1120
- return null;
1275
+ const nodes = adapter.evaluateXPathAll(element, "./dsbhrgt:boreholeSampleDescription/bhrgtcom:descriptiveBoreholeLog/bhrgtcom:postSedimentaryDiscontinuity", nsResolver);
1276
+ const out = [];
1277
+ for (const node of nodes) {
1278
+ const getText = createXPathTextGetter(node, adapter, namespaces);
1279
+ const beginDepth = parseFloat(getText("./bhrgtcom:beginDepth"));
1280
+ const endDepth = parseFloat(getText("./bhrgtcom:endDepth"));
1281
+ if (beginDepth === null || endDepth === null) {
1282
+ continue;
1283
+ }
1284
+ out.push({
1285
+ beginDepth,
1286
+ endDepth,
1287
+ inRock: getText("./bhrgtcom:inRock"),
1288
+ discontinuityType: getText("./bhrgtcom:discontinuityType"),
1289
+ compositeDiscontinuity: getText("./bhrgtcom:compositeDiscontinuity"),
1290
+ spacing: parseFloat(getText("./bhrgtcom:spacing")),
1291
+ smooth: getText("./bhrgtcom:smooth"),
1292
+ apertureClass: getText("./bhrgtcom:apertureClass"),
1293
+ infillMaterial: getText("./bhrgtcom:infillMaterial"),
1294
+ });
1121
1295
  }
1122
- const getText = createXPathTextGetter(historyNode, adapter, namespaces);
1123
- // Parse dates
1124
- const objectRegistrationTimeStr = getText("./brocom:objectRegistrationTime");
1125
- const registrationCompletionTimeStr = getText("./brocom:registrationCompletionTime");
1126
- const latestCorrectionTimeStr = getText("./brocom:latestCorrectionTime");
1127
- return {
1128
- objectRegistrationTime: parseDate(objectRegistrationTimeStr),
1129
- registrationStatus: getText("./brocom:registrationStatus"),
1130
- registrationCompletionTime: parseDate(registrationCompletionTimeStr),
1131
- latestCorrectionTime: parseDate(latestCorrectionTimeStr),
1132
- corrected: parseBoolean(getText("./brocom:corrected")),
1133
- underReview: parseBoolean(getText("./brocom:underReview")),
1134
- deregistered: parseBoolean(getText("./brocom:deregistered")),
1135
- reregistered: parseBoolean(getText("./brocom:reregistered")),
1136
- };
1296
+ return out;
1137
1297
  }
1298
+ /**
1299
+ * Process registration history from document element
1300
+ *
1301
+ * Extracts BRO registration history information.
1302
+ */
1303
+ // Registration history is parsed by the shared processRegistrationHistory in
1304
+ // bore-resolver-utils (identical across CPT/BHR-GT/BHR-G).
1138
1305
  /**
1139
1306
  * Process report history from document element
1140
1307
  *