cog-tiler-wasm 0.2.0 → 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/README.md CHANGED
@@ -210,10 +210,12 @@ fully transparent.
210
210
  colormaps (see [above](#titiler-style-cog-api)). Next: band-math
211
211
  **`expression`** and `color_formula`.
212
212
  - **Warping** of projected/4326 sources and **paletted/categorical** rendering
213
- are done in [`cog-tiler.js`](cog-tiler.js) (proj4js + geotiff.js). Next: expose
214
- the source proj string + color table **upstream in `whitebox-wasm`** (it
215
- already parses both) to drop the geotiff.js dependency, then move the warp into
216
- the Rust crate (`proj4rs`).
213
+ are done in [`cog-tiler.js`](cog-tiler.js) (proj4js + geotiff.js). **Planar**
214
+ (`INTERLEAVE=BAND`) multi-band COGs are read per-band via geotiff.js too, since
215
+ whitebox-wasm's streaming decoder is chunky-only. Next: planar support
216
+ **upstream in `whitebox-wasm`** (and exposing its proj string + color table) to
217
+ drop the geotiff.js dependency, then move the warp into the Rust crate
218
+ (`proj4rs`).
217
219
  - **Edge / WASI serving** - run the same module as a serverless XYZ endpoint
218
220
  near the data, not only in the browser.
219
221
  - **STAC / mosaics** - multi-asset orchestration.
package/cog-tiler.js CHANGED
@@ -239,7 +239,18 @@ export async function openCog(source) {
239
239
  if (!Array.isArray(levels) || levels.length === 0) {
240
240
  throw new Error("levels_json() returned no levels");
241
241
  }
242
- const base = { url: label, range, stream, levels, gt, nodata: stream.nodata };
242
+ // Open the GeoTIFF with geotiff.js when we need the CRS (non-3857) or to check
243
+ // the planar config (multi-band). whitebox-wasm's streaming decoder is
244
+ // chunky-only, so planar (INTERLEAVE=BAND) multi-band COGs are read per-band
245
+ // through geotiff.js instead (see _assembleWindow / point).
246
+ const multiBand = levels[0].bands > 1;
247
+ let tiff = null, img = null, planar = false;
248
+ if (stream.epsg !== 3857 || multiBand) {
249
+ tiff = await openTiff();
250
+ img = await tiff.getImage();
251
+ planar = multiBand && img.fileDirectory.PlanarConfiguration === 2;
252
+ }
253
+ const base = { url: label, range, stream, levels, gt, nodata: stream.nodata, tiff, planar };
243
254
 
244
255
  if (stream.epsg === 3857) {
245
256
  return new CogSource({
@@ -253,8 +264,6 @@ export async function openCog(source) {
253
264
  }
254
265
 
255
266
  // Warp path: read the real source CRS + optional palette from the header.
256
- const tiff = await openTiff();
257
- const img = await tiff.getImage();
258
267
  const srcDef = geokeysToProj4.toProj4(img.getGeoKeys()).proj4;
259
268
  if (!srcDef) throw new Error("could not derive source CRS from GeoTIFF geokeys");
260
269
  const toSource = proj4("EPSG:3857", srcDef); // forward: mercator -> source
@@ -326,10 +335,27 @@ export class CogSource {
326
335
  return p;
327
336
  }
328
337
 
329
- // Fetch (parallel, cached) + decode the source tiles covering a level pixel
330
- // window and assemble band `band` (0-based) into a row-major f64 buffer
331
- // (NaN = no data). decode_tile_f64 is pixel-interleaved, so stride by `bands`.
338
+ /** geotiff.js image for an overview level (cached). Used for planar reads. */
339
+ _tiffImage(level) {
340
+ if (!this._imgs) this._imgs = new Map();
341
+ let p = this._imgs.get(level);
342
+ if (!p) {
343
+ p = this.tiff.getImage(level);
344
+ this._imgs.set(level, p);
345
+ }
346
+ return p;
347
+ }
348
+
349
+ // Fetch + decode band `band` (0-based) over a level pixel window into a
350
+ // row-major buffer. Chunky COGs go through whitebox (cached, NaN for gaps);
351
+ // planar (INTERLEAVE=BAND) COGs are read per-band via geotiff.js, which
352
+ // whitebox's chunky-only streaming decoder can't address.
332
353
  async _assembleWindow(level, x, y, w, h, band = 0) {
354
+ if (this.planar) {
355
+ const img = await this._tiffImage(level);
356
+ const rasters = await img.readRasters({ window: [x, y, x + w, y + h], samples: [band] });
357
+ return rasters[0]; // typed array, length w*h, row-major
358
+ }
333
359
  const lv = this.levels[level];
334
360
  const tiles = JSON.parse(this.stream.tiles_for_window(level, x, y, w, h));
335
361
  const decoded = await Promise.all(tiles.map((t) => this._getTile(level, t)));
@@ -570,14 +596,23 @@ export class CogSource {
570
596
  if (col < 0 || col >= l0.width || row < 0 || row >= l0.height) {
571
597
  return { coordinates: [lon, lat], values: [], band_names: [], outside: true };
572
598
  }
573
- const tcol = Math.floor(col / l0.tile_width), trow = Math.floor(row / l0.tile_height);
574
- const [off, len] = Array.from(this.stream.tile_range(0, tcol, trow));
575
- const px = await this._getTile(0, { col: tcol, row: trow, offset: off, length: len });
576
- const base = ((row % l0.tile_height) * l0.tile_width + (col % l0.tile_width)) * l0.bands;
577
599
  const bands = bidx ? bidx.map((b) => b - 1) : Array.from({ length: l0.bands }, (_, i) => i);
600
+ let values;
601
+ if (this.planar) {
602
+ // Planar: whitebox can't address bands 1..n; read the pixel via geotiff.js.
603
+ const img = await this._tiffImage(0);
604
+ const r = await img.readRasters({ window: [col, row, col + 1, row + 1], samples: bands });
605
+ values = r.map((b) => b[0]);
606
+ } else {
607
+ const tcol = Math.floor(col / l0.tile_width), trow = Math.floor(row / l0.tile_height);
608
+ const [off, len] = Array.from(this.stream.tile_range(0, tcol, trow));
609
+ const px = await this._getTile(0, { col: tcol, row: trow, offset: off, length: len });
610
+ const base = ((row % l0.tile_height) * l0.tile_width + (col % l0.tile_width)) * l0.bands;
611
+ values = bands.map((b) => px[base + b]);
612
+ }
578
613
  return {
579
614
  coordinates: [lon, lat],
580
- values: bands.map((b) => px[base + b]),
615
+ values,
581
616
  band_names: bands.map((b) => `b${b + 1}`),
582
617
  };
583
618
  }
Binary file
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "Qiusheng Wu <giswqs@gmail.com>"
6
6
  ],
7
7
  "description": "Serverless Cloud Optimized GeoTIFF (COG) dynamic tiling in WebAssembly. TiTiler-style XYZ tiles, no backend.",
8
- "version": "0.2.0",
8
+ "version": "0.2.1",
9
9
  "license": "MIT",
10
10
  "repository": {
11
11
  "type": "git",