RasterFlow Overview
SAM3 Notebook
RasterFlow Datasets
RasterFlow Billing
- Estimate each task before you start it, from the area and resolution you chose. The preview mosaic and overviews in this walkthrough are billed separately from the recipe’s mosaic and inference.
- Confirm 30 cm NAIP covers your area in the window you asked for, and get back the years that would work when it does not, before anything is billed.
- Establish how old the imagery is from the scene index, which is the only place the flight date is available.
- Build a preview mosaic and inspect it for gaps and seams before the model runs.
- Run one inference call with several text prompts and read the polygons and confidence scores it returns.
- Set an area floor, persist the detections, and put them on a map, then score roof age against the staleness of the evidence.
Spend your data budget on the properties that need it
Managing a portfolio of 40,000 properties usually means you only have the budget to closely inspect a few hundred. Licensing national roof-condition and hazard datasets charges you for nationwide coverage when you only need three counties, and renews every year. RasterFlow runs on demand instead: describe what you want to find in plain language, define your target area, and receive georeferenced polygons with confidence scores. Estimate the charges for each task from your area of interest before you run it. RasterFlow provides a fast triage layer, low-cost detection you run ahead of the better and more expensive options, helping you pinpoint the exact addresses that justify an expensive full inspection or condition report, instead of having to buy the whole thing.What an inspection run costs
RasterFlow Spatial Units come from pixels, so you can estimate each task before you start it. The recipe in step 7 bills a mosaic and SAM3 inference. This walkthrough also builds a separate preview mosaic and runs Build Multiscales in step 6. The estimates below assume 30 cm NAIP, four-band mosaics, three RGB bands for SAM3 inference, and one time period. Each task carries a 1 RasterFlow Spatial Unit minimum.What one prompt returns
This run has already finished, so there is nothing to run or set up here. The screenshot below comes from a SAM3 run over the whole of Marion County, Oregon: 3,085 km² in one call. The red polygons are the"roofs" prompt.
Two things had to be computed before this map existed, both of them by that finished run:
- The input mosaic. RasterFlow pulled the 30 cm NAIP scenes covering the county over the date window and stitched them into one seamless Zarr mosaic. That is the imagery under the polygons, and it is what SAM3 reads.
- The detections. SAM3 ran over that mosaic and returned a polygon and a confidence score for every object it found. For a county-sized result those polygons are tiled into a single PMTiles layer so the whole county renders in a browser.
predict_mosaic_geometries_recipe() does both steps in one call.

A finished SAM3 run over Marion County, Oregon, viewed in Wherobots Cloud. Every red polygon is a roof SAM3 detected from the prompt 'roofs'. The sublayers on the left are the text prompts submitted in the same call.
Open the Marion County run on the map
Several prompts in one run
That run carried eight text prompts. The output is a single PMTiles layer with one sublayer per prompt, so you toggle whichever question you are asking.
The sublayers of the Marion County output. Each one is a text prompt passed to the same predict_mosaic_geometries_recipe() call.
spatial pixels × bands × time periods; prompt count is not a term in that estimate. See RasterFlow Billing for the calculation, and check Workload History for the units actually billed on your run.
For a property portfolio, one pass answers several underwriting questions:
Before you start
Wherobots requirements
Wherobots requirements
- RasterFlow access in your Organization. RasterFlow is in Public Preview for all paid Organizations.
- A Wherobots notebook, a VS Code Extension workspace, or a Job Run with
rasterflow_remoteavailable. - The Micro runtime is enough. RasterFlow manages its own compute, so a larger runtime adds cost without making the run faster.
Area of interest requirements
Area of interest requirements
- An area of interest in the continental United States, in any format GeoPandas can read (GeoJSON, GeoParquet, or Shapefile) or an in-memory
GeoDataFrame. - 30 cm NAIP coverage for that area and date range. Both SAM3 recipes are fixed to 30 cm NAIP, and not every state has 30 cm in every year. Some states have none. Confirm 30 cm NAIP coverage for your area before you commit to one.
Inspect your own area of interest
Each step below is work you do in a notebook, against your own area of interest. If you have not opened the finished Marion County run yet, do that first: it needs no account or runtime, and it gives you a reference output to compare yours against. The code below is the notebook’s own cells. Everything you would change to point the run somewhere else is in the first cell, and the rest of the notebook reads from it.Start a Micro runtime and open the notebook
examples/Analyzing_Data/RasterFlow_SAM3.ipynb in a Wherobots notebook.Set what you edit in one cell
Point the run at your own area of interest
wkls resolves a city or county name to a boundary, so there is no boundary file to find. The recipe reads the area of interest from storage, so it is written out to your own S3 path first.The screenshots above are from Keizer, Oregon, a city of 18.8 km² inside the Marion County run. Starting there lets you compare your own output against the reference map.wkls. The two estimates below are for the recipe’s mosaic and SAM3 inference only; the separate preview tasks add charges. The last column is the acquisition year that carries complete 30 cm NAIP coverage for that area, so set START and END to it.Confirm 30 cm NAIP coverage for your area
- The area has to be fully contained, not merely overlapped. A set of scenes covering most of your area still fails.
- The filter is on
time, notyear. A flight on 2022-07-14 falls outside a window of 2023-01-01 to 2024-01-01, so an area flown in mid-2022 needs a 2022 window even though the calendar years sit next to each other.
Read how old the imagery is
covering now holds the exact scenes the mosaic will be built from, and their time values are the flight dates. This is the age of the evidence behind every detection you are about to produce, so read it here, before the run.Build a preview mosaic and look at it
build_mosaics() stitches the 30 cm NAIP scenes verified above into one Zarr store. resolution is left at its default, the native resolution of the dataset: 30 cm for NAIP_30CM, which is what SAM3 needs.build_zarr_multiscales() reads that store and writes a second one carrying overview levels and per-band histograms. A mosaic is written at a single native resolution, so without overviews a map has to stream full-resolution pixels for every pan and zoom.- Gaps. A hole means this preview mosaic has no imagery there. Investigate it before inference; the recipe builds a separate mosaic and may have different edges.
- Seams. A tone shift across a straight line is where two flight dates meet.
- The season. NAIP is flown leaf-on, so canopy sitting over a roof here is canopy the model sees too.
- Coarse zoom levels. Blank or washed out means nodata pixels are being averaged into the overviews. Rebuild with an explicit fill value,
rf_client.build_zarr_multiscales(source_store=mosaic_store, nodata=0)for 8-bit NAIP.
Run the inference recipe
Read the detections
geometry: the georeferenced polygon, in EPSG:4326 lon/latlabel: the text prompt that matched, exactly as you wrote itbbox_score: confidence score for the detectiontime: the time coordinate of the mosaic the pixel came from, not the flight date. Read the vintage off the scene dates printed in step 5 instead.source_store: the mosaic the detection came fromlocal_x_offset,local_y_offset,global_x_offset,global_y_offset: the patch the detection was found in
bbox struct (xmin, ymin, xmax, ymax): the GeoParquet covering for each polygon. GeoPandas consumes it as spatial metadata, so it is absent from detections_gdf.columns while sedona.read exposes it as a column.Set area floors and save to the catalog
ST_AreaSpheroid measures on the WGS 84 spheroid and reads coordinates as lon/lat. The recipe returns EPSG:4326, so it can be applied directly. If you ever hand it projected geometries, every area comes back near zero and the filter silently empties your table, so the cell below derives the CRS from the output rather than assuming it.layer column, so a multi-prompt run stays queryable and mappable as one layer set."roads" segments.Map your own detections
layer column becomes one sublayer per prompt, so a multi-prompt run stays togglable on the map, the way the Marion County run is.How to approach output triage
The map from the last step is where triage happens. The rest of this section is what to expect when you zoom in, not more to run.
Roof detections at full zoom in Keizer, Oregon. An attached garage comes through inside the house polygon. At a 0.3 confidence threshold, detached garages and sheds get no polygon at all.
- Primary structures. SAM3 traces the roofline closely, L-shaped and T-shaped plans included. An attached garage lands inside the same polygon as the house.
- Detached garages, sheds, and small outbuildings. Most get no polygon at
confidence_threshold=0.3. To pick them up, lower the threshold and take on the extra false positives, or prompt for them in a separate pass. - Tree canopy. NAIP is flown during the agricultural growing season, so every scene is leaf-on. A roof under mature canopy comes back partial or missing, and the built-in datasets carry no leaf-off alternative.
- A detection is not a building record. RasterFlow does not deduplicate these polygons against a parcel or a policy. One structure can produce two polygons, and the model can return a row of attached houses as one. Match to a footprint layer, then review unmatched and multi-footprint detections before counting buildings: see the Advanced Roof Inspection Guide.
Data freshness
Age drives an important component of a roof’s risk profile. As such, the age of the imagery matters considerably.Caveats and limitations
United States only, at 30 cm
United States only, at 30 cm
No condition or material classification
No condition or material classification
"metal roofs" or "tarped roofs", but SAM3’s accuracy on those categories is not benchmarked. Validate any such prompt against a sample you have ground truth for before it feeds a pricing decision.Confidence threshold is a recall and precision trade
Confidence threshold is a recall and precision trade
confidence_threshold is required and has no safe default. At 0.3, primary structures detect reliably and small accessory structures are missed. Lower it to catch more and expect more false positives: driveway aprons, patios, and pale ground surfaces read as roofs. Tune it on a small area first, and check the result on a map instead of a row count.Prompt wording changes the output
Prompt wording changes the output
"roofs", "roof", and "rooftops" are three different prompts and can return different detections. Fix the wording once you have validated it, and record it alongside the results.Patch size is resized to 1008x1008
Patch size is resized to 1008x1008
patch_size to 1008x1008. You can use it to control how much spatial context the model sees per pass, but a patch larger than 1008x1008 upsamples the imagery instead of adding detail.Public Preview
Public Preview
Augment old data with newer available datasets
If your imagery is from 2023 and you are underwriting in 2026, the roof in the picture is three years older than it looks. Add those years back before you score it. Divide the roof’s age by the expected service life of the covering. That service life depends on the material, roughly 20 years for three-tab asphalt shingle and 25 to 30 for architectural laminate. The number in the query below is a stand-in for whatever your own prediction curve says.Register your own policy records
my_portfolio first. It needs one row per insured property, with policy_id, a geometry to match against the detections, and roof_installed_year:Score the roofs against imagery age
roofs layer.roof_installed_year has to come from your own records: policy data, permit data, or a prior inspection. No open dataset carries it, and RasterFlow does not infer it. Without it, imagery_age_years on its own still works as a staleness flag, telling you which parts of your inspection rest on the oldest evidence.
