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