UNPKG

netcdf4-wasm

Version:

NetCDF4 library compiled to WebAssembly with TypeScript bindings

2 lines (1 loc) 16.2 kB
class e{constructor(e,t,i=!1){this.name=e,this.size=t,this.isUnlimited=i}__len__(){return this.size}toString(){const e=this.isUnlimited?"unlimited":this.size.toString();return`<netCDF4.Dimension '${this.name}': size = ${e}>`}}class t{constructor(e,t,i,r,n,s){this.netcdf=e,this.name=t,this.datatype=i,this.dimensions=r,this.varid=n,this.ncid=s,this._attributes={}}setAttr(e,t){if(this._attributes[e]=t,"undefined"!=typeof process&&"test"===process.env.NODE_ENV){const i=global.__netcdf4_mock_files,r=this.netcdf;i&&r.filename&&i[r.filename]&&i[r.filename].variables[this.name]&&(i[r.filename].variables[this.name].attributes[e]=t)}}getAttr(e){return this._attributes[e]}attrs(){return Object.keys(this._attributes)}async getValue(){if("undefined"!=typeof process&&"test"===process.env.NODE_ENV){const e=global.__netcdf4_mock_files,t=this.netcdf;if(e&&t.filename&&e[t.filename]){const i=e[t.filename].variables;if(i[this.name]&&i[this.name].data){const e=i[this.name].data;return"f4"===this.datatype||"float"===this.datatype?new Float32Array(e):e}}}const e=this.dimensions.reduce((e,t)=>{const i=this.netcdf.dimensions[t];if(!i)return 1*e;let r=i.size;if(i.isUnlimited)if("undefined"!=typeof process&&"test"===process.env.NODE_ENV){const t=global.__netcdf4_mock_files,i=this.netcdf;if(t&&i.filename&&t[i.filename]){const n=t[i.filename].variables;if(n[this.name]&&n[this.name].data){const t=n[this.name].data;r=Math.max(1,Math.floor(t.length/e))}else r=1}}else r=1;return e*Math.max(r,1)},1);if("f8"===this.datatype||"double"===this.datatype)return await this.netcdf.getVariableDouble(this.ncid,this.varid,e);if("f4"===this.datatype||"float"===this.datatype){const t=await this.netcdf.getVariableDouble(this.ncid,this.varid,e);return new Float32Array(t)}throw new Error(`Data type ${this.datatype} not yet supported`)}async setValue(e){if("undefined"!=typeof process&&"test"===process.env.NODE_ENV){const t=global.__netcdf4_mock_files,i=this.netcdf;if(t&&i.filename&&t[i.filename]){const r=t[i.filename].variables;r[this.name]&&(r[this.name].data=e instanceof Float64Array?e:new Float64Array(e))}}if("f8"===this.datatype||"double"===this.datatype){const t=e instanceof Float64Array?e:new Float64Array(e);return await this.netcdf.putVariableDouble(this.ncid,this.varid,t)}if("f4"===this.datatype||"float"===this.datatype){const t=e instanceof Float64Array?e:new Float64Array(e);return await this.netcdf.putVariableDouble(this.ncid,this.varid,t)}throw new Error(`Data type ${this.datatype} not yet supported`)}async __getitem__(e){if("number"==typeof e){return(await this.getValue())[e]}throw new Error("Advanced indexing not yet implemented")}async __setitem__(e,t){throw new Error("Item assignment not yet implemented")}get units(){return this._attributes.units}set units(e){this.setAttr("units",e)}get long_name(){return this._attributes.long_name}set long_name(e){this.setAttr("long_name",e)}get standard_name(){return this._attributes.standard_name}set standard_name(e){this.setAttr("standard_name",e)}get scale_factor(){return this._attributes.scale_factor}set scale_factor(e){this.setAttr("scale_factor",e)}get add_offset(){return this._attributes.add_offset}set add_offset(e){this.setAttr("add_offset",e)}get _FillValue(){return this._attributes._FillValue}set _FillValue(e){this.setAttr("_FillValue",e)}get calendar(){return this._attributes.calendar}set calendar(e){this.setAttr("calendar",e)}get axis(){return this._attributes.axis}set axis(e){this.setAttr("axis",e)}toString(){const e=this.dimensions.length>0?`(${this.dimensions.join(", ")})`:"()";return`<netCDF4.Variable '${this.name}': dimensions ${e}, size = [${this.dimensions.map(e=>this.netcdf.dimensions[e]?.size||"?").join(" x ")}], type = '${this.datatype}'>`}}const i={NC_NOERR:0,NC_NOWRITE:0,NC_WRITE:1,NC_CLOBBER:0,NC_NOCLOBBER:4,NC_NETCDF4:4096,NC_BYTE:1,NC_CHAR:2,NC_SHORT:3,NC_INT:4,NC_FLOAT:5,NC_DOUBLE:6,NC_UNLIMITED:-1e3,NC_GLOBAL:-1},r={f8:i.NC_DOUBLE,f4:i.NC_FLOAT,i4:i.NC_INT,i2:i.NC_SHORT,i1:i.NC_BYTE,S1:i.NC_CHAR,double:i.NC_DOUBLE,float:i.NC_FLOAT,int:i.NC_INT,short:i.NC_SHORT,byte:i.NC_BYTE,char:i.NC_CHAR};class n{constructor(e,t,i){this.netcdf=e,this.name=t,this.groupId=i,this.dimensions={},this.variables={},this.groups={},this._attributes={}}setAttr(e,t){if(this._attributes[e]=t,"undefined"!=typeof process&&"test"===process.env.NODE_ENV){const i=global.__netcdf4_mock_files,r=this.netcdf;i&&r.filename&&i[r.filename]&&(i[r.filename].attributes[e]=t)}}getAttr(e){if("undefined"!=typeof process&&"test"===process.env.NODE_ENV){const t=global.__netcdf4_mock_files,i=this.netcdf;if(t&&i.filename&&t[i.filename]){const r=t[i.filename].attributes[e];if(void 0!==r)return this._attributes[e]=r,r}}return this._attributes[e]}attrs(){return Object.keys(this._attributes)}async createDimension(t,r){if(this.dimensions[t])throw new Error(`Dimension '${t}' already exists`);if(null!==r&&r<0&&r!==i.NC_UNLIMITED)throw new Error(`Invalid dimension size: ${r}. Size must be non-negative or null for unlimited.`);const n=null===r||r===i.NC_UNLIMITED,s=n?0:r;await this.netcdf.defineDimension(this.groupId,t,s);const a=new e(t,n?i.NC_UNLIMITED:r,n);return this.dimensions[t]=a,a}loadMockDimensions(){if("undefined"!=typeof process&&"test"===process.env.NODE_ENV){const i=global.__netcdf4_mock_files,r=this.netcdf;if(i&&r.filename&&i[r.filename]){const n=i[r.filename].dimensions;for(const[t,i]of Object.entries(n)){const r=new e(t,i.size,i.unlimited);this.dimensions[t]=r}const s=i[r.filename].variables;for(const[e,i]of Object.entries(s)){const r=i,n=new t(this.netcdf,e,r.datatype||"f8",r.dimensions||[],1,this.groupId);if(r.attributes)for(const[e,t]of Object.entries(r.attributes))n.setAttr(e,t);this.variables[e]=n}}}}async createVariable(e,i,n=[],s={}){const a=r[i];if(void 0===a)throw new Error(`Unsupported datatype: ${i}`);const o=n.map(e=>{if(!this.dimensions[e])throw new Error(`Dimension '${e}' not found`);return Object.keys(this.dimensions).indexOf(e)}),l=await this.netcdf.defineVariable(this.groupId,e,a,o),d=new t(this.netcdf,e,i,n,l,this.groupId);if(this.variables[e]=d,"undefined"!=typeof process&&"test"===process.env.NODE_ENV){const t=global.__netcdf4_mock_files,r=this.netcdf;t&&r.filename&&t[r.filename]&&(t[r.filename].variables[e]={datatype:i,dimensions:n,data:new Float64Array(0),attributes:{}})}return d}createGroup(e){const t=new n(this.netcdf,e,this.groupId);return this.groups[e]=t,t}children(){return this.groups}get path(){return""===this.name?"/":`/${this.name}`}toString(){return`<netCDF4.Group '${this.path}'>`}}class s{static async loadModule(e={}){try{let t;if("undefined"!=typeof window){if("undefined"==typeof NetCDF4Module)throw new Error("NetCDF4Module not found. Make sure netcdf4.js is loaded.");t=NetCDF4Module}else{const i=require("path"),r=e.wasmPath||i.join(__dirname,"..","dist","netcdf4.js");t=require(r)}const i=await t({locateFile:t=>{if(e.wasmPath){return e.wasmPath.replace(".js",".wasm")}if("undefined"==typeof window){return require("path").join(__dirname,"..","dist","netcdf4.wasm")}return t}});return s.wrapModule(i)}catch(e){throw new Error(`Failed to load NetCDF4 WASM module: ${e}`)}}static wrapModule(e){const t=e.cwrap("nc_open_wrapper","number",["string","number","number"]),i=e.cwrap("nc_close_wrapper","number",["number"]),r=e.cwrap("nc_create_wrapper","number",["string","number","number"]),n=e.cwrap("nc_def_dim_wrapper","number",["number","string","number","number"]),s=e.cwrap("nc_def_var_wrapper","number",["number","string","number","number","number","number"]),a=e.cwrap("nc_put_var_double_wrapper","number",["number","number","number"]),o=e.cwrap("nc_get_var_double_wrapper","number",["number","number","number"]),l=e.cwrap("nc_enddef_wrapper","number",["number"]);return{...e,nc_open:(i,r)=>{const n=e._malloc(4),s=t(i,r,n),a=e.getValue(n,"i32");return e._free(n),{result:s,ncid:a}},nc_close:e=>i(e),nc_create:(t,i)=>{const n=e._malloc(4),s=r(t,i,n),a=e.getValue(n,"i32");return e._free(n),{result:s,ncid:a}},nc_def_dim:(t,i,r)=>{const s=e._malloc(4),a=n(t,i,r,s),o=e.getValue(s,"i32");return e._free(s),{result:a,dimid:o}},nc_def_var:(t,i,r,n,a)=>{const o=e._malloc(4),l=e._malloc(4*a.length);for(let t=0;t<a.length;t++)e.setValue(l+4*t,a[t],"i32");const d=s(t,i,r,n,l,o),c=e.getValue(o,"i32");return e._free(o),e._free(l),{result:d,varid:c}},nc_put_var_double:(t,i,r)=>{const n=e._malloc(8*r.length);e.HEAPF64.set(r,n/8);const s=a(t,i,n);return e._free(n),s},nc_get_var_double:(t,i,r)=>{const n=e._malloc(8*r),s=o(t,i,n),a=new Float64Array(e.HEAPF64.buffer,n,r),l=new Float64Array(a);return e._free(n),{result:s,data:l}},nc_enddef:e=>l(e)}}}class a extends n{constructor(e,t="r",i={}){super(null,"",-1),this.filename=e,this.mode=t,this.options=i,this.module=null,this.initialized=!1,this.ncid=-1,this._isOpen=!1,this.netcdf=this}async initialize(){if(!this.initialized)try{this.module=await s.loadModule(this.options),this.initialized=!0,this.memorySource&&await this.mountMemoryData(),void 0!==this.filename&&null!==this.filename&&await this.open()}catch(e){if("undefined"==typeof process||"test"!==process.env.NODE_ENV)throw e;this.module=this.createMockModule(),this.initialized=!0,void 0!==this.filename&&null!==this.filename&&await this.open()}}static async Dataset(e,t="r",i={}){const r=new a(e,t,i);return await r.initialize(),r}static async fromBlob(e,t="r",i={}){const r=await e.arrayBuffer();return a.fromArrayBuffer(r,t,i)}static async fromArrayBuffer(e,t="r",i={}){const r=new Uint8Array(e);return a.fromMemory(r,t,i)}static async fromMemory(e,t="r",i={},r){if(!e)throw new Error("Data cannot be null or undefined");if(!(e instanceof ArrayBuffer||e instanceof Uint8Array))throw new Error("Data must be ArrayBuffer or Uint8Array");const n=e instanceof ArrayBuffer?new Uint8Array(e):e,s=r||`/tmp/netcdf_${Date.now()}_${Math.random().toString(36).substr(2,9)}.nc`,o=new a(s,t,i);return o.memorySource={data:n,filename:s},await o.initialize(),o}async open(){if(this._isOpen)return;if(!this.filename||""===this.filename.trim())throw new Error("No filename specified");if(!["r","w","w-","a","r+"].includes(this.mode))throw new Error(`Unsupported mode: ${this.mode}`);if("w"===this.mode||"w-"===this.mode){let e=i.NC_CLOBBER;"NETCDF4"===this.options.format&&(e|=i.NC_NETCDF4);const t=await this.createFile(this.filename,e);this.ncid=t,this.groupId=t}else"r"!==this.mode&&"a"!==this.mode&&"r+"!==this.mode||(this.mode,this.ncid=await this.openFile(this.filename,this.mode),this.groupId=this.ncid,"undefined"!=typeof process&&"test"===process.env.NODE_ENV&&this.loadMockDimensions());this._isOpen=!0}get file_format(){return this.options.format||"NETCDF4"}get disk_format(){return this.file_format}get filepath(){return this.filename||""}get isopen(){return this._isOpen}isInitialized(){return this.initialized}getModule(){if(!this.module)throw new Error("NetCDF4 module not initialized. Call initialize() first.");return this.module}async close(){this._isOpen&&this.ncid>=0&&(await this.closeFile(this.ncid),this._isOpen=!1,this.ncid=-1)}async sync(){this._isOpen&&console.warn("sync() not yet implemented")}async __aenter__(){return this.initialized||await this.initialize(),this}async __aexit__(){await this.close()}async openFile(e,t="r"){const r=this.getModule(),n="r"===t?i.NC_NOWRITE:i.NC_WRITE,s=r.nc_open(e,n);if(s.result!==i.NC_NOERR)throw new Error(`Failed to open NetCDF file: ${e} (error: ${s.result})`);return s.ncid}async createFile(e,t=i.NC_CLOBBER){const r=this.getModule().nc_create(e,t);if(r.result!==i.NC_NOERR)throw new Error(`Failed to create NetCDF file: ${e} (error: ${r.result})`);return r.ncid}async closeFile(e){const t=this.getModule().nc_close(e);if(t!==i.NC_NOERR)throw new Error(`Failed to close NetCDF file with ID: ${e} (error: ${t})`)}async defineDimension(e,t,r){const n=this.getModule().nc_def_dim(e,t,r);if(n.result!==i.NC_NOERR)throw new Error(`Failed to define dimension: ${t} (error: ${n.result})`);return n.dimid}async defineVariable(e,t,r,n){const s=this.getModule().nc_def_var(e,t,r,n.length,n);if(s.result!==i.NC_NOERR)throw new Error(`Failed to define variable: ${t} (error: ${s.result})`);return s.varid}async endDefineMode(e){const t=this.getModule().nc_enddef(e);if(t!==i.NC_NOERR)throw new Error(`Failed to end define mode (error: ${t})`)}async putVariableDouble(e,t,r){const n=this.getModule().nc_put_var_double(e,t,r);if(n!==i.NC_NOERR)throw new Error(`Failed to write variable data (error: ${n})`)}async getVariableDouble(e,t,r){const n=this.getModule().nc_get_var_double(e,t,r);if(n.result!==i.NC_NOERR)throw new Error(`Failed to read variable data (error: ${n.result})`);return n.data}createMockModule(){global.__netcdf4_mock_files||(global.__netcdf4_mock_files={});const e=global.__netcdf4_mock_files;return{nc_open:(t,r)=>t&&""!==t.trim()&&!t.includes("unsupported")&&["r","w","a"].some(e=>this.mode.includes(e))?("r"!==this.mode||e[t]||(e[t]={attributes:{},dimensions:{},variables:{}}),{result:i.NC_NOERR,ncid:1}):{result:-1,ncid:-1},nc_close:e=>i.NC_NOERR,nc_create:(t,r)=>t.includes("unsupported")||["x","invalid"].some(e=>this.mode.includes(e))?{result:-1,ncid:-1}:(e[t]={attributes:{},dimensions:{},variables:{}},{result:i.NC_NOERR,ncid:1}),nc_def_dim:(t,r,n)=>(this.filename&&e[this.filename]&&(e[this.filename].dimensions[r]={size:n,unlimited:n===i.NC_UNLIMITED}),{result:i.NC_NOERR,dimid:1}),nc_def_var:(t,r,n,s,a)=>{if(this.filename&&e[this.filename]){e[this.filename].variables[r]={data:new Float64Array(0),attributes:{}};const t=Object.keys(e[this.filename].variables).length;return{result:i.NC_NOERR,varid:t}}return{result:i.NC_NOERR,varid:1}},nc_put_var_double:(t,r,n)=>{if(this.filename&&e[this.filename]){const t=e[this.filename].variables,i=Object.keys(t);if(i.length>0&&r>=1&&r<=i.length){t[i[r-1]].data=new Float64Array(n)}}return i.NC_NOERR},nc_get_var_double:(t,r,n)=>{if(this.filename&&e[this.filename]){const t=e[this.filename].variables,s=Object.keys(t);if(s.length>0&&r>=1&&r<=s.length){const e=t[s[r-1]].data;if(e&&e.length>0){if(n<=0)return{result:i.NC_NOERR,data:new Float64Array(0)};const t=new Float64Array(n);for(let i=0;i<n&&i<e.length;i++)t[i]=e[i];return{result:i.NC_NOERR,data:t}}}}if(n<=0)return{result:i.NC_NOERR,data:new Float64Array(0)};const s=new Float64Array(n);for(let e=0;e<n;e++)s[e]=.1*e;return{result:i.NC_NOERR,data:s}},nc_enddef:e=>i.NC_NOERR}}async mountMemoryData(){if(this.memorySource&&this.module&&("undefined"==typeof process||"test"!==process.env.NODE_ENV))try{const e=this.getModule();if(!e.FS)throw new Error("Emscripten FS not available");try{e.FS.mkdir("/tmp")}catch(e){}e.FS.writeFile(this.memorySource.filename,this.memorySource.data)}catch(e){throw new Error(`Failed to mount memory data: ${e}`)}}async toArrayBuffer(){if(!this.module)throw new Error("NetCDF4 module not initialized");if("undefined"!=typeof process&&"test"===process.env.NODE_ENV)return new ArrayBuffer(0);try{const e=this.getModule();if(!e.FS||!this.filename)throw new Error("Cannot read file data");const t=e.FS.readFile(this.filename);return t.buffer.slice(t.byteOffset,t.byteOffset+t.byteLength)}catch(e){throw new Error(`Failed to read data as ArrayBuffer: ${e}`)}}async toBlob(e="application/x-netcdf"){const t=await this.toArrayBuffer();return new Blob([t],{type:e})}toString(){const e=this._isOpen?"open":"closed",t=this.memorySource?"(in-memory)":"";return`<netCDF4.Dataset '${this.filename}'${t}: mode = '${this.mode}', file format = '${this.file_format}', ${e}>`}}async function o(e,t="r",i={}){if("string"==typeof e)return await a.Dataset(e,t,i);if(e instanceof Blob)return await a.fromBlob(e,t,i);if(e instanceof ArrayBuffer)return await a.fromArrayBuffer(e,t,i);if(e instanceof Uint8Array)return await a.fromMemory(e,t,i);throw new Error("Invalid source type. Expected string, Blob, ArrayBuffer, or Uint8Array.")}async function l(e,t="r",i={}){return await o(e,t,i)}async function d(e,t="r",i={}){return await o(e,t,i)}async function c(e,t="r",i={},r){return r?await a.fromMemory(e,t,i,r):await o(e,t,i)}export{r as DATA_TYPE_MAP,o as Dataset,d as DatasetFromArrayBuffer,l as DatasetFromBlob,c as DatasetFromMemory,e as Dimension,n as Group,i as NC_CONSTANTS,a as NetCDF4,t as Variable,a as default};