|
| 1 | +# ReadAP WASM Library - API Summary |
| 2 | + |
| 3 | +A powerful TypeScript/WebAssembly library for OpenDAP data access with automatic fetching, xarray-style selection, and efficient typed array handling. |
| 4 | + |
| 5 | +## Features |
| 6 | + |
| 7 | +### ✅ **Complete Implementation** |
| 8 | + |
| 9 | +- **Automatic Data Fetching**: Uses JavaScript fetch API for seamless data access |
| 10 | +- **Network-free Core**: The underlying readap library remains portable across platforms |
| 11 | +- **xarray-style Selection**: Intuitive `isel` (index) and `sel` (value) selection patterns |
| 12 | +- **Nearest Neighbor Lookup**: Automatic coordinate value → index mapping |
| 13 | +- **Typed Arrays**: Efficient JavaScript typed arrays (Float64Array, Int32Array, etc.) |
| 14 | +- **Lazy Loading**: Coordinates and data loaded only when needed |
| 15 | +- **Error Handling**: Comprehensive HTTP and parsing error handling |
| 16 | + |
| 17 | +## High-Level API |
| 18 | + |
| 19 | +### Dataset Creation |
| 20 | + |
| 21 | +```typescript |
| 22 | +// Automatic metadata loading |
| 23 | +const dataset = await OpenDAPDataset.fromURL("http://example.com/data.nc"); |
| 24 | + |
| 25 | +// Manual control (lazy loading) |
| 26 | +const dataset = OpenDAPDataset.fromURLLazy("http://example.com/data.nc"); |
| 27 | +await dataset.parseDAS(await fetch(dataset.dasUrl()).then(r => r.text())); |
| 28 | +await dataset.parseDDS(await fetch(dataset.ddsUrl()).then(r => r.text())); |
| 29 | +``` |
| 30 | + |
| 31 | +### Data Access |
| 32 | + |
| 33 | +```typescript |
| 34 | +// Simple variable access |
| 35 | +const tempData = await dataset.getVariable("temperature"); |
| 36 | +console.log(tempData.data); // Float64Array or appropriate typed array |
| 37 | + |
| 38 | +// Multiple variables |
| 39 | +const data = await dataset.getVariables(["temperature", "pressure"]); |
| 40 | + |
| 41 | +// With constraints |
| 42 | +const selection = dataset.isel({ time: 0, lat: [10, 20] }); |
| 43 | +const slicedData = await dataset.getVariable("temperature", selection); |
| 44 | +``` |
| 45 | + |
| 46 | +### Selection API |
| 47 | + |
| 48 | +```typescript |
| 49 | +// Index-based selection (isel) |
| 50 | +const indexSel = dataset.isel({ |
| 51 | + time: { type: "single", value: 0 }, |
| 52 | + lat: { type: "range", start: 10, end: 20 }, |
| 53 | + lon: { type: "multiple", values: [0, 5, 10] } |
| 54 | +}); |
| 55 | + |
| 56 | +// Value-based selection (sel) with nearest neighbor |
| 57 | +const valueSel = dataset.sel({ |
| 58 | + time: "2023-01-15", // nearest neighbor to time coordinate |
| 59 | + lat: [40.0, 50.0], // range between coordinate values |
| 60 | + lon: -74.0 // single coordinate value (nearest neighbor) |
| 61 | +}); |
| 62 | + |
| 63 | +// Chained selections |
| 64 | +const combined = dataset |
| 65 | + .isel({ time: 0 }) |
| 66 | + .sel({ lat: [40, 50] }); |
| 67 | +``` |
| 68 | + |
| 69 | +### Coordinate Management |
| 70 | + |
| 71 | +```typescript |
| 72 | +// Automatic coordinate loading for sel operations |
| 73 | +await dataset.loadCoordinates("time"); |
| 74 | +await dataset.loadCoordinates("lat"); |
| 75 | + |
| 76 | +// Manual coordinate addition |
| 77 | +const timeCoords = new Float64Array([0, 6, 12, 18, 24]); // hours |
| 78 | +dataset.addCoordinates("time", timeCoords); |
| 79 | +``` |
| 80 | + |
| 81 | +## Low-Level API |
| 82 | + |
| 83 | +### URL Building |
| 84 | + |
| 85 | +```typescript |
| 86 | +const urlBuilder = new OpenDAPUrlBuilder("http://example.com/data"); |
| 87 | +console.log(urlBuilder.dasUrl()); // http://example.com/data.das |
| 88 | +console.log(urlBuilder.ddsUrl()); // http://example.com/data.dds |
| 89 | +console.log(urlBuilder.dodsUrl("temperature[0:10]")); // with constraints |
| 90 | +``` |
| 91 | + |
| 92 | +### Constraint Building |
| 93 | + |
| 94 | +```typescript |
| 95 | +const builder = new ConstraintBuilder() |
| 96 | + .isel({ time: { type: "single", value: 0 } }) |
| 97 | + .sel({ lat: { type: "range", min: 40.0, max: 50.0 } }); |
| 98 | + |
| 99 | +console.log(builder.build()); // "time[0],lat[nearest_indices]" |
| 100 | +``` |
| 101 | + |
| 102 | +### Direct Parsing |
| 103 | + |
| 104 | +```typescript |
| 105 | +// Parse OpenDAP formats directly |
| 106 | +const dasResult = OpenDAPDataset.fromDAS(dasText); |
| 107 | +const ddsResult = OpenDAPDataset.fromDDS(ddsText); |
| 108 | +const dodsResult = dataset.parseDODS(dodsBytes); // Uint8Array → Object with typed arrays |
| 109 | +``` |
| 110 | + |
| 111 | +## Example Usage |
| 112 | + |
| 113 | +```typescript |
| 114 | +import init, { OpenDAPDataset } from '@readap-wasm/readap-wasm'; |
| 115 | + |
| 116 | +async function main() { |
| 117 | + await init(); |
| 118 | + |
| 119 | + // Load dataset with automatic metadata fetching |
| 120 | + const dataset = await OpenDAPDataset.fromURL('http://example.com/ocean.nc'); |
| 121 | + |
| 122 | + // Check available variables |
| 123 | + console.log('Variables:', dataset.getVariableNames()); |
| 124 | + |
| 125 | + // Load coordinates for value-based selection |
| 126 | + await dataset.loadCoordinates('time'); |
| 127 | + await dataset.loadCoordinates('lat'); |
| 128 | + await dataset.loadCoordinates('lon'); |
| 129 | + |
| 130 | + // Select data using coordinate values (nearest neighbor) |
| 131 | + const selection = dataset.sel({ |
| 132 | + time: "2023-01-15T12:00:00Z", |
| 133 | + lat: [40.0, 45.0], // latitude range |
| 134 | + lon: -70.0 // single longitude |
| 135 | + }); |
| 136 | + |
| 137 | + // Fetch temperature data with selection |
| 138 | + const tempData = await dataset.getVariable('temperature', selection); |
| 139 | + console.log('Temperature:', tempData.data); // Float32Array or Float64Array |
| 140 | + |
| 141 | + // Get multiple variables efficiently |
| 142 | + const oceanData = await dataset.getVariables(['temperature', 'salinity', 'velocity']); |
| 143 | + |
| 144 | + // Chain selections for complex queries |
| 145 | + const surface = dataset |
| 146 | + .sel({ depth: 0 }) // surface level |
| 147 | + .isel({ time: [0, 1, 2] }) // first 3 time steps |
| 148 | + .sel({ lat: [35, 45], lon: [-80, -60] }); // geographic subset |
| 149 | + |
| 150 | + const surfaceTemp = await dataset.getVariable('temperature', surface); |
| 151 | +} |
| 152 | +``` |
| 153 | + |
| 154 | +## Architecture Benefits |
| 155 | + |
| 156 | +1. **Portable Core**: The readap library has no network dependencies, making it usable in any Rust environment |
| 157 | +2. **Efficient WASM**: Direct memory transfer between Rust and JavaScript using typed arrays |
| 158 | +3. **Smart Caching**: Coordinates are cached to avoid redundant network requests |
| 159 | +4. **Error Resilience**: Comprehensive error handling for network failures and data parsing issues |
| 160 | +5. **TypeScript Ready**: Full type safety with appropriate typed array selection based on data types |
| 161 | + |
| 162 | +## Performance Features |
| 163 | + |
| 164 | +- **Zero-copy data transfer** where possible between WASM and JavaScript |
| 165 | +- **Lazy coordinate loading** - coordinates fetched only when needed for `sel` operations |
| 166 | +- **Efficient nearest neighbor** lookup using binary search algorithms |
| 167 | +- **Minimal network requests** through intelligent constraint building |
| 168 | +- **Browser optimization** with proper fetch API usage and CORS support |
| 169 | + |
| 170 | +This implementation provides both the low-level control needed for advanced users and the high-level convenience required for typical scientific data workflows. |
0 commit comments