whitebox-wasm 0.5.1 → 0.6.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/README.md CHANGED
@@ -126,12 +126,42 @@ for (const t of tiles) {
126
126
 
127
127
  - `new CogStream(headerBytes)` - parse the IFD chain + tile index (throws if the
128
128
  prefix is too short; fetch more and retry).
129
+ - `first_ifd_offset(headerBytes)` - where the directory actually is (see below).
130
+ - `CogStream.from_windows(prefix, tailOffset, tail)` - parse from a front prefix
131
+ plus a window elsewhere in the file.
129
132
  - `num_levels`, `epsg`, `nodata`, `geo_transform()`, `levels_json()`
130
133
  - `bounding_box()`, `center()`, `center_lonlat()`, `bounds_lonlat()` (same semantics as `GeoTiffReader`)
131
134
  - `tiles_for_window(level, x, y, w, h)` -> JSON `[{col,row,offset,length}]`
132
135
  - `tile_range(level, col, row)` -> `[offset, length]`
133
136
  - `decode_tile_f64(level, tileBytes)` -> `Float64Array` (one decoded tile)
134
137
 
138
+ #### Plain GeoTIFFs, whose directory sits at the end
139
+
140
+ A COG keeps every IFD at the front of the file, so a 64 KB prefix reaches it. A
141
+ **plain** GeoTIFF - what GDAL and libtiff write unless you ask for a COG - puts
142
+ the directory *after* the pixel data, so growing a front-of-file prefix can only
143
+ reach it by downloading the entire file. Read the offset first and fetch that
144
+ region instead:
145
+
146
+ ```js
147
+ import init, { CogStream, first_ifd_offset } from "whitebox-wasm";
148
+
149
+ // An open-ended `Range: bytes=N-` asks for everything from N to the end, so the
150
+ // file's length never has to be looked up.
151
+ const range = (a, b) => fetch(url, { headers: { Range: `bytes=${a}-${b ?? ""}` } })
152
+ .then(r => r.arrayBuffer()).then(b => new Uint8Array(b));
153
+
154
+ const prefix = await range(0, 16383);
155
+ const ifd = first_ifd_offset(prefix); // 74026916 on a 74 MB file
156
+ const stream = ifd < prefix.length
157
+ ? new CogStream(prefix) // COG: it is already in hand
158
+ : CogStream.from_windows(prefix, ifd, await range(ifd));
159
+ ```
160
+
161
+ That reads under 1 MB of a 74 MB raster. If a tag array happens to sit *before*
162
+ the directory, `from_windows` throws `need more header bytes at offset N`; start
163
+ the tail earlier and retry.
164
+
135
165
  Use a higher `level` (overview) for zoomed-out views. See
136
166
  [`examples/cog-stream.mjs`](../../examples/cog-stream.mjs) for a full window read
137
167
  that fetches only the tiles it needs (about 13% of a 5.7 MiB file for a 256x256
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "whitebox-wasm",
3
3
  "type": "module",
4
4
  "description": "Pure-Rust geospatial toolkit (raster, vector, LiDAR, projections) compiled to WebAssembly",
5
- "version": "0.5.1",
5
+ "version": "0.6.0",
6
6
  "license": "MIT OR Apache-2.0",
7
7
  "repository": {
8
8
  "type": "git",
package/whitebox-cli.wasm CHANGED
Binary file
@@ -75,6 +75,11 @@ export class CogBuilder {
75
75
  * 2. Pick a level (0 = full res, higher = overviews) and a pixel window.
76
76
  * 3. `tiles_for_window(level, x, y, w, h)` returns the tiles and their byte
77
77
  * ranges; range-fetch each, then `decode_tile_f64(level, bytes)`.
78
+ *
79
+ * A *plain* (non-COG) GeoTIFF, as GDAL and libtiff write one by default, keeps
80
+ * its directory at the **end** of the file, so no front-of-file prefix short of
81
+ * the whole file can reach it. Use `first_ifd_offset` to locate the directory
82
+ * and `CogStream.from_windows` to parse it from a small tail window.
78
83
  */
79
84
  export class CogStream {
80
85
  free(): void;
@@ -101,6 +106,19 @@ export class CogStream {
101
106
  * come back full-size; clip to the image/window on the JS side.
102
107
  */
103
108
  decode_tile_f64(level: number, tile_bytes: Uint8Array): Float64Array;
109
+ /**
110
+ * Parse the tile layout from two disjoint windows of the file: a
111
+ * front-of-file `prefix` and a `tail` starting at absolute byte offset
112
+ * `tail_offset`.
113
+ *
114
+ * This is the path for a plain GeoTIFF whose directory sits at the end of
115
+ * the file. Fetch the first few kilobytes plus the region from
116
+ * `first_ifd_offset` to the end and pass both; the bytes in between are
117
+ * pixel data that header parsing never reads. If a tag array happens to sit
118
+ * *before* the directory, this throws "need more header bytes at offset N" —
119
+ * widen the tail to cover N and retry.
120
+ */
121
+ static from_windows(prefix: Uint8Array, tail_offset: number, tail: Uint8Array): CogStream;
104
122
  /**
105
123
  * Level-0 geo-transform `[x_origin, pixel_width, row_rot, y_origin, col_rot,
106
124
  * pixel_height]`, or empty if not georeferenced.
@@ -254,6 +272,17 @@ export function __start(): void;
254
272
  */
255
273
  export function convex_hull(points_xy: Float64Array): Float64Array;
256
274
 
275
+ /**
276
+ * Offset of a GeoTIFF's first IFD, read from the file's first 8 (classic TIFF)
277
+ * or 16 (BigTIFF) bytes. An offset past the prefix you already hold means the
278
+ * directory lives further into the file: fetch that region and hand it to
279
+ * `CogStream.from_windows`.
280
+ *
281
+ * Throws on an offset a JS number cannot hold exactly, which only a corrupt
282
+ * header produces: passing it back would address the wrong bytes.
283
+ */
284
+ export function first_ifd_offset(header_bytes: Uint8Array): number;
285
+
257
286
  /**
258
287
  * Decode only a GeoTIFF's header and return its metadata as JSON. O(header)
259
288
  * memory, so it works on multi-gigabyte rasters that whole-image reads cannot
@@ -376,6 +405,7 @@ export interface InitOutput {
376
405
  readonly cogstream_center_lonlat: (a: number, b: number) => void;
377
406
  readonly cogstream_decode_tile_f64: (a: number, b: number, c: number, d: number, e: number) => void;
378
407
  readonly cogstream_epsg: (a: number) => number;
408
+ readonly cogstream_from_windows: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
379
409
  readonly cogstream_geo_transform: (a: number, b: number) => void;
380
410
  readonly cogstream_levels_json: (a: number, b: number) => void;
381
411
  readonly cogstream_new: (a: number, b: number, c: number) => void;
@@ -384,6 +414,7 @@ export interface InitOutput {
384
414
  readonly cogstream_tile_range: (a: number, b: number, c: number, d: number, e: number) => void;
385
415
  readonly cogstream_tiles_for_window: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void;
386
416
  readonly convex_hull: (a: number, b: number, c: number) => void;
417
+ readonly first_ifd_offset: (a: number, b: number, c: number) => void;
387
418
  readonly geotiff_info: (a: number, b: number, c: number) => void;
388
419
  readonly geotiff_read_band_f64: (a: number, b: number, c: number, d: number) => void;
389
420
  readonly geotiff_stats: (a: number, b: number, c: number) => void;
package/whitebox_wasm.js CHANGED
@@ -206,8 +206,19 @@ if (Symbol.dispose) CogBuilder.prototype[Symbol.dispose] = CogBuilder.prototype.
206
206
  * 2. Pick a level (0 = full res, higher = overviews) and a pixel window.
207
207
  * 3. `tiles_for_window(level, x, y, w, h)` returns the tiles and their byte
208
208
  * ranges; range-fetch each, then `decode_tile_f64(level, bytes)`.
209
+ *
210
+ * A *plain* (non-COG) GeoTIFF, as GDAL and libtiff write one by default, keeps
211
+ * its directory at the **end** of the file, so no front-of-file prefix short of
212
+ * the whole file can reach it. Use `first_ifd_offset` to locate the directory
213
+ * and `CogStream.from_windows` to parse it from a small tail window.
209
214
  */
210
215
  export class CogStream {
216
+ static __wrap(ptr) {
217
+ const obj = Object.create(CogStream.prototype);
218
+ obj.__wbg_ptr = ptr;
219
+ CogStreamFinalization.register(obj, obj.__wbg_ptr, obj);
220
+ return obj;
221
+ }
211
222
  __destroy_into_raw() {
212
223
  const ptr = this.__wbg_ptr;
213
224
  this.__wbg_ptr = 0;
@@ -322,6 +333,41 @@ export class CogStream {
322
333
  const ret = wasm.cogstream_epsg(this.__wbg_ptr);
323
334
  return ret === Number.MAX_SAFE_INTEGER ? undefined : ret;
324
335
  }
336
+ /**
337
+ * Parse the tile layout from two disjoint windows of the file: a
338
+ * front-of-file `prefix` and a `tail` starting at absolute byte offset
339
+ * `tail_offset`.
340
+ *
341
+ * This is the path for a plain GeoTIFF whose directory sits at the end of
342
+ * the file. Fetch the first few kilobytes plus the region from
343
+ * `first_ifd_offset` to the end and pass both; the bytes in between are
344
+ * pixel data that header parsing never reads. If a tag array happens to sit
345
+ * *before* the directory, this throws "need more header bytes at offset N" —
346
+ * widen the tail to cover N and retry.
347
+ * @param {Uint8Array} prefix
348
+ * @param {number} tail_offset
349
+ * @param {Uint8Array} tail
350
+ * @returns {CogStream}
351
+ */
352
+ static from_windows(prefix, tail_offset, tail) {
353
+ try {
354
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
355
+ const ptr0 = passArray8ToWasm0(prefix, wasm.__wbindgen_export2);
356
+ const len0 = WASM_VECTOR_LEN;
357
+ const ptr1 = passArray8ToWasm0(tail, wasm.__wbindgen_export2);
358
+ const len1 = WASM_VECTOR_LEN;
359
+ wasm.cogstream_from_windows(retptr, ptr0, len0, tail_offset, ptr1, len1);
360
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
361
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
362
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
363
+ if (r2) {
364
+ throw takeObject(r1);
365
+ }
366
+ return CogStream.__wrap(r0);
367
+ } finally {
368
+ wasm.__wbindgen_add_to_stack_pointer(16);
369
+ }
370
+ }
325
371
  /**
326
372
  * Level-0 geo-transform `[x_origin, pixel_width, row_rot, y_origin, col_rot,
327
373
  * pixel_height]`, or empty if not georeferenced.
@@ -1016,6 +1062,35 @@ export function convex_hull(points_xy) {
1016
1062
  }
1017
1063
  }
1018
1064
 
1065
+ /**
1066
+ * Offset of a GeoTIFF's first IFD, read from the file's first 8 (classic TIFF)
1067
+ * or 16 (BigTIFF) bytes. An offset past the prefix you already hold means the
1068
+ * directory lives further into the file: fetch that region and hand it to
1069
+ * `CogStream.from_windows`.
1070
+ *
1071
+ * Throws on an offset a JS number cannot hold exactly, which only a corrupt
1072
+ * header produces: passing it back would address the wrong bytes.
1073
+ * @param {Uint8Array} header_bytes
1074
+ * @returns {number}
1075
+ */
1076
+ export function first_ifd_offset(header_bytes) {
1077
+ try {
1078
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
1079
+ const ptr0 = passArray8ToWasm0(header_bytes, wasm.__wbindgen_export2);
1080
+ const len0 = WASM_VECTOR_LEN;
1081
+ wasm.first_ifd_offset(retptr, ptr0, len0);
1082
+ var r0 = getDataViewMemory0().getFloat64(retptr + 8 * 0, true);
1083
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
1084
+ var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
1085
+ if (r3) {
1086
+ throw takeObject(r2);
1087
+ }
1088
+ return r0;
1089
+ } finally {
1090
+ wasm.__wbindgen_add_to_stack_pointer(16);
1091
+ }
1092
+ }
1093
+
1019
1094
  /**
1020
1095
  * Decode only a GeoTIFF's header and return its metadata as JSON. O(header)
1021
1096
  * memory, so it works on multi-gigabyte rasters that whole-image reads cannot
Binary file