Skip to content

Docs add async geotiff tutorial#528

Open
aboydnw wants to merge 4 commits into
microsoft:developfrom
aboydnw:docs-add-async-geotiff-tutorial
Open

Docs add async geotiff tutorial#528
aboydnw wants to merge 4 commits into
microsoft:developfrom
aboydnw:docs-add-async-geotiff-tutorial

Conversation

@aboydnw
Copy link
Copy Markdown
Contributor

@aboydnw aboydnw commented Jun 2, 2026

Based on the outline here

Related notebook: microsoft/PlanetaryComputerExamples#315

aboydnw and others added 4 commits May 30, 2026 00:12
Adds a tutorial walking through pixel-level Cloud Optimized GeoTIFF
reads with async-geotiff — no GDAL dependency, async-first, Rust core.
Companion notebook lives in PlanetaryComputerExamples at
quickstarts/async-geotiff.ipynb and is wired in via
external_docs_config.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nshots

Replace nonexistent APIs (obstore async_=True, GeoTIFF.open(full_href),
generate_tms, lonboard RasterLayer.from_stac) with the shipping
equivalents: from_asset(asset) + empty open() path, the Sentinel-2 visual
RGB asset, and a BitmapTileLayer fed by the Planetary Computer tiler.
Add the window-preview and Lonboard-scene screenshots.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@aboydnw aboydnw marked this pull request as ready for review June 2, 2026 20:34
Copy link
Copy Markdown

@kylebarron kylebarron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! This is a good start!

@@ -0,0 +1,164 @@
# Reading Planetary Computer COGs with async-geotiff

[async-geotiff](https://github.com/developmentseed/async-geotiff) is a Python Cloud Optimized GeoTIFF reader with no GDAL dependency. The core is Rust, image decoding runs in a thread pool, buffers are zero-copy, and every API is fully type-hinted. Use it when you want async I/O for pixel-level analysis without putting GDAL on the system.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
[async-geotiff](https://github.com/developmentseed/async-geotiff) is a Python Cloud Optimized GeoTIFF reader with no GDAL dependency. The core is Rust, image decoding runs in a thread pool, buffers are zero-copy, and every API is fully type-hinted. Use it when you want async I/O for pixel-level analysis without putting GDAL on the system.
[async-geotiff](https://github.com/developmentseed/async-geotiff) is a Python [Cloud Optimized GeoTIFF](https://cogeo.org/) reader with no GDAL dependency. The core is Rust, image decoding runs in a thread pool, buffers are zero-copy, and every API is fully type-hinted. Use it when you want async I/O for pixel-level analysis without putting GDAL on the system.

uv add async-geotiff obstore planetary-computer pystac-client lonboard matplotlib
```

`async-geotiff` is the user-facing library. `async-tiff` is the lower-level Rust core. Use it directly only if you're building library infrastructure on top.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
`async-geotiff` is the user-facing library. `async-tiff` is the lower-level Rust core. Use it directly only if you're building library infrastructure on top.
`async-geotiff` is the high-level library for reading GeoTIFF and COG files. `async-tiff` is the lower-level Rust core for generically reading TIFF files. It shouldn't be necessary to touch for most users.

print(geotiff.overviews) # finest → coarsest
```

The header read is a single range request. This is the same pattern used by [obstore.](./obstore.md)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The header read is a single range request. This is the same pattern used by [obstore.](./obstore.md)
The header read usually fits in one or two range requests, facilitated by [obstore](./obstore.md).


## Pick an overview

`geotiff.overviews` is ordered finest-to-coarsest. Index `0` is the full-resolution image. A coarser overview is the right choice for previews or zoomed-out work:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the geotiff itself is the full-resolution image. Index 0 of the overviews is the finest resolution after the full-resolution data.

array = await full_res.read(window=window)
```

The returned `Array` has:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import numpy as np
import matplotlib.pyplot as plt

plt.imshow(np.transpose(array.data, (1, 2, 0)))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case it's clearer, this is shipped as reshape_as_image so that users don't have to remember the band ordering (1, 2, 0) is easy to forget or get wrong IMO


## Visualize the scene with Lonboard

For an interactive map view of the same Sentinel-2 item, stream its COG tiles through the Planetary Computer tiler into a [Lonboard](https://developmentseed.org/lonboard/) `BitmapTileLayer`:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a note, the BitmapTileLayer uses titiler on the backend to send formatted PNG tiles, this doesn't read the COG data directly. That's fine if you'd like to point out how this integrates with the planetary computer titiler server!

In case you want an example of how to read the COG data directly through async-geotiff, you'd want to use RasterLayer.from_geotiff which natively integrates with async-geotiff.

Comment on lines +141 to +155
Each `read()` is independent. Fire many at once with `asyncio.gather`

async-geotiff issues range requests in parallel and decodes them on the Rust thread pool:

```python
import asyncio

windows = [
Window(c, r, 256, 256)
for c in range(0, 2048, 256) for r in range(0, 2048, 256)
]
arrays = await asyncio.gather(
*[full_res.read(window=w) for w in windows]
)
```
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to #527 (comment), this isn't an ideal example to suggest to people, because we're instructing them to make entirely independent requests for each window of the file.

But this is something that read handles automatically, and it'll be faster because it can minimize the total number of requests that need to be made.

So here, a single

window = Window(0, 0, 2048, 2048)
full_res.read(window=window)

would be a lot better than making many independent window reads


## When to use something else

- For resampling, reprojection, or warping, hand the array to [rasterio](https://rasterio.readthedocs.io/).
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- For resampling, reprojection, or warping, hand the array to [rasterio](https://rasterio.readthedocs.io/).
- For resampling, reprojection, or warping, use [rasterio](https://rasterio.readthedocs.io/), either alone or in combination with async-geotiff.

- For resampling, reprojection, or warping, hand the array to [rasterio](https://rasterio.readthedocs.io/).
- For interactive visualization, see [Lonboard](https://developmentseed.org/lonboard/).
- For the raw-bytes layer beneath async-geotiff, see [obstore](https://developmentseed.org/obstore/).
- For library authors building on the Rust core, drop to [async-tiff](https://github.com/developmentseed/async-tiff).
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Python library authors who want to build on top of GeoTIFF should still be using async-geotiff, not async-tiff. It's really only people who want generic TIFF support, and don't want to specialize their code to support only GeoTIFF, who should be using async-tiff.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants