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.
- package/dist/component/colorLegend.d.ts +25 -0
- package/dist/component/loadFile.d.ts +43 -0
- package/dist/component/loadonMap.d.ts +46 -0
- package/dist/component/statusInfo.d.ts +41 -0
- package/dist/composables/useGridLabels.d.ts +13 -0
- package/dist/composables/useLeafletMap.d.ts +56 -0
- package/dist/composables/useMapRendering.d.ts +38 -0
- package/dist/composables/useNetCdf.d.ts +49 -0
- package/dist/index.d.ts +9 -0
- package/dist/netcdf4-wasm/CONTRIBUTING.md +160 -0
- package/dist/netcdf4-wasm/LICENSE +22 -0
- package/dist/netcdf4-wasm/README.md +81 -0
- package/dist/netcdf4-wasm/dist/constants.d.ts +158 -0
- package/dist/netcdf4-wasm/dist/constants.d.ts.map +1 -0
- package/dist/netcdf4-wasm/dist/constants.js +249 -0
- package/dist/netcdf4-wasm/dist/constants.js.map +1 -0
- package/dist/netcdf4-wasm/dist/dimension.d.ts +9 -0
- package/dist/netcdf4-wasm/dist/dimension.d.ts.map +1 -0
- package/dist/netcdf4-wasm/dist/dimension.js +19 -0
- package/dist/netcdf4-wasm/dist/dimension.js.map +1 -0
- package/dist/netcdf4-wasm/dist/group.d.ts +35 -0
- package/dist/netcdf4-wasm/dist/group.d.ts.map +1 -0
- package/dist/netcdf4-wasm/dist/group.js +189 -0
- package/dist/netcdf4-wasm/dist/group.js.map +1 -0
- package/dist/netcdf4-wasm/dist/index.d.ts +17 -0
- package/dist/netcdf4-wasm/dist/index.d.ts.map +1 -0
- package/dist/netcdf4-wasm/dist/index.js +49 -0
- package/dist/netcdf4-wasm/dist/index.js.map +1 -0
- package/dist/netcdf4-wasm/dist/netcdf-getters.d.ts +120 -0
- package/dist/netcdf4-wasm/dist/netcdf-getters.d.ts.map +1 -0
- package/dist/netcdf4-wasm/dist/netcdf-getters.js +816 -0
- package/dist/netcdf4-wasm/dist/netcdf-getters.js.map +1 -0
- package/dist/netcdf4-wasm/dist/netcdf-worker.d.ts +2 -0
- package/dist/netcdf4-wasm/dist/netcdf-worker.d.ts.map +1 -0
- package/dist/netcdf4-wasm/dist/netcdf-worker.js +154 -0
- package/dist/netcdf4-wasm/dist/netcdf-worker.js.map +1 -0
- package/dist/netcdf4-wasm/dist/netcdf4-wasm.js +2 -0
- package/dist/netcdf4-wasm/dist/netcdf4-wasm.wasm +0 -0
- package/dist/netcdf4-wasm/dist/netcdf4.d.ts +218 -0
- package/dist/netcdf4-wasm/dist/netcdf4.d.ts.map +1 -0
- package/dist/netcdf4-wasm/dist/netcdf4.js +1049 -0
- package/dist/netcdf4-wasm/dist/netcdf4.js.map +1 -0
- package/dist/netcdf4-wasm/dist/slice.d.ts +57 -0
- package/dist/netcdf4-wasm/dist/slice.d.ts.map +1 -0
- package/dist/netcdf4-wasm/dist/slice.js +60 -0
- package/dist/netcdf4-wasm/dist/slice.js.map +1 -0
- package/dist/netcdf4-wasm/dist/test-setup.d.ts +13 -0
- package/dist/netcdf4-wasm/dist/test-setup.d.ts.map +1 -0
- package/dist/netcdf4-wasm/dist/test-setup.js +78 -0
- package/dist/netcdf4-wasm/dist/test-setup.js.map +1 -0
- package/dist/netcdf4-wasm/dist/types.d.ts +444 -0
- package/dist/netcdf4-wasm/dist/types.d.ts.map +1 -0
- package/dist/netcdf4-wasm/dist/types.js +3 -0
- package/dist/netcdf4-wasm/dist/types.js.map +1 -0
- package/dist/netcdf4-wasm/dist/variable.d.ts +36 -0
- package/dist/netcdf4-wasm/dist/variable.d.ts.map +1 -0
- package/dist/netcdf4-wasm/dist/variable.js +152 -0
- package/dist/netcdf4-wasm/dist/variable.js.map +1 -0
- package/dist/netcdf4-wasm/dist/wasm-module.d.ts +6 -0
- package/dist/netcdf4-wasm/dist/wasm-module.d.ts.map +1 -0
- package/dist/netcdf4-wasm/dist/wasm-module.js +1502 -0
- package/dist/netcdf4-wasm/dist/wasm-module.js.map +1 -0
- package/dist/netcdf4-wasm/package.json +78 -0
- package/dist/netcdf4-wasm.wasm +0 -0
- package/dist/types/colorsJson.d.ts +36 -0
- package/dist/types/netcdf.d.ts +70 -0
- package/dist/utils/color.d.ts +277 -0
- package/dist/utils/colorScales.d.ts +28 -0
- package/dist/utils/colorsJsonService.d.ts +24 -0
- package/dist/utils/dataProcessing.d.ts +64 -0
- package/dist/utils/errorHandling.d.ts +69 -0
- package/dist/utils/imageUtils.d.ts +29 -0
- package/dist/utils/performance.d.ts +75 -0
- package/dist/wasm/constants.d.ts +158 -0
- package/dist/wasm/constants.d.ts.map +1 -0
- package/dist/wasm/constants.js +249 -0
- package/dist/wasm/constants.js.map +1 -0
- package/dist/wasm/dimension.d.ts +9 -0
- package/dist/wasm/dimension.d.ts.map +1 -0
- package/dist/wasm/dimension.js +19 -0
- package/dist/wasm/dimension.js.map +1 -0
- package/dist/wasm/group.d.ts +35 -0
- package/dist/wasm/group.d.ts.map +1 -0
- package/dist/wasm/group.js +189 -0
- package/dist/wasm/group.js.map +1 -0
- package/dist/wasm/index.d.ts +17 -0
- package/dist/wasm/index.d.ts.map +1 -0
- package/dist/wasm/index.js +49 -0
- package/dist/wasm/index.js.map +1 -0
- package/dist/wasm/netcdf-getters.d.ts +120 -0
- package/dist/wasm/netcdf-getters.d.ts.map +1 -0
- package/dist/wasm/netcdf-getters.js +816 -0
- package/dist/wasm/netcdf-getters.js.map +1 -0
- package/dist/wasm/netcdf-worker.d.ts +2 -0
- package/dist/wasm/netcdf-worker.d.ts.map +1 -0
- package/dist/wasm/netcdf-worker.js +154 -0
- package/dist/wasm/netcdf-worker.js.map +1 -0
- package/dist/wasm/netcdf4-wasm.js +2 -0
- package/dist/wasm/netcdf4-wasm.wasm +0 -0
- package/dist/wasm/netcdf4.d.ts +218 -0
- package/dist/wasm/netcdf4.d.ts.map +1 -0
- package/dist/wasm/netcdf4.js +1049 -0
- package/dist/wasm/netcdf4.js.map +1 -0
- package/dist/wasm/slice.d.ts +57 -0
- package/dist/wasm/slice.d.ts.map +1 -0
- package/dist/wasm/slice.js +60 -0
- package/dist/wasm/slice.js.map +1 -0
- package/dist/wasm/test-setup.d.ts +13 -0
- package/dist/wasm/test-setup.d.ts.map +1 -0
- package/dist/wasm/test-setup.js +78 -0
- package/dist/wasm/test-setup.js.map +1 -0
- package/dist/wasm/types.d.ts +444 -0
- package/dist/wasm/types.d.ts.map +1 -0
- package/dist/wasm/types.js +3 -0
- package/dist/wasm/types.js.map +1 -0
- package/dist/wasm/variable.d.ts +36 -0
- package/dist/wasm/variable.d.ts.map +1 -0
- package/dist/wasm/variable.js +152 -0
- package/dist/wasm/variable.js.map +1 -0
- package/dist/wasm/wasm-module.d.ts +6 -0
- package/dist/wasm/wasm-module.d.ts.map +1 -0
- package/dist/wasm/wasm-module.js +1502 -0
- package/dist/wasm/wasm-module.js.map +1 -0
- package/dist/xm-netcdf-loader.cjs.js +2 -0
- package/dist/xm-netcdf-loader.cjs.js.map +1 -0
- package/dist/xm-netcdf-loader.es.js +18532 -0
- package/dist/xm-netcdf-loader.es.js.map +1 -0
- package/dist/xm-netcdf-loader.umd.js +2 -0
- package/dist/xm-netcdf-loader.umd.js.map +1 -0
- 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
|