Skip to content

Commit ac971fa

Browse files
authored
docs(geotiff): Write initial readme for @developmentseed/geotiff (#230)
1 parent 3dc6281 commit ac971fa

File tree

3 files changed

+75
-3
lines changed

3 files changed

+75
-3
lines changed

packages/geotiff/README.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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.

packages/geotiff/src/geotiff.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { index, xy } from "./transform.js";
1414
* Separates data IFDs from mask IFDs, pairs them by resolution level,
1515
* and exposes sorted overviews. Mirrors the Python async-geotiff API.
1616
*
17-
* Construct via `GeoTIFF.open(source)` or `GeoTIFF.fromTiff(tiff)`.
17+
* Construct via `GeoTIFF.create(source)` or `GeoTIFF.fromTiff(tiff)`.
1818
*/
1919
export class GeoTIFF {
2020
/**
@@ -59,7 +59,7 @@ export class GeoTIFF {
5959
*
6060
* This creates and initialises the underlying Tiff, then classifies IFDs.
6161
*/
62-
static async open(source: Source): Promise<GeoTIFF> {
62+
static async create(source: Source): Promise<GeoTIFF> {
6363
const tiff = await Tiff.create(source);
6464
return GeoTIFF.fromTiff(tiff);
6565
}

packages/geotiff/tests/helpers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export async function loadGeoTIFF(
3030
): Promise<GeoTIFF> {
3131
const path = fixturePath(name, variant);
3232
const source = new SourceFile(path);
33-
return GeoTIFF.open(source);
33+
return GeoTIFF.create(source);
3434
}
3535

3636
// ── Mock helpers ────────────────────────────────────────────────────────

0 commit comments

Comments
 (0)