Skip to content

This is the multi-page printable view of this section. .

Return to the regular view of this page.

Get started

Install dependencies, run the example server, and render a tile through the library.

Follow the shortest path from a fresh checkout to a visible map tile.

1 - Requirements

Rust, system libraries, and optional documentation tooling.

Library and examples

  • Rust 1.92 or newer, matching Cargo.toml.
  • The default feature set requires system libproj 9.x.
  • Git is required when working from the repository.

On macOS:

brew install proj

On Debian or Ubuntu:

sudo apt install libproj-dev libsqlite3-dev libtiff-dev clang

On other platforms, install libproj with the platform’s supported package manager or build without the default features. The project does not currently provide platform-specific Windows installation commands.

If you only need same-CRS tiles or the built-in EPSG:4326 ↔ EPSG:3857 fast path, build without the default features:

cargo build --no-default-features

Documentation site

The OINK site under website/ requires Go 1.27 or newer and Hugo Extended 0.165.0 or newer. Hugo must report extended in its version string.

go version
hugo version

The library itself does not depend on Hugo or Go.

2 - Quick start

Run the example server and request your first rendered tile.

Clone and run

git clone https://github.com/mapseekai/async-geotiff-rs.git
cd async-geotiff-rs
COG_URL='https://raw.githubusercontent.com/cogeotiff/rio-tiler/0b08b7f35a8b639cee2f35a0cb565034f7b55bfd/tests/fixtures/cog.tif'
cargo run --release --example serve -- "$COG_URL"

The pinned 800 KiB rio-tiler fixture is a public, range-capable COG intended for tests and first-run verification. Replace it with your own COG after the smoke test. The server listens on 127.0.0.1:8080 by default and prints the available viewer and tile URLs when startup completes.

Open one of the included viewers:

The viewer automatically centers on the opened dataset and is the most reliable visual smoke test. You can also request the world tile directly:

curl --fail --output tile.png \
  'http://127.0.0.1:8080/tiles/WebMercatorQuad/0/0/0.png'

Success produces a PNG file that you can open in any image viewer. A valid tile coordinate outside the source extent returns HTTP 204 instead of a synthetic blank image; if that happens, use one of the included viewers to request an in-bounds tile at the dataset’s calculated center and zoom.

Browse local files

Run the server without a URL to discover data/*.tif and data/*.tiff files:

mkdir -p data
cp /path/to/example.tif data/
cargo run --release --example serve

The viewers then display a dataset switcher.

Optional formats

PNG is always available. Enable lossless WebP explicitly:

cargo run --release --features webp --example serve -- \
  https://example.com/cog.tif

The /formats endpoint reports only the encoders compiled into the running binary.

3 - Library usage

Open a COG and render a tile directly from Rust.

Add the crates.io dependency to your application:

cargo new async-geotiff-demo
cd async-geotiff-demo
[dependencies]
async-geotiff = "0.13.0"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }

To avoid the system libproj dependency while retaining the mosaic APIs used by the example server, set default-features = false, features = ["mosaic"] on the dependency. General CRS transformations will then be unavailable. For local development, replace the version with path = "../async-geotiff-rs", adjusting the path to your checkout.

Render one tile

use std::sync::Arc;

use async_geotiff::io::cog::CogReader;
use async_geotiff::style::TileStyle;
use async_geotiff::tiler::Tiler;
use async_geotiff::tms::{TileCoord, TileMatrixSet};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let url = "https://raw.githubusercontent.com/cogeotiff/rio-tiler/0b08b7f35a8b639cee2f35a0cb565034f7b55bfd/tests/fixtures/cog.tif";
    let reader = Arc::new(
        CogReader::builder()
            .source_id(url)
            .open_http(url)
            .await?,
    );
    let tiler = Tiler::new(reader);
    let style = TileStyle::from_raster_query("version=3.0&type=pseudocolor&bidx=1&colormap_name=viridis&method=linear&rescale=0,3000")?;
    let coord = TileCoord {
        tms: TileMatrixSet::WebMercatorQuad,
        z: 0,
        x: 0,
        y: 0,
    };

    if let Some(bytes) = tiler.tile(coord, &style).await? {
        std::fs::write("tile.png", bytes)?;
    } else {
        eprintln!("tile is outside the dataset or projection domain");
    }
    Ok(())
}

TileStyle::from_raster_query accepts the same query string used by the example HTTP server, without the leading ?. Save the example as src/main.rs, then run cargo run. The pinned URL is a small, range-capable test COG; replace it after verifying the integration.

Point queries and geographic bounds

CogReader::point(x, y, &coord_crs, bidx) returns a nearest-pixel value without rendering. Coordinates use coord_crs; a point outside the image returns None. Band indices are one-based and refer to the bands returned by read_window. An empty selection returns all of those bands in their existing order.

When no independent TIFF mask is present, the first Alpha sample is promoted to the validity mask and removed from the returned bands. Thus ordinary RGBA data exposes RGB to point queries. With an independent mask, the reader keeps its native bands and uses that mask instead. A masked pixel returns Some(PointValue { valid: false, .. }), rather than a band-selection error. Raster Style’s separate restriction on Alpha sources still applies to rendering.

wgs84_bounds() gives the geographic envelope used for dataset statistics. When a projected footprint contains a pole, the envelope includes that pole and spans [-180, 180] longitude. An envelope can include space outside the actual image; query pixels or use their validity mask to determine coverage.

The low-level io::stats::Histogram::quantile API accepts public u64 bin counts and accumulates them without u64 overflow, including directly constructed histograms. The 100th percentile is the upper edge of the last nonempty bin, excluding trailing empty bins even when their preceding counts are very large. These embedded histograms are not the statistics used by Raster Style.

Share memory and CPU budgets

For multiple COGs, create one block cache and give every reader a stable, distinct source ID:

use std::sync::Arc;

use async_geotiff::io::block_cache::BlockCache;
use async_geotiff::io::cache::CacheConfig;
use async_geotiff::io::cog::CogReader;

let cache = Arc::new(BlockCache::new(CacheConfig::default()));

let first = CogReader::builder()
    .source_id("scene-a")
    .block_cache(Arc::clone(&cache))
    .open_http("https://example.com/a.tif")
    .await?;

let second = CogReader::builder()
    .source_id("scene-b")
    .block_cache(Arc::clone(&cache))
    .open_http("https://example.com/b.tif")
    .await?;

Readers with the same cache share its default 512 MiB budget. The source ID is part of the cache key: reuse it only for the same logical COG.