|
| 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 | +### Convenient access to overviews |
| 10 | + |
| 11 | +The `.overviews` attribute on `GeoTIFF` contains an array of reduced-resolution overviews in order from highest to lowest resolution. The `Overview` class makes it easy to |
| 12 | + |
| 13 | +### Automatic NoData Mask handling |
| 14 | + |
| 15 | +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. |
| 16 | + |
| 17 | +### Dynamically load compressions as needed |
| 18 | + |
| 19 | +Instead of bundling support for all compressions out of the box, dynamically load the decompressors as required. |
| 20 | + |
| 21 | +Until you try to load an image compressed with, say, [LERC], you don't pay for the bundle size of the dependency. |
| 22 | + |
| 23 | +[LERC]: https://github.com/Esri/lerc |
| 24 | + |
| 25 | +### Transparent caching and chunking |
| 26 | + |
| 27 | +There are a lot of great utilities in [`chunkd`](https://github.com/blacha/chunkd) that work out of the box here. |
| 28 | + |
| 29 | +```ts |
| 30 | +import { SourceCache, SourceChunk } from '@chunkd/middleware'; |
| 31 | +import { SourceView } from '@chunkd/source'; |
| 32 | +import { SourceHttp } from '@chunkd/source-http'; |
| 33 | +import { GeoTIFF } from '@developmentseed/geotiff'; |
| 34 | + |
| 35 | +// 16MB Cache |
| 36 | +const cache = new SourceCache({ size: 16 * 1024 * 1024 }); |
| 37 | + |
| 38 | +// Chunk requests into 16KB fetches |
| 39 | +const chunk = new SourceChunk({ size: 16 * 1024 }); |
| 40 | + |
| 41 | +// Raw source to HTTP file |
| 42 | +const httpSource = new SourceHttp('https://blayne.chard.com/world.webp.google.cog.tiff'); |
| 43 | + |
| 44 | +// HTTP source with chunking and caching |
| 45 | +const tiffSource = new SourceView(httpSource, [chunk, cache]); |
| 46 | +const geotiff = await GeoTIFF.create(tiffSource); |
| 47 | +const tile = await geotiff.fetchTile(0, 0); |
| 48 | +``` |
| 49 | + |
| 50 | +### Nearly-identical API to Python `async-geotiff` |
| 51 | + |
| 52 | +The TypeScript API is nearly identical to our Python project [`async-geotiff`]. |
| 53 | + |
| 54 | +This is extremely useful for us as we build visualization projects for both Python and the browser. |
| 55 | + |
| 56 | +[`async-geotiff`]: https://github.com/developmentseed/async-geotiff |
| 57 | + |
| 58 | +## Why not build on top of geotiff.js? |
| 59 | + |
| 60 | +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]. |
| 61 | + |
| 62 | +[geotiff.js]: https://geotiffjs.github.io/ |
| 63 | + |
| 64 | +- **Fully typed**: `@cogeotiff/core` is fully typed in expressive TypeScript, making it much more enjoyable to build on top of. Even the low-level |
| 65 | +- `@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. |
| 66 | +- **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. |
| 67 | +- **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. |
| 68 | +- **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. |
| 69 | +- **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. |
| 70 | +- **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. |
| 71 | + |
| 72 | +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. |
0 commit comments