@bedrock-engineer/bro-xml-parser 0.1.7 → 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 (71) hide show
  1. package/dist/adapters/browser-adapter.d.ts.map +1 -1
  2. package/dist/adapters/browser-adapter.js +6 -2
  3. package/dist/adapters/browser-adapter.js.map +1 -1
  4. package/dist/adapters/node-adapter.d.ts.map +1 -1
  5. package/dist/adapters/node-adapter.js +6 -2
  6. package/dist/adapters/node-adapter.js.map +1 -1
  7. package/dist/browser.d.ts +2 -2
  8. package/dist/browser.d.ts.map +1 -1
  9. package/dist/browser.js +1 -1
  10. package/dist/browser.js.map +1 -1
  11. package/dist/colors.d.ts +18 -8
  12. package/dist/colors.d.ts.map +1 -1
  13. package/dist/colors.js +1 -1
  14. package/dist/colors.js.map +1 -1
  15. package/dist/index.d.ts +1 -1
  16. package/dist/index.js +1 -1
  17. package/dist/node.d.ts +2 -2
  18. package/dist/node.d.ts.map +1 -1
  19. package/dist/node.js +1 -1
  20. package/dist/node.js.map +1 -1
  21. package/dist/parser.d.ts +2 -2
  22. package/dist/parser.js +2 -2
  23. package/dist/resolvers/bhrg-resolvers.d.ts +3 -18
  24. package/dist/resolvers/bhrg-resolvers.d.ts.map +1 -1
  25. package/dist/resolvers/bhrg-resolvers.js +296 -43
  26. package/dist/resolvers/bhrg-resolvers.js.map +1 -1
  27. package/dist/resolvers/bore-resolver-utils.d.ts +21 -2
  28. package/dist/resolvers/bore-resolver-utils.d.ts.map +1 -1
  29. package/dist/resolvers/bore-resolver-utils.js +73 -23
  30. package/dist/resolvers/bore-resolver-utils.js.map +1 -1
  31. package/dist/resolvers/bore-resolvers.d.ts +35 -18
  32. package/dist/resolvers/bore-resolvers.d.ts.map +1 -1
  33. package/dist/resolvers/bore-resolvers.js +428 -159
  34. package/dist/resolvers/bore-resolvers.js.map +1 -1
  35. package/dist/resolvers/cpt-resolvers.d.ts +1 -8
  36. package/dist/resolvers/cpt-resolvers.d.ts.map +1 -1
  37. package/dist/resolvers/cpt-resolvers.js +2 -29
  38. package/dist/resolvers/cpt-resolvers.js.map +1 -1
  39. package/dist/resolvers/gml-resolvers.d.ts.map +1 -1
  40. package/dist/resolvers/gml-resolvers.js +11 -1
  41. package/dist/resolvers/gml-resolvers.js.map +1 -1
  42. package/dist/resolvers/measurement-resolver.d.ts.map +1 -1
  43. package/dist/resolvers/measurement-resolver.js +6 -6
  44. package/dist/resolvers/measurement-resolver.js.map +1 -1
  45. package/dist/resolvers/type-resolvers.d.ts +22 -13
  46. package/dist/resolvers/type-resolvers.d.ts.map +1 -1
  47. package/dist/resolvers/type-resolvers.js +55 -43
  48. package/dist/resolvers/type-resolvers.js.map +1 -1
  49. package/dist/resolvers.d.ts +5 -4
  50. package/dist/resolvers.d.ts.map +1 -1
  51. package/dist/resolvers.js +6 -4
  52. package/dist/resolvers.js.map +1 -1
  53. package/dist/schema-presets.js +3 -3
  54. package/dist/schema-presets.js.map +1 -1
  55. package/dist/schemas/bhrg-schema.d.ts.map +1 -1
  56. package/dist/schemas/bhrg-schema.js +39 -14
  57. package/dist/schemas/bhrg-schema.js.map +1 -1
  58. package/dist/schemas/bore-schema.d.ts.map +1 -1
  59. package/dist/schemas/bore-schema.js +74 -14
  60. package/dist/schemas/bore-schema.js.map +1 -1
  61. package/dist/schemas/common-fields.d.ts +16 -0
  62. package/dist/schemas/common-fields.d.ts.map +1 -0
  63. package/dist/schemas/common-fields.js +31 -0
  64. package/dist/schemas/common-fields.js.map +1 -0
  65. package/dist/schemas/cpt-schema.d.ts.map +1 -1
  66. package/dist/schemas/cpt-schema.js +25 -14
  67. package/dist/schemas/cpt-schema.js.map +1 -1
  68. package/dist/types/index.d.ts +559 -61
  69. package/dist/types/index.d.ts.map +1 -1
  70. package/dist/types/index.js.map +1 -1
  71. package/package.json +15 -5
@@ -4,9 +4,9 @@
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
- import { parseBoolean, parseFloat, parseJaNee } from "./type-resolvers.js";
9
+ import { parseBoolean, parseFloat, parseDate } from "./type-resolvers.js";
10
10
  /**
11
11
  * Parse dispersedInhomogeneity field
12
12
  * Special case: 'geen' (none) means false, any other value means true
@@ -35,118 +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,
92
+ },
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,
45
107
  },
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: parseJaNee,
60
- },
61
- {
62
- xpath: "./bhrgtcom:slant",
63
- key: "slant",
64
- transform: parseJaNee,
65
- },
66
- // Soil-level fields (inside <soil>)
67
- {
68
- xpath: "./bhrgtcom:soil/bhrgtcom:tertiaryConstituent",
69
- key: "tertiaryConstituent",
70
- },
71
- {
72
- xpath: "./bhrgtcom:soil/bhrgtcom:colour",
73
- key: "color",
74
- omitIfEmpty: true,
75
- },
76
- {
77
- xpath: "./bhrgtcom:soil/bhrgtcom:dispersedInhomogeneity",
78
- key: "dispersedInhomogeneity",
79
- transform: parseDispersedInhomogeneity,
80
- },
81
- {
82
- xpath: "./bhrgtcom:soil/bhrgtcom:organicMatterContentClass",
83
- key: "organicMatterContentClass",
84
- },
85
- {
86
- xpath: "./bhrgtcom:soil/bhrgtcom:carbonateContentClass",
87
- key: "carbonateContentClass",
88
- },
89
- {
90
- xpath: "./bhrgtcom:soil/bhrgtcom:sandMedianClass",
91
- key: "sandMedianClass",
92
- },
93
- // Layer structure properties (at layer level)
94
- {
95
- xpath: "./bhrgtcom:bedded",
96
- key: "bedded",
97
- transform: parseJaNee,
98
- },
99
- {
100
- xpath: "./bhrgtcom:internalStructureIntact",
101
- key: "internalStructureIntact",
102
- transform: parseJaNee,
103
- },
104
- // Soil structure properties (inside <soil>)
105
- {
106
- xpath: "./bhrgtcom:soil/bhrgtcom:mixed",
107
- key: "mixed",
108
- transform: parseJaNee,
109
- },
110
- {
111
- xpath: "./bhrgtcom:soil/bhrgtcom:mottled",
112
- key: "mottled",
113
- transform: parseJaNee,
114
- },
115
- // Fine-grained soil consistency
116
- {
117
- xpath: "./bhrgtcom:soil/bhrgtcom:fineSoilConsistency",
118
- key: "fineSoilConsistency",
119
- },
120
- // Organic soil properties
121
- {
122
- xpath: "./bhrgtcom:soil/bhrgtcom:organicSoilConsistency",
123
- key: "organicSoilConsistency",
124
- },
125
- {
126
- xpath: "./bhrgtcom:soil/bhrgtcom:organicSoilTexture",
127
- key: "organicSoilTexture",
128
- },
129
- {
130
- xpath: "./bhrgtcom:soil/bhrgtcom:peatTensileStrength",
131
- key: "peatTensileStrength",
132
- },
133
- ],
134
- // Extract nested grainshape object
135
- postProcess: (layerNode, layer, adapter, namespaces) => {
136
- const nsResolver = createNamespaceResolver(namespaces);
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
+ };
137
203
  const grainshapeNode = adapter.evaluateXPath(layerNode, "./bhrgtcom:soil/bhrgtcom:grainshape", nsResolver);
138
204
  if (grainshapeNode) {
139
- const getText = createXPathTextGetter(grainshapeNode, adapter, namespaces);
205
+ const getGrain = createXPathTextGetter(grainshapeNode, adapter, namespaces);
140
206
  const grainshape = {
141
- sizeFraction: getText("./bhrgtcom:sizeFraction"),
142
- angularity: getText("./bhrgtcom:angularity"),
143
- sphericity: getText("./bhrgtcom:sphericity"),
144
- roughness: getText("./bhrgtcom:roughness"),
207
+ sizeFraction: getGrain("./bhrgtcom:sizeFraction"),
208
+ angularity: getGrain("./bhrgtcom:angularity"),
209
+ sphericity: getGrain("./bhrgtcom:sphericity"),
210
+ roughness: getGrain("./bhrgtcom:roughness"),
145
211
  };
146
- layer.grainshape = grainshape;
212
+ soilLayer.grainshape = grainshape;
147
213
  }
148
- },
149
- });
214
+ layers.push(soilLayer);
215
+ }
216
+ return layers;
217
+ }
150
218
  /**
151
219
  * Empty structure constants for determinations
152
220
  */
@@ -156,6 +224,7 @@ const EMPTY_CONSISTENCY_LIMITS = {
156
224
  fractionLarger500um: null,
157
225
  usedMedium: null,
158
226
  performanceIrregularity: null,
227
+ conusType: null,
159
228
  liquidLimit: null,
160
229
  plasticLimit: null,
161
230
  plasticityIndex: null,
@@ -185,7 +254,11 @@ const EMPTY_SATURATED_PERMEABILITY = {
185
254
  waterDegassed: null,
186
255
  temperature: null,
187
256
  maximumGradient: null,
257
+ ringWaterRepellent: null,
258
+ waterContentAfterwards: null,
259
+ materialIrregularity: [],
188
260
  saturatedPermeabilityAtSpecificDensity: [],
261
+ saturatedPermeabilityAtSpecificLoad: [],
189
262
  };
190
263
  /**
191
264
  * Parse water content determination from XML node
@@ -235,6 +308,7 @@ function parseOrganicMatterContentDetermination(node, adapter, namespaces) {
235
308
  determinationProcedure: getText("./bhrgtcom:determinationProcedure"),
236
309
  determinationMethod: getText("./bhrgtcom:determinationMethod"),
237
310
  removedMaterial: getText("./bhrgtcom:removedMaterial"),
311
+ lutumCorrectionApplied: parseBoolean(getText("./bhrgtcom:lutumCorrectionApplied")),
238
312
  organicMatterContent: parseFloat(getText("./bhrgtcom:organicMatterContent")),
239
313
  };
240
314
  }
@@ -259,6 +333,7 @@ function parseVolumetricMassDensityOfSolidsDetermination(node, adapter, namespac
259
333
  determinationProcedure: getText("./bhrgtcom:determinationProcedure"),
260
334
  determinationMethod: getText("./bhrgtcom:determinationMethod"),
261
335
  liquidUsed: getText("./bhrgtcom:usedMedium"),
336
+ sampleContainerVolume: getText("./bhrgtcom:sampleContainerVolume"),
262
337
  volumetricMassDensityOfSolids: parseFloat(getText("./bhrgtcom:volumetricMassDensitySolids")),
263
338
  };
264
339
  }
@@ -274,7 +349,39 @@ function parseParticleSizeDistributionDetermination(node, adapter, namespaces) {
274
349
  const standardNode = basicDistNode
275
350
  ? findChildElement(basicDistNode, "./bhrgtcom:standardDistributionFractionLarger63um", adapter, namespaces)
276
351
  : null;
352
+ // Coarser <63μm resolution — an alternative to detailedDistributionFractionSmaller63um.
353
+ // A sample carries one or the other; the shared 0-2/32-50/50-63 buckets fall back to this.
354
+ const standardSmallerNode = basicDistNode
355
+ ? findChildElement(basicDistNode, "./bhrgtcom:standardDistributionFractionSmaller63um", adapter, namespaces)
356
+ : null;
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;
277
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"),
278
385
  determinationProcedure: getText("./bhrgtcom:determinationProcedure"),
279
386
  determinationMethod: getText("./bhrgtcom:determinationMethod"),
280
387
  fractionDistribution: getText("./bhrgtcom:fractionDistribution"),
@@ -292,6 +399,11 @@ function parseParticleSizeDistributionDetermination(node, adapter, namespaces) {
292
399
  // Detailed distribution < 63μm
293
400
  fraction0to2um: detailedNode
294
401
  ? parseFloat(getText("./bhrgtcom:basicParticleSizeDistribution/bhrgtcom:detailedDistributionFractionSmaller63um/bhrgtcom:fraction0to2um"))
402
+ : standardSmallerNode
403
+ ? parseFloat(getText(`${standardSmallerBase}/bhrgtcom:fraction0to2um`))
404
+ : null,
405
+ fraction2to32um: standardSmallerNode
406
+ ? parseFloat(getText(`${standardSmallerBase}/bhrgtcom:fraction2to32um`))
295
407
  : null,
296
408
  fraction2to4um: detailedNode
297
409
  ? parseFloat(getText("./bhrgtcom:basicParticleSizeDistribution/bhrgtcom:detailedDistributionFractionSmaller63um/bhrgtcom:fraction2to4um"))
@@ -307,10 +419,14 @@ function parseParticleSizeDistributionDetermination(node, adapter, namespaces) {
307
419
  : null,
308
420
  fraction32to50um: detailedNode
309
421
  ? parseFloat(getText("./bhrgtcom:basicParticleSizeDistribution/bhrgtcom:detailedDistributionFractionSmaller63um/bhrgtcom:fraction32to50um"))
310
- : null,
422
+ : standardSmallerNode
423
+ ? parseFloat(getText(`${standardSmallerBase}/bhrgtcom:fraction32to50um`))
424
+ : null,
311
425
  fraction50to63um: detailedNode
312
426
  ? parseFloat(getText("./bhrgtcom:basicParticleSizeDistribution/bhrgtcom:detailedDistributionFractionSmaller63um/bhrgtcom:fraction50to63um"))
313
- : null,
427
+ : standardSmallerNode
428
+ ? parseFloat(getText(`${standardSmallerBase}/bhrgtcom:fraction50to63um`))
429
+ : null,
314
430
  // Standard distribution > 63μm
315
431
  fraction63to90um: standardNode
316
432
  ? parseFloat(getText("./bhrgtcom:basicParticleSizeDistribution/bhrgtcom:standardDistributionFractionLarger63um/bhrgtcom:fraction63to90um"))
@@ -376,8 +492,9 @@ function parseConsistencyLimitsDetermination(node, adapter, namespaces) {
376
492
  const plasticityAtSpecificWaterContent = extractArray(determinationNode, "./bhrgtcom:plasticityAtSpecificWaterContent", (_pNode, getPlasticityText) => {
377
493
  const waterContent = parseFloat(getPlasticityText("./bhrgtcom:waterContent"));
378
494
  const numberOfFalls = parseInt(getPlasticityText("./bhrgtcom:numberOfFalls") ?? "0", 10);
495
+ const penetrationDepth = parseFloat(getPlasticityText("./bhrgtcom:penetrationDepth"));
379
496
  return waterContent !== null && !isNaN(numberOfFalls)
380
- ? { waterContent, numberOfFalls }
497
+ ? { waterContent, numberOfFalls, penetrationDepth }
381
498
  : null;
382
499
  }, adapter, namespaces);
383
500
  return {
@@ -386,6 +503,7 @@ function parseConsistencyLimitsDetermination(node, adapter, namespaces) {
386
503
  fractionLarger500um: parseFloat(getText("./bhrgtcom:fractionLarger500um")),
387
504
  usedMedium: getText("./bhrgtcom:usedMedium"),
388
505
  performanceIrregularity: getText("./bhrgtcom:performanceIrregularity"),
506
+ conusType: getText("./bhrgtcom:conusType"),
389
507
  liquidLimit: parseFloat(getText("./bhrgtcom:liquidLimit")),
390
508
  plasticLimit: parseFloat(getText("./bhrgtcom:plasticLimit")),
391
509
  plasticityIndex: parseFloat(getText("./bhrgtcom:plasticityIndex")),
@@ -411,6 +529,26 @@ function parseSettlementCharacteristicsDetermination(node, adapter, namespaces)
411
529
  ? { time, height }
412
530
  : null;
413
531
  });
532
+ // Optional alternative time-series: stress/strain during settlement (5 columns)
533
+ const stressChangeNode = findChildElement(stepNode, "./bhrgtcom:stressChangeDuringSettlement/bhrgtcom:values", adapter, namespaces);
534
+ const stressChangeDuringSettlement = stressChangeNode
535
+ ? parseCSVRows(stressChangeNode.textContent, (columns) => {
536
+ if (columns.length < 5) {
537
+ return null;
538
+ }
539
+ const elapsedTime = parseFloat(columns[0]);
540
+ const verticalStrain = parseFloat(columns[1]);
541
+ return elapsedTime !== null && verticalStrain !== null
542
+ ? {
543
+ elapsedTime,
544
+ verticalStrain,
545
+ excessPoreWaterPressure: parseFloat(columns[2]),
546
+ verticalEffectiveStress: parseFloat(columns[3]),
547
+ horizontalEffectiveStress: parseFloat(columns[4]),
548
+ }
549
+ : null;
550
+ })
551
+ : undefined;
414
552
  return {
415
553
  stepNumber,
416
554
  wetPerformed: parseBoolean(getStepText("./bhrgtcom:wetPerformed")),
@@ -419,8 +557,25 @@ function parseSettlementCharacteristicsDetermination(node, adapter, namespaces)
419
557
  stepType: getStepText("./bhrgtcom:stepType"),
420
558
  verticalStress: parseFloat(getStepText("./bhrgtcom:verticalStress")),
421
559
  heightChangeDuringSettlement,
560
+ ...(stressChangeDuringSettlement ? { stressChangeDuringSettlement } : {}),
422
561
  };
423
562
  }, adapter, namespaces);
563
+ // Optional saturation stage performed before compression
564
+ const saturationNode = findChildElement(determinationNode, "./bhrgtcom:saturationStageAtCompression", adapter, namespaces);
565
+ let saturationStageAtCompression;
566
+ if (saturationNode) {
567
+ const getSatText = createXPathTextGetter(saturationNode, adapter, namespaces);
568
+ saturationStageAtCompression = {
569
+ porousDiscWet: parseBoolean(getSatText("./bhrgtcom:porousDiscWet")),
570
+ usedMedium: getSatText("./bhrgtcom:usedMedium"),
571
+ backPressure: parseFloat(getSatText("./bhrgtcom:backPressure")),
572
+ constantHeight: parseBoolean(getSatText("./bhrgtcom:constantHeight")),
573
+ specimenHeightAfterwards: parseFloat(getSatText("./bhrgtcom:specimenHeightAfterwards")),
574
+ disturbanceInduced: parseBoolean(getSatText("./bhrgtcom:disturbanceInduced")),
575
+ maximumStressDifference: parseFloat(getSatText("./bhrgtcom:maximumStressDifference")),
576
+ maximumStrain: parseFloat(getSatText("./bhrgtcom:maximumStrain")),
577
+ };
578
+ }
424
579
  return {
425
580
  determinationProcedure: getText("./bhrgtcom:determinationProcedure"),
426
581
  determinationMethod: getText("./bhrgtcom:determinationMethod"),
@@ -432,6 +587,7 @@ function parseSettlementCharacteristicsDetermination(node, adapter, namespaces)
432
587
  apparatusDeformationApplied: parseBoolean(getText("./bhrgtcom:apparatusDeformationApplied")),
433
588
  bearingFrictionCorrectionApplied: parseBoolean(getText("./bhrgtcom:bearingFrictionCorrectionApplied")),
434
589
  irregularResult: parseBoolean(getText("./bhrgtcom:irregularResult")),
590
+ ...(saturationStageAtCompression ? { saturationStageAtCompression } : {}),
435
591
  determinationSteps,
436
592
  };
437
593
  }
@@ -451,6 +607,18 @@ function parseSaturatedPermeabilityDetermination(node, adapter, namespaces) {
451
607
  ? { dryVolumetricMassDensity, saturatedPermeability }
452
608
  : null;
453
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 !== "");
454
622
  return {
455
623
  determinationProcedure: getText("./bhrgtcom:determinationProcedure"),
456
624
  determinationMethod: getText("./bhrgtcom:determinationMethod"),
@@ -462,7 +630,11 @@ function parseSaturatedPermeabilityDetermination(node, adapter, namespaces) {
462
630
  waterDegassed: parseBoolean(getText("./bhrgtcom:waterDegassed")),
463
631
  temperature: parseFloat(getText("./bhrgtcom:temperature")),
464
632
  maximumGradient: parseFloat(getText("./bhrgtcom:maximumGradient")),
633
+ ringWaterRepellent: getText("./bhrgtcom:ringWaterRepellent"),
634
+ waterContentAfterwards: parseFloat(getText("./bhrgtcom:waterContentAfterwards")),
635
+ materialIrregularity,
465
636
  saturatedPermeabilityAtSpecificDensity,
637
+ saturatedPermeabilityAtSpecificLoad,
466
638
  };
467
639
  }
468
640
  /**
@@ -504,6 +676,15 @@ function parseShearStressChangeDuringLoadingDeterminations(node, adapter, namesp
504
676
  cellDeformationApplied: parseBoolean(getText("./bhrgtcom:cellDeformationApplied")),
505
677
  stopCriterion: getText("./bhrgtcom:stopCriterion"),
506
678
  };
679
+ // Parse made specimen (optional nested object; only for remoulded specimens)
680
+ const madeSpecimenNode = findChildElement(actualDeterminationNode, "./bhrgtcom:madeSpecimenForLoading", adapter, namespaces);
681
+ if (madeSpecimenNode) {
682
+ const getMadeText = createXPathTextGetter(madeSpecimenNode, adapter, namespaces);
683
+ determination.madeSpecimenForLoading = {
684
+ makingMethod: getMadeText("./bhrgtcom:makingMethod"),
685
+ dryVolumetricMassDensity: parseFloat(getMadeText("./bhrgtcom:dryVolumetricMassDensity")),
686
+ };
687
+ }
507
688
  // Parse membrane correction (optional nested object)
508
689
  const membraneCorrectionNode = findChildElement(actualDeterminationNode, "./bhrgtcom:membraneCorrection", adapter, namespaces);
509
690
  if (membraneCorrectionNode) {
@@ -538,6 +719,7 @@ function parseShearStressChangeDuringLoadingDeterminations(node, adapter, namesp
538
719
  effectivePressure: parseFloat(getSatText("./bhrgtcom:effectivePressure")),
539
720
  skemptonBCoefficient: parseFloat(getSatText("./bhrgtcom:skemptonB_Coefficient")),
540
721
  disturbanceInduced: parseBoolean(getSatText("./bhrgtcom:disturbanceInduced")),
722
+ stressDifference: parseFloat(getSatText("./bhrgtcom:stressDifference")),
541
723
  };
542
724
  }
543
725
  // Parse consolidation stage (optional nested object with time-series data)
@@ -618,6 +800,8 @@ function parseMaximumUndrainedShearStrengthDetermination(node, adapter, namespac
618
800
  verticallyDetermined: parseBoolean(getText("./bhrgtcom:verticallyDetermined")),
619
801
  sampleMoistness: getText("./bhrgtcom:sampleMoistness"),
620
802
  maximumUndrainedShearStrength: parseFloat(getText("./bhrgtcom:maximumUndrainedShearStrength")),
803
+ lowestMaximumUndrainedShearStrength: parseFloat(getText("./bhrgtcom:lowestMaximumUndrainedShearStrength")),
804
+ highestMaximumUndrainedShearStrength: parseFloat(getText("./bhrgtcom:highestMaximumUndrainedShearStrength")),
621
805
  };
622
806
  }
623
807
  /**
@@ -798,7 +982,7 @@ const DETERMINATION_CONFIGS = [
798
982
  * Extracts laboratory analysis data including all investigated intervals
799
983
  * and their determination results.
800
984
  *
801
- * @param value - Not used (we work with the node directly)
985
+ * @param _value - Not used (we work with the node directly)
802
986
  * @param context - Resolver context containing the XML node and adapter
803
987
  * @returns BoreholeSampleAnalysis object or undefined if not present
804
988
  */
@@ -813,16 +997,7 @@ export function processBoreholeSampleAnalysis(_value, context) {
813
997
  // Extract analysis metadata
814
998
  const analysisReportDateNode = adapter.evaluateXPath(analysisNode, "./bhrgtcom:analysisReportDate", nsResolver);
815
999
  const analysisProcedureNode = adapter.evaluateXPath(analysisNode, "./bhrgtcom:analysisProcedure", nsResolver);
816
- let analysisReportDate = null;
817
- if (analysisReportDateNode?.textContent) {
818
- const dateStr = analysisReportDateNode.textContent.trim();
819
- try {
820
- analysisReportDate = new Date(dateStr);
821
- }
822
- catch {
823
- analysisReportDate = null;
824
- }
825
- }
1000
+ const analysisReportDate = parseDate(analysisReportDateNode?.textContent);
826
1001
  const analysisProcedure = analysisProcedureNode?.textContent?.trim() ?? null;
827
1002
  // Find all investigated intervals
828
1003
  const intervalNodes = adapter.evaluateXPathAll(analysisNode, "./bhrgtcom:investigatedInterval", nsResolver);
@@ -924,7 +1099,7 @@ export function processSampledIntervals(_value, context) {
924
1099
  preTreatment: getText("./bhrgtcom:preTreatment"),
925
1100
  samplingMethod: getText("./bhrgtcom:samplingMethod"),
926
1101
  samplingQuality: getText("./bhrgtcom:samplingQuality"),
927
- orientatedSampled: parseJaNee(getText("./bhrgtcom:orientatedSampled")),
1102
+ orientatedSampled: parseBoolean(getText("./bhrgtcom:orientatedSampled")),
928
1103
  };
929
1104
  // Check for nested sampler element
930
1105
  const samplerNode = adapter.evaluateXPath(intervalNode, "./bhrgtcom:sampler", nsResolver);
@@ -936,15 +1111,26 @@ export function processSampledIntervals(_value, context) {
936
1111
  sampleContainerLength: parseFloat(getSamplerText("./bhrgtcom:sampleContainerLength")),
937
1112
  cuttingShoeInsideDiameter: parseFloat(getSamplerText("./bhrgtcom:cuttingShoeInsideDiameter")),
938
1113
  cuttingShoeOutsideDiameter: parseFloat(getSamplerText("./bhrgtcom:cuttingShoeOutsideDiameter")),
939
- stockingUsed: parseJaNee(getSamplerText("./bhrgtcom:stockingUsed")),
940
- rightAngledCuttingShoe: parseJaNee(getSamplerText("./bhrgtcom:rightAngledCuttingShoe")),
1114
+ stockingUsed: parseBoolean(getSamplerText("./bhrgtcom:stockingUsed")),
1115
+ rightAngledCuttingShoe: parseBoolean(getSamplerText("./bhrgtcom:rightAngledCuttingShoe")),
941
1116
  taperAngle: parseFloat(getSamplerText("./bhrgtcom:taperAngle")),
942
- lubricationFluidUsed: parseJaNee(getSamplerText("./bhrgtcom:lubricationFluidUsed")),
943
- coreCatcherPresent: parseJaNee(getSamplerText("./bhrgtcom:coreCatcherPresent")),
944
- pistonPresent: parseJaNee(getSamplerText("./bhrgtcom:pistonPresent")),
1117
+ lubricationFluidUsed: parseBoolean(getSamplerText("./bhrgtcom:lubricationFluidUsed")),
1118
+ coreCatcherPresent: parseBoolean(getSamplerText("./bhrgtcom:coreCatcherPresent")),
1119
+ pistonPresent: parseBoolean(getSamplerText("./bhrgtcom:pistonPresent")),
945
1120
  };
946
1121
  interval.sampler = sampler;
947
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
+ }
948
1134
  intervals.push(interval);
949
1135
  }
950
1136
  return intervals;
@@ -970,14 +1156,88 @@ export function processCompletedIntervals(_value, context) {
970
1156
  intervals.push({
971
1157
  beginDepth,
972
1158
  endDepth,
973
- permanentCasingPresent: parseJaNee(getText("./bhrgtcom:permanentCasingPresent")),
1159
+ permanentCasingPresent: parseBoolean(getText("./bhrgtcom:permanentCasingPresent")),
1160
+ diameterPermanentCasing: parseFloat(getText("./bhrgtcom:diameterPermanentCasing")),
1161
+ materialPermanentCasing: getText("./bhrgtcom:materialPermanentCasing"),
974
1162
  backfillMaterial: getText("./bhrgtcom:backfillMaterial"),
975
- backfillMaterialWashed: parseJaNee(getText("./bhrgtcom:backfillMaterialWashed")),
976
- backfillMaterialCertified: parseJaNee(getText("./bhrgtcom:backfillMaterialCertified")),
1163
+ backfillMaterialWashed: parseBoolean(getText("./bhrgtcom:backfillMaterialWashed")),
1164
+ backfillMaterialCertified: parseBoolean(getText("./bhrgtcom:backfillMaterialCertified")),
977
1165
  });
978
1166
  }
979
1167
  return intervals;
980
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
+ }
981
1241
  /**
982
1242
  * Process not described intervals from boreholeSampleDescription element
983
1243
  *
@@ -1005,34 +1265,43 @@ export function processNotDescribedIntervals(_value, context) {
1005
1265
  return intervals;
1006
1266
  }
1007
1267
  /**
1008
- * Process registration history from document element
1268
+ * Process post-sedimentary discontinuities from the descriptive borehole log
1009
1269
  *
1010
- * Extracts BRO registration history information.
1270
+ * Fractures/fault planes crossing described intervals (e.g. in rock).
1011
1271
  */
1012
- export function processRegistrationHistory(_value, context) {
1272
+ export function processPostSedimentaryDiscontinuities(_value, context) {
1013
1273
  const { element, adapter, namespaces } = context;
1014
1274
  const nsResolver = createNamespaceResolver(namespaces);
1015
- // Find registrationHistory element (in default namespace dsbhrgt)
1016
- const historyNode = adapter.evaluateXPath(element, "./dsbhrgt:registrationHistory", nsResolver);
1017
- if (!historyNode) {
1018
- 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
+ });
1019
1295
  }
1020
- const getText = createXPathTextGetter(historyNode, adapter, namespaces);
1021
- // Parse dates
1022
- const objectRegistrationTimeStr = getText("./brocom:objectRegistrationTime");
1023
- const registrationCompletionTimeStr = getText("./brocom:registrationCompletionTime");
1024
- return {
1025
- objectRegistrationTime: objectRegistrationTimeStr ? new Date(objectRegistrationTimeStr) : null,
1026
- registrationStatus: getText("./brocom:registrationStatus"),
1027
- registrationCompletionTime: registrationCompletionTimeStr
1028
- ? new Date(registrationCompletionTimeStr)
1029
- : null,
1030
- corrected: parseJaNee(getText("./brocom:corrected")),
1031
- underReview: parseJaNee(getText("./brocom:underReview")),
1032
- deregistered: parseJaNee(getText("./brocom:deregistered")),
1033
- reregistered: parseJaNee(getText("./brocom:reregistered")),
1034
- };
1296
+ return out;
1035
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).
1036
1305
  /**
1037
1306
  * Process report history from document element
1038
1307
  *
@@ -1048,22 +1317,22 @@ export function processReportHistory(_value, context) {
1048
1317
  }
1049
1318
  const getText = createXPathTextGetter(historyNode, adapter, namespaces);
1050
1319
  // Parse dates - these elements are in the default namespace (no prefix)
1051
- const reportStartDateStr = getText("./dsbhrgt:reportStartDate/brocom:date");
1052
- const reportEndDateStr = getText("./dsbhrgt:reportEndDate/brocom:date");
1320
+ const reportStartDateStr = getText("./dsbhrgt:reportStartDate");
1321
+ const reportEndDateStr = getText("./dsbhrgt:reportEndDate");
1053
1322
  // Find intermediate events (in default namespace)
1054
1323
  const eventNodes = adapter.evaluateXPathAll(historyNode, "./dsbhrgt:intermediateEvent", nsResolver);
1055
1324
  const intermediateEvents = [];
1056
1325
  for (const eventNode of eventNodes) {
1057
1326
  const getEventText = createXPathTextGetter(eventNode, adapter, namespaces);
1058
- const eventDateStr = getEventText("./dsbhrgt:eventDate/brocom:date");
1327
+ const eventDateStr = getEventText("./dsbhrgt:eventDate");
1059
1328
  intermediateEvents.push({
1060
1329
  eventName: getEventText("./dsbhrgt:eventName"),
1061
- eventDate: eventDateStr ? new Date(eventDateStr) : null,
1330
+ eventDate: parseDate(eventDateStr),
1062
1331
  });
1063
1332
  }
1064
1333
  return {
1065
- reportStartDate: reportStartDateStr ? new Date(reportStartDateStr) : null,
1066
- reportEndDate: reportEndDateStr ? new Date(reportEndDateStr) : null,
1334
+ reportStartDate: parseDate(reportStartDateStr),
1335
+ reportEndDate: parseDate(reportEndDateStr),
1067
1336
  intermediateEvents,
1068
1337
  };
1069
1338
  }