@developmentseed/geotiff 0.3.0-beta.3

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.
Files changed (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +125 -0
  3. package/dist/api.d.ts +34 -0
  4. package/dist/api.d.ts.map +1 -0
  5. package/dist/api.js +84 -0
  6. package/dist/api.js.map +1 -0
  7. package/dist/array.d.ts +75 -0
  8. package/dist/array.d.ts.map +1 -0
  9. package/dist/array.js +141 -0
  10. package/dist/array.js.map +1 -0
  11. package/dist/codecs/canvas.d.ts +3 -0
  12. package/dist/codecs/canvas.d.ts.map +1 -0
  13. package/dist/codecs/canvas.js +32 -0
  14. package/dist/codecs/canvas.js.map +1 -0
  15. package/dist/codecs/decompression-stream.d.ts +6 -0
  16. package/dist/codecs/decompression-stream.d.ts.map +1 -0
  17. package/dist/codecs/decompression-stream.js +19 -0
  18. package/dist/codecs/decompression-stream.js.map +1 -0
  19. package/dist/codecs/deflate.d.ts +2 -0
  20. package/dist/codecs/deflate.d.ts.map +1 -0
  21. package/dist/codecs/deflate.js +5 -0
  22. package/dist/codecs/deflate.js.map +1 -0
  23. package/dist/codecs/lerc.d.ts +3 -0
  24. package/dist/codecs/lerc.d.ts.map +1 -0
  25. package/dist/codecs/lerc.js +16 -0
  26. package/dist/codecs/lerc.js.map +1 -0
  27. package/dist/codecs/lzw.d.ts +3 -0
  28. package/dist/codecs/lzw.d.ts.map +1 -0
  29. package/dist/codecs/lzw.js +8 -0
  30. package/dist/codecs/lzw.js.map +1 -0
  31. package/dist/codecs/predictor.d.ts +15 -0
  32. package/dist/codecs/predictor.d.ts.map +1 -0
  33. package/dist/codecs/predictor.js +88 -0
  34. package/dist/codecs/predictor.js.map +1 -0
  35. package/dist/colormap.d.ts +10 -0
  36. package/dist/colormap.d.ts.map +1 -0
  37. package/dist/colormap.js +31 -0
  38. package/dist/colormap.js.map +1 -0
  39. package/dist/crs.d.ts +86 -0
  40. package/dist/crs.d.ts.map +1 -0
  41. package/dist/crs.js +462 -0
  42. package/dist/crs.js.map +1 -0
  43. package/dist/decode/api.d.ts +28 -0
  44. package/dist/decode/api.d.ts.map +1 -0
  45. package/dist/decode/api.js +80 -0
  46. package/dist/decode/api.js.map +1 -0
  47. package/dist/decode.d.ts +34 -0
  48. package/dist/decode.d.ts.map +1 -0
  49. package/dist/decode.js +84 -0
  50. package/dist/decode.js.map +1 -0
  51. package/dist/fetch.d.ts +27 -0
  52. package/dist/fetch.d.ts.map +1 -0
  53. package/dist/fetch.js +116 -0
  54. package/dist/fetch.js.map +1 -0
  55. package/dist/geotiff.d.ts +116 -0
  56. package/dist/geotiff.d.ts.map +1 -0
  57. package/dist/geotiff.js +239 -0
  58. package/dist/geotiff.js.map +1 -0
  59. package/dist/ifd.d.ts +84 -0
  60. package/dist/ifd.d.ts.map +1 -0
  61. package/dist/ifd.js +113 -0
  62. package/dist/ifd.js.map +1 -0
  63. package/dist/index.d.ts +10 -0
  64. package/dist/index.d.ts.map +1 -0
  65. package/dist/index.js +6 -0
  66. package/dist/index.js.map +1 -0
  67. package/dist/overview.d.ts +55 -0
  68. package/dist/overview.d.ts.map +1 -0
  69. package/dist/overview.js +78 -0
  70. package/dist/overview.js.map +1 -0
  71. package/dist/tile-matrix-set.d.ts +36 -0
  72. package/dist/tile-matrix-set.d.ts.map +1 -0
  73. package/dist/tile-matrix-set.js +87 -0
  74. package/dist/tile-matrix-set.js.map +1 -0
  75. package/dist/tile.d.ts +11 -0
  76. package/dist/tile.d.ts.map +1 -0
  77. package/dist/tile.js +2 -0
  78. package/dist/tile.js.map +1 -0
  79. package/dist/transform.d.ts +28 -0
  80. package/dist/transform.d.ts.map +1 -0
  81. package/dist/transform.js +51 -0
  82. package/dist/transform.js.map +1 -0
  83. package/dist/tsconfig.tsbuildinfo +1 -0
  84. package/package.json +59 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Development Seed
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,125 @@
1
+ # @developmentseed/geotiff
2
+
3
+ Fast, high-level GeoTIFF reader written in TypeScript for the browser, wrapping [`@cogeotiff/core`][cogeotiff-lib].
4
+
5
+ [cogeotiff-lib]: https://github.com/blacha/cogeotiff
6
+
7
+ ## Features
8
+
9
+ ### Easy access to COG tiles
10
+
11
+ Use `GeoTIFF.fetchTile` to load `Tile` instances.
12
+
13
+ ```ts
14
+ import { GeoTIFF } from "@developmentseed/geotiff";
15
+
16
+ // RGB Sentinel-2 image over NYC
17
+ const url = "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/18/T/WL/2026/1/S2B_18TWL_20260101_0_L2A/TCI.tif"
18
+ const geotiff = await GeoTIFF.fromUrl(url);
19
+
20
+ // Read full-resolution tile from bottom right of image.
21
+ // tile ordering starts at top left.
22
+ const tile = await geotiff.fetchTile(10, 10);
23
+ const {
24
+ x, // 10
25
+ y, // 10
26
+ } = tile;
27
+
28
+ // tile.array holds data & relevant metadata
29
+ const {
30
+ data, // Uint8Array(3145728) [49, 51, 46, 42, ...]
31
+ count, // 3 (RGB)
32
+ height, // 1024
33
+ width, // 1024
34
+ transform, // [ 10, 0, 602380, 0, -10, 4497620 ]
35
+ crs, // 32618 (EPSG code)
36
+ mask, // null
37
+ nodata, // 0
38
+ } = tile.array;
39
+ ```
40
+
41
+ ### Convenient access to overviews
42
+
43
+ The `.overviews` attribute on `GeoTIFF` contains an array of reduced-resolution overviews, ordered from highest to lowest resolution. Use these to fetch tiles at your desired resolution.
44
+
45
+ ```ts
46
+ const geotiff = await GeoTIFF.fromUrl("https://example.com/image.tif");
47
+ // Read from the first overview
48
+ // (the next-highest resolution after the full-resolution image)
49
+ const overview = geotiff.overviews[0];
50
+ // Read top-left tile of the overview
51
+ const tile = await overview.fetchTile(0, 0);
52
+ ```
53
+
54
+ ### Automatic Nodata Mask handling
55
+
56
+ With a library like `geotiff.js` or the underlying `@cogeotiff/core`, you have to do extra work to keep track of which of the internal images represent _data_ versus _masks_. We automatically handle nodata values and mask arrays.
57
+
58
+ ### Easy CRS handling
59
+
60
+ GeoTIFFs can be defined either by an integer EPSG code or by a user-defined CRS.
61
+
62
+ For integer EPSG codes, the `GeoTIFF.crs` method returns the integer directly, allowing downstream applications to decide how to resolve the EPSG code into WKT or PROJJSON formats (e.g. by querying `https://epsg.io`).
63
+
64
+ For user-defined CRSes, we automatically parse the CRS into a PROJJSON object, which can be passed directly into `proj4js`. This is natively implemented **without a large cache of PROJ data**. This avoids the need for a large additional dependency like [`geotiff-geokeys-to-proj4`], which would otherwise add 1.5MB to your bundle.
65
+
66
+ [`geotiff-geokeys-to-proj4`]: https://github.com/matafokka/geotiff-geokeys-to-proj4
67
+
68
+ If you have an image where the CRS fails to parse, please create an issue.
69
+
70
+ ### Dynamically load compressions as needed
71
+
72
+ Instead of bundling support for all compressions out of the box, dynamically load the decompressors as required.
73
+
74
+ Until you try to load an image compressed with, say, [LERC], you don't pay for the bundle size of the dependency.
75
+
76
+ [LERC]: https://github.com/Esri/lerc
77
+
78
+ ### Full user control over caching and chunking
79
+
80
+ There are a lot of great utilities in [`chunkd`](https://github.com/blacha/chunkd) that work out of the box here.
81
+
82
+ ```ts
83
+ import { SourceCache, SourceChunk } from '@chunkd/middleware';
84
+ import { SourceView } from '@chunkd/source';
85
+ import { SourceHttp } from '@chunkd/source-http';
86
+ import { GeoTIFF } from '@developmentseed/geotiff';
87
+
88
+ // 16MB Cache
89
+ const cache = new SourceCache({ size: 16 * 1024 * 1024 });
90
+
91
+ // Chunk requests into 16KB fetches
92
+ const chunk = new SourceChunk({ size: 16 * 1024 });
93
+
94
+ // Raw source to HTTP file
95
+ const httpSource = new SourceHttp('https://blayne.chard.com/world.webp.google.cog.tiff');
96
+
97
+ // HTTP source with chunking and caching
98
+ const tiffSource = new SourceView(httpSource, [chunk, cache]);
99
+ const geotiff = await GeoTIFF.open(tiffSource);
100
+ const tile = await geotiff.fetchTile(0, 0);
101
+ ```
102
+
103
+ ### Nearly-identical API to Python `async-geotiff`
104
+
105
+ The TypeScript API is nearly identical to our Python project [`async-geotiff`].
106
+
107
+ This is extremely useful for us as we build visualization projects for both Python and the browser.
108
+
109
+ [`async-geotiff`]: https://github.com/developmentseed/async-geotiff
110
+
111
+ ## Why not build on top of geotiff.js?
112
+
113
+ The initial implementation of deck.gl-raster used [geotiff.js], and geotiff.js was great for quickly getting started. But there's a few reasons why this project switched to [`@cogeotiff/core`][cogeotiff-lib].
114
+
115
+ [geotiff.js]: https://geotiffjs.github.io/
116
+
117
+ - **Fully typed**: `@cogeotiff/core` is fully typed in expressive TypeScript, making it much more enjoyable to build on top of. Even the low-level
118
+ - `@cogeotiff/core` implements a bunch of optimizations, like reading [_and utilizing_](https://github.com/blacha/cogeotiff/blob/4781a6375adf419da9f0319d15c8a67284dfb0c4/packages/core/src/tiff.image.ts#L566-L572) the [GDAL "ghost header"](https://gdal.org/en/stable/drivers/raster/cog.html#header-ghost-area) out of the box. In contrast, geotiff.js [can parse](https://github.com/geotiffjs/geotiff.js/blob/ae88c5e8d7b254cdd86d84fcd50254863663980d/src/geotiff.js#L529) but won't automatically use the ghost values.
119
+ - **Project scope**: geotiff.js has a _lot_ of code unrelated to the needs of deck.gl-raster. All we need here is really efficient access to individual tiles from the COG. geotiff.js has a bunch of features we don't need: resampling, tile-merging, conversion to RGB, overview choice based on a target resolution, or writing GeoTIFFs.
120
+ - **Complexity**: this is subjective. Overall geotiff.js seems to be... fine. But there are various parts of geotiff.js that give me pause. Vendoring a full 1000-line JPEG decoder? Perhaps this is because they need to support JPEG decoding in Node as well (points to differences in project scopes), but it doesn't give me confidence that I could fix a problem there if I had to.
121
+ - **Confidence to build on top of**: this is subjective, but geotiff.js doesn't feel focused, like the way that `@cogeotiff/core` is very focused on its targeted, narrow API.
122
+ - **JSDoc is hard to read and contribute to**: this is very subjective, but I find it _much_ harder to read and contribute to geotiff.js code written with [JSDoc](https://jsdoc.app/) instead of pure TypeScript.
123
+ - **Code quality**: there are various parts of geotiff.js with code just... [commented out](https://github.com/geotiffjs/geotiff.js/blob/ae88c5e8d7b254cdd86d84fcd50254863663980d/src/geotiffimage.js#L161-L174). And the function has [no documentation](https://github.com/geotiffjs/geotiff.js/blob/ae88c5e8d7b254cdd86d84fcd50254863663980d/src/geotiffimage.js#L100-L110). Why is it normalizing? The [`needsNormalization` function](https://github.com/geotiffjs/geotiff.js/blob/ae88c5e8d7b254cdd86d84fcd50254863663980d/src/geotiffimage.js#L86-L98) also has no documentation, and is hard to understand what the equality is checking because it doesn't use TypeScript-standard enums, which would make the code itself readable.
124
+
125
+ Overall, geotiff.js seems like a fine library, and it was useful to get started with to prove my proof of concept quickly. But if I'm building an entire stack on top of a COG reader, I need to have a huge amount of confidence on what I'm building on, and `@cogeotiff/core` gives that confidence.
package/dist/api.d.ts ADDED
@@ -0,0 +1,34 @@
1
+ import type { PlanarConfiguration } from "@cogeotiff/core";
2
+ import { Compression, SampleFormat } from "@cogeotiff/core";
3
+ import type { RasterTypedArray } from "./array.js";
4
+ import type { Predictor } from "./ifd.js";
5
+ /** The result of a decoding process */
6
+ export type DecodedPixels = {
7
+ layout: "pixel-interleaved";
8
+ data: RasterTypedArray;
9
+ } | {
10
+ layout: "band-separate";
11
+ bands: RasterTypedArray[];
12
+ };
13
+ /** Metadata from the TIFF IFD, passed to decoders that need it. */
14
+ export type DecoderMetadata = {
15
+ sampleFormat: SampleFormat;
16
+ bitsPerSample: number;
17
+ samplesPerPixel: number;
18
+ width: number;
19
+ height: number;
20
+ predictor: Predictor;
21
+ planarConfiguration: PlanarConfiguration;
22
+ };
23
+ /**
24
+ * A decoder returns either:
25
+ * - An ArrayBuffer of raw decompressed bytes (byte-level codecs like deflate, zstd)
26
+ * - A DecodedPixels with typed pixel data (image codecs like LERC, JPEG)
27
+ */
28
+ export type Decoder = (bytes: ArrayBuffer, metadata: DecoderMetadata) => Promise<ArrayBuffer | DecodedPixels>;
29
+ export declare const registry: Map<Compression, () => Promise<Decoder>>;
30
+ /**
31
+ * Decode a tile's bytes according to its compression and image metadata.
32
+ */
33
+ export declare function decode(bytes: ArrayBuffer, compression: Compression, metadata: DecoderMetadata): Promise<DecodedPixels>;
34
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C,uCAAuC;AACvC,MAAM,MAAM,aAAa,GACrB;IAAE,MAAM,EAAE,mBAAmB,CAAC;IAAC,IAAI,EAAE,gBAAgB,CAAA;CAAE,GACvD;IAAE,MAAM,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,gBAAgB,EAAE,CAAA;CAAE,CAAC;AAE3D,mEAAmE;AACnE,MAAM,MAAM,eAAe,GAAG;IAC5B,YAAY,EAAE,YAAY,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,SAAS,CAAC;IACrB,mBAAmB,EAAE,mBAAmB,CAAC;CAC1C,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,OAAO,GAAG,CACpB,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,eAAe,KACtB,OAAO,CAAC,WAAW,GAAG,aAAa,CAAC,CAAC;AAM1C,eAAO,MAAM,QAAQ,yBAA8B,OAAO,CAAC,OAAO,CAAC,CAAG,CAAC;AA4BvE;;GAEG;AACH,wBAAsB,MAAM,CAC1B,KAAK,EAAE,WAAW,EAClB,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,eAAe,GACxB,OAAO,CAAC,aAAa,CAAC,CAkCxB"}
package/dist/api.js ADDED
@@ -0,0 +1,84 @@
1
+ import { Compression, SampleFormat } from "@cogeotiff/core";
2
+ import { decode as decodeViaCanvas } from "./codecs/canvas.js";
3
+ import { applyPredictor } from "./codecs/predictor.js";
4
+ async function decodeUncompressed(bytes) {
5
+ return bytes;
6
+ }
7
+ export const registry = new Map();
8
+ registry.set(Compression.None, () => Promise.resolve(decodeUncompressed));
9
+ registry.set(Compression.Deflate, () => import("./codecs/deflate.js").then((m) => m.decode));
10
+ registry.set(Compression.DeflateOther, () => import("./codecs/deflate.js").then((m) => m.decode));
11
+ registry.set(Compression.Lzw, () => import("./codecs/lzw.js").then((m) => m.decode));
12
+ // registry.set(Compression.Zstd, () =>
13
+ // import("../codecs/zstd.js").then((m) => m.decode),
14
+ // );
15
+ // registry.set(Compression.Lzma, () =>
16
+ // import("../codecs/lzma.js").then((m) => m.decode),
17
+ // );
18
+ // registry.set(Compression.Jp2000, () =>
19
+ // import("../codecs/jp2000.js").then((m) => m.decode),
20
+ // );
21
+ registry.set(Compression.Jpeg, () => Promise.resolve(decodeViaCanvas));
22
+ registry.set(Compression.Jpeg6, () => Promise.resolve(decodeViaCanvas));
23
+ registry.set(Compression.Webp, () => Promise.resolve(decodeViaCanvas));
24
+ registry.set(Compression.Lerc, () => import("./codecs/lerc.js").then((m) => m.decode));
25
+ /**
26
+ * Decode a tile's bytes according to its compression and image metadata.
27
+ */
28
+ export async function decode(bytes, compression, metadata) {
29
+ const loader = registry.get(compression);
30
+ if (!loader) {
31
+ throw new Error(`Unsupported compression: ${compression}`);
32
+ }
33
+ const decoder = await loader();
34
+ const result = await decoder(bytes, metadata);
35
+ if (result instanceof ArrayBuffer) {
36
+ const { predictor, width, height, bitsPerSample, samplesPerPixel, planarConfiguration, } = metadata;
37
+ const predicted = applyPredictor(result, predictor, width, height, bitsPerSample, samplesPerPixel, planarConfiguration);
38
+ return {
39
+ layout: "pixel-interleaved",
40
+ data: toTypedArray(predicted, metadata),
41
+ };
42
+ }
43
+ return result;
44
+ }
45
+ /**
46
+ * Convert a raw ArrayBuffer of pixel data into a typed array based on the
47
+ * sample format and bits per sample. This is used for codecs that return raw
48
+ * bytes.
49
+ */
50
+ function toTypedArray(buffer, metadata) {
51
+ const { sampleFormat, bitsPerSample } = metadata;
52
+ switch (sampleFormat) {
53
+ case SampleFormat.Uint:
54
+ switch (bitsPerSample) {
55
+ case 8:
56
+ return new Uint8Array(buffer);
57
+ case 16:
58
+ return new Uint16Array(buffer);
59
+ case 32:
60
+ return new Uint32Array(buffer);
61
+ }
62
+ break;
63
+ case SampleFormat.Int:
64
+ switch (bitsPerSample) {
65
+ case 8:
66
+ return new Int8Array(buffer);
67
+ case 16:
68
+ return new Int16Array(buffer);
69
+ case 32:
70
+ return new Int32Array(buffer);
71
+ }
72
+ break;
73
+ case SampleFormat.Float:
74
+ switch (bitsPerSample) {
75
+ case 32:
76
+ return new Float32Array(buffer);
77
+ case 64:
78
+ return new Float64Array(buffer);
79
+ }
80
+ break;
81
+ }
82
+ throw new Error(`Unsupported sample format/depth: SampleFormat=${sampleFormat}, BitsPerSample=${bitsPerSample}`);
83
+ }
84
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ import type { Affine } from "@developmentseed/affine";
2
+ import type { ProjJson } from "./crs.js";
3
+ /** Typed arrays supported for raster sample storage. */
4
+ export type RasterTypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array;
5
+ /** Common metadata shared by all raster layouts. */
6
+ type RasterArrayBase = {
7
+ /** Number of bands (samples per pixel). */
8
+ count: number;
9
+ /** Height in pixels. */
10
+ height: number;
11
+ /** Width in pixels. */
12
+ width: number;
13
+ /**
14
+ * Optional validity mask. Length = height * width.
15
+ * 1 = valid pixel, 0 = nodata. null when no mask IFD is present.
16
+ */
17
+ mask: Uint8Array | null;
18
+ /**
19
+ * Affine geotransform [a, b, c, d, e, f] mapping pixel (col, row) to
20
+ * geographic (x, y):
21
+ * x = a * col + b * row + c
22
+ * y = d * col + e * row + f
23
+ */
24
+ transform: Affine;
25
+ crs: number | ProjJson;
26
+ nodata: number | null;
27
+ };
28
+ /** Raster stored in one typed array per band (band-major / planar). */
29
+ export type BandRasterArray = RasterArrayBase & {
30
+ layout: "band-separate";
31
+ /**
32
+ * One typed array per band, each length = width * height.
33
+ *
34
+ * This is the preferred representation when uploading one texture per band.
35
+ */
36
+ bands: RasterTypedArray[];
37
+ };
38
+ /** Raster stored in one pixel-interleaved typed array. */
39
+ export type PixelRasterArray = RasterArrayBase & {
40
+ layout: "pixel-interleaved";
41
+ /**
42
+ * Pixel-interleaved raster data:
43
+ * [p00_band0, p00_band1, ..., p01_band0, ...]
44
+ *
45
+ * Length = width * height * count.
46
+ */
47
+ data: RasterTypedArray;
48
+ };
49
+ /** Decoded raster data from a GeoTIFF region. */
50
+ export type RasterArray = BandRasterArray | PixelRasterArray;
51
+ /** Options for packing band data to a 4-channel pixel-interleaved array. */
52
+ export type PackBandsToRGBAOptions = {
53
+ /**
54
+ * Source band index for each RGBA output channel.
55
+ * Use null to write `fillValue` for that output channel.
56
+ */
57
+ order?: [number | null, number | null, number | null, number | null];
58
+ /** Fill value used when an output channel has no source band. */
59
+ fillValue?: number;
60
+ };
61
+ /** Convert any raster layout to a band-separate representation. */
62
+ export declare function toBandSeparate(array: RasterArray): BandRasterArray;
63
+ /** Convert any raster layout to a pixel-interleaved representation. */
64
+ export declare function toPixelInterleaved(array: RasterArray, order?: readonly number[]): PixelRasterArray;
65
+ /** Reorder bands while keeping a band-separate representation. */
66
+ export declare function reorderBands(array: RasterArray, order: readonly number[]): BandRasterArray;
67
+ /**
68
+ * Pack selected source bands into an RGBA pixel-interleaved typed array.
69
+ *
70
+ * This is useful as a fallback path when a single 4-channel texture upload
71
+ * is preferred over one texture per band.
72
+ */
73
+ export declare function packBandsToRGBA(array: RasterArray, options?: PackBandsToRGBAOptions): PixelRasterArray;
74
+ export {};
75
+ //# sourceMappingURL=array.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"array.d.ts","sourceRoot":"","sources":["../src/array.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEzC,wDAAwD;AACxD,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,UAAU,GACV,iBAAiB,GACjB,UAAU,GACV,WAAW,GACX,UAAU,GACV,WAAW,GACX,YAAY,GACZ,YAAY,CAAC;AAEjB,oDAAoD;AACpD,KAAK,eAAe,GAAG;IACrB,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IAEd,wBAAwB;IACxB,MAAM,EAAE,MAAM,CAAC;IAEf,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;IAExB;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB,GAAG,EAAE,MAAM,GAAG,QAAQ,CAAC;IAEvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AAEF,uEAAuE;AACvE,MAAM,MAAM,eAAe,GAAG,eAAe,GAAG;IAC9C,MAAM,EAAE,eAAe,CAAC;IACxB;;;;OAIG;IACH,KAAK,EAAE,gBAAgB,EAAE,CAAC;CAC3B,CAAC;AAEF,0DAA0D;AAC1D,MAAM,MAAM,gBAAgB,GAAG,eAAe,GAAG;IAC/C,MAAM,EAAE,mBAAmB,CAAC;IAC5B;;;;;OAKG;IACH,IAAI,EAAE,gBAAgB,CAAC;CACxB,CAAC;AAEF,iDAAiD;AACjD,MAAM,MAAM,WAAW,GAAG,eAAe,GAAG,gBAAgB,CAAC;AAE7D,4EAA4E;AAC5E,MAAM,MAAM,sBAAsB,GAAG;IACnC;;;OAGG;IACH,KAAK,CAAC,EAAE,CAAC,MAAM,GAAG,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;IACrE,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,mEAAmE;AACnE,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,eAAe,CA4BlE;AAED,uEAAuE;AACvE,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,WAAW,EAClB,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,GACxB,gBAAgB,CAqClB;AAED,kEAAkE;AAClE,wBAAgB,YAAY,CAC1B,KAAK,EAAE,WAAW,EAClB,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB,eAAe,CASjB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,WAAW,EAClB,OAAO,GAAE,sBAA2B,GACnC,gBAAgB,CA2BlB"}
package/dist/array.js ADDED
@@ -0,0 +1,141 @@
1
+ /** Convert any raster layout to a band-separate representation. */
2
+ export function toBandSeparate(array) {
3
+ validateRasterShape(array);
4
+ if (array.layout === "band-separate") {
5
+ return array;
6
+ }
7
+ const sampleCount = array.width * array.height;
8
+ const bands = new Array(array.count);
9
+ const Ctor = array.data.constructor;
10
+ for (let b = 0; b < array.count; b++) {
11
+ bands[b] = new Ctor(sampleCount);
12
+ }
13
+ for (let i = 0; i < sampleCount; i++) {
14
+ const base = i * array.count;
15
+ for (let b = 0; b < array.count; b++) {
16
+ bands[b][i] = array.data[base + b];
17
+ }
18
+ }
19
+ return {
20
+ ...array,
21
+ layout: "band-separate",
22
+ bands,
23
+ };
24
+ }
25
+ /** Convert any raster layout to a pixel-interleaved representation. */
26
+ export function toPixelInterleaved(array, order) {
27
+ validateRasterShape(array);
28
+ const defaultOrder = Array.from({ length: array.count }, (_, i) => i);
29
+ const bandOrder = order ?? defaultOrder;
30
+ validateBandOrder(bandOrder, array.count);
31
+ const sampleCount = array.width * array.height;
32
+ if (array.layout === "pixel-interleaved" && isIdentityOrder(bandOrder)) {
33
+ return array;
34
+ }
35
+ const Ctor = (array.layout === "pixel-interleaved"
36
+ ? array.data.constructor
37
+ : array.bands[0].constructor);
38
+ const data = new Ctor(sampleCount * bandOrder.length);
39
+ const bandSource = toBandSeparate(array).bands;
40
+ for (let i = 0; i < sampleCount; i++) {
41
+ const outBase = i * bandOrder.length;
42
+ for (let c = 0; c < bandOrder.length; c++) {
43
+ data[outBase + c] = bandSource[bandOrder[c]][i];
44
+ }
45
+ }
46
+ return {
47
+ ...array,
48
+ layout: "pixel-interleaved",
49
+ count: bandOrder.length,
50
+ data,
51
+ };
52
+ }
53
+ /** Reorder bands while keeping a band-separate representation. */
54
+ export function reorderBands(array, order) {
55
+ validateRasterShape(array);
56
+ validateBandOrder(order, array.count);
57
+ const src = toBandSeparate(array);
58
+ return {
59
+ ...src,
60
+ count: order.length,
61
+ bands: order.map((bandIndex) => src.bands[bandIndex]),
62
+ };
63
+ }
64
+ /**
65
+ * Pack selected source bands into an RGBA pixel-interleaved typed array.
66
+ *
67
+ * This is useful as a fallback path when a single 4-channel texture upload
68
+ * is preferred over one texture per band.
69
+ */
70
+ export function packBandsToRGBA(array, options = {}) {
71
+ const order = options.order ?? [0, 1, 2, null];
72
+ const fillValue = options.fillValue ?? 0;
73
+ validateRasterShape(array);
74
+ const src = toBandSeparate(array);
75
+ const sampleCount = src.width * src.height;
76
+ const Ctor = src.bands[0].constructor;
77
+ const data = new Ctor(sampleCount * 4);
78
+ for (let i = 0; i < sampleCount; i++) {
79
+ const outBase = i * 4;
80
+ for (let c = 0; c < 4; c++) {
81
+ const bandIndex = order[c];
82
+ data[outBase + c] =
83
+ bandIndex == null ? fillValue : src.bands[bandIndex][i];
84
+ }
85
+ }
86
+ return {
87
+ ...src,
88
+ layout: "pixel-interleaved",
89
+ count: 4,
90
+ data,
91
+ };
92
+ }
93
+ function validateBandOrder(order, count) {
94
+ if (order.length === 0) {
95
+ throw new Error("Band order must include at least one channel");
96
+ }
97
+ for (const bandIndex of order) {
98
+ if (!Number.isInteger(bandIndex)) {
99
+ throw new Error(`Band index must be an integer: ${String(bandIndex)}`);
100
+ }
101
+ if (bandIndex < 0 || bandIndex >= count) {
102
+ throw new Error(`Band index ${bandIndex} is out of range for ${count} band(s)`);
103
+ }
104
+ }
105
+ }
106
+ function validateRasterShape(array) {
107
+ if (array.width <= 0 || array.height <= 0) {
108
+ throw new Error("Raster width and height must be positive");
109
+ }
110
+ if (array.count <= 0) {
111
+ throw new Error("Raster count must be positive");
112
+ }
113
+ const sampleCount = array.width * array.height;
114
+ const expectedMaskLength = sampleCount;
115
+ if (array.mask != null && array.mask.length !== expectedMaskLength) {
116
+ throw new Error(`Mask length ${array.mask.length} does not match width * height (${expectedMaskLength})`);
117
+ }
118
+ if (array.layout === "band-separate") {
119
+ if (array.bands.length !== array.count) {
120
+ throw new Error(`Band count mismatch: bands.length=${array.bands.length}, count=${array.count}`);
121
+ }
122
+ for (const [index, band] of array.bands.entries()) {
123
+ if (band.length !== sampleCount) {
124
+ throw new Error(`Band ${index} length ${band.length} does not match width * height (${sampleCount})`);
125
+ }
126
+ }
127
+ return;
128
+ }
129
+ const expectedDataLength = sampleCount * array.count;
130
+ if (array.data.length !== expectedDataLength) {
131
+ throw new Error(`Data length ${array.data.length} does not match width * height * count (${expectedDataLength})`);
132
+ }
133
+ }
134
+ function isIdentityOrder(order) {
135
+ for (let i = 0; i < order.length; i++) {
136
+ if (order[i] !== i)
137
+ return false;
138
+ }
139
+ return true;
140
+ }
141
+ //# sourceMappingURL=array.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"array.js","sourceRoot":"","sources":["../src/array.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { DecodedPixels, DecoderMetadata } from "../decode.js";
2
+ export declare function decode(bytes: ArrayBuffer, metadata: DecoderMetadata): Promise<DecodedPixels>;
3
+ //# sourceMappingURL=canvas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas.d.ts","sourceRoot":"","sources":["../../src/codecs/canvas.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAOnE,wBAAsB,MAAM,CAC1B,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,eAAe,GACxB,OAAO,CAAC,aAAa,CAAC,CA8BxB"}
@@ -0,0 +1,32 @@
1
+ // TODO: in the future, have an API that returns an ImageBitmap directly from
2
+ // the decoder, to avoid copying pixel data from GPU -> CPU memory
3
+ // Then deck.gl could use the ImageBitmap directly as a texture source without
4
+ // copying again from CPU -> GPU memory
5
+ // https://github.com/developmentseed/deck.gl-raster/issues/228
6
+ export async function decode(bytes, metadata) {
7
+ const blob = new Blob([bytes]);
8
+ const imageBitmap = await createImageBitmap(blob);
9
+ const canvas = new OffscreenCanvas(imageBitmap.width, imageBitmap.height);
10
+ const ctx = canvas.getContext("2d");
11
+ ctx.drawImage(imageBitmap, 0, 0);
12
+ imageBitmap.close();
13
+ const { width, height } = canvas;
14
+ const imageData = ctx.getImageData(0, 0, width, height);
15
+ const rgba = imageData.data;
16
+ const samplesPerPixel = metadata.samplesPerPixel;
17
+ if (samplesPerPixel === 4) {
18
+ return { layout: "pixel-interleaved", data: rgba };
19
+ }
20
+ if (samplesPerPixel === 3) {
21
+ const pixelCount = width * height;
22
+ const rgb = new Uint8ClampedArray(pixelCount * 3);
23
+ for (let i = 0, j = 0; i < rgb.length; i += 3, j += 4) {
24
+ rgb[i] = rgba[j];
25
+ rgb[i + 1] = rgba[j + 1];
26
+ rgb[i + 2] = rgba[j + 2];
27
+ }
28
+ return { layout: "pixel-interleaved", data: rgb };
29
+ }
30
+ throw new Error(`Unsupported SamplesPerPixel for JPEG: ${samplesPerPixel}`);
31
+ }
32
+ //# sourceMappingURL=canvas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas.js","sourceRoot":"","sources":["../../src/codecs/canvas.ts"],"names":[],"mappings":"AAEA,6EAA6E;AAC7E,kEAAkE;AAClE,8EAA8E;AAC9E,uCAAuC;AACvC,+DAA+D;AAC/D,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,KAAkB,EAClB,QAAyB;IAEzB,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/B,MAAM,WAAW,GAAG,MAAM,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAElD,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,WAAW,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC1E,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAE,CAAC;IACrC,GAAG,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACjC,WAAW,CAAC,KAAK,EAAE,CAAC;IAEpB,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IACjC,MAAM,SAAS,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IACxD,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC;IAE5B,MAAM,eAAe,GAAG,QAAQ,CAAC,eAAe,CAAC;IACjD,IAAI,eAAe,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACrD,CAAC;IAED,IAAI,eAAe,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,CAAC;QAClC,MAAM,GAAG,GAAG,IAAI,iBAAiB,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;QAClD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACtD,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAE,CAAC;YAClB,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAE,CAAC;YAC1B,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAE,CAAC;QAC5B,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;IACpD,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,yCAAyC,eAAe,EAAE,CAAC,CAAC;AAC9E,CAAC"}
@@ -0,0 +1,6 @@
1
+ export declare function assert(expression: unknown, msg?: string | undefined): asserts expression;
2
+ export declare function decompressWithDecompressionStream(data: ArrayBuffer | Response, { format, signal }: {
3
+ format: CompressionFormat;
4
+ signal?: AbortSignal;
5
+ }): Promise<ArrayBuffer>;
6
+ //# sourceMappingURL=decompression-stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decompression-stream.d.ts","sourceRoot":"","sources":["../../src/codecs/decompression-stream.ts"],"names":[],"mappings":"AAAA,wBAAgB,MAAM,CACpB,UAAU,EAAE,OAAO,EACnB,GAAG,GAAE,MAAM,GAAG,SAAc,GAC3B,OAAO,CAAC,UAAU,CAIpB;AAED,wBAAsB,iCAAiC,CACrD,IAAI,EAAE,WAAW,GAAG,QAAQ,EAC5B,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,iBAAiB,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GACtE,OAAO,CAAC,WAAW,CAAC,CAatB"}
@@ -0,0 +1,19 @@
1
+ export function assert(expression, msg = "") {
2
+ if (!expression) {
3
+ throw new Error(msg);
4
+ }
5
+ }
6
+ export async function decompressWithDecompressionStream(data, { format, signal }) {
7
+ const response = data instanceof Response ? data : new Response(data);
8
+ assert(response.body, "Response does not contain body.");
9
+ try {
10
+ const decompressedResponse = new Response(response.body.pipeThrough(new DecompressionStream(format), { signal }));
11
+ const buffer = await decompressedResponse.arrayBuffer();
12
+ return buffer;
13
+ }
14
+ catch {
15
+ signal?.throwIfAborted();
16
+ throw new Error(`Failed to decode ${format}`);
17
+ }
18
+ }
19
+ //# sourceMappingURL=decompression-stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decompression-stream.js","sourceRoot":"","sources":["../../src/codecs/decompression-stream.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,MAAM,CACpB,UAAmB,EACnB,MAA0B,EAAE;IAE5B,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC;IACvB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iCAAiC,CACrD,IAA4B,EAC5B,EAAE,MAAM,EAAE,MAAM,EAAuD;IAEvE,MAAM,QAAQ,GAAG,IAAI,YAAY,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC;IACtE,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,iCAAiC,CAAC,CAAC;IACzD,IAAI,CAAC;QACH,MAAM,oBAAoB,GAAG,IAAI,QAAQ,CACvC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,mBAAmB,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CACvE,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC,WAAW,EAAE,CAAC;QACxD,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,EAAE,cAAc,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,oBAAoB,MAAM,EAAE,CAAC,CAAC;IAChD,CAAC;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function decode(bytes: ArrayBuffer): Promise<ArrayBuffer>;
2
+ //# sourceMappingURL=deflate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deflate.d.ts","sourceRoot":"","sources":["../../src/codecs/deflate.ts"],"names":[],"mappings":"AAEA,wBAAsB,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAErE"}
@@ -0,0 +1,5 @@
1
+ import { decompressWithDecompressionStream } from "./decompression-stream.js";
2
+ export async function decode(bytes) {
3
+ return decompressWithDecompressionStream(bytes, { format: "deflate" });
4
+ }
5
+ //# sourceMappingURL=deflate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deflate.js","sourceRoot":"","sources":["../../src/codecs/deflate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iCAAiC,EAAE,MAAM,2BAA2B,CAAC;AAE9E,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,KAAkB;IAC7C,OAAO,iCAAiC,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;AACzE,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { DecodedPixels } from "../decode.js";
2
+ export declare function decode(bytes: ArrayBuffer): Promise<DecodedPixels>;
3
+ //# sourceMappingURL=lerc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lerc.d.ts","sourceRoot":"","sources":["../../src/codecs/lerc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAgBlD,wBAAsB,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAIvE"}
@@ -0,0 +1,16 @@
1
+ let wasmInitialized = false;
2
+ async function getLerc() {
3
+ // This import is cached by the module loader
4
+ const lerc = await import("lerc");
5
+ if (!wasmInitialized) {
6
+ await lerc.load();
7
+ wasmInitialized = true;
8
+ }
9
+ return lerc;
10
+ }
11
+ export async function decode(bytes) {
12
+ const lerc = await getLerc();
13
+ const result = lerc.decode(bytes);
14
+ return { layout: "band-separate", bands: result.pixels };
15
+ }
16
+ //# sourceMappingURL=lerc.js.map