# Compositing

A composite combines many burst products into one image per grid square. [ResolutionWeighter](../reference/resolution_weighter.ResolutionWeighter.md#s1bursts.resolution_weighter.ResolutionWeighter) writes composites on the 10 km squares of the Military Grid Reference System (MGRS), so results from different bursts, orbits and dates line up pixel for pixel.


# Get the grid squares

[MGRSGrid](../reference/MGRSGrid.md#s1bursts.MGRSGrid) lists the grid squares that overlap an area. Its `resolution` is the pixel size of the composite and must equal the `grid_spacing` the products were processed with:

``` python
from shapely.geometry import box
from s1bursts import MGRSGrid

aoi = box(7.03, 46.06, 7.12, 46.14)
squares = list(MGRSGrid(aoi, resolution=20))
```

Each square is an [MGRSGeoBox](../reference/MGRSGeoBox.md#s1bursts.MGRSGeoBox), which knows its MGRS code, its pixel grid and its UTM zone, `square.crs`. Both classes come from [freezebase](https://lqgentner.github.io/freezebase/), whose guide [The MGRS grid](https://lqgentner.github.io/freezebase/user-guide/mgrs-grid.html) explains the grid and why it suits composites.

The products must be in the zone of the square: process them with `crs=square.crs` and keep the default `align_to_standard_grid=True`. A product on a different grid is rejected.

> **Warning: Do not process products for a composite with `crs="auto"`**
>
> `"auto"` takes each burst's own zone, and a burst whose center lies in the neighboring zone would be projected there, not in the zone of the square.

[From an area to composites](area-to-composites.md) shows the whole loop from an area to the composites of its squares.


# Compose with local resolution weighting

[compose_rtc_lrw()](../reference/resolution_weighter.ResolutionWeighter.md#s1bursts.resolution_weighter.ResolutionWeighter.compose_rtc_lrw) for backscatter and [compose_coherence_lrw()](../reference/resolution_weighter.ResolutionWeighter.md#s1bursts.resolution_weighter.ResolutionWeighter.compose_coherence_lrw) for coherence combine all products that cover a square, whatever their orbit or direction. Each pixel is weighted toward the acquisitions with the highest spatial resolution at that pixel. This is called local resolution weighting (LRW). [Local resolution weighting](resolution-weighting.md) explains how it works.

``` python
from s1bursts import ResolutionWeighter, TimeRange

weighter = ResolutionWeighter(fm)
window = TimeRange("2025-08-01", "2025-08-31")
for square in squares:
    composite = weighter.compose_rtc_lrw(
        rtc.products, square, write_qa=True, time_range=window,
    )
```

The method returns the composite, a record whose file `fm.get_product_path(composite)` holds the backscatter. Pass the [TimeRange](../reference/time_range.TimeRange.md#s1bursts.time_range.TimeRange) of your search as `time_range`, so that every square records the same compositing window.

The weights come from the RTC products, so [compose_coherence_lrw()](../reference/resolution_weighter.ResolutionWeighter.md#s1bursts.resolution_weighter.ResolutionWeighter.compose_coherence_lrw) takes the RTC products of the same dates as well:

``` python
coherence_composite = weighter.compose_coherence_lrw(
    coherence.products, rtc.products, square, write_qa=True, time_range=window,
)
```


# Quality layers

With `write_qa=True`, two quality layers are written beside an LRW composite:

- The number of observations (`QA-NUM`): how many acquisitions contributed to each pixel.
- The composite quality map (`QA-CQM`): how fine the combined detail of each pixel is.

The composite lists them in its `layers`, and `fm.open()` reads each layer's file:

``` python
from s1bursts.product import Layer

with fm.open(composite, Layer.QA_CQM) as src:
    cqm = src.read(1)
```


# Composites per pass direction

The pass composites keep ascending and descending orbits apart:

| Method | Combines | Use it for |
|----|----|----|
| [compose_rtc_by_pass()](../reference/resolution_weighter.ResolutionWeighter.md#s1bursts.resolution_weighter.ResolutionWeighter.compose_rtc_by_pass) | RTC | Backscatter by viewing direction |
| [compose_gtc_by_pass()](../reference/resolution_weighter.ResolutionWeighter.md#s1bursts.resolution_weighter.ResolutionWeighter.compose_gtc_by_pass) | GTC | Backscatter without terrain flattening |
| [compose_coherence_by_pass()](../reference/resolution_weighter.ResolutionWeighter.md#s1bursts.resolution_weighter.ResolutionWeighter.compose_coherence_by_pass) | Coherence | Coherence by viewing direction |

They return a dictionary of composites keyed by pass direction, `"Ascending"` and `"Descending"`. They take a plain average without resolution weights, and their only quality layer is the number of observations.


# Choose how to combine

Every method uses `aggregation="mean"` by default. Pass `aggregation="median"`, or a number between 0 and 1 for a quantile, to reduce the influence of outliers.

Composites are written in linear units, like the products they combine. Convert backscatter to decibels after reading it, as `10 * np.log10(gamma0)`, so that the averaging has already happened on linear values.

All inputs of one call must share a polarization, the processing settings and the SNAP version, the inputs of both pass directions included, and coherence inputs must share a temporal baseline. A pass composite that already exists must match the settings and SNAP version of the direction being added. A SNAP version that could not be determined matches any. The RTC products that weight a coherence composite must share their settings among themselves, and the DEM and SNAP version with the coherence products. The composite records the settings of the coherence products only.

Like processing, compositing skips a square whose files already exist, after checking that they were made on the same grid and with the same settings. Pass `overwrite=True` to replace composites made with other settings.
