cog-tiler-wasm 0.3.0 → 0.3.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/cog-tiler.js CHANGED
@@ -25,6 +25,7 @@ import initTiler, { colorize, colormap_names } from "./cog_tiler_wasm.js";
25
25
  import proj4 from "proj4";
26
26
  import * as GeoTIFF from "geotiff";
27
27
  import geokeysToProj4 from "geotiff-geokeys-to-proj4";
28
+ import { sampleWindowBilinear } from "./sampling.js";
28
29
 
29
30
  const OS = 20037508.342789244; // Web Mercator half-extent (m)
30
31
  const TILE = 256; // output tile size (px)
@@ -224,22 +225,6 @@ function bilin(v00, v10, v01, v11, tx, ty) {
224
225
  return top + (bot - top) * ty;
225
226
  }
226
227
 
227
- // Bilinear sample of a row-major window at fractional (fc,fr). Returns NaN when
228
- // the cell center is outside the window so out-of-raster pixels stay transparent
229
- // (no edge smear); falls back to nearest at the window edge / next to nodata.
230
- function sampleWindowBilinear(buf, w, h, fc, fr) {
231
- const c0 = Math.floor(fc), r0 = Math.floor(fr);
232
- if (c0 < 0 || c0 >= w || r0 < 0 || r0 >= h) return NaN;
233
- const c1 = Math.min(c0 + 1, w - 1), r1 = Math.min(r0 + 1, h - 1);
234
- const v00 = buf[r0 * w + c0];
235
- if (Number.isNaN(v00)) return NaN;
236
- const v10 = buf[r0 * w + c1], v01 = buf[r1 * w + c0], v11 = buf[r1 * w + c1];
237
- if (Number.isNaN(v10) || Number.isNaN(v01) || Number.isNaN(v11)) return v00; // edge/nodata
238
- const tx = fc - c0, ty = fr - r0;
239
- const top = v00 + (v10 - v00) * tx, bot = v01 + (v11 - v01) * tx;
240
- return top + (bot - top) * ty;
241
- }
242
-
243
228
  // Build a byte reader from a URL string, a Blob/File, or an in-memory source
244
229
  // (ArrayBuffer, Uint8Array). `range(a,b)` yields bytes [a..b]; `openTiff()`
245
230
  // returns a geotiff.js GeoTIFF for reading the CRS / color table from the header.
@@ -569,7 +554,7 @@ export class CogSource {
569
554
  // RGB composite: bilinear-sample each band, rescale -> curve -> gamma.
570
555
  let ok = true;
571
556
  for (let k = 0; k < 3; k++) {
572
- const v = sampleWindowBilinear(bufs[k], ww, hh, fcol, frow);
557
+ const v = sampleWindowBilinear(bufs[k], ww, hh, fcol, frow, ndSet ? nodata : undefined);
573
558
  if (!isFinite(v) || (ndSet && v === nodata)) { ok = false; break; }
574
559
  const [mn, mx] = rescales[k] || rescales[0];
575
560
  const t = transferCurve(Math.max(0, Math.min(1, (v - mn) / ((mx - mn) || 1))), stretch, gamma);
@@ -579,7 +564,7 @@ export class CogSource {
579
564
  else { out[o] = out[o + 1] = out[o + 2] = 0; }
580
565
  } else {
581
566
  // Continuous single band: bilinear; colormap applied below.
582
- const v = sampleWindowBilinear(bufs[0], ww, hh, fcol, frow);
567
+ const v = sampleWindowBilinear(bufs[0], ww, hh, fcol, frow, ndSet ? nodata : undefined);
583
568
  if (!isFinite(v) || (ndSet && v === nodata)) continue;
584
569
  grid[py * outW + px] = v;
585
570
  }
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.3.0",
8
+ "version": "0.3.1",
9
9
  "license": "MIT",
10
10
  "repository": {
11
11
  "type": "git",
@@ -16,6 +16,7 @@
16
16
  "cog_tiler_wasm.js",
17
17
  "cog_tiler_wasm.d.ts",
18
18
  "cog-tiler.js",
19
+ "sampling.js",
19
20
  "cog-tiler.d.ts",
20
21
  "README.md",
21
22
  "LICENSE"
package/sampling.js ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Pure resampling helpers, kept dependency-free so they can be unit-tested
3
+ * without loading the wasm/geotiff stack that `cog-tiler.js` pulls in.
4
+ */
5
+
6
+ // Bilinear sample of a row-major window at fractional (fc,fr). Returns NaN when
7
+ // the cell center is outside the window so out-of-raster pixels stay transparent
8
+ // (no edge smear); falls back to nearest at the window edge / next to nodata.
9
+ //
10
+ // `nodata` is the declared sentinel, when the dataset has one. It must be tested
11
+ // here rather than only on the result: interpolating across the boundary blends
12
+ // the sentinel into the value, and the product matches neither the sentinel nor
13
+ // NaN, so a caller's `v === nodata` check cannot recognize it. With a large
14
+ // negative sentinel (-9999 is the GDAL convention) the blend lands far below the
15
+ // rescale window, clamps to its low end, and paints a ring of the colormap's
16
+ // first color around every nodata boundary -- e.g. a blue border along a swath
17
+ // edge under `jet`. Treating a sentinel neighbor exactly like a NaN one keeps
18
+ // the boundary pixel at its own valid value instead.
19
+ export function sampleWindowBilinear(buf, w, h, fc, fr, nodata) {
20
+ const c0 = Math.floor(fc), r0 = Math.floor(fr);
21
+ if (c0 < 0 || c0 >= w || r0 < 0 || r0 >= h) return NaN;
22
+ const c1 = Math.min(c0 + 1, w - 1), r1 = Math.min(r0 + 1, h - 1);
23
+ // `nodata` is undefined when the dataset declares none; `v === undefined` is
24
+ // false for every numeric sample, so the test costs nothing in that case.
25
+ const isVoid = (v) => Number.isNaN(v) || v === nodata;
26
+ const v00 = buf[r0 * w + c0];
27
+ if (isVoid(v00)) return NaN;
28
+ const v10 = buf[r0 * w + c1], v01 = buf[r1 * w + c0], v11 = buf[r1 * w + c1];
29
+ if (isVoid(v10) || isVoid(v01) || isVoid(v11)) return v00; // edge/nodata
30
+ const tx = fc - c0, ty = fr - r0;
31
+ const top = v00 + (v10 - v00) * tx, bot = v01 + (v11 - v01) * tx;
32
+ return top + (bot - top) * ty;
33
+ }