# Refactor: extract shared TileGridStitcher for region+route image generation

**Task**: AZ-367_refactor_extract_tile_grid_stitcher
**Name**: Shared image stitcher with overlay primitives
**Description**: Extract `TileGridStitcher` (+ `DrawCross` and `DrawRectangleBorder` overlay primitives) from `RegionService` and `RouteProcessingService`.
**Complexity**: 3 points
**Dependencies**: AZ-364 (C11 — route-side caller is restructured at the same time)
**Component**: Common (or new Imaging project) + Services.RegionProcessing + Services.RouteManagement
**Tracker**: AZ-367
**Epic**: AZ-350

## Problem

`SatelliteProvider.Services.RegionProcessing/RegionService.cs:240-321` and `SatelliteProvider.Services.RouteManagement/RouteProcessingService.cs:453-570` both implement "place tiles in a grid by (TileX, TileY) and overlay markers". Basic placement loop, min/max calculation, and `Image.LoadAsync<Rgb24>` per tile are duplicated. Differences are only the overlays (region: red cross at center; route: yellow geofence rectangles + red crosses at route points).

## Outcome

- One `TileGridStitcher` class in `Common` (or a new `SatelliteProvider.Imaging` project).
- Region and route image generation paths both use the stitcher.
- Output images are pixel-for-pixel identical for existing test scenarios.
- 37 unit + 5 smoke tests stay green.

## Scope

### Included
- Add `TileGridStitcher` with `Task<Image<Rgb24>> StitchAsync(IEnumerable<TilePlacement> tiles, CancellationToken ct)`.
- Add overlay primitives: `DrawCross(Image, Point, Color, ArmLength)` and `DrawRectangleBorder(Image, Rect, Color, Thickness)` exposed as instance methods.
- Replace the duplicate stitcher logic in `RegionService` and (post-C11) the `RouteImageRenderer` collaborator.
- Add unit tests for the stitcher with synthetic tiles.

### Excluded
- Changing the SixLabors.ImageSharp version.
- Adding new overlay shapes beyond cross + rectangle border.

## Acceptance Criteria

**AC-1: Single stitcher used by both consumers**
Given the post-refactor source
When grepped for the per-tile placement loop pattern
Then matches are confined to `TileGridStitcher`.

**AC-2: Pixel-identical outputs**
Given the existing region and route smoke-test scenarios
When the post-refactor code runs
Then the stitched output images are pixel-for-pixel identical to the pre-refactor outputs.

**AC-3: Tests stay green**
Given the post-refactor build
When `scripts/run-tests.sh --smoke` runs
Then all 37 unit + 5 smoke scenarios pass.

## Constraints

- ImageSharp 3.1.11 dependency preserved.
- Output image format (PNG/JPG) unchanged.

## Risks & Mitigation

**Risk 1: subtle pixel diff after extraction**
- *Risk*: refactoring the placement loop may change rounding / interpolation behavior.
- *Mitigation*: keep the original arithmetic exactly; rely on the integration tests' image comparison as a guard.

Full change entry: `_docs/04_refactoring/03-code-quality-refactoring/list-of-changes.md` (C14).
