> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wherobots.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Build Multiscales with RasterFlow

> Explore large RasterFlow mosaics and model outputs on a map at any zoom level: build_zarr_multiscales adds the overview levels a map client needs to render them responsively.

<Badge color="purple">Public Preview</Badge>

<br />

<br />

<CardGroup cols={2}>
  <Card title="RasterFlow Overview" icon="file-lines" href="/develop/rasterflow/">
    Learn about RasterFlow's key features and capabilities
  </Card>

  <Card title="Reference" icon="code" href="/reference/rasterflow/client#build_zarr_multiscales">
    Browse the `build_zarr_multiscales` API documentation
  </Card>

  <Card title="RasterFlow Datasets" icon="satellite" href="/develop/rasterflow/rasterflow-datasets">
    Learn about built-in datasets and how to bring your own.
  </Card>

  <Card title="Run as a Job" icon="bolt" href="/develop/rasterflow/rasterflow-jobs">
    Submit RasterFlow workflows as automated Job Runs.
  </Card>
</CardGroup>

RasterFlow writes its mosaics and model outputs as Zarr stores at a single, native resolution. That is the right format for analysis and inference, but it is a poor fit for interactive viewing: every pan or zoom has to stream full-resolution pixels, even when the whole scene is on screen at once.

`build_zarr_multiscales` takes an existing Zarr store and writes a new, *visualization-ready* store that adds downsampled overview levels — an image pyramid — plus histogram statistics. A map client can then stream coarse tiles when you are zoomed out and full-resolution pixels when you zoom in.

## Benefits

<AccordionGroup>
  <Accordion title="Interactive visualization" icon="map">
    Overview levels let the Wherobots Cloud map viewer and the `wherobots_gl.Map()` widget render large mosaics responsively instead of pulling native-resolution pixels at every zoom level.
  </Accordion>

  <Accordion title="Histogram statistics" icon="chart-simple">
    The workflow computes value distributions alongside the pyramid, so display tooling can pick sensible default color limits per band rather than guessing at a stretch.
  </Accordion>

  <Accordion title="Works on any Zarr store" icon="layer-group">
    The same call accepts mosaics from `build_mosaics` and `build_gti_mosaics`, model outputs from `predict_mosaic` and its recipe variants, and change-detection outputs from `run_mosaics_change`. It also accepts a georeferenced Zarr store you bring yourself, so you can make your own imagery visualization-ready without running it through RasterFlow first.
  </Accordion>

  <Accordion title="Non-destructive" icon="copy">
    The source store is read, never modified. The pyramid is written to a new, visualization-ready store, so your analysis-ready output stays exactly as RasterFlow produced it.
  </Accordion>
</AccordionGroup>

## Before you start

<AccordionGroup cols={2}>
  <Accordion title="Wherobots requirements" icon="cloud">
    * Access to RasterFlow in your Organization.
    * A Wherobots notebook, a [VS Code Extension](/develop/vscode-extension/notebooks) workspace, or a [Job Run](/develop/rasterflow/rasterflow-jobs) with `rasterflow_remote` available.
    * The **Micro** [runtime](/develop/runtimes/) is sufficient. RasterFlow manages its own compute, so runtime size does not affect how fast the multiscale build runs.
  </Accordion>

  <Accordion title="Input requirements" icon="database">
    * A Zarr store URI that RasterFlow can read. This can be an `s3://` path produced by an earlier RasterFlow workflow, or a georeferenced Zarr store of your own.
    * Write access to the destination bucket.
  </Accordion>
</AccordionGroup>

## Build a multiscale Zarr store

<Steps>
  <Step title="Get the URI of the source store">
    Mosaic and inference workflows return a `MosaicResult`. Its `first_row_mosaic` attribute is the URI of the store for the first mosaic location, which is what you want when your Area of Interest has a single geometry.

    ```python theme={"system"}
    from datetime import datetime

    from rasterflow_remote import RasterflowClient
    from rasterflow_remote.data_models import DatasetEnum

    rf_client = RasterflowClient()

    mosaic_index = rf_client.build_mosaics(
        datasets=[DatasetEnum.NAIP_30CM],
        aoi=aoi_uri,
        start=datetime(2023, 1, 1),
        end=datetime(2024, 1, 1),
        target_crs=3857,
    )

    mosaic_store = mosaic_index.first_row_mosaic
    ```

    <Note>
      If your AOI produces more than one mosaic location, read `mosaic_index.mosaic_index_gdf` to see every output store and call `build_zarr_multiscales` once per store you want to visualize.
    </Note>
  </Step>

  <Step title="Build the visualization-ready store">
    ```python theme={"system"}
    optimized_store = rf_client.build_zarr_multiscales(source_store=mosaic_store)

    print(f"Optimized store: {optimized_store.uri}")
    ```

    The method returns a [`UriOutput`](/reference/rasterflow/data-models#urioutput). Its `uri` attribute points at the new multiscale Zarr store.
  </Step>

  <Step title="Open the result on the map">
    `UriOutput` also exposes a `map_url` attribute that links directly to the store in the Wherobots Cloud [map viewer](https://cloud.wherobots.com/map). It is `None` when no viewer URL is available for the store.

    ```python theme={"system"}
    print(f"View on the map: {optimized_store.map_url}")
    ```

    Opening that URL should show your imagery redrawing smoothly as you zoom, rather than stalling on full-resolution reads.
  </Step>
</Steps>

## Build a visualization-ready store from a model output

Inference and change detection outputs are Zarr stores too, so the workflow is identical; build the pyramid on the prediction store.

```python theme={"system"}
from rasterflow_remote.data_models import ModelRecipes

model_output_index = rf_client.predict_mosaic_recipe(
    aoi=aoi_uri,
    start=datetime(2023, 1, 1),
    end=datetime(2024, 1, 1),
    model_recipe=ModelRecipes.FTW,
    target_crs="EPSG:3857",
)

model_output_store = model_output_index.first_row_mosaic
optimized_store = rf_client.build_zarr_multiscales(source_store=model_output_store)
```

<Tip>
  Build multiscales on the prediction store *before* you [vectorize](/reference/rasterflow/client#vectorize_mosaic) it. Being able to see the raw scores on a map makes it much easier to pick a sensible `threshold` for vectorization.
</Tip>

## Control the fill value

Overview levels have to know which pixels carry no data, otherwise nodata pixels get averaged into the downsampled levels and the result looks washed out or blank at coarse zooms.

By default, `build_zarr_multiscales` uses the source array's `fill_value`. When the source store does not declare one, it falls back to `NaN` for floating-point types and `0` for non-floating types. Set `nodata` explicitly when the source store's fill value is missing or wrong:

```python theme={"system"}
optimized_store = rf_client.build_zarr_multiscales(
    source_store=mosaic_store,
    nodata=0,
)
```

## Control histogram statistics

Use `histogram_dims` to choose the dimensions that get their own histogram. Passing `["band"]` produces one histogram per band, which is what you want for multi-band imagery where each band has a different value range:

```python theme={"system"}
optimized_store = rf_client.build_zarr_multiscales(
    source_store=mosaic_store,
    histogram_dims=["band"],
)
```

Leaving `histogram_dims` as `None` auto-detects: the workflow uses `["band"]` if the store has a `band` dimension, and otherwise computes a single global histogram. The default is the right choice for most RasterFlow outputs.

## Route the output to a specific bucket

Pass `bucket` to write the visualization-ready store under an S3 URI prefix other than your configured Wherobots Managed Storage bucket:

```python theme={"system"}
optimized_store = rf_client.build_zarr_multiscales(
    source_store=mosaic_store,
    bucket="s3://my-bucket/rasterflow-outputs/",
)
```

For the storage destinations available to RasterFlow and how access to your own bucket is authenticated, see [Use RasterFlow with Storage Integration](/develop/rasterflow/rasterflow-storage-integration).

## Troubleshooting

<AccordionGroup>
  <Accordion title="WorkflowExecutionError" icon="circle-question">
    **Potential Cause:** The workflow failed to start. Common reasons are a `source_store` URI that RasterFlow cannot read or a destination `bucket` it cannot write to.

    **Solution:**

    * Verify the `source_store` URI, including the `.zarr` suffix, by printing it before the call.
    * Confirm your [storage integration](/develop/storage-management/s3-storage-integration) grants read access to the source and write access to the destination.
  </Accordion>

  <Accordion title="Coarse zoom levels look blank or washed out" icon="circle-question">
    **Potential Cause:** Nodata pixels are being averaged into the overview levels because the source store's fill value is missing or does not match the data.

    **Solution:**

    * Pass `nodata` explicitly to match the value your imagery actually uses for no-data pixels.
    * Rebuild the multiscale store after changing `nodata`; overview levels are computed at build time, not at read time.
  </Accordion>

  <Accordion title="The map viewer still feels slow" icon="circle-question">
    **Potential Cause:** You are viewing the source store rather than the visualization-ready one. The two URIs are different — building multiscales does not modify the source.

    **Solution:**

    * Open `optimized_store.uri` or `optimized_store.map_url`, not the URI you passed as `source_store`.
  </Accordion>
</AccordionGroup>

## Limitations

<AccordionGroup>
  <Accordion title="Zarr input only">
    `build_zarr_multiscales` reads Zarr stores. It does not build overviews for GeoTIFF or COG inputs; mosaic those into a Zarr store first with [`build_gti_mosaics`](/reference/rasterflow/client#build_gti_mosaics).
  </Accordion>

  <Accordion title="Writes a new store rather than updating in place">
    The workflow always writes a new store.
  </Accordion>

  <Accordion title="One store per call">
    The method takes a single `source_store`. To build visualization-ready stores for a multi-location mosaic index, iterate over the stores in `mosaic_index_gdf` and call the method for each one.
  </Accordion>

  <Accordion title="Inference reads one array, not the group">
    A visualization-ready store is a group of arrays, one per overview level. Run inference on the source store, or point at a level within the group: `s3://path/to/store.zarr/0` is the full-resolution level.
  </Accordion>

  <Accordion title="The resampling method is fixed">
    `build_zarr_multiscales` downsamples each overview level with a fixed resampling method and takes no `resampling` parameter. This is separate from [`build_mosaics`](/reference/rasterflow/client#build_mosaics), which does let you set `resampling` when the mosaic itself is built. If you need a different method for overview levels, contact [support@wherobots.com](mailto:support@wherobots.com).
  </Accordion>
</AccordionGroup>

## Usage and best practices

<Tabs>
  <Tab title="Do">
    * **Build multiscales for large stores:** A native-resolution store still opens on the map, but the viewer renders it only once you zoom in far enough. Small stores are fine to view directly; large ones stay blank at coarse zoom until you build overview levels.
    * **Build only what you intend to look at:** Build multiscales on the specific mosaic or prediction store you want on a map, not on every intermediate output.
    * **Set `nodata` when you know it:** An explicit fill value avoids nodata bleeding into coarse overview levels.
    * **Use the Micro runtime:** RasterFlow manages its own compute, so a larger runtime adds cost without speeding up the build.
  </Tab>

  <Tab title="Don't">
    * **Don't expect the source store to change:** The source is left untouched; downstream steps must reference the returned URI to see the pyramid.
    * **Don't rebuild on every run:** If the source store has not changed, reuse the visualization-ready store you already built.
  </Tab>
</Tabs>

## Next steps

<CardGroup cols={3}>
  <Card title="Building NAIP mosaics" icon="satellite" href="/tutorials/example-notebooks/rasterflow-naip-mosaic">
    Walk through a complete mosaic build that ends in a visualization-ready store on the map.
  </Card>

  <Card title="Run RasterFlow as a Job" icon="bolt" href="/develop/rasterflow/rasterflow-jobs">
    Add the multiscale build to an automated, production-scale job script.
  </Card>

  <Card title="Client API reference" icon="code" href="/reference/rasterflow/client#build_zarr_multiscales">
    See the full signature, parameters, and return type for `build_zarr_multiscales`.
  </Card>
</CardGroup>

## API reference

For detailed API documentation, see:

* [Client API Reference](/reference/rasterflow/client) - `RasterflowClient` methods
* [Data Models Reference](/reference/rasterflow/data-models) - Enums and configuration objects
* [Exceptions Reference](/reference/rasterflow/exceptions) - Error handling
