xm-netcdf-loader 1.0.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 (130) hide show
  1. package/dist/component/colorLegend.d.ts +25 -0
  2. package/dist/component/loadFile.d.ts +43 -0
  3. package/dist/component/loadonMap.d.ts +46 -0
  4. package/dist/component/statusInfo.d.ts +41 -0
  5. package/dist/composables/useGridLabels.d.ts +13 -0
  6. package/dist/composables/useLeafletMap.d.ts +56 -0
  7. package/dist/composables/useMapRendering.d.ts +38 -0
  8. package/dist/composables/useNetCdf.d.ts +49 -0
  9. package/dist/index.d.ts +9 -0
  10. package/dist/netcdf4-wasm/CONTRIBUTING.md +160 -0
  11. package/dist/netcdf4-wasm/LICENSE +22 -0
  12. package/dist/netcdf4-wasm/README.md +81 -0
  13. package/dist/netcdf4-wasm/dist/constants.d.ts +158 -0
  14. package/dist/netcdf4-wasm/dist/constants.d.ts.map +1 -0
  15. package/dist/netcdf4-wasm/dist/constants.js +249 -0
  16. package/dist/netcdf4-wasm/dist/constants.js.map +1 -0
  17. package/dist/netcdf4-wasm/dist/dimension.d.ts +9 -0
  18. package/dist/netcdf4-wasm/dist/dimension.d.ts.map +1 -0
  19. package/dist/netcdf4-wasm/dist/dimension.js +19 -0
  20. package/dist/netcdf4-wasm/dist/dimension.js.map +1 -0
  21. package/dist/netcdf4-wasm/dist/group.d.ts +35 -0
  22. package/dist/netcdf4-wasm/dist/group.d.ts.map +1 -0
  23. package/dist/netcdf4-wasm/dist/group.js +189 -0
  24. package/dist/netcdf4-wasm/dist/group.js.map +1 -0
  25. package/dist/netcdf4-wasm/dist/index.d.ts +17 -0
  26. package/dist/netcdf4-wasm/dist/index.d.ts.map +1 -0
  27. package/dist/netcdf4-wasm/dist/index.js +49 -0
  28. package/dist/netcdf4-wasm/dist/index.js.map +1 -0
  29. package/dist/netcdf4-wasm/dist/netcdf-getters.d.ts +120 -0
  30. package/dist/netcdf4-wasm/dist/netcdf-getters.d.ts.map +1 -0
  31. package/dist/netcdf4-wasm/dist/netcdf-getters.js +816 -0
  32. package/dist/netcdf4-wasm/dist/netcdf-getters.js.map +1 -0
  33. package/dist/netcdf4-wasm/dist/netcdf-worker.d.ts +2 -0
  34. package/dist/netcdf4-wasm/dist/netcdf-worker.d.ts.map +1 -0
  35. package/dist/netcdf4-wasm/dist/netcdf-worker.js +154 -0
  36. package/dist/netcdf4-wasm/dist/netcdf-worker.js.map +1 -0
  37. package/dist/netcdf4-wasm/dist/netcdf4-wasm.js +2 -0
  38. package/dist/netcdf4-wasm/dist/netcdf4-wasm.wasm +0 -0
  39. package/dist/netcdf4-wasm/dist/netcdf4.d.ts +218 -0
  40. package/dist/netcdf4-wasm/dist/netcdf4.d.ts.map +1 -0
  41. package/dist/netcdf4-wasm/dist/netcdf4.js +1049 -0
  42. package/dist/netcdf4-wasm/dist/netcdf4.js.map +1 -0
  43. package/dist/netcdf4-wasm/dist/slice.d.ts +57 -0
  44. package/dist/netcdf4-wasm/dist/slice.d.ts.map +1 -0
  45. package/dist/netcdf4-wasm/dist/slice.js +60 -0
  46. package/dist/netcdf4-wasm/dist/slice.js.map +1 -0
  47. package/dist/netcdf4-wasm/dist/test-setup.d.ts +13 -0
  48. package/dist/netcdf4-wasm/dist/test-setup.d.ts.map +1 -0
  49. package/dist/netcdf4-wasm/dist/test-setup.js +78 -0
  50. package/dist/netcdf4-wasm/dist/test-setup.js.map +1 -0
  51. package/dist/netcdf4-wasm/dist/types.d.ts +444 -0
  52. package/dist/netcdf4-wasm/dist/types.d.ts.map +1 -0
  53. package/dist/netcdf4-wasm/dist/types.js +3 -0
  54. package/dist/netcdf4-wasm/dist/types.js.map +1 -0
  55. package/dist/netcdf4-wasm/dist/variable.d.ts +36 -0
  56. package/dist/netcdf4-wasm/dist/variable.d.ts.map +1 -0
  57. package/dist/netcdf4-wasm/dist/variable.js +152 -0
  58. package/dist/netcdf4-wasm/dist/variable.js.map +1 -0
  59. package/dist/netcdf4-wasm/dist/wasm-module.d.ts +6 -0
  60. package/dist/netcdf4-wasm/dist/wasm-module.d.ts.map +1 -0
  61. package/dist/netcdf4-wasm/dist/wasm-module.js +1502 -0
  62. package/dist/netcdf4-wasm/dist/wasm-module.js.map +1 -0
  63. package/dist/netcdf4-wasm/package.json +78 -0
  64. package/dist/netcdf4-wasm.wasm +0 -0
  65. package/dist/types/colorsJson.d.ts +36 -0
  66. package/dist/types/netcdf.d.ts +70 -0
  67. package/dist/utils/color.d.ts +277 -0
  68. package/dist/utils/colorScales.d.ts +28 -0
  69. package/dist/utils/colorsJsonService.d.ts +24 -0
  70. package/dist/utils/dataProcessing.d.ts +64 -0
  71. package/dist/utils/errorHandling.d.ts +69 -0
  72. package/dist/utils/imageUtils.d.ts +29 -0
  73. package/dist/utils/performance.d.ts +75 -0
  74. package/dist/wasm/constants.d.ts +158 -0
  75. package/dist/wasm/constants.d.ts.map +1 -0
  76. package/dist/wasm/constants.js +249 -0
  77. package/dist/wasm/constants.js.map +1 -0
  78. package/dist/wasm/dimension.d.ts +9 -0
  79. package/dist/wasm/dimension.d.ts.map +1 -0
  80. package/dist/wasm/dimension.js +19 -0
  81. package/dist/wasm/dimension.js.map +1 -0
  82. package/dist/wasm/group.d.ts +35 -0
  83. package/dist/wasm/group.d.ts.map +1 -0
  84. package/dist/wasm/group.js +189 -0
  85. package/dist/wasm/group.js.map +1 -0
  86. package/dist/wasm/index.d.ts +17 -0
  87. package/dist/wasm/index.d.ts.map +1 -0
  88. package/dist/wasm/index.js +49 -0
  89. package/dist/wasm/index.js.map +1 -0
  90. package/dist/wasm/netcdf-getters.d.ts +120 -0
  91. package/dist/wasm/netcdf-getters.d.ts.map +1 -0
  92. package/dist/wasm/netcdf-getters.js +816 -0
  93. package/dist/wasm/netcdf-getters.js.map +1 -0
  94. package/dist/wasm/netcdf-worker.d.ts +2 -0
  95. package/dist/wasm/netcdf-worker.d.ts.map +1 -0
  96. package/dist/wasm/netcdf-worker.js +154 -0
  97. package/dist/wasm/netcdf-worker.js.map +1 -0
  98. package/dist/wasm/netcdf4-wasm.js +2 -0
  99. package/dist/wasm/netcdf4-wasm.wasm +0 -0
  100. package/dist/wasm/netcdf4.d.ts +218 -0
  101. package/dist/wasm/netcdf4.d.ts.map +1 -0
  102. package/dist/wasm/netcdf4.js +1049 -0
  103. package/dist/wasm/netcdf4.js.map +1 -0
  104. package/dist/wasm/slice.d.ts +57 -0
  105. package/dist/wasm/slice.d.ts.map +1 -0
  106. package/dist/wasm/slice.js +60 -0
  107. package/dist/wasm/slice.js.map +1 -0
  108. package/dist/wasm/test-setup.d.ts +13 -0
  109. package/dist/wasm/test-setup.d.ts.map +1 -0
  110. package/dist/wasm/test-setup.js +78 -0
  111. package/dist/wasm/test-setup.js.map +1 -0
  112. package/dist/wasm/types.d.ts +444 -0
  113. package/dist/wasm/types.d.ts.map +1 -0
  114. package/dist/wasm/types.js +3 -0
  115. package/dist/wasm/types.js.map +1 -0
  116. package/dist/wasm/variable.d.ts +36 -0
  117. package/dist/wasm/variable.d.ts.map +1 -0
  118. package/dist/wasm/variable.js +152 -0
  119. package/dist/wasm/variable.js.map +1 -0
  120. package/dist/wasm/wasm-module.d.ts +6 -0
  121. package/dist/wasm/wasm-module.d.ts.map +1 -0
  122. package/dist/wasm/wasm-module.js +1502 -0
  123. package/dist/wasm/wasm-module.js.map +1 -0
  124. package/dist/xm-netcdf-loader.cjs.js +2 -0
  125. package/dist/xm-netcdf-loader.cjs.js.map +1 -0
  126. package/dist/xm-netcdf-loader.es.js +18532 -0
  127. package/dist/xm-netcdf-loader.es.js.map +1 -0
  128. package/dist/xm-netcdf-loader.umd.js +2 -0
  129. package/dist/xm-netcdf-loader.umd.js.map +1 -0
  130. package/package.json +45 -0
@@ -0,0 +1,816 @@
1
+ import { NC_CONSTANTS, DATA_TYPE_SIZE, CONSTANT_DTYPE_MAP } from './constants.js';
2
+ import { resolveDim } from "./slice.js";
3
+ export function getGroupVariables(module, ncid, groupPath) {
4
+ const workingNcid = groupPath ? getGroupNCID(module, ncid, groupPath) : ncid;
5
+ const variables = {};
6
+ const varCount = getVarCount(module, workingNcid);
7
+ for (let varid = 0; varid < varCount; varid++) {
8
+ const nameResult = module.nc_inq_varname(workingNcid, varid);
9
+ if (nameResult.result !== NC_CONSTANTS.NC_NOERR || !nameResult.name) {
10
+ console.warn(`Failed to get variable name for varid ${varid} (error: ${nameResult.result})`);
11
+ continue;
12
+ }
13
+ // A coordinate variable is one whose name matches a dimension.
14
+ const isCoordinate = findDimInHierarchy(module, workingNcid, nameResult.name);
15
+ if (isCoordinate)
16
+ continue;
17
+ variables[nameResult.name] = {
18
+ id: varid,
19
+ ncid: workingNcid // CRITICAL: Store the ncid where this variable lives!
20
+ };
21
+ }
22
+ return variables;
23
+ }
24
+ export function getVarCount(module, ncid) {
25
+ const result = module.nc_inq_nvars(ncid);
26
+ if (result.result !== NC_CONSTANTS.NC_NOERR) {
27
+ throw new Error(`Failed to get number of variables (error: ${result.result})`);
28
+ }
29
+ return result.nvars || 0;
30
+ }
31
+ export function getDimCount(module, ncid) {
32
+ const result = module.nc_inq_ndims(ncid);
33
+ if (result.result !== NC_CONSTANTS.NC_NOERR) {
34
+ throw new Error(`Failed to get number of dimensions (error: ${result.result})`);
35
+ }
36
+ return result.ndims || 0;
37
+ }
38
+ export function getDims(module, ncid, groupPath) {
39
+ const workingNcid = groupPath ? getGroupNCID(module, ncid, groupPath) : ncid;
40
+ const dimIDs = getDimIDs(module, workingNcid);
41
+ const dims = {};
42
+ for (const dimid of dimIDs) {
43
+ const dim = getDim(module, workingNcid, dimid);
44
+ dims[dim.name] = {
45
+ size: dim.len,
46
+ units: dim.units ?? null,
47
+ id: dim.id
48
+ };
49
+ }
50
+ return dims;
51
+ }
52
+ export function getDimIDs(module, ncid, includeParents = false) {
53
+ const result = module.nc_inq_dimids(ncid, includeParents ? 1 : 0);
54
+ if (result.result !== NC_CONSTANTS.NC_NOERR) {
55
+ throw new Error(`Failed to get dimension IDs (error: ${result.result})`);
56
+ }
57
+ return result.dimids ?? [];
58
+ }
59
+ function findCoordinateVariable(module, startNcid, name) {
60
+ let current = startNcid;
61
+ while (current !== null) {
62
+ const result = module.nc_inq_varid(current, name);
63
+ if (result.result === NC_CONSTANTS.NC_NOERR) {
64
+ return {
65
+ ncid: current,
66
+ varid: result.varid
67
+ };
68
+ }
69
+ current = getGroupParent(module, current);
70
+ }
71
+ return null;
72
+ }
73
+ export function getDim(module, ncid, dimid) {
74
+ const result = module.nc_inq_dim(ncid, dimid);
75
+ if (result.result !== NC_CONSTANTS.NC_NOERR) {
76
+ throw new Error(`Failed to get dim (error: ${result.result})`);
77
+ }
78
+ const { result: _r, ...dim } = result;
79
+ let varID = null;
80
+ let units = null;
81
+ let coordNcid = null;
82
+ // Search upward for coordinate variable
83
+ const coord = findCoordinateVariable(module, ncid, dim.name);
84
+ if (coord) {
85
+ varID = coord.varid;
86
+ coordNcid = coord.ncid;
87
+ units = getAttributeValues(module, coordNcid, varID, "units");
88
+ }
89
+ return {
90
+ ...dim,
91
+ id: varID,
92
+ units,
93
+ coordNcid // useful for debugging
94
+ };
95
+ }
96
+ export function getAttributeValues(module, ncid, varid, attname) {
97
+ const attInfo = module.nc_inq_att(ncid, varid, attname);
98
+ if (attInfo.result !== NC_CONSTANTS.NC_NOERR) {
99
+ console.warn(`Failed to get attribute info for ${attname} (error: ${attInfo.result})`);
100
+ return null;
101
+ }
102
+ const attType = attInfo.type;
103
+ if (!attType)
104
+ throw new Error("Failed to allocate memory for attribute type.");
105
+ // Dispatch table: maps NetCDF type -> module getter
106
+ const getterMap = {
107
+ [NC_CONSTANTS.NC_CHAR]: module.nc_get_att_text,
108
+ [NC_CONSTANTS.NC_SHORT]: module.nc_get_att_short,
109
+ [NC_CONSTANTS.NC_INT]: module.nc_get_att_int,
110
+ [NC_CONSTANTS.NC_FLOAT]: module.nc_get_att_float,
111
+ [NC_CONSTANTS.NC_DOUBLE]: module.nc_get_att_double,
112
+ [NC_CONSTANTS.NC_BYTE]: module.nc_get_att_schar,
113
+ [NC_CONSTANTS.NC_UBYTE]: module.nc_get_att_uchar,
114
+ [NC_CONSTANTS.NC_UINT]: module.nc_get_att_uint,
115
+ [NC_CONSTANTS.NC_USHORT]: module.nc_get_att_ushort,
116
+ [NC_CONSTANTS.NC_LONGLONG]: module.nc_get_att_longlong,
117
+ [NC_CONSTANTS.NC_UINT64]: module.nc_get_att_ulonglong,
118
+ [NC_CONSTANTS.NC_STRING]: module.nc_get_att_string
119
+ };
120
+ const getter = getterMap[attType];
121
+ if (!getter)
122
+ throw new Error(`Unsupported attribute type ${attType}`);
123
+ const attValue = getter(ncid, varid, attname, attInfo.len);
124
+ return attValue.data;
125
+ }
126
+ export function getGlobalAttributes(module, ncid, groupPath) {
127
+ const workingNcid = groupPath ? getGroupNCID(module, ncid, groupPath) : ncid;
128
+ const attributes = {};
129
+ const nattsResult = module.nc_inq_natts(workingNcid);
130
+ if (nattsResult.result !== NC_CONSTANTS.NC_NOERR) {
131
+ throw new Error(`Failed to get number of global attributes (error: ${nattsResult.result})`);
132
+ }
133
+ const nAtts = nattsResult.natts;
134
+ const attNames = [];
135
+ for (let i = 0; i < nAtts; i++) {
136
+ const name = getAttributeName(module, workingNcid, NC_CONSTANTS.NC_GLOBAL, i);
137
+ attNames.push(name);
138
+ }
139
+ if (attNames.length === 0)
140
+ return attributes;
141
+ for (const attname of attNames) {
142
+ if (!attname)
143
+ continue;
144
+ attributes[attname] = getAttributeValues(module, workingNcid, NC_CONSTANTS.NC_GLOBAL, attname);
145
+ }
146
+ return attributes;
147
+ }
148
+ export function getAttributeName(module, ncid, varid, attId) {
149
+ const result = module.nc_inq_attname(ncid, varid, attId);
150
+ if (result.result !== NC_CONSTANTS.NC_NOERR) {
151
+ throw new Error(`Failed to get attribute (error: ${result.result})`);
152
+ }
153
+ return result.name;
154
+ }
155
+ export function getFullMetadata(module, ncid, groupPath) {
156
+ const workingNcid = groupPath ? getGroupNCID(module, ncid, groupPath) : ncid;
157
+ const varIds = getVarIDs(module, workingNcid);
158
+ const metas = [];
159
+ for (const varid of varIds) {
160
+ const varMeta = getVariableInfo(module, workingNcid, varid);
161
+ const { attributes, ...varDeets } = varMeta;
162
+ metas.push({ ...varDeets, ...attributes });
163
+ }
164
+ return metas;
165
+ }
166
+ export function getVarIDs(module, ncid) {
167
+ const result = module.nc_inq_varids(ncid);
168
+ if (result.result !== NC_CONSTANTS.NC_NOERR) {
169
+ throw new Error(`Failed to get variable IDs (error: ${result.result})`);
170
+ }
171
+ return result.varids ?? [];
172
+ }
173
+ export function getEnumType(module, ncid, xtype) {
174
+ // Get basic enum info
175
+ const { result: infoResult, name, baseType, baseSize, numMembers } = module.nc_inq_enum(ncid, xtype);
176
+ if (infoResult !== NC_CONSTANTS.NC_NOERR || !name || baseType === undefined || numMembers === undefined) {
177
+ throw new Error(`Failed to get enum info (error: ${infoResult})`);
178
+ }
179
+ // Get all members
180
+ const members = [];
181
+ for (let i = 0; i < numMembers; i++) {
182
+ const { result, name: memberName, value } = module.nc_inq_enum_member(ncid, xtype, i, baseType);
183
+ if (result === NC_CONSTANTS.NC_NOERR && memberName !== undefined && value !== undefined) {
184
+ members.push({ name: memberName, value });
185
+ }
186
+ }
187
+ return {
188
+ name,
189
+ baseType,
190
+ baseSize: baseSize,
191
+ numMembers,
192
+ members
193
+ };
194
+ }
195
+ export function getTypeClass(module, ncid, xtype) {
196
+ // Atomic types return themselves as the class
197
+ if (xtype < 13) { // Below NC_VLEN
198
+ return xtype;
199
+ }
200
+ // For user-defined types, query the class
201
+ const { result, typeClass } = module.nc_inq_user_type(ncid, xtype);
202
+ if (result === NC_CONSTANTS.NC_NOERR && typeClass !== undefined) {
203
+ return typeClass;
204
+ }
205
+ return xtype;
206
+ }
207
+ function buildEnumDict(module, ncid, enumTypeId, enumBaseType) {
208
+ const enumInqResult = module.nc_inq_enum(ncid, enumTypeId);
209
+ const { result: enumResult, numMembers } = enumInqResult;
210
+ if (enumResult !== NC_CONSTANTS.NC_NOERR || numMembers === undefined) {
211
+ throw new Error(`Failed to get enum info (error: ${enumResult})`);
212
+ }
213
+ const enumDict = {};
214
+ for (let i = 0; i < numMembers; i++) {
215
+ const memberResult = module.nc_inq_enum_member(ncid, enumTypeId, i, enumBaseType);
216
+ const { result: memberResultCode, name: memberName, value } = memberResult;
217
+ if (memberResultCode === NC_CONSTANTS.NC_NOERR && memberName !== undefined && value !== undefined) {
218
+ // Note: bigint values exceeding Number.MAX_SAFE_INTEGER will lose precision.
219
+ // In practice enum values are small named constants so this is acceptable.
220
+ const numValue = typeof value === 'bigint' ? Number(value) : value;
221
+ enumDict[numValue] = memberName;
222
+ }
223
+ else {
224
+ console.warn(`[buildEnumDict] Skipping member ${i}: result=${memberResultCode}, name=${memberName}, value=${value}`);
225
+ }
226
+ }
227
+ return enumDict;
228
+ }
229
+ function resolveEnumContext(module, ncid, varType) {
230
+ const typeClass = getTypeClass(module, ncid, varType);
231
+ if (typeClass === NC_CONSTANTS.NC_VLEN ||
232
+ typeClass === NC_CONSTANTS.NC_OPAQUE ||
233
+ typeClass === NC_CONSTANTS.NC_COMPOUND) {
234
+ throw new Error(`Unsupported type class: ${typeClass} (VLEN, OPAQUE, and COMPOUND not yet implemented)`);
235
+ }
236
+ if (typeClass !== NC_CONSTANTS.NC_ENUM) {
237
+ return { isEnum: false, baseType: varType };
238
+ }
239
+ const { result, baseType } = module.nc_inq_enum(ncid, varType);
240
+ if (result !== NC_CONSTANTS.NC_NOERR || baseType === undefined) {
241
+ throw new Error(`Failed to get enum base type (error: ${result})`);
242
+ }
243
+ const enumDict = buildEnumDict(module, ncid, varType, baseType);
244
+ return { isEnum: true, baseType, enumDict };
245
+ }
246
+ function resolveVariableType(module, ncid, varid) {
247
+ const result = module.nc_inq_var(ncid, varid);
248
+ if (result.result !== NC_CONSTANTS.NC_NOERR) {
249
+ throw new Error(`Failed to get variable info (error: ${result.result})`);
250
+ }
251
+ const dimids = result.dimids ? Array.from(result.dimids) : [];
252
+ const size = dimids.reduce((acc, dimid) => acc * getDim(module, ncid, dimid).len, 1);
253
+ const nctype = result.type;
254
+ const enumCtx = resolveEnumContext(module, ncid, nctype);
255
+ return { nctype, size, enumCtx };
256
+ }
257
+ export function getVariableInfo(module, ncid, variable, groupPath) {
258
+ const workingNcid = groupPath ? getGroupNCID(module, ncid, groupPath) : ncid;
259
+ const info = {};
260
+ const isId = typeof variable === "number";
261
+ let varid = variable;
262
+ if (!isId) {
263
+ const result = module.nc_inq_varid(workingNcid, variable);
264
+ varid = result.varid;
265
+ }
266
+ const result = module.nc_inq_var(workingNcid, varid);
267
+ if (result.result !== NC_CONSTANTS.NC_NOERR) {
268
+ throw new Error(`Failed to get variable info (error: ${result.result})`);
269
+ }
270
+ const varType = result.type;
271
+ const enumCtx = resolveEnumContext(module, workingNcid, varType);
272
+ const actualType = enumCtx.baseType;
273
+ const typeMultiplier = DATA_TYPE_SIZE[actualType];
274
+ // Dim Info
275
+ const dimids = result.dimids;
276
+ const dims = [];
277
+ const shape = [];
278
+ const dimensions = [];
279
+ let size = 1;
280
+ if (dimids) {
281
+ for (let i = 0; i < dimids.length; i++) {
282
+ const dimid = dimids[i];
283
+ const dim = getDim(module, workingNcid, dimid);
284
+ size *= dim.len;
285
+ dims.push(dim);
286
+ shape.push(dim.len);
287
+ dimensions.push(dim.name);
288
+ }
289
+ }
290
+ // Attribute Info
291
+ const attNames = [];
292
+ if (result.natts) {
293
+ for (let i = 0; i < result.natts; i++) {
294
+ attNames.push(getAttributeName(module, workingNcid, varid, i));
295
+ }
296
+ }
297
+ const atts = {};
298
+ for (const attname of attNames) {
299
+ if (!attname)
300
+ continue;
301
+ atts[attname] = getAttributeValues(module, workingNcid, varid, attname);
302
+ }
303
+ // Chunking Info
304
+ const chunkResult = module.nc_inq_var_chunking(workingNcid, varid);
305
+ const isChunked = chunkResult.chunking === NC_CONSTANTS.NC_CHUNKED;
306
+ const chunks = isChunked ? chunkResult.chunkSizes : shape;
307
+ const chunkElements = chunks.reduce((a, b) => a * b, 1);
308
+ // Output
309
+ info["name"] = result.name;
310
+ info["dtype"] = enumCtx.isEnum ? `enum(${CONSTANT_DTYPE_MAP[actualType]})` : CONSTANT_DTYPE_MAP[actualType];
311
+ info["dtype_base"] = CONSTANT_DTYPE_MAP[actualType];
312
+ info["nctype"] = varType;
313
+ info["nctype_base"] = actualType;
314
+ info["shape"] = shape;
315
+ info["dims"] = dims;
316
+ info["dimensions"] = dimensions;
317
+ info["size"] = size;
318
+ info["totalSize"] = size * typeMultiplier;
319
+ info["attributes"] = atts;
320
+ info["chunked"] = isChunked;
321
+ info["chunks"] = chunks;
322
+ info["chunkSize"] = chunkElements * typeMultiplier;
323
+ if (enumCtx.isEnum) {
324
+ info["enum"] = enumCtx.enumDict;
325
+ info["enumType"] = { name: result.name, baseType: actualType };
326
+ }
327
+ return info;
328
+ }
329
+ // Helper function to convert enum values to names
330
+ function convertEnumValuesToNames(data, enumDict) {
331
+ const enumNames = [];
332
+ for (let i = 0; i < data.length; i++) {
333
+ const value = data[i];
334
+ const numValue = typeof value === 'bigint' ? Number(value) : Number(value);
335
+ enumNames.push(enumDict[numValue] ?? `Unknown(${numValue})`);
336
+ }
337
+ return enumNames;
338
+ }
339
+ export function getVariableArray(module, ncid, variable, groupPath, options) {
340
+ const workingNcid = groupPath ? getGroupNCID(module, ncid, groupPath) : ncid;
341
+ let varid;
342
+ if (typeof variable === "number") {
343
+ varid = variable;
344
+ }
345
+ else {
346
+ const result = module.nc_inq_varid(workingNcid, variable);
347
+ if (result.result !== NC_CONSTANTS.NC_NOERR) {
348
+ throw new Error(`Failed to get variable id for '${variable}' (error: ${result.result})`);
349
+ }
350
+ varid = result.varid;
351
+ }
352
+ const { size: arraySize, enumCtx } = resolveVariableType(module, workingNcid, varid);
353
+ const arrayType = enumCtx.baseType;
354
+ const readers = {
355
+ [NC_CONSTANTS.NC_CHAR]: (...args) => module.nc_get_var_text(...args),
356
+ [NC_CONSTANTS.NC_BYTE]: (...args) => module.nc_get_var_schar(...args),
357
+ [NC_CONSTANTS.NC_UBYTE]: (...args) => module.nc_get_var_uchar(...args),
358
+ [NC_CONSTANTS.NC_SHORT]: (...args) => module.nc_get_var_short(...args),
359
+ [NC_CONSTANTS.NC_USHORT]: (...args) => module.nc_get_var_ushort(...args),
360
+ [NC_CONSTANTS.NC_INT]: (...args) => module.nc_get_var_int(...args),
361
+ [NC_CONSTANTS.NC_UINT]: (...args) => module.nc_get_var_uint(...args),
362
+ [NC_CONSTANTS.NC_FLOAT]: (...args) => module.nc_get_var_float(...args),
363
+ [NC_CONSTANTS.NC_DOUBLE]: (...args) => module.nc_get_var_double(...args),
364
+ [NC_CONSTANTS.NC_INT64]: (...args) => module.nc_get_var_longlong(...args),
365
+ [NC_CONSTANTS.NC_LONGLONG]: (...args) => module.nc_get_var_longlong(...args),
366
+ [NC_CONSTANTS.NC_UINT64]: (...args) => module.nc_get_var_ulonglong(...args),
367
+ [NC_CONSTANTS.NC_ULONGLONG]: (...args) => module.nc_get_var_ulonglong(...args),
368
+ [NC_CONSTANTS.NC_STRING]: (...args) => module.nc_get_var_string(...args),
369
+ };
370
+ let arrayData;
371
+ if (enumCtx.isEnum) {
372
+ arrayData = module.nc_get_var_generic(workingNcid, varid, arraySize, arrayType);
373
+ }
374
+ else {
375
+ const reader = readers[arrayType];
376
+ if (!reader) {
377
+ console.warn(`Unknown NetCDF type ${arrayType}, falling back to double`);
378
+ arrayData = module.nc_get_var_double(workingNcid, varid, arraySize);
379
+ }
380
+ else {
381
+ arrayData = reader(workingNcid, varid, arraySize);
382
+ }
383
+ }
384
+ if (arrayData.result !== NC_CONSTANTS.NC_NOERR) {
385
+ throw new Error(`Failed to read array data (error: ${arrayData.result})`);
386
+ }
387
+ if (!arrayData.data) {
388
+ throw new Error("nc_get_var returned no data");
389
+ }
390
+ if (enumCtx.isEnum && options?.convertEnumsToNames && enumCtx.enumDict) {
391
+ return convertEnumValuesToNames(arrayData.data, enumCtx.enumDict);
392
+ }
393
+ return arrayData.data;
394
+ }
395
+ export function getSlicedVariableArray(module, ncid, variable, start, count, groupPath, options) {
396
+ const workingNcid = groupPath ? getGroupNCID(module, ncid, groupPath) : ncid;
397
+ let varid;
398
+ if (typeof variable === "number") {
399
+ varid = variable;
400
+ }
401
+ else {
402
+ const result = module.nc_inq_varid(workingNcid, variable);
403
+ if (result.result !== NC_CONSTANTS.NC_NOERR) {
404
+ throw new Error(`Failed to get variable id for '${variable}' (error: ${result.result})`);
405
+ }
406
+ varid = result.varid;
407
+ }
408
+ const { enumCtx } = resolveVariableType(module, workingNcid, varid);
409
+ const arrayType = enumCtx.baseType;
410
+ const readers = {
411
+ [NC_CONSTANTS.NC_BYTE]: (...args) => module.nc_get_vara_schar(...args),
412
+ [NC_CONSTANTS.NC_UBYTE]: (...args) => module.nc_get_vara_uchar(...args),
413
+ [NC_CONSTANTS.NC_SHORT]: (...args) => module.nc_get_vara_short(...args),
414
+ [NC_CONSTANTS.NC_USHORT]: (...args) => module.nc_get_vara_ushort(...args),
415
+ [NC_CONSTANTS.NC_INT]: (...args) => module.nc_get_vara_int(...args),
416
+ [NC_CONSTANTS.NC_UINT]: (...args) => module.nc_get_vara_uint(...args),
417
+ [NC_CONSTANTS.NC_FLOAT]: (...args) => module.nc_get_vara_float(...args),
418
+ [NC_CONSTANTS.NC_DOUBLE]: (...args) => module.nc_get_vara_double(...args),
419
+ [NC_CONSTANTS.NC_INT64]: (...args) => module.nc_get_vara_longlong(...args),
420
+ [NC_CONSTANTS.NC_UINT64]: (...args) => module.nc_get_vara_ulonglong(...args),
421
+ [NC_CONSTANTS.NC_STRING]: (...args) => module.nc_get_vara_string(...args),
422
+ };
423
+ let arrayData;
424
+ if (enumCtx.isEnum) {
425
+ arrayData = module.nc_get_vara_generic(workingNcid, varid, start, count, arrayType);
426
+ }
427
+ else {
428
+ const reader = readers[arrayType];
429
+ if (!reader) {
430
+ console.warn(`Unknown NetCDF type ${arrayType}, falling back to double`);
431
+ arrayData = module.nc_get_vara_double(workingNcid, varid, start, count);
432
+ }
433
+ else {
434
+ arrayData = reader(workingNcid, varid, start, count);
435
+ }
436
+ }
437
+ if (arrayData.result !== NC_CONSTANTS.NC_NOERR) {
438
+ throw new Error(`Failed to read sliced array data (error: ${arrayData.result})`);
439
+ }
440
+ if (!arrayData.data) {
441
+ throw new Error("nc_get_vara returned no data");
442
+ }
443
+ if (enumCtx.isEnum && options?.convertEnumsToNames && enumCtx.enumDict) {
444
+ return convertEnumValuesToNames(arrayData.data, enumCtx.enumDict);
445
+ }
446
+ return arrayData.data;
447
+ }
448
+ export function getVariableArrayWithSelection(module, ncid, variable, selection, groupPath, options) {
449
+ const workingNcid = groupPath ? getGroupNCID(module, ncid, groupPath) : ncid;
450
+ // Resolve varid
451
+ let varid;
452
+ if (typeof variable === "number") {
453
+ varid = variable;
454
+ }
455
+ else {
456
+ const r = module.nc_inq_varid(workingNcid, variable);
457
+ if (r.result !== NC_CONSTANTS.NC_NOERR) {
458
+ throw new Error(`Failed to get variable id for '${variable}' (error: ${r.result})`);
459
+ }
460
+ varid = r.varid;
461
+ }
462
+ // Query variable dimensions
463
+ const varInfo = module.nc_inq_var(workingNcid, varid);
464
+ if (varInfo.result !== NC_CONSTANTS.NC_NOERR) {
465
+ throw new Error(`Failed to query variable info (error: ${varInfo.result})`);
466
+ }
467
+ const dimids = Array.from(varInfo.dimids ?? []);
468
+ const ndim = dimids.length;
469
+ if (selection.length !== ndim) {
470
+ throw new Error(`Selection has ${selection.length} dimension(s) but variable has ${ndim}`);
471
+ }
472
+ // Fetch each dimension's size — resolveDim handles BigInt safely
473
+ const dimSizes = dimids.map((dimid) => {
474
+ const r = module.nc_inq_dimlen(workingNcid, dimid);
475
+ if (r.result !== NC_CONSTANTS.NC_NOERR) {
476
+ throw new Error(`Failed to query dim length for dimid ${dimid} (error: ${r.result})`);
477
+ }
478
+ return r.len;
479
+ });
480
+ // Resolve each selection
481
+ const resolved = selection.map((sel, i) => resolveDim(sel, dimSizes[i]));
482
+ const start = resolved.map(d => d.start);
483
+ const count = resolved.map(d => d.count);
484
+ const stride = resolved.map(d => d.step);
485
+ // Validate parameters against dimension sizes
486
+ for (let i = 0; i < start.length; i++) {
487
+ const dimSize = Number(dimSizes[i]);
488
+ if (start[i] < 0 || start[i] >= dimSize) {
489
+ throw new Error(`Invalid start[${i}] = ${start[i]} for dimension size ${dimSize}`);
490
+ }
491
+ if (count[i] === 0)
492
+ continue; // valid empty slice — skip further checks
493
+ const lastIdx = start[i] + (count[i] - 1) * stride[i];
494
+ if (lastIdx >= dimSize) {
495
+ throw new Error(`Stride read would exceed dimension bounds: start[${i}]=${start[i]}, ` +
496
+ `count[${i}]=${count[i]}, stride[${i}]=${stride[i]}, last_idx=${lastIdx}, dimSize=${dimSize}`);
497
+ }
498
+ }
499
+ // Guard against integer overflow in WASM — total elements must fit in a signed 32-bit int
500
+ // not sure how well is this, ~2 billion elements, will perform in JS but may cause issues in WASM memory allocation or indexing
501
+ const totalElements = count.reduce((acc, c) => acc * c, 1);
502
+ if (totalElements > 2 ** 31 - 1) {
503
+ throw new Error(`Selection would read ${totalElements} elements, exceeding the safe limit of 2^31 - 1`);
504
+ }
505
+ // Resolve variable type — use workingNcid so enum lookup is in the right group
506
+ const { enumCtx } = resolveVariableType(module, workingNcid, varid);
507
+ const arrayType = enumCtx.baseType;
508
+ // Return appropriately typed empty array if any dimension has count 0
509
+ if (count.some(c => c === 0)) {
510
+ const emptyReaders = {
511
+ [NC_CONSTANTS.NC_BYTE]: () => new Int8Array(0),
512
+ [NC_CONSTANTS.NC_UBYTE]: () => new Uint8Array(0),
513
+ [NC_CONSTANTS.NC_SHORT]: () => new Int16Array(0),
514
+ [NC_CONSTANTS.NC_USHORT]: () => new Uint16Array(0),
515
+ [NC_CONSTANTS.NC_INT]: () => new Int32Array(0),
516
+ [NC_CONSTANTS.NC_UINT]: () => new Uint32Array(0),
517
+ [NC_CONSTANTS.NC_FLOAT]: () => new Float32Array(0),
518
+ [NC_CONSTANTS.NC_DOUBLE]: () => new Float64Array(0),
519
+ [NC_CONSTANTS.NC_INT64]: () => new BigInt64Array(0),
520
+ [NC_CONSTANTS.NC_LONGLONG]: () => new BigInt64Array(0),
521
+ [NC_CONSTANTS.NC_UINT64]: () => new BigUint64Array(0),
522
+ [NC_CONSTANTS.NC_ULONGLONG]: () => new BigUint64Array(0),
523
+ [NC_CONSTANTS.NC_STRING]: () => [],
524
+ };
525
+ return (emptyReaders[arrayType] ?? (() => new Float64Array(0)))();
526
+ }
527
+ let arrayData;
528
+ if (enumCtx.isEnum) {
529
+ arrayData = module.nc_get_vars_generic(workingNcid, varid, start, count, stride, arrayType);
530
+ }
531
+ else {
532
+ const readers = {
533
+ [NC_CONSTANTS.NC_CHAR]: (...args) => module.nc_get_vars_text(...args),
534
+ [NC_CONSTANTS.NC_BYTE]: (...args) => module.nc_get_vars_schar(...args),
535
+ [NC_CONSTANTS.NC_UBYTE]: (...args) => module.nc_get_vars_uchar(...args),
536
+ [NC_CONSTANTS.NC_SHORT]: (...args) => module.nc_get_vars_short(...args),
537
+ [NC_CONSTANTS.NC_USHORT]: (...args) => module.nc_get_vars_ushort(...args),
538
+ [NC_CONSTANTS.NC_INT]: (...args) => module.nc_get_vars_int(...args),
539
+ [NC_CONSTANTS.NC_UINT]: (...args) => module.nc_get_vars_uint(...args),
540
+ [NC_CONSTANTS.NC_FLOAT]: (...args) => module.nc_get_vars_float(...args),
541
+ [NC_CONSTANTS.NC_DOUBLE]: (...args) => module.nc_get_vars_double(...args),
542
+ [NC_CONSTANTS.NC_INT64]: (...args) => module.nc_get_vars_longlong(...args),
543
+ [NC_CONSTANTS.NC_LONGLONG]: (...args) => module.nc_get_vars_longlong(...args),
544
+ [NC_CONSTANTS.NC_UINT64]: (...args) => module.nc_get_vars_ulonglong(...args),
545
+ [NC_CONSTANTS.NC_ULONGLONG]: (...args) => module.nc_get_vars_ulonglong(...args),
546
+ [NC_CONSTANTS.NC_STRING]: (...args) => module.nc_get_vars_string(...args),
547
+ };
548
+ const reader = readers[arrayType];
549
+ if (!reader) {
550
+ arrayData = module.nc_get_vars_double(workingNcid, varid, start, count, stride);
551
+ }
552
+ else {
553
+ arrayData = reader(workingNcid, varid, start, count, stride);
554
+ }
555
+ }
556
+ if (arrayData.result !== NC_CONSTANTS.NC_NOERR) {
557
+ throw new Error(`Failed to read array data (error: ${arrayData.result})`);
558
+ }
559
+ if (!arrayData.data) {
560
+ throw new Error("nc_get_vars returned no data");
561
+ }
562
+ let result = arrayData.data;
563
+ if (enumCtx.isEnum && options?.convertEnumsToNames && enumCtx.enumDict) {
564
+ result = convertEnumValuesToNames(result, enumCtx.enumDict);
565
+ }
566
+ return result;
567
+ }
568
+ //---- Group Functions ----//
569
+ /**
570
+ * Get group ncid by path (supports nested groups)
571
+ * Uses nc_inq_grp_full_ncid for absolute paths and manual traversal for relative paths
572
+ * @param module - NetCDF4 module
573
+ * @param ncid - Current ncid (can be root or any group)
574
+ * @param groupPath - Can be absolute ("/group1/subgroup") or relative ("subgroup" or "group1/subgroup")
575
+ * @returns The ncid of the requested group
576
+ */
577
+ export function getGroupNCID(module, ncid, groupPath) {
578
+ // Optimization: if path is root, return the ncid
579
+ if (groupPath === '/' || groupPath === '') {
580
+ return ncid;
581
+ }
582
+ // Try using nc_inq_grp_full_ncid for absolute paths (more efficient)
583
+ if (groupPath.startsWith('/')) {
584
+ const result = module.nc_inq_grp_full_ncid(ncid, groupPath);
585
+ if (result.result === NC_CONSTANTS.NC_NOERR) {
586
+ return result.grp_ncid;
587
+ }
588
+ // Get current path for better error message
589
+ const currentPath = getGroupPath(module, ncid);
590
+ throw new Error(`Failed to find group '${groupPath}' from '${currentPath}' (error: ${result.result})`);
591
+ }
592
+ // Manual traversal for relative paths
593
+ const parts = groupPath.split('/').filter(p => p.length > 0);
594
+ let currentNcid = ncid;
595
+ for (const part of parts) {
596
+ const result = module.nc_inq_grp_ncid(currentNcid, part);
597
+ if (result.result !== NC_CONSTANTS.NC_NOERR) {
598
+ const currentPath = getGroupPath(module, currentNcid);
599
+ throw new Error(`Failed to get group ncid for '${part}' in path '${groupPath}' from '${currentPath}' (error: ${result.result})`);
600
+ }
601
+ currentNcid = result.grp_ncid;
602
+ }
603
+ return currentNcid;
604
+ }
605
+ /**
606
+ * Alias for getGroupNCID (matches nc_inq_ncid API)
607
+ * @param module - NetCDF4 module
608
+ * @param ncid - Current ncid
609
+ * @param groupName - Group name or path
610
+ * @returns The ncid of the requested group
611
+ */
612
+ export const getNCID = getGroupNCID;
613
+ /**
614
+ * Get immediate child groups (non-recursive)
615
+ * @param module - NetCDF4 module
616
+ * @param ncid - Group ncid to query
617
+ * @returns Object mapping group names to their ncids
618
+ */
619
+ export function getGroups(module, ncid) {
620
+ const result = module.nc_inq_grps(ncid);
621
+ if (result.result !== NC_CONSTANTS.NC_NOERR) {
622
+ throw new Error(`Failed to get groups (error: ${result.result})`);
623
+ }
624
+ const groups = {};
625
+ const grpids = result.grpids || [];
626
+ for (const grpid of grpids) {
627
+ const nameResult = module.nc_inq_grpname(grpid);
628
+ if (nameResult.result === NC_CONSTANTS.NC_NOERR && nameResult.name) {
629
+ groups[nameResult.name] = grpid;
630
+ }
631
+ }
632
+ return groups;
633
+ }
634
+ /**
635
+ * Get all groups recursively (returns nested structure)
636
+ * @param module - NetCDF4 module
637
+ * @param ncid - Group ncid to start from (usually root)
638
+ * @returns Nested object structure with group names, ncids, and subgroups
639
+ */
640
+ export function getGroupsRecursive(module, ncid) {
641
+ const result = module.nc_inq_grps(ncid);
642
+ if (result.result !== NC_CONSTANTS.NC_NOERR) {
643
+ throw new Error(`Failed to get groups (error: ${result.result})`);
644
+ }
645
+ const groups = {};
646
+ const grpids = result.grpids || [];
647
+ for (const grpid of grpids) {
648
+ const nameResult = module.nc_inq_grpname(grpid);
649
+ if (nameResult.result === NC_CONSTANTS.NC_NOERR && nameResult.name) {
650
+ groups[nameResult.name] = {
651
+ ncid: grpid,
652
+ subgroups: getGroupsRecursive(module, grpid) // Recursive call
653
+ };
654
+ }
655
+ }
656
+ return groups;
657
+ }
658
+ /**
659
+ * Get the name of a group
660
+ * @param module - NetCDF4 module
661
+ * @param ncid - Group ncid
662
+ * @returns Group name
663
+ */
664
+ export function getGroupName(module, ncid) {
665
+ const result = module.nc_inq_grpname(ncid);
666
+ if (result.result !== NC_CONSTANTS.NC_NOERR) {
667
+ throw new Error(`Failed to get group name (error: ${result.result})`);
668
+ }
669
+ return result.name || '';
670
+ }
671
+ /**
672
+ * Get the full absolute path of a group
673
+ * Uses nc_inq_grpname_full for efficient path retrieval
674
+ * @param module - NetCDF4 module
675
+ * @param ncid - Group ncid
676
+ * @returns Full path like "/group1/subgroup"
677
+ */
678
+ export function getGroupPath(module, ncid) {
679
+ // Use nc_inq_grpname_full to get the complete path efficiently
680
+ const result = module.nc_inq_grpname_full(ncid);
681
+ if (result.result !== NC_CONSTANTS.NC_NOERR) {
682
+ throw new Error(`Failed to get group full name (error: ${result.result})`);
683
+ }
684
+ return result.full_name || '/';
685
+ }
686
+ /**
687
+ * Get the length of a group's full path name
688
+ * @param module - NetCDF4 module
689
+ * @param ncid - Group ncid
690
+ * @returns Length of the full group path name
691
+ */
692
+ export function getGroupPathLength(module, ncid) {
693
+ const result = module.nc_inq_grpname_len(ncid);
694
+ if (result.result !== NC_CONSTANTS.NC_NOERR) {
695
+ throw new Error(`Failed to get group name length (error: ${result.result})`);
696
+ }
697
+ return result.lenp || 0;
698
+ }
699
+ /**
700
+ * Get the parent group ncid
701
+ * @param module - NetCDF4 module
702
+ * @param ncid - Group ncid
703
+ * @returns Parent group ncid, or null if this is the root group
704
+ */
705
+ export function getGroupParent(module, ncid) {
706
+ const result = module.nc_inq_grp_parent(ncid);
707
+ if (result.result !== NC_CONSTANTS.NC_NOERR) {
708
+ return null; // No parent (at root)
709
+ }
710
+ return result.parent_ncid;
711
+ }
712
+ function findDimInHierarchy(module, startNcid, name) {
713
+ let current = startNcid;
714
+ while (current !== null) {
715
+ const result = module.nc_inq_dimid(current, name);
716
+ if (result.result === NC_CONSTANTS.NC_NOERR) {
717
+ return true;
718
+ }
719
+ current = getGroupParent(module, current);
720
+ }
721
+ return false;
722
+ }
723
+ /**
724
+ * Get complete hierarchy: groups + their variables, dimensions, and attributes recursively
725
+ * This is the unified method that should be used for exploring the file structure
726
+ * @param module - NetCDF4 module
727
+ * @param ncid - Group ncid to start from
728
+ * @param groupPath - Optional path to a specific starting group
729
+ * @param includeParentDims - Whether to include parent dimensions in each group (default: false)
730
+ * @returns Complete hierarchical structure
731
+ */
732
+ export function getCompleteHierarchy(module, ncid, groupPath, includeParentDims = false) {
733
+ const workingNcid = groupPath ? getGroupNCID(module, ncid, groupPath) : ncid;
734
+ // Get variables at this level (now includes ncid)
735
+ const variables = getGroupVariables(module, workingNcid);
736
+ // Get dimensions at this level
737
+ const dimensions = getDims(module, workingNcid);
738
+ // If includeParentDims is true, also get parent dimensions
739
+ if (includeParentDims) {
740
+ const result = module.nc_inq_dimids(workingNcid, 1);
741
+ if (result.result === NC_CONSTANTS.NC_NOERR && result.dimids) {
742
+ const parentDims = {};
743
+ for (const dimid of result.dimids) {
744
+ const dim = getDim(module, workingNcid, dimid);
745
+ // Only add if not already in dimensions (avoid duplicates)
746
+ if (!dimensions[dim.name]) {
747
+ parentDims[dim.name] = {
748
+ size: dim.len,
749
+ units: dim.units ?? null,
750
+ id: dim.id,
751
+ inherited: true
752
+ };
753
+ }
754
+ }
755
+ // Merge parent dimensions
756
+ Object.assign(dimensions, parentDims);
757
+ }
758
+ }
759
+ // Get attributes at this level
760
+ const attributes = getGlobalAttributes(module, workingNcid);
761
+ // Get the full path for this group
762
+ const fullPath = getGroupPath(module, workingNcid);
763
+ // Get subgroups and recurse
764
+ const groupsResult = module.nc_inq_grps(workingNcid);
765
+ const subgroups = {};
766
+ if (groupsResult.result === NC_CONSTANTS.NC_NOERR && groupsResult.grpids) {
767
+ for (const grpid of groupsResult.grpids) {
768
+ const nameResult = module.nc_inq_grpname(grpid);
769
+ if (nameResult.result === NC_CONSTANTS.NC_NOERR && nameResult.name) {
770
+ // Recursively get the complete hierarchy for this subgroup
771
+ subgroups[nameResult.name] = getCompleteHierarchy(module, grpid, undefined, includeParentDims);
772
+ }
773
+ }
774
+ }
775
+ return {
776
+ ncid: workingNcid, // Include the ncid at this level
777
+ path: fullPath, // Include the full path
778
+ variables,
779
+ dimensions,
780
+ attributes,
781
+ groups: subgroups
782
+ };
783
+ }
784
+ /**
785
+ * Get all variables recursively from all groups
786
+ * Returns a flat structure with full paths as keys
787
+ * @param module - NetCDF4 module
788
+ * @param ncid - Group ncid to start from
789
+ * @param currentPath - Current path (used internally for recursion)
790
+ * @returns Flat dictionary with full variable paths
791
+ */
792
+ export function getVariables(module, ncid, currentPath) {
793
+ const allVars = {};
794
+ // Get the actual path if not provided
795
+ const path = currentPath || getGroupPath(module, ncid);
796
+ // Get variables at current level
797
+ const vars = getGroupVariables(module, ncid);
798
+ for (const [name, varData] of Object.entries(vars)) {
799
+ const fullPath = path === '/' ? name : `${path}/${name}`;
800
+ allVars[fullPath] = { ...varData, path, ncid };
801
+ }
802
+ // Recurse into subgroups
803
+ const groupsResult = module.nc_inq_grps(ncid);
804
+ if (groupsResult.result === NC_CONSTANTS.NC_NOERR && groupsResult.grpids) {
805
+ for (const grpid of groupsResult.grpids) {
806
+ const nameResult = module.nc_inq_grpname(grpid);
807
+ if (nameResult.result === NC_CONSTANTS.NC_NOERR && nameResult.name) {
808
+ const newPath = path === '/' ? nameResult.name : `${path}/${nameResult.name}`;
809
+ const subVars = getVariables(module, grpid, newPath);
810
+ Object.assign(allVars, subVars);
811
+ }
812
+ }
813
+ }
814
+ return allVars;
815
+ }
816
+ //# sourceMappingURL=netcdf-getters.js.map